@croutonian/with-openapi 0.2.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/LICENSE +21 -0
- package/README.md +499 -0
- package/dist/index.d.ts +451 -0
- package/dist/index.js +1419 -0
- package/package.json +77 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { FetchHandler, Middleware } from "@supabase/middleware";
|
|
2
|
+
import { SchemaDraft } from "@cfworker/json-schema";
|
|
3
|
+
import { OpenAPIObject, OperationObject, ParameterLocation, ParameterStyle, ReferenceObject, SchemaObject, SecurityRequirementObject } from "openapi3-ts/oas31";
|
|
4
|
+
|
|
5
|
+
//#region src/document.d.ts
|
|
6
|
+
/** The HTTP methods an OpenAPI Path Item Object may declare an operation for. */
|
|
7
|
+
declare const HTTP_METHODS: readonly ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
|
|
8
|
+
/** One of the eight methods {@link HTTP_METHODS} lists. */
|
|
9
|
+
type HttpMethod = (typeof HTTP_METHODS)[number];
|
|
10
|
+
/** One segment of a path template: a literal, or a `{name}` placeholder. */
|
|
11
|
+
type Segment = {
|
|
12
|
+
readonly kind: 'static';
|
|
13
|
+
readonly value: string;
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: 'param';
|
|
16
|
+
readonly name: string;
|
|
17
|
+
};
|
|
18
|
+
/** A parameter, with its defaults filled in and its schema pre-resolved. */
|
|
19
|
+
interface IndexedParameter {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly in: ParameterLocation;
|
|
22
|
+
/** The Parameter Object's own prose, surfaced on any violation about it. */
|
|
23
|
+
readonly description: string | undefined;
|
|
24
|
+
readonly required: boolean;
|
|
25
|
+
readonly style: ParameterStyle;
|
|
26
|
+
readonly explode: boolean;
|
|
27
|
+
readonly allowEmptyValue: boolean;
|
|
28
|
+
/** As written in the document — handed to the validator unresolved. */
|
|
29
|
+
readonly schema: SchemaObject | ReferenceObject | undefined;
|
|
30
|
+
/** `$ref` chain followed, so deserialization can read `type`. */
|
|
31
|
+
readonly resolved: SchemaObject | undefined;
|
|
32
|
+
}
|
|
33
|
+
/** One entry of a Request Body Object's `content` map. */
|
|
34
|
+
interface IndexedContent {
|
|
35
|
+
readonly mediaType: string;
|
|
36
|
+
readonly schema: SchemaObject | ReferenceObject | undefined;
|
|
37
|
+
readonly resolved: SchemaObject | undefined;
|
|
38
|
+
}
|
|
39
|
+
/** A Request Body Object, flattened. */
|
|
40
|
+
interface IndexedRequestBody {
|
|
41
|
+
readonly required: boolean;
|
|
42
|
+
/** The Request Body Object's own prose, surfaced on body-level violations. */
|
|
43
|
+
readonly description: string | undefined;
|
|
44
|
+
readonly contents: readonly IndexedContent[];
|
|
45
|
+
}
|
|
46
|
+
/** Everything the middleware needs about one operation, resolved up front. */
|
|
47
|
+
interface IndexedOperation {
|
|
48
|
+
readonly method: HttpMethod;
|
|
49
|
+
readonly route: string;
|
|
50
|
+
readonly operation: OperationObject;
|
|
51
|
+
readonly operationId: string | undefined;
|
|
52
|
+
readonly security: SecurityRequirementObject[] | undefined;
|
|
53
|
+
readonly parameters: Readonly<Record<ParameterLocation, readonly IndexedParameter[]>>;
|
|
54
|
+
readonly requestBody: IndexedRequestBody | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Query string names the declared parameters may legitimately consume —
|
|
57
|
+
* a `form`/`explode` object contributes its property names, not its own.
|
|
58
|
+
* Only read when `additionalQuery: 'reject'`.
|
|
59
|
+
*/
|
|
60
|
+
readonly knownQueryNames: ReadonlySet<string>;
|
|
61
|
+
/** `deepObject` parameters consume anything starting with `name[`. */
|
|
62
|
+
readonly knownQueryPrefixes: readonly string[];
|
|
63
|
+
}
|
|
64
|
+
/** A path template, its segments, and the operations declared under it. */
|
|
65
|
+
interface IndexedRoute {
|
|
66
|
+
readonly template: string;
|
|
67
|
+
readonly segments: readonly Segment[];
|
|
68
|
+
readonly operations: ReadonlyMap<HttpMethod, IndexedOperation>;
|
|
69
|
+
/** Declared methods, upper-cased — the `Allow` header on a 405. */
|
|
70
|
+
readonly allow: readonly string[];
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/cors.d.ts
|
|
74
|
+
/**
|
|
75
|
+
* Which origins may call the API.
|
|
76
|
+
*
|
|
77
|
+
* A literal `'*'` with `credentials` is forbidden by the Fetch standard, so in
|
|
78
|
+
* that combination the request's `Origin` is reflected instead.
|
|
79
|
+
*/
|
|
80
|
+
type CorsOrigin = '*' | string | readonly string[] | ((origin: string | null) => boolean);
|
|
81
|
+
/** CORS configuration. Everything not listed here comes from the document. */
|
|
82
|
+
interface OpenApiCorsOptions {
|
|
83
|
+
/**
|
|
84
|
+
* Allowed origins. **Required, and never derived** — a document describes
|
|
85
|
+
* where an API lives, not who may call it.
|
|
86
|
+
*/
|
|
87
|
+
origin: CorsOrigin;
|
|
88
|
+
/** Send `Access-Control-Allow-Credentials: true`. @defaultValue `false` */
|
|
89
|
+
credentials?: boolean;
|
|
90
|
+
/** `Access-Control-Max-Age` in seconds, for preflight caching. */
|
|
91
|
+
maxAge?: number;
|
|
92
|
+
/**
|
|
93
|
+
* Request headers to allow **on top of** the ones derived from the document.
|
|
94
|
+
* Reach for this when a header is genuinely not describable in OpenAPI; a
|
|
95
|
+
* header the API actually reads belongs in the document instead.
|
|
96
|
+
*/
|
|
97
|
+
allowedHeaders?: readonly string[];
|
|
98
|
+
/** Response headers to expose on top of the ones derived from the document. */
|
|
99
|
+
exposedHeaders?: readonly string[];
|
|
100
|
+
/** Status for a successful preflight. @defaultValue `204` */
|
|
101
|
+
optionsSuccessStatus?: number;
|
|
102
|
+
}
|
|
103
|
+
/** Built once at construction; answers preflights and stamps responses. */
|
|
104
|
+
interface CorsPolicy {
|
|
105
|
+
/** A preflight is `OPTIONS` carrying `Access-Control-Request-Method`. */
|
|
106
|
+
isPreflight(req: Request): boolean;
|
|
107
|
+
/** The preflight response for a matched route. */
|
|
108
|
+
preflight(req: Request, route: IndexedRoute): Response;
|
|
109
|
+
/**
|
|
110
|
+
* Copy a response, adding the response-side CORS headers.
|
|
111
|
+
*
|
|
112
|
+
* The matched route is passed in rather than looked up, because it is what
|
|
113
|
+
* `Access-Control-Expose-Headers` is derived from and the caller already
|
|
114
|
+
* holds it. A side channel keyed on the request would be the alternative,
|
|
115
|
+
* and the authoring guide's rule 3 exists to prevent exactly that.
|
|
116
|
+
*/
|
|
117
|
+
stamp(response: Response, req: Request, route: IndexedRoute | undefined): Response;
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/reference.d.ts
|
|
121
|
+
/** Default CDN for Scalar's standalone browser build. */
|
|
122
|
+
declare const SCALAR_CDN_URL = "https://cdn.jsdelivr.net/npm/@scalar/api-reference";
|
|
123
|
+
/** Everything {@link ScalarReferenceOptions.html} is given to render a page. */
|
|
124
|
+
interface ScalarHtmlInput {
|
|
125
|
+
/** Absolute path the document JSON is served from. */
|
|
126
|
+
readonly documentPath: string;
|
|
127
|
+
/** Page `<title>`. */
|
|
128
|
+
readonly title: string;
|
|
129
|
+
/** Script URL for Scalar's standalone build. */
|
|
130
|
+
readonly cdnUrl: string;
|
|
131
|
+
/** Config object passed to `Scalar.createApiReference`, `url` included. */
|
|
132
|
+
readonly configuration: Record<string, unknown>;
|
|
133
|
+
}
|
|
134
|
+
/** Configuration for the reference endpoint. */
|
|
135
|
+
interface ScalarReferenceOptions {
|
|
136
|
+
/**
|
|
137
|
+
* Path the HTML page is served from. Matched exactly, and *before* the
|
|
138
|
+
* document's own routes, so it does not need to appear in the document.
|
|
139
|
+
*
|
|
140
|
+
* @defaultValue `'/reference'`
|
|
141
|
+
*/
|
|
142
|
+
path?: string;
|
|
143
|
+
/**
|
|
144
|
+
* Path the document JSON is served from.
|
|
145
|
+
*
|
|
146
|
+
* @defaultValue `` `${path}/openapi.json` ``
|
|
147
|
+
*/
|
|
148
|
+
documentPath?: string;
|
|
149
|
+
/** Page title. @defaultValue the document's `info.title`, or `'API Reference'` */
|
|
150
|
+
title?: string;
|
|
151
|
+
/** Script URL for Scalar's standalone build. @defaultValue {@link SCALAR_CDN_URL} */
|
|
152
|
+
cdnUrl?: string;
|
|
153
|
+
/**
|
|
154
|
+
* Extra options merged into the `Scalar.createApiReference` config — theme,
|
|
155
|
+
* `darkMode`, `proxyUrl`, and anything else Scalar accepts. `url` is set
|
|
156
|
+
* from {@link documentPath} and can be overridden here.
|
|
157
|
+
*
|
|
158
|
+
* @see https://scalar.com/products/api-references/configuration
|
|
159
|
+
*/
|
|
160
|
+
configuration?: Record<string, unknown>;
|
|
161
|
+
/** `Cache-Control` on both responses. @defaultValue `'no-cache'` */
|
|
162
|
+
cacheControl?: string;
|
|
163
|
+
/** Render the page yourself, ignoring every option above except the paths. */
|
|
164
|
+
html?: (input: ScalarHtmlInput) => string;
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/types.d.ts
|
|
168
|
+
/** Where a parameter was declared. */
|
|
169
|
+
type ParameterIn = 'path' | 'query' | 'header' | 'cookie';
|
|
170
|
+
/** Deserialized, coerced parameters, grouped by where they came from. */
|
|
171
|
+
interface OpenApiParams {
|
|
172
|
+
readonly path: Readonly<Record<string, unknown>>;
|
|
173
|
+
readonly query: Readonly<Record<string, unknown>>;
|
|
174
|
+
readonly header: Readonly<Record<string, unknown>>;
|
|
175
|
+
readonly cookie: Readonly<Record<string, unknown>>;
|
|
176
|
+
}
|
|
177
|
+
/** One thing wrong with a request. */
|
|
178
|
+
interface OpenApiViolation {
|
|
179
|
+
/** Which half of the request it is about. */
|
|
180
|
+
readonly in: ParameterIn | 'body';
|
|
181
|
+
/** Parameter name. Absent for a violation of the body as a whole. */
|
|
182
|
+
readonly name?: string;
|
|
183
|
+
/** JSON pointer into the offending value, from the schema validator. */
|
|
184
|
+
readonly location?: string;
|
|
185
|
+
/** The JSON Schema keyword that failed, when one did. */
|
|
186
|
+
readonly keyword?: string;
|
|
187
|
+
/** What the validator objected to, mechanically. */
|
|
188
|
+
readonly message: string;
|
|
189
|
+
/**
|
|
190
|
+
* The document's own prose for whatever failed — the Parameter Object's
|
|
191
|
+
* `description`, or the `description` on the schema the failing keyword
|
|
192
|
+
* belongs to.
|
|
193
|
+
*
|
|
194
|
+
* {@link message} says what is wrong; this says what the thing is *for*, and
|
|
195
|
+
* it is the half a caller can usually act on. Absent when the document does
|
|
196
|
+
* not describe that field, or when `validate.describe` is off.
|
|
197
|
+
*/
|
|
198
|
+
readonly description?: string;
|
|
199
|
+
}
|
|
200
|
+
/** Why the middleware is refusing a request. */
|
|
201
|
+
type OpenApiRejectionKind = 'route_not_found' | 'method_not_allowed' | 'unsupported_media_type' | 'validation_failed';
|
|
202
|
+
/** Everything known about a refusal, handed to {@link WithOpenApiConfig.reject}. */
|
|
203
|
+
interface OpenApiRejection {
|
|
204
|
+
readonly kind: OpenApiRejectionKind;
|
|
205
|
+
/** Status the default response would use. */
|
|
206
|
+
readonly status: number;
|
|
207
|
+
/** The request's method, as sent. */
|
|
208
|
+
readonly method: string;
|
|
209
|
+
/** The request's pathname, before `basePath` is stripped. */
|
|
210
|
+
readonly pathname: string;
|
|
211
|
+
/** Path template that matched, when one did. */
|
|
212
|
+
readonly route?: string;
|
|
213
|
+
/** Methods the route does declare. Set on `method_not_allowed`. */
|
|
214
|
+
readonly allow?: readonly string[];
|
|
215
|
+
/** Media types the operation accepts. Set on `unsupported_media_type`. */
|
|
216
|
+
readonly accepts?: readonly string[];
|
|
217
|
+
/** Empty for the routing kinds, which have nothing per-field to report. */
|
|
218
|
+
readonly violations: readonly OpenApiViolation[];
|
|
219
|
+
}
|
|
220
|
+
/** Which halves of a request to check, and how strictly. */
|
|
221
|
+
interface OpenApiValidateOptions {
|
|
222
|
+
/** Check path parameters. @defaultValue `true` */
|
|
223
|
+
path?: boolean;
|
|
224
|
+
/** Check query parameters. @defaultValue `true` */
|
|
225
|
+
query?: boolean;
|
|
226
|
+
/** Check header parameters. @defaultValue `true` */
|
|
227
|
+
header?: boolean;
|
|
228
|
+
/** Check cookie parameters. @defaultValue `true` */
|
|
229
|
+
cookie?: boolean;
|
|
230
|
+
/**
|
|
231
|
+
* Check — and therefore read and parse — the request body. With this off,
|
|
232
|
+
* `ctx.openapi.body` is `undefined` and the handler reads the body itself.
|
|
233
|
+
*
|
|
234
|
+
* @defaultValue `true`
|
|
235
|
+
*/
|
|
236
|
+
body?: boolean;
|
|
237
|
+
/**
|
|
238
|
+
* What to do with query parameters the operation does not declare.
|
|
239
|
+
* `'allow'` ignores them; `'reject'` treats each as a violation.
|
|
240
|
+
*
|
|
241
|
+
* @defaultValue `'allow'`
|
|
242
|
+
*/
|
|
243
|
+
additionalQuery?: 'allow' | 'reject';
|
|
244
|
+
/** Status for a validation failure. @defaultValue `400` */
|
|
245
|
+
status?: number;
|
|
246
|
+
/**
|
|
247
|
+
* Include the document's `description` prose on each violation, so a caller
|
|
248
|
+
* is told what a field is for and not only that it is wrong.
|
|
249
|
+
*
|
|
250
|
+
* A document's descriptions are written for its consumers, which is who
|
|
251
|
+
* reads these errors — but turn this off if yours carries notes you would
|
|
252
|
+
* rather not return in a response body.
|
|
253
|
+
*
|
|
254
|
+
* @defaultValue `true`
|
|
255
|
+
*/
|
|
256
|
+
describe?: boolean;
|
|
257
|
+
/**
|
|
258
|
+
* Most violations to report in one rejection. One bad array in a large body
|
|
259
|
+
* can fail thousands of times, and nobody reads past the first few.
|
|
260
|
+
*
|
|
261
|
+
* @defaultValue `20`
|
|
262
|
+
*/
|
|
263
|
+
maxViolations?: number;
|
|
264
|
+
}
|
|
265
|
+
/** Per-instance configuration for `withOpenApi`. */
|
|
266
|
+
interface WithOpenApiConfig {
|
|
267
|
+
/**
|
|
268
|
+
* The OpenAPI 3.1 document. Read once, at construction: `$ref`s are
|
|
269
|
+
* followed, parameters merged, and path templates compiled, so a request
|
|
270
|
+
* costs a segment walk rather than a document traversal.
|
|
271
|
+
*
|
|
272
|
+
* External `$ref`s are not fetched — bundle the document first.
|
|
273
|
+
*/
|
|
274
|
+
document: OpenAPIObject;
|
|
275
|
+
/**
|
|
276
|
+
* Prefix stripped from the pathname before matching, for an API mounted
|
|
277
|
+
* under a sub-path. The reference endpoint's paths are **not** relative to
|
|
278
|
+
* it; they are matched against the full pathname.
|
|
279
|
+
*/
|
|
280
|
+
basePath?: string;
|
|
281
|
+
/**
|
|
282
|
+
* Check requests against the document, and reject the ones that do not fit.
|
|
283
|
+
*
|
|
284
|
+
* `false` turns rejection off entirely: routes are still matched and
|
|
285
|
+
* parameters still deserialized onto `ctx.openapi.params`, but nothing is
|
|
286
|
+
* refused for being invalid. Pass an object to check some halves of the
|
|
287
|
+
* request and not others.
|
|
288
|
+
*
|
|
289
|
+
* @defaultValue `true`
|
|
290
|
+
*/
|
|
291
|
+
validate?: boolean | OpenApiValidateOptions;
|
|
292
|
+
/**
|
|
293
|
+
* Convert parameter text into the JSON types the schemas describe, so
|
|
294
|
+
* `?limit=10` satisfies `type: integer`. Off, every non-string parameter in
|
|
295
|
+
* every document fails its own type check.
|
|
296
|
+
*
|
|
297
|
+
* @defaultValue `true`
|
|
298
|
+
*/
|
|
299
|
+
coerce?: boolean;
|
|
300
|
+
/**
|
|
301
|
+
* What to do with a pathname no path template matches. `'reject'` answers
|
|
302
|
+
* `404`; `'pass'` falls through to the handler with
|
|
303
|
+
* `ctx.openapi.matched === false`.
|
|
304
|
+
*
|
|
305
|
+
* @defaultValue `'reject'`
|
|
306
|
+
*/
|
|
307
|
+
onUnknownRoute?: 'reject' | 'pass';
|
|
308
|
+
/**
|
|
309
|
+
* What to do when the path matches but the operation is not declared for
|
|
310
|
+
* the request's method. `'reject'` answers `405` with an `Allow` header;
|
|
311
|
+
* `'pass'` falls through with `ctx.openapi.matched === false`.
|
|
312
|
+
*
|
|
313
|
+
* @defaultValue `'reject'`
|
|
314
|
+
*/
|
|
315
|
+
onUnknownMethod?: 'reject' | 'pass';
|
|
316
|
+
/**
|
|
317
|
+
* Serve a Scalar API reference and the document JSON. `true` takes every
|
|
318
|
+
* default — the page at `/reference`, the document at
|
|
319
|
+
* `/reference/openapi.json`.
|
|
320
|
+
*
|
|
321
|
+
* @defaultValue off
|
|
322
|
+
*/
|
|
323
|
+
reference?: boolean | ScalarReferenceOptions;
|
|
324
|
+
/**
|
|
325
|
+
* Answer CORS preflights and stamp `Access-Control-*` headers, with the
|
|
326
|
+
* policy derived from the document: a path's declared operations are its
|
|
327
|
+
* allowed methods, its `in: header` parameters and security schemes are its
|
|
328
|
+
* allowed request headers, its Response Objects' `headers` are what it
|
|
329
|
+
* exposes.
|
|
330
|
+
*
|
|
331
|
+
* `origin` is the one field that is never derived and so is required — a
|
|
332
|
+
* document says where an API lives, not who may call it.
|
|
333
|
+
*
|
|
334
|
+
* Rejections are stamped too. An unstamped `400` reaches a browser as an
|
|
335
|
+
* opaque CORS error rather than the violations it is carrying.
|
|
336
|
+
*
|
|
337
|
+
* @defaultValue off
|
|
338
|
+
*/
|
|
339
|
+
cors?: OpenApiCorsOptions;
|
|
340
|
+
/**
|
|
341
|
+
* JSON Schema draft the document's schemas are written against. Inferred
|
|
342
|
+
* from the document's `openapi` version, which is almost always right.
|
|
343
|
+
*/
|
|
344
|
+
schemaDraft?: SchemaDraft;
|
|
345
|
+
/**
|
|
346
|
+
* Leave a request alone entirely — no matching, no reference endpoint, no
|
|
347
|
+
* rejection. The handler still sees `ctx.openapi`, with
|
|
348
|
+
* `matched === false`.
|
|
349
|
+
*/
|
|
350
|
+
skip?: (req: Request) => boolean;
|
|
351
|
+
/**
|
|
352
|
+
* Answer a refusal yourself. Return `undefined` to fall back to the default
|
|
353
|
+
* response for that {@link OpenApiRejection}.
|
|
354
|
+
*/
|
|
355
|
+
reject?: (rejection: OpenApiRejection, req: Request) => Response | undefined | Promise<Response | undefined>;
|
|
356
|
+
}
|
|
357
|
+
/** `ctx.openapi` when an operation in the document describes the request. */
|
|
358
|
+
interface OpenApiMatched {
|
|
359
|
+
readonly matched: true;
|
|
360
|
+
/** The document, as passed to `withOpenApi`. */
|
|
361
|
+
readonly document: OpenAPIObject;
|
|
362
|
+
/** Path template that matched, e.g. `'/users/{id}'`. */
|
|
363
|
+
readonly route: string;
|
|
364
|
+
/** Lowercase method the operation was declared under. */
|
|
365
|
+
readonly method: HttpMethod;
|
|
366
|
+
/** The Operation Object, with its own `$ref` (if any) already followed. */
|
|
367
|
+
readonly operation: OperationObject;
|
|
368
|
+
readonly operationId: string | undefined;
|
|
369
|
+
/**
|
|
370
|
+
* Security requirements in force — the operation's, falling back to the
|
|
371
|
+
* document's. Contributed for a downstream auth middleware to act on; this
|
|
372
|
+
* middleware never enforces them.
|
|
373
|
+
*/
|
|
374
|
+
readonly security: SecurityRequirementObject[] | undefined;
|
|
375
|
+
/** Deserialized and (unless turned off) coerced parameters. */
|
|
376
|
+
readonly params: OpenApiParams;
|
|
377
|
+
/**
|
|
378
|
+
* The parsed request body. `undefined` when the operation declares none,
|
|
379
|
+
* when none was sent, when body validation is off, or when the media type
|
|
380
|
+
* is binary and was deliberately left unread.
|
|
381
|
+
*/
|
|
382
|
+
readonly body: unknown;
|
|
383
|
+
/** The `content` key that matched the request's content type. */
|
|
384
|
+
readonly mediaType: string | undefined;
|
|
385
|
+
/** `false` when `validate: false` — the request was matched, not checked. */
|
|
386
|
+
readonly validated: boolean;
|
|
387
|
+
}
|
|
388
|
+
/** `ctx.openapi` when the document does not describe the request. */
|
|
389
|
+
interface OpenApiUnmatched {
|
|
390
|
+
readonly matched: false;
|
|
391
|
+
/** Why nothing matched. */
|
|
392
|
+
readonly reason: 'skipped' | 'no_route' | 'no_operation';
|
|
393
|
+
readonly document: OpenAPIObject;
|
|
394
|
+
/** Set when the path matched but the method was not declared under it. */
|
|
395
|
+
readonly route: string | undefined;
|
|
396
|
+
/** The request's method, as sent. */
|
|
397
|
+
readonly method: string;
|
|
398
|
+
readonly operation: undefined;
|
|
399
|
+
readonly operationId: undefined;
|
|
400
|
+
readonly security: undefined;
|
|
401
|
+
readonly params: OpenApiParams;
|
|
402
|
+
readonly body: undefined;
|
|
403
|
+
readonly mediaType: undefined;
|
|
404
|
+
readonly validated: false;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* What lands at `ctx.openapi`.
|
|
408
|
+
*
|
|
409
|
+
* With the default `onUnknownRoute`/`onUnknownMethod` of `'reject'` and no
|
|
410
|
+
* `skip`, the handler only ever sees {@link OpenApiMatched} — anything else was
|
|
411
|
+
* already answered with a `404` or a `405`. Narrow with
|
|
412
|
+
* `if (!ctx.openapi.matched)` where those are set to `'pass'`.
|
|
413
|
+
*/
|
|
414
|
+
type OpenApiContribution = OpenApiMatched | OpenApiUnmatched;
|
|
415
|
+
//#endregion
|
|
416
|
+
//#region src/with-openapi.d.ts
|
|
417
|
+
/**
|
|
418
|
+
* Middleware that holds an API to its own description.
|
|
419
|
+
*
|
|
420
|
+
* @example Reject anything the document does not describe, and serve the docs.
|
|
421
|
+
* ```ts
|
|
422
|
+
* import { pipeline } from '@supabase/middleware'
|
|
423
|
+
* import { withOpenApi } from '@croutonian/with-openapi'
|
|
424
|
+
* import document from './openapi.json' with { type: 'json' }
|
|
425
|
+
*
|
|
426
|
+
* export default {
|
|
427
|
+
* fetch: pipeline(
|
|
428
|
+
* [withOpenApi({ document, reference: true })],
|
|
429
|
+
* async (_req, ctx) => {
|
|
430
|
+
* if (!ctx.openapi.matched) return new Response(null, { status: 404 })
|
|
431
|
+
* return Response.json({ op: ctx.openapi.operationId })
|
|
432
|
+
* },
|
|
433
|
+
* ),
|
|
434
|
+
* }
|
|
435
|
+
* ```
|
|
436
|
+
*
|
|
437
|
+
* @example Describe and document, but do not enforce.
|
|
438
|
+
* ```ts
|
|
439
|
+
* withOpenApi({
|
|
440
|
+
* document,
|
|
441
|
+
* validate: false,
|
|
442
|
+
* onUnknownRoute: 'pass',
|
|
443
|
+
* reference: { path: '/docs' },
|
|
444
|
+
* })
|
|
445
|
+
* ```
|
|
446
|
+
*
|
|
447
|
+
* @category Middleware
|
|
448
|
+
*/
|
|
449
|
+
declare const withOpenApi: Middleware<'openapi', WithOpenApiConfig, Record<never, never>, OpenApiContribution>;
|
|
450
|
+
//#endregion
|
|
451
|
+
export { type CorsOrigin, type CorsPolicy, type FetchHandler, type HttpMethod, type OpenApiContribution, type OpenApiCorsOptions, type OpenApiMatched, type OpenApiParams, type OpenApiRejection, type OpenApiRejectionKind, type OpenApiUnmatched, type OpenApiValidateOptions, type OpenApiViolation, type ParameterIn, SCALAR_CDN_URL, type ScalarHtmlInput, type ScalarReferenceOptions, type SchemaDraft, type WithOpenApiConfig, withOpenApi };
|