@schemavaults/openapi-operations 0.1.9 → 0.3.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 CHANGED
@@ -80,6 +80,27 @@ export const health = defineOperation({
80
80
  `defineOperationGroup({ pathPrefix, tags, operations })` applies a shared prefix and tags.
81
81
  Throw `new OperationError(status, { error, message })` from a handler to short-circuit.
82
82
 
83
+ Schemas built by OTHER packages (e.g. `@schemavaults/auth-common`) may have been constructed
84
+ before this package installed the `.openapi()` extension (zod v4 copies prototype methods onto
85
+ each instance at construction time), in which case `schema.openapi(...)` throws at module load
86
+ depending on import order. Use `withOpenApi(schema, refId?, metadata)` for them:
87
+
88
+ ```ts
89
+ import { withOpenApi } from "@schemavaults/openapi-operations";
90
+ export const Organization = withOpenApi(organizationDefinitionSchema, "Organization", { description: "..." });
91
+ ```
92
+
93
+ Request bodies take two extra flags:
94
+
95
+ - `lenientContentType: true` also parses bodies labelled `text/plain` (or carrying no
96
+ `Content-Type` at all) as the declared media type. Browsers send
97
+ `text/plain;charset=UTF-8` for `fetch(url, { body: JSON.stringify(x) })` without an
98
+ explicit header, so JSON endpoints with such callers need it.
99
+ - `documentOnly: true` describes the body in the OpenAPI document but leaves the
100
+ request untouched: `ctx.body` is `undefined` and the handler reads `ctx.request`
101
+ itself. For endpoints whose parsing / error format is mandated by a protocol (the
102
+ OAuth 2.0 token endpoint's `{ error, error_description }`, ...).
103
+
83
104
  ### Auth schemes
84
105
 
85
106
  An `AuthSchemeDefinition` is a named OpenAPI security scheme plus docs metadata. Built-ins:
@@ -118,10 +139,14 @@ import { createOperationsApp, toNextRouteHandlers } from "@schemavaults/openapi-
118
139
 
119
140
  const app = createOperationsApp<{ dbh: Kysely<AuthDatabase> }, UserData>({
120
141
  operations: [getApp, health],
142
+ // Built per request BEFORE credentials are resolved (keep it cheap, open
143
+ // expensive resources lazily); released by disposeContext afterwards.
121
144
  context: async () => ({ dbh: await getDbh() }),
145
+ disposeContext: async ({ dbh }) => dbh.destroy(),
122
146
  authResolvers: {
123
- // keyed by scheme name; return null when no credential for that scheme is present
124
- "schemavaults-access-token": async (c) => {
147
+ // keyed by scheme name; return null when no credential for that scheme is
148
+ // present. The third argument is the per-request context.
149
+ "schemavaults-access-token": async (c, _scheme, { dbh }) => {
125
150
  const token = c.req.header("authorization")?.replace(/^Bearer /, "");
126
151
  if (!token) return null;
127
152
  const user = await verifyAccessToken(token); // throw OperationError(401, ...) if invalid
@@ -137,6 +162,8 @@ const app = createOperationsApp<{ dbh: Kysely<AuthDatabase> }, UserData>({
137
162
  // Served at GET /openapi.json. Pass a function to build it per request,
138
163
  // e.g. to set `servers` from the incoming Host / X-Forwarded-* headers.
139
164
  openapi: { document: openApiDocument },
165
+ // Unexpected failures: log / persist them before the generic 500 goes out.
166
+ onError: (error, c, { operation, context }) => reportException(error, operation.operationId),
140
167
  });
141
168
 
142
169
  // app/api/[[...route]]/route.ts
@@ -147,6 +174,47 @@ export const { GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS } = toNextRouteHandl
147
174
  export default toVercelHandler(app);
148
175
  ```
149
176
 
177
+ ### One route file per operation
178
+
179
+ A catch-all is not required. To give every operation its own Next.js `route.ts`
180
+ (or Vercel function file) while still serving one OpenAPI document that lists all of
181
+ them, bind the shared runtime configuration and the full operation catalogue once with
182
+ `createOperationsAppFactory()` and build a small app per route file from it:
183
+
184
+ ```ts
185
+ // src/lib/api/app.ts
186
+ import { createOperationsAppFactory, operationHttpMethods, toNextRouteHandlers } from "@schemavaults/openapi-operations";
187
+
188
+ export const operations = [getApp, health]; // the catalogue buildOpenApiDocument() is given
189
+ export const api = createOperationsAppFactory<{ dbh: Kysely<AuthDatabase> }, UserData>({
190
+ operations,
191
+ authResolvers,
192
+ context: async () => ({ dbh: await getDbh() }),
193
+ });
194
+
195
+ // app/api/apps/[app_id]/route.ts (Next.js segment [app_id] ↔ OpenAPI {app_id})
196
+ export const runtime = "nodejs";
197
+ export const { GET } = toNextRouteHandlers(api.app([getApp]), operationHttpMethods([getApp]));
198
+
199
+ // app/api/health/route.ts
200
+ export const { GET } = toNextRouteHandlers(api.app([health]), operationHttpMethods([health]));
201
+
202
+ // app/api/openapi.json/route.ts
203
+ export const { GET } = toNextRouteHandlers(
204
+ api.openApiDocumentApp({ path: "/api/openapi.json", document: openApiDocument }),
205
+ ["get"],
206
+ );
207
+ ```
208
+
209
+ Each app routes on the full request pathname, so no `basePath` is needed and the
210
+ dynamic segment is parsed by the app itself (Next.js' `params` are never read).
211
+ `operationHttpMethods()` exports only the methods the operations declare, leaving 405s
212
+ for the rest to Next.js. The factory validates the whole catalogue up front (unique
213
+ operations, a resolver for every scheme) and `api.app()` throws for an operation that
214
+ is not in it, so a route file cannot serve something the document does not describe.
215
+ The reverse (a documented operation with no route file) is a file-layout question;
216
+ the example resource server in this repository checks it with a small `bun test`.
217
+
150
218
  Per request the app: resolves the context, tries each accepted scheme's resolver in
151
219
  order (401 + `WWW-Authenticate` if none yields a principal), enforces the route guard
152
220
  (403), required scopes (403 `insufficient_scope`), organization membership (403), then
@@ -5,8 +5,8 @@ export type NextRouteHandlers<TMethods extends HttpMethod = HttpMethod> = {
5
5
  readonly [M in TMethods as Uppercase<M>]: NextRouteHandler;
6
6
  };
7
7
  /**
8
- * Next.js App Router route-handler exports for the operations app. Typical
9
- * usage from a catch-all route so one Hono app serves every operation:
8
+ * Next.js App Router route-handler exports for the operations app. Either
9
+ * mount one app from a catch-all route so it serves every operation:
10
10
  *
11
11
  * ```ts
12
12
  * // app/api/[[...route]]/route.ts
@@ -14,7 +14,21 @@ export type NextRouteHandlers<TMethods extends HttpMethod = HttpMethod> = {
14
14
  * export const { GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS } = toNextRouteHandlers(app);
15
15
  * ```
16
16
  *
17
- * Pass `methods` to only export a subset (e.g. when a route file coexists
18
- * with hand-written handlers for other methods).
17
+ * or give each operation its own `route.ts` at the matching Next.js path
18
+ * (`app/api/apps/[app_id]/route.ts` for `/api/apps/{app_id}`) and mount an
19
+ * app built for just that operation, via `createOperationsAppFactory()`,
20
+ * exporting only the methods it declares so Next.js answers 405 for the
21
+ * rest:
22
+ *
23
+ * ```ts
24
+ * // app/api/apps/[app_id]/route.ts
25
+ * export const { GET } = toNextRouteHandlers(api.app([getApp]), operationHttpMethods([getApp]));
26
+ * ```
27
+ *
28
+ * The app receives the raw request, so it routes on the full pathname
29
+ * regardless of which route file it is exported from; no `basePath` is
30
+ * needed for per-route mounting. Pass `methods` to only export a subset
31
+ * (e.g. when a route file coexists with hand-written handlers for other
32
+ * methods).
19
33
  */
20
34
  export declare function toNextRouteHandlers<const TMethods extends readonly HttpMethod[] = typeof HTTP_METHODS>(app: Hono, methods?: TMethods): NextRouteHandlers<TMethods[number]>;
@@ -1,8 +1,8 @@
1
1
  import { handle } from "hono/vercel";
2
2
  import { HTTP_METHODS } from "../http-method";
3
3
  /**
4
- * Next.js App Router route-handler exports for the operations app. Typical
5
- * usage from a catch-all route so one Hono app serves every operation:
4
+ * Next.js App Router route-handler exports for the operations app. Either
5
+ * mount one app from a catch-all route so it serves every operation:
6
6
  *
7
7
  * ```ts
8
8
  * // app/api/[[...route]]/route.ts
@@ -10,8 +10,22 @@ import { HTTP_METHODS } from "../http-method";
10
10
  * export const { GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS } = toNextRouteHandlers(app);
11
11
  * ```
12
12
  *
13
- * Pass `methods` to only export a subset (e.g. when a route file coexists
14
- * with hand-written handlers for other methods).
13
+ * or give each operation its own `route.ts` at the matching Next.js path
14
+ * (`app/api/apps/[app_id]/route.ts` for `/api/apps/{app_id}`) and mount an
15
+ * app built for just that operation, via `createOperationsAppFactory()`,
16
+ * exporting only the methods it declares so Next.js answers 405 for the
17
+ * rest:
18
+ *
19
+ * ```ts
20
+ * // app/api/apps/[app_id]/route.ts
21
+ * export const { GET } = toNextRouteHandlers(api.app([getApp]), operationHttpMethods([getApp]));
22
+ * ```
23
+ *
24
+ * The app receives the raw request, so it routes on the full pathname
25
+ * regardless of which route file it is exported from; no `basePath` is
26
+ * needed for per-route mounting. Pass `methods` to only export a subset
27
+ * (e.g. when a route file coexists with hand-written handlers for other
28
+ * methods).
15
29
  */
16
30
  export function toNextRouteHandlers(app, methods = HTTP_METHODS) {
17
31
  const handler = handle(app);
@@ -1 +1 @@
1
- {"version":3,"file":"nextjs.js","sourceRoot":"","sources":["../../src/adapters/nextjs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,YAAY,EAAmB,MAAM,gBAAgB,CAAC;AAQ/D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAS,EACT,UAAoB,YAAmC;IAEvD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,QAAQ,GAAqC,EAAE,CAAC;IACtD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,GAAG,OAAO,CAAC;IAC3C,CAAC;IACD,OAAO,QAA+C,CAAC;AACzD,CAAC"}
1
+ {"version":3,"file":"nextjs.js","sourceRoot":"","sources":["../../src/adapters/nextjs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,YAAY,EAAmB,MAAM,gBAAgB,CAAC;AAQ/D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAS,EACT,UAAoB,YAAmC;IAEvD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,QAAQ,GAAqC,EAAE,CAAC;IACtD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,GAAG,OAAO,CAAC;IAC3C,CAAC;IACD,OAAO,QAA+C,CAAC;AACzD,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
1
- export { z } from "./zod-openapi";
2
- export type { ZodType, ZodObject } from "./zod-openapi";
1
+ export { z, withOpenApi } from "./zod-openapi";
2
+ export type { ZodType, ZodObject, OpenApiSchemaMetadata } from "./zod-openapi";
3
3
  export { HTTP_METHODS, HTTP_METHODS_WITH_REQUEST_BODY, isHttpMethod } from "./http-method";
4
4
  export type { HttpMethod } from "./http-method";
5
5
  export { defineAuthScheme, publicAccess, requireAuth, isPublicOperationAuth, ROUTE_GUARD_TYPES, schemaVaultsAccessTokenBearerScheme, schemaVaultsAccessTokenCookieScheme, schemaVaultsRefreshTokenCookieScheme, oidcClientSecretBasicScheme, oidcClientSecretPostScheme, apiKeyHeaderScheme, } from "./auth-scheme";
6
6
  export type { AuthSchemeDefinition, AuthRequirements, OperationAuth, PublicOperationAuth, RequiredOperationAuth, OrganizationRoleRequirement, RouteGuardType, } from "./auth-scheme";
7
- export { defineOperation, createOperationDefiner, defineOperationGroup, defaultOperationId, assertUniqueOperations, } from "./operation";
7
+ export { defineOperation, createOperationDefiner, defineOperationGroup, defaultOperationId, assertUniqueOperations, operationHttpMethods, } from "./operation";
8
8
  export type { AnyOperationDefinition, AuthPrincipal, EmptyResponseStatusOf, InferBody, InferParsed, OperationDefiner, OperationDefinition, OperationGroup, OperationHandlerContext, OperationHandlerResult, OperationInput, OperationRequestDefinition, RequestBodyContentType, RequestBodyDefinition, ResponseBodyOf, ResponseDefinition, ResponseStatusOf, ResponsesDefinition, } from "./operation";
9
9
  export { buildOpenApiDocument, collectAuthSchemes, toRouteConfig, toSecuritySchemeComponent, } from "./openapi/build-openapi-document";
10
10
  export type { BuildOpenApiDocumentOptions } from "./openapi/build-openapi-document";
11
+ export type { OpenAPIObject } from "openapi3-ts/oas31";
11
12
  export { SCHEMAVAULTS_AUTH_EXTENSION, SCHEMAVAULTS_SCHEME_TITLE_EXTENSION, SCHEMAVAULTS_SCHEME_CHALLENGE_EXTENSION, toSchemaVaultsAuthExtension, isSchemaVaultsAuthExtension, } from "./openapi/extensions";
12
13
  export type { SchemaVaultsAuthExtension } from "./openapi/extensions";
13
14
  export { extractPathParameterNames, openApiPathToHonoPath, honoPathToOpenApiPath, isOpenApiPath, } from "./openapi/path-format";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- export { z } from "./zod-openapi";
1
+ export { z, withOpenApi } from "./zod-openapi";
2
2
  export { HTTP_METHODS, HTTP_METHODS_WITH_REQUEST_BODY, isHttpMethod } from "./http-method";
3
3
  export { defineAuthScheme, publicAccess, requireAuth, isPublicOperationAuth, ROUTE_GUARD_TYPES, schemaVaultsAccessTokenBearerScheme, schemaVaultsAccessTokenCookieScheme, schemaVaultsRefreshTokenCookieScheme, oidcClientSecretBasicScheme, oidcClientSecretPostScheme, apiKeyHeaderScheme, } from "./auth-scheme";
4
- export { defineOperation, createOperationDefiner, defineOperationGroup, defaultOperationId, assertUniqueOperations, } from "./operation";
4
+ export { defineOperation, createOperationDefiner, defineOperationGroup, defaultOperationId, assertUniqueOperations, operationHttpMethods, } from "./operation";
5
5
  export { buildOpenApiDocument, collectAuthSchemes, toRouteConfig, toSecuritySchemeComponent, } from "./openapi/build-openapi-document";
6
6
  export { SCHEMAVAULTS_AUTH_EXTENSION, SCHEMAVAULTS_SCHEME_TITLE_EXTENSION, SCHEMAVAULTS_SCHEME_CHALLENGE_EXTENSION, toSchemaVaultsAuthExtension, isSchemaVaultsAuthExtension, } from "./openapi/extensions";
7
7
  export { extractPathParameterNames, openApiPathToHonoPath, honoPathToOpenApiPath, isOpenApiPath, } from "./openapi/path-format";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAGlC,OAAO,EAAE,YAAY,EAAE,8BAA8B,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG3F,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,mCAAmC,EACnC,mCAAmC,EACnC,oCAAoC,EACpC,2BAA2B,EAC3B,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAWvB,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAsBrB,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,yBAAyB,GAC1B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACL,2BAA2B,EAC3B,mCAAmC,EACnC,uCAAuC,EACvC,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,qBAAqB,EACrB,aAAa,GACd,MAAM,uBAAuB,CAAC;AAE/B,cAAc,WAAW,CAAC;AAI1B,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,YAAY,EAAE,8BAA8B,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG3F,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,mCAAmC,EACnC,mCAAmC,EACnC,oCAAoC,EACpC,2BAA2B,EAC3B,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAWvB,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAsBrB,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,yBAAyB,GAC1B,MAAM,kCAAkC,CAAC;AAI1C,OAAO,EACL,2BAA2B,EAC3B,mCAAmC,EACnC,uCAAuC,EACvC,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,qBAAqB,EACrB,aAAa,GACd,MAAM,uBAAuB,CAAC;AAE/B,cAAc,WAAW,CAAC;AAI1B,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -3,13 +3,30 @@ import type { OrganizationMembershipRoleType } from "@schemavaults/auth-common/o
3
3
  import { type HttpMethod } from "./http-method";
4
4
  import type { OperationAuth, PublicOperationAuth } from "./auth-scheme";
5
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> {
6
+ export interface RequestBodyDefinition<TSchema extends ZodType = ZodType, TDocumentOnly extends boolean = boolean> {
7
7
  /** Media type the body is parsed as (default `application/json`). */
8
8
  readonly contentType?: RequestBodyContentType;
9
9
  readonly schema: TSchema;
10
10
  readonly description?: string;
11
11
  /** Default true. */
12
12
  readonly required?: boolean;
13
+ /**
14
+ * Also parse the body as `contentType` when the request labels it
15
+ * `text/plain` or carries no Content-Type header at all. Browsers send
16
+ * `text/plain;charset=UTF-8` for `fetch(url, { body: JSON.stringify(x) })`
17
+ * without an explicit header, so JSON APIs with such callers need this.
18
+ * Default false (415 on a media type mismatch).
19
+ */
20
+ readonly lenientContentType?: boolean;
21
+ /**
22
+ * Describe the body in the OpenAPI document but leave the request
23
+ * untouched: the runtime neither reads nor validates it and `ctx.body`
24
+ * is `undefined`, so the handler reads `ctx.request` itself. For
25
+ * endpoints whose error format is mandated by a protocol (the OAuth 2.0
26
+ * token endpoint's `{ error, error_description }`, ...) and endpoints
27
+ * with bespoke parsing that must keep its exact responses.
28
+ */
29
+ readonly documentOnly?: TDocumentOnly;
13
30
  }
14
31
  export interface ResponseDefinition<TSchema extends ZodType | undefined = ZodType | undefined> {
15
32
  readonly description: string;
@@ -31,7 +48,9 @@ export interface OperationRequestDefinition<TParams extends ZodObject | undefine
31
48
  readonly body?: TBody;
32
49
  }
33
50
  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;
51
+ export type InferBody<T> = T extends {
52
+ readonly documentOnly: true;
53
+ } ? undefined : T extends RequestBodyDefinition<infer S> ? S extends ZodType ? z.output<S> : undefined : undefined;
35
54
  export type ResponseStatusOf<TResponses extends ResponsesDefinition> = Extract<keyof TResponses, number>;
36
55
  export type ResponseBodyOf<TResponses extends ResponsesDefinition, S extends keyof TResponses> = TResponses[S] extends {
37
56
  readonly schema: infer TSchema;
@@ -165,4 +184,10 @@ export interface OperationGroup {
165
184
  * `createOperationsApp` like any other operation list.
166
185
  */
167
186
  export declare function defineOperationGroup(group: OperationGroup): AnyOperationDefinition[];
187
+ /**
188
+ * Distinct HTTP methods declared by the given operations, in declaration
189
+ * order. Hands `toNextRouteHandlers()` exactly the methods a route file
190
+ * should export, so Next.js answers 405 for the others itself.
191
+ */
192
+ export declare function operationHttpMethods(operations: readonly AnyOperationDefinition[]): HttpMethod[];
168
193
  export declare function assertUniqueOperations(operations: readonly AnyOperationDefinition[]): void;
package/dist/operation.js CHANGED
@@ -93,6 +93,14 @@ export function defineOperationGroup(group) {
93
93
  return Object.freeze({ ...operation, path, tags });
94
94
  });
95
95
  }
96
+ /**
97
+ * Distinct HTTP methods declared by the given operations, in declaration
98
+ * order. Hands `toNextRouteHandlers()` exactly the methods a route file
99
+ * should export, so Next.js answers 405 for the others itself.
100
+ */
101
+ export function operationHttpMethods(operations) {
102
+ return Array.from(new Set(operations.map((operation) => operation.method)));
103
+ }
96
104
  export function assertUniqueOperations(operations) {
97
105
  const ids = new Set();
98
106
  const routes = new Set();
@@ -1 +1 @@
1
- {"version":3,"file":"operation.js","sourceRoot":"","sources":["../src/operation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"operation.js","sourceRoot":"","sources":["../src/operation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ import type { Hono } from "hono";
2
+ import type { AnyOperationDefinition } from "../operation";
3
+ import { type CreateOperationsAppOptions, type OpenApiDocumentRouteOptions } from "./create-operations-app";
4
+ import { type AuthResolvers } from "./resolve-auth";
5
+ export interface CreateOperationsAppFactoryOptions<TContext = unknown, TUser = unknown> {
6
+ /**
7
+ * Every operation the API exposes: the catalogue `buildOpenApiDocument()`
8
+ * is given. Apps built by the factory may only serve operations from it,
9
+ * so a route file cannot mount an operation the document does not list.
10
+ */
11
+ readonly operations: readonly AnyOperationDefinition[];
12
+ /** Credential resolvers keyed by auth scheme name, shared by every app. */
13
+ readonly authResolvers?: AuthResolvers<TUser, TContext>;
14
+ /** Builds the per-request context handed to handlers as `ctx.context`. */
15
+ readonly context?: CreateOperationsAppOptions<TContext, TUser>["context"];
16
+ /** Releases the per-request context once the response was produced. */
17
+ readonly disposeContext?: CreateOperationsAppOptions<TContext, TUser>["disposeContext"];
18
+ /** Called for unexpected (non-OperationError) failures before the 500 is sent. */
19
+ readonly onError?: CreateOperationsAppOptions<TContext, TUser>["onError"];
20
+ }
21
+ /** Per-app options a factory caller may still set. */
22
+ export type OperationsAppFactoryAppOptions<TContext = unknown, TUser = unknown> = Pick<CreateOperationsAppOptions<TContext, TUser>, "basePath" | "configure" | "openapi">;
23
+ export interface OperationsAppFactory<TContext = unknown, TUser = unknown> {
24
+ /** The full catalogue the factory was created with. */
25
+ readonly operations: readonly AnyOperationDefinition[];
26
+ /** The shared resolvers every app built by the factory authenticates with. */
27
+ readonly authResolvers: AuthResolvers<TUser, TContext>;
28
+ /**
29
+ * Builds a Hono app serving the given operations (default: the whole
30
+ * catalogue) with the shared resolvers / context / error reporting.
31
+ * Throws when an operation is not part of the catalogue.
32
+ */
33
+ app(operations?: readonly AnyOperationDefinition[], options?: OperationsAppFactoryAppOptions<TContext, TUser>): Hono;
34
+ /** Builds a Hono app that only serves the OpenAPI document. */
35
+ openApiDocumentApp(openapi: OpenApiDocumentRouteOptions, options?: Omit<OperationsAppFactoryAppOptions<TContext, TUser>, "openapi">): Hono;
36
+ /** Throws unless every given operation is part of the catalogue. */
37
+ assertRegistered(operations: readonly AnyOperationDefinition[]): void;
38
+ }
39
+ /**
40
+ * Binds the shared runtime configuration (auth resolvers, per-request
41
+ * context, error reporting) and the full operation catalogue once, so many
42
+ * small apps can be built from it: one per Next.js `route.ts` / Vercel
43
+ * function file, each serving only the operation(s) at its path, while a
44
+ * single OpenAPI document generated from the same catalogue still describes
45
+ * all of them.
46
+ *
47
+ * ```ts
48
+ * // src/lib/api/app.ts
49
+ * export const api = createOperationsAppFactory<Ctx, UserData>({
50
+ * operations, // the same list buildOpenApiDocument() is given
51
+ * authResolvers,
52
+ * context: () => ({ ... }),
53
+ * });
54
+ *
55
+ * // app/api/health/route.ts
56
+ * export const { GET } = toNextRouteHandlers(api.app([health]), operationHttpMethods([health]));
57
+ *
58
+ * // app/api/openapi.json/route.ts
59
+ * export const { GET } = toNextRouteHandlers(
60
+ * api.openApiDocumentApp({ path: "/api/openapi.json", document }),
61
+ * ["get"],
62
+ * );
63
+ * ```
64
+ *
65
+ * Every app validates against the whole catalogue up front: duplicate
66
+ * operations or a scheme without a resolver fail at module load of the
67
+ * first route rather than when the affected route is first hit.
68
+ */
69
+ export declare function createOperationsAppFactory<TContext = unknown, TUser = unknown>(options: CreateOperationsAppFactoryOptions<TContext, TUser>): OperationsAppFactory<TContext, TUser>;
@@ -0,0 +1,68 @@
1
+ import { assertUniqueOperations } from "../operation";
2
+ import { createOperationsApp, } from "./create-operations-app";
3
+ import { assertResolversForOperations } from "./resolve-auth";
4
+ /**
5
+ * Binds the shared runtime configuration (auth resolvers, per-request
6
+ * context, error reporting) and the full operation catalogue once, so many
7
+ * small apps can be built from it: one per Next.js `route.ts` / Vercel
8
+ * function file, each serving only the operation(s) at its path, while a
9
+ * single OpenAPI document generated from the same catalogue still describes
10
+ * all of them.
11
+ *
12
+ * ```ts
13
+ * // src/lib/api/app.ts
14
+ * export const api = createOperationsAppFactory<Ctx, UserData>({
15
+ * operations, // the same list buildOpenApiDocument() is given
16
+ * authResolvers,
17
+ * context: () => ({ ... }),
18
+ * });
19
+ *
20
+ * // app/api/health/route.ts
21
+ * export const { GET } = toNextRouteHandlers(api.app([health]), operationHttpMethods([health]));
22
+ *
23
+ * // app/api/openapi.json/route.ts
24
+ * export const { GET } = toNextRouteHandlers(
25
+ * api.openApiDocumentApp({ path: "/api/openapi.json", document }),
26
+ * ["get"],
27
+ * );
28
+ * ```
29
+ *
30
+ * Every app validates against the whole catalogue up front: duplicate
31
+ * operations or a scheme without a resolver fail at module load of the
32
+ * first route rather than when the affected route is first hit.
33
+ */
34
+ export function createOperationsAppFactory(options) {
35
+ const catalogue = Object.freeze([...options.operations]);
36
+ assertUniqueOperations(catalogue);
37
+ const resolvers = options.authResolvers ?? {};
38
+ assertResolversForOperations(catalogue, resolvers);
39
+ const registered = new Set(catalogue);
40
+ const assertRegistered = (operations) => {
41
+ for (const operation of operations) {
42
+ if (!registered.has(operation)) {
43
+ throw new TypeError(`${operation.method.toUpperCase()} ${operation.path} (${operation.operationId}) is not part of the operations catalogue this factory was created with, so it would be served without appearing in the OpenAPI document`);
44
+ }
45
+ }
46
+ };
47
+ const app = (operations = catalogue, appOptions = {}) => {
48
+ assertRegistered(operations);
49
+ return createOperationsApp({
50
+ operations,
51
+ authResolvers: resolvers,
52
+ context: options.context,
53
+ disposeContext: options.disposeContext,
54
+ onError: options.onError,
55
+ basePath: appOptions.basePath,
56
+ configure: appOptions.configure,
57
+ openapi: appOptions.openapi,
58
+ });
59
+ };
60
+ return {
61
+ operations: catalogue,
62
+ authResolvers: resolvers,
63
+ app,
64
+ openApiDocumentApp: (openapi, appOptions = {}) => app([], { ...appOptions, openapi }),
65
+ assertRegistered,
66
+ };
67
+ }
68
+ //# sourceMappingURL=create-operations-app-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-operations-app-factory.js","sourceRoot":"","sources":["../../src/runtime/create-operations-app-factory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EACL,mBAAmB,GAGpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,4BAA4B,EAAsB,MAAM,gBAAgB,CAAC;AAgDlF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAA2D;IAE3D,MAAM,SAAS,GAAsC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IAC5F,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAClC,MAAM,SAAS,GAAmC,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;IAC9E,4BAA4B,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAyB,SAAS,CAAC,CAAC;IAE9D,MAAM,gBAAgB,GAAG,CAAC,UAA6C,EAAQ,EAAE;QAC/E,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC/B,MAAM,IAAI,SAAS,CACjB,GAAG,SAAS,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC,WAAW,0IAA0I,CACxN,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,GAAG,GAAiD,CACxD,UAAU,GAAG,SAAS,EACtB,UAAU,GAAG,EAAE,EACf,EAAE;QACF,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAC7B,OAAO,mBAAmB,CAAkB;YAC1C,UAAU;YACV,aAAa,EAAE,SAAS;YACxB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,QAAQ,EAAE,UAAU,CAAC,QAAQ;YAC7B,SAAS,EAAE,UAAU,CAAC,SAAS;YAC/B,OAAO,EAAE,UAAU,CAAC,OAAO;SAC5B,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO;QACL,UAAU,EAAE,SAAS;QACrB,aAAa,EAAE,SAAS;QACxB,GAAG;QACH,kBAAkB,EAAE,CAAC,OAAO,EAAE,UAAU,GAAG,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,UAAU,EAAE,OAAO,EAAE,CAAC;QACrF,gBAAgB;KACjB,CAAC;AACJ,CAAC"}
@@ -12,23 +12,41 @@ export interface OpenApiDocumentRouteOptions {
12
12
  */
13
13
  readonly document: OpenAPIObject | ((c: Context) => OpenAPIObject | Promise<OpenAPIObject>);
14
14
  }
15
+ /** What `onError` learns about the failed request besides the error itself. */
16
+ export interface OperationFailureInfo<TContext = unknown> {
17
+ readonly operation: AnyOperationDefinition;
18
+ /** The per-request context, when it had been built before the failure. */
19
+ readonly context: TContext | undefined;
20
+ }
15
21
  export interface CreateOperationsAppOptions<TContext = unknown, TUser = unknown> {
16
22
  readonly operations: readonly AnyOperationDefinition[];
17
23
  /**
18
24
  * Prefix stripped by the deployment before routing (e.g. `/api` when the
19
25
  * app is mounted from `app/api/[[...route]]/route.ts`). Operation paths
20
26
  * stay absolute in the OpenAPI document; leave unset to route on them
21
- * verbatim.
27
+ * verbatim (Next.js route handlers and Vercel functions receive the full
28
+ * request URL, so they never need it).
22
29
  */
23
30
  readonly basePath?: string;
24
31
  /** Credential resolvers keyed by auth scheme name. */
25
- readonly authResolvers?: AuthResolvers<TUser>;
26
- /** Builds the per-request context handed to handlers as `ctx.context`. */
32
+ readonly authResolvers?: AuthResolvers<TUser, TContext>;
33
+ /**
34
+ * Builds the per-request context handed to auth resolvers and to handlers
35
+ * as `ctx.context`. Built before credentials are resolved, so it should
36
+ * be cheap (open expensive resources lazily) and is released through
37
+ * `disposeContext` once the response has been produced.
38
+ */
27
39
  readonly context?: (c: Context) => Promise<TContext> | TContext;
40
+ /**
41
+ * Releases the per-request context (database handles, cache connections)
42
+ * after the handler returned or failed. Errors thrown here are reported
43
+ * to `onError` (or the console) and never change the response.
44
+ */
45
+ readonly disposeContext?: (context: TContext, c: Context) => void | Promise<void>;
28
46
  /** Serve the OpenAPI document from the app; omit to not expose it. */
29
47
  readonly openapi?: OpenApiDocumentRouteOptions;
30
48
  /** Called for unexpected (non-OperationError) failures before the 500 is sent. */
31
- readonly onError?: (error: unknown, c: Context) => void | Promise<void>;
49
+ readonly onError?: (error: unknown, c: Context, info: OperationFailureInfo<TContext>) => void | Promise<void>;
32
50
  /** Extra middleware / routes registered before the operations (CORS, logging, ...). */
33
51
  readonly configure?: (app: Hono) => void;
34
52
  }
@@ -52,11 +52,29 @@ export function createOperationsApp(options) {
52
52
  return c.json(resolved);
53
53
  });
54
54
  }
55
+ const report = async (error, c, info) => {
56
+ if (options.onError) {
57
+ try {
58
+ await options.onError(error, c, info);
59
+ }
60
+ catch {
61
+ // never let error reporting mask the response
62
+ }
63
+ }
64
+ else {
65
+ console.error(`[openapi-operations] ${info.operation.method.toUpperCase()} ${info.operation.path} failed:`, error);
66
+ }
67
+ };
55
68
  for (const operation of options.operations) {
56
69
  app.on(operation.method.toUpperCase(), openApiPathToHonoPath(operation.path), async (c) => {
70
+ let context = undefined;
71
+ let built = false;
57
72
  try {
58
- const context = options.context ? await options.context(c) : undefined;
59
- const auth = await resolveAuth(c, operation.auth, resolvers);
73
+ if (options.context) {
74
+ context = await options.context(c);
75
+ built = true;
76
+ }
77
+ const auth = await resolveAuth(c, operation.auth, resolvers, context);
60
78
  const validated = await validateRequest(c, operation);
61
79
  const ctx = buildHandlerContext(c, operation, validated, auth, context);
62
80
  const result = await operation.handler(ctx);
@@ -68,23 +86,23 @@ export function createOperationsApp(options) {
68
86
  catch (error) {
69
87
  if (error instanceof OperationError)
70
88
  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
- }
89
+ await report(error, c, { operation, context });
82
90
  return jsonResponse(500, {
83
91
  success: false,
84
92
  error: OPERATION_ERROR_CODES.internal,
85
93
  message: "Internal Server Error",
86
94
  });
87
95
  }
96
+ finally {
97
+ if (built && options.disposeContext) {
98
+ try {
99
+ await options.disposeContext(context, c);
100
+ }
101
+ catch (error) {
102
+ await report(error, c, { operation, context });
103
+ }
104
+ }
105
+ }
88
106
  });
89
107
  }
90
108
  return root;
@@ -1 +1 @@
1
- {"version":3,"file":"create-operations-app.js","sourceRoot":"","sources":["../../src/runtime/create-operations-app.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create-operations-app.js","sourceRoot":"","sources":["../../src/runtime/create-operations-app.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  export { createOperationsApp } from "./create-operations-app";
2
- export type { CreateOperationsAppOptions, OpenApiDocumentRouteOptions, } from "./create-operations-app";
2
+ export type { CreateOperationsAppOptions, OpenApiDocumentRouteOptions, OperationFailureInfo, } from "./create-operations-app";
3
+ export { createOperationsAppFactory } from "./create-operations-app-factory";
4
+ export type { CreateOperationsAppFactoryOptions, OperationsAppFactory, OperationsAppFactoryAppOptions, } from "./create-operations-app-factory";
3
5
  export { OperationError, OPERATION_ERROR_CODES, jsonResponse } from "./errors";
4
6
  export type { OperationErrorBody, OperationValidationIssue } from "./errors";
5
7
  export { resolveAuth, grantedScopes, missingScopes } from "./resolve-auth";
@@ -1,4 +1,5 @@
1
1
  export { createOperationsApp } from "./create-operations-app";
2
+ export { createOperationsAppFactory } from "./create-operations-app-factory";
2
3
  export { OperationError, OPERATION_ERROR_CODES, jsonResponse } from "./errors";
3
4
  export { resolveAuth, grantedScopes, missingScopes } from "./resolve-auth";
4
5
  export { validateRequest, queryToObject, headersToObject, readRequestBody } from "./validate-request";
@@ -1 +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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAM9D,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAM7E,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"}
@@ -5,11 +5,13 @@ import type { AnyOperationDefinition, AuthPrincipal } from "../operation";
5
5
  * Verifies the credential transported by one auth scheme. Returns null when
6
6
  * the request carries no credential for the scheme (so the next accepted
7
7
  * scheme is tried); throws {@link OperationError} to reject outright (e.g.
8
- * a credential that IS present but invalid).
8
+ * a credential that IS present but invalid). Receives the per-request
9
+ * context built by `createOperationsApp({ context })` so it can share the
10
+ * request's database handle / caches with the handler.
9
11
  */
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;
12
+ export type AuthResolver<TUser = unknown, TContext = unknown> = (c: Context, scheme: AuthSchemeDefinition, context: TContext) => Promise<AuthPrincipal<TUser> | null> | AuthPrincipal<TUser> | null;
13
+ export type AuthResolvers<TUser = unknown, TContext = unknown> = Readonly<Record<string, AuthResolver<TUser, TContext>>>;
14
+ export declare function assertResolversForOperations(operations: readonly AnyOperationDefinition[], resolvers: AuthResolvers<any, any>): void;
13
15
  export declare function grantedScopes(principal: AuthPrincipal): string[];
14
16
  export declare function missingScopes(principal: AuthPrincipal, required: readonly string[]): string[];
15
17
  /**
@@ -18,4 +20,4 @@ export declare function missingScopes(principal: AuthPrincipal, required: readon
18
20
  * required scopes, and organization membership. Returns the principal, or
19
21
  * null for public operations.
20
22
  */
21
- export declare function resolveAuth<TUser>(c: Context, auth: OperationAuth, resolvers: AuthResolvers<TUser>): Promise<AuthPrincipal<TUser> | null>;
23
+ export declare function resolveAuth<TUser, TContext = unknown>(c: Context, auth: OperationAuth, resolvers: AuthResolvers<TUser, TContext>, context?: TContext): Promise<AuthPrincipal<TUser> | null>;
@@ -1,5 +1,7 @@
1
1
  import { OPERATION_ERROR_CODES, OperationError } from "./errors";
2
- export function assertResolversForOperations(operations, resolvers) {
2
+ export function assertResolversForOperations(operations,
3
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
+ resolvers) {
3
5
  for (const operation of operations) {
4
6
  if (operation.auth.type !== "required")
5
7
  continue;
@@ -31,7 +33,7 @@ export function missingScopes(principal, required) {
31
33
  * required scopes, and organization membership. Returns the principal, or
32
34
  * null for public operations.
33
35
  */
34
- export async function resolveAuth(c, auth, resolvers) {
36
+ export async function resolveAuth(c, auth, resolvers, context = undefined) {
35
37
  if (auth.type === "public")
36
38
  return null;
37
39
  let principal = null;
@@ -39,7 +41,7 @@ export async function resolveAuth(c, auth, resolvers) {
39
41
  const resolver = resolvers[scheme.name];
40
42
  if (!resolver)
41
43
  continue;
42
- principal = await resolver(c, scheme);
44
+ principal = await resolver(c, scheme, context);
43
45
  if (principal)
44
46
  break;
45
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-auth.js","sourceRoot":"","sources":["../../src/runtime/resolve-auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolve-auth.js","sourceRoot":"","sources":["../../src/runtime/resolve-auth.ts"],"names":[],"mappings":"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"}
@@ -50,6 +50,10 @@ export async function readRequestBody(c, definition) {
50
50
  const actual = mediaTypeOf(c);
51
51
  const contentLength = c.req.header("content-length");
52
52
  const looksEmpty = actual === null && (contentLength === undefined || contentLength === "0");
53
+ const lenient = definition.lenientContentType === true;
54
+ // A body labelled text/plain (or not labelled at all) is re-parsed as the
55
+ // expected media type when the operation opted into leniency.
56
+ const relabelled = lenient && (actual === null || actual === "text/plain");
53
57
  if (looksEmpty) {
54
58
  if (!required)
55
59
  return undefined;
@@ -61,7 +65,9 @@ export async function readRequestBody(c, definition) {
61
65
  ],
62
66
  });
63
67
  }
64
- if (actual !== expected && !(expected === "text/plain" && actual?.startsWith("text/"))) {
68
+ if (actual !== expected &&
69
+ !relabelled &&
70
+ !(expected === "text/plain" && actual?.startsWith("text/"))) {
65
71
  throw new OperationError(415, {
66
72
  error: OPERATION_ERROR_CODES.unsupportedMediaType,
67
73
  message: `Expected a ${expected} request body but received ${actual ?? "none"}`,
@@ -70,15 +76,21 @@ export async function readRequestBody(c, definition) {
70
76
  try {
71
77
  switch (expected) {
72
78
  case "application/json":
73
- return await c.req.json();
79
+ // c.req.json() trusts the declared media type; parse the raw text
80
+ // ourselves so relabelled text/plain bodies work too.
81
+ return JSON.parse(await c.req.text());
74
82
  case "application/x-www-form-urlencoded":
83
+ if (relabelled) {
84
+ return Object.fromEntries(new URLSearchParams(await c.req.text()));
85
+ }
86
+ return await c.req.parseBody({ all: true });
75
87
  case "multipart/form-data":
76
88
  return await c.req.parseBody({ all: true });
77
89
  case "text/plain":
78
90
  return await c.req.text();
79
91
  default:
80
92
  if (expected.endsWith("+json"))
81
- return await c.req.json();
93
+ return JSON.parse(await c.req.text());
82
94
  return await c.req.text();
83
95
  }
84
96
  }
@@ -110,7 +122,7 @@ export async function validateRequest(c, operation) {
110
122
  ? (await parseWith(headers, headersToObject(c), "headers"))
111
123
  : EMPTY;
112
124
  let parsedBody = undefined;
113
- if (body) {
125
+ if (body && body.documentOnly !== true) {
114
126
  const raw = await readRequestBody(c, body);
115
127
  if (raw !== undefined || (body.required ?? true)) {
116
128
  parsedBody = await parseWith(body.schema, raw, "body");
@@ -1 +1 @@
1
- {"version":3,"file":"validate-request.js","sourceRoot":"","sources":["../../src/runtime/validate-request.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,qBAAqB,EACrB,cAAc,GAEf,MAAM,UAAU,CAAC;AAElB,KAAK,UAAU,SAAS,CACtB,MAAe,EACf,KAAc,EACd,QAA8C;IAE9C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC;IACvC,MAAM,MAAM,GAA+B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC7E,QAAQ;QACR,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAC5D,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,CAAC,CAAC,CAAC;IACJ,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE;QAC5B,KAAK,EAAE,qBAAqB,CAAC,UAAU;QACvC,OAAO,EAAE,mBAAmB,QAAQ,EAAE;QACtC,MAAM;KACP,CAAC,CAAC;AACL,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,aAAa,CAAC,CAAU;IACtC,MAAM,MAAM,GAAsC,EAAE,CAAC;IACrD,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC5D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;YACxB,IAAI,OAAO,MAAM,KAAK,QAAQ;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QACvD,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QACvB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,CAAU;IACxC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvC,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;IACpC,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,WAAW,CAAC,CAAU;IAC7B,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAC5C,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC5C,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,OAAO,IAAI,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,CAAU,EACV,UAAiC;IAEjC,MAAM,QAAQ,GAAG,CAAC,UAAU,CAAC,WAAW,IAAI,kBAAkB,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9E,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,IAAI,CAAC;IAC7C,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAC9B,MAAM,aAAa,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACrD,MAAM,UAAU,GACd,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,IAAI,aAAa,KAAK,GAAG,CAAC,CAAC;IAE5E,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,CAAC,QAAQ;YAAE,OAAO,SAAS,CAAC;QAChC,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE;YAC5B,KAAK,EAAE,qBAAqB,CAAC,UAAU;YACvC,OAAO,EAAE,KAAK,QAAQ,2BAA2B;YACjD,MAAM,EAAE;gBACN,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,0BAA0B,EAAE,IAAI,EAAE,UAAU,EAAE;aACtF;SACF,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,YAAY,IAAI,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACvF,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE;YAC5B,KAAK,EAAE,qBAAqB,CAAC,oBAAoB;YACjD,OAAO,EAAE,cAAc,QAAQ,8BAA8B,MAAM,IAAI,MAAM,EAAE;SAChF,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC;QACH,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,kBAAkB;gBACrB,OAAO,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAC5B,KAAK,mCAAmC,CAAC;YACzC,KAAK,qBAAqB;gBACxB,OAAO,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9C,KAAK,YAAY;gBACf,OAAO,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAC5B;gBACE,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;oBAAE,OAAO,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC1D,OAAO,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE;YAC5B,KAAK,EAAE,qBAAqB,CAAC,UAAU;YACvC,OAAO,EAAE,aAAa,QAAQ,eAAe;YAC7C,MAAM,EAAE;gBACN;oBACE,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,EAAE;oBACR,OAAO,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB;oBAChE,IAAI,EAAE,WAAW;iBAClB;aACF;SACF,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AASD,MAAM,KAAK,GAAoC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAEjE,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,CAAU,EACV,SAAiC;IAEjC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC;IAC3D,MAAM,YAAY,GAAG,MAAM;QACzB,CAAC,CAAE,CAAC,MAAM,SAAS,CAAC,MAAmB,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,QAAQ,CAAC,CAA6B;QAC9F,CAAC,CAAC,KAAK,CAAC;IACV,MAAM,WAAW,GAAG,KAAK;QACvB,CAAC,CAAE,CAAC,MAAM,SAAS,CAAC,KAAkB,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAA6B;QAC/F,CAAC,CAAC,KAAK,CAAC;IACV,MAAM,aAAa,GAAG,OAAO;QAC3B,CAAC,CAAE,CAAC,MAAM,SAAS,CAAC,OAAoB,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAA6B;QACrG,CAAC,CAAC,KAAK,CAAC;IACV,IAAI,UAAU,GAAY,SAAS,CAAC;IACpC,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,EAAE,CAAC;YACjD,UAAU,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAChG,CAAC"}
1
+ {"version":3,"file":"validate-request.js","sourceRoot":"","sources":["../../src/runtime/validate-request.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,27 @@
8
8
  * `z` from here (or call `extendZodWithOpenApi` themselves on the same zod
9
9
  * instance) instead of importing zod directly.
10
10
  */
11
- import { z } from "zod";
11
+ import { z, type ZodType } from "zod";
12
+ import { type ZodOpenAPIMetadata } from "@asteasolutions/zod-to-openapi";
12
13
  export { z };
13
14
  export type { ZodType, ZodObject } from "zod";
15
+ export type OpenApiSchemaMetadata = Partial<ZodOpenAPIMetadata>;
16
+ /**
17
+ * `schema.openapi(...)` for schemas built BEFORE this package was evaluated.
18
+ *
19
+ * zod v4 copies `ZodType.prototype` methods onto each schema instance when it
20
+ * is constructed, so a schema exported by another package (e.g.
21
+ * `@schemavaults/auth-common`) only has its own `.openapi` when that package
22
+ * happened to be evaluated after this one — which depends on the import order
23
+ * of whichever bundle loads first and throws `... .openapi is not a function`
24
+ * at module load otherwise. Invoking the prototype method explicitly sidesteps
25
+ * that: it returns a fresh copy of the schema carrying the metadata, exactly
26
+ * like a direct call would. Use it for every schema you did not build with the
27
+ * `z` exported from here.
28
+ *
29
+ * ```ts
30
+ * export const App = withOpenApi(schemaVaultsAppDefinitionSchema, "App", { description: "..." });
31
+ * const params = z.object({ app_id: withOpenApi(appIdSchema, { example: "my-app" }) });
32
+ * ```
33
+ */
34
+ export declare function withOpenApi<T extends ZodType>(schema: T, refIdOrMetadata: string | OpenApiSchemaMetadata, metadata?: OpenApiSchemaMetadata): T;
@@ -9,7 +9,31 @@
9
9
  * instance) instead of importing zod directly.
10
10
  */
11
11
  import { z } from "zod";
12
- import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
12
+ import { extendZodWithOpenApi, } from "@asteasolutions/zod-to-openapi";
13
13
  extendZodWithOpenApi(z);
14
14
  export { z };
15
+ /**
16
+ * `schema.openapi(...)` for schemas built BEFORE this package was evaluated.
17
+ *
18
+ * zod v4 copies `ZodType.prototype` methods onto each schema instance when it
19
+ * is constructed, so a schema exported by another package (e.g.
20
+ * `@schemavaults/auth-common`) only has its own `.openapi` when that package
21
+ * happened to be evaluated after this one — which depends on the import order
22
+ * of whichever bundle loads first and throws `... .openapi is not a function`
23
+ * at module load otherwise. Invoking the prototype method explicitly sidesteps
24
+ * that: it returns a fresh copy of the schema carrying the metadata, exactly
25
+ * like a direct call would. Use it for every schema you did not build with the
26
+ * `z` exported from here.
27
+ *
28
+ * ```ts
29
+ * export const App = withOpenApi(schemaVaultsAppDefinitionSchema, "App", { description: "..." });
30
+ * const params = z.object({ app_id: withOpenApi(appIdSchema, { example: "my-app" }) });
31
+ * ```
32
+ */
33
+ export function withOpenApi(schema, refIdOrMetadata, metadata) {
34
+ const openapi = z.ZodType.prototype.openapi;
35
+ return typeof refIdOrMetadata === "string"
36
+ ? openapi.call(schema, refIdOrMetadata, metadata)
37
+ : openapi.call(schema, refIdOrMetadata);
38
+ }
15
39
  //# sourceMappingURL=zod-openapi.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"zod-openapi.js","sourceRoot":"","sources":["../src/zod-openapi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,CAAC,EAAgB,MAAM,KAAK,CAAC;AACtC,OAAO,EACL,oBAAoB,GAErB,MAAM,gCAAgC,CAAC;AAExC,oBAAoB,CAAC,CAAC,CAAC,CAAC;AAExB,OAAO,EAAE,CAAC,EAAE,CAAC;AAOb;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,WAAW,CACzB,MAAS,EACT,eAA+C,EAC/C,QAAgC;IAEhC,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,OAAgE,CAAC;IACrG,OAAO,OAAO,eAAe,KAAK,QAAQ;QACxC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,EAAE,QAAQ,CAAC;QACjD,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAC5C,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@schemavaults/openapi-operations",
3
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.9",
4
+ "version": "0.3.0",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
7
7
  "repository": {