@tetsujs/openapi 0.4.2 → 0.5.1
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 +94 -0
- package/dist/src/controller.d.ts +6 -0
- package/dist/src/controller.d.ts.map +1 -1
- package/dist/src/emit.d.ts +1 -0
- package/dist/src/emit.d.ts.map +1 -1
- package/dist/src/envelopes.d.ts +9 -11
- package/dist/src/envelopes.d.ts.map +1 -1
- package/dist/src/errors.d.ts +74 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/index.d.ts +28 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +186 -86
- package/dist/src/index.js.map +9 -8
- package/dist/src/operation.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/controller.ts +8 -0
- package/src/emit.ts +1 -0
- package/src/envelopes.ts +84 -58
- package/src/errors.ts +177 -0
- package/src/index.ts +34 -1
- package/src/operation.ts +174 -93
package/README.md
CHANGED
|
@@ -75,6 +75,7 @@ the hash, to be prefixed with `sha384-`.
|
|
|
75
75
|
| `assets` | the renderer on jsDelivr, pinned | your own renderer URLs, with `integrity` hashes |
|
|
76
76
|
| `documentSelf` | `false` | include `/openapi.json` and `/docs` in the document |
|
|
77
77
|
| `onWarning` | `console.warn` | receives what could not be described |
|
|
78
|
+
| `errors` | the framework's envelope | an error format of your own — see [below](#an-error-format-of-your-own) |
|
|
78
79
|
|
|
79
80
|
## What goes into the document
|
|
80
81
|
|
|
@@ -151,6 +152,99 @@ When every alternative of a status is an envelope, the union carries a
|
|
|
151
152
|
`discriminator` on `error`, with the mapping from each code to its
|
|
152
153
|
definition, and a generated client narrows on the code.
|
|
153
154
|
|
|
155
|
+
A status is described by what answers with it. The route's part is the
|
|
156
|
+
`description` of the schema it declares — `.describe()` in Zod and
|
|
157
|
+
ArkType, `v.description()` in Valibot, `{ description }` in TypeBox — for
|
|
158
|
+
any status, a `200` as much as a `404`; a union described as a whole is
|
|
159
|
+
one description, one described branch by branch is one per branch. A
|
|
160
|
+
hook's part is its `documented()` description, the framework's its own.
|
|
161
|
+
One description is the status's description; several are a list, each
|
|
162
|
+
led by its code:
|
|
163
|
+
|
|
164
|
+
```md
|
|
165
|
+
- `ACCOUNT_DISABLED`: this account is disabled
|
|
166
|
+
- `CAPTCHA_FAILED`: the captcha token is missing or did not pass
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
A status nothing describes keeps its reason phrase — `Not Found`,
|
|
170
|
+
`Successful response`.
|
|
171
|
+
|
|
172
|
+
## An error format of your own
|
|
173
|
+
|
|
174
|
+
Every failure reaches the application's `onError` hooks — a thrown
|
|
175
|
+
`HttpError`, a validation or body failure, a `404`, a `405`, a rate limit's
|
|
176
|
+
refusal — so one hook answers all of them in your format:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
const inOurFormat = hook.onError(({ error }) => {
|
|
180
|
+
if (!(error instanceof HttpError)) return undefined;
|
|
181
|
+
|
|
182
|
+
const { status, error: code, ...rest } = error.body as ErrorBody;
|
|
183
|
+
|
|
184
|
+
return Response.json({ code, ...rest }, { status });
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The document is generated from the routes, not from that hook, so it is
|
|
189
|
+
told the same format with `errors`:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const errors: ErrorFormat = {
|
|
193
|
+
schema: ({ error, message, fields }) => ({
|
|
194
|
+
type: "object",
|
|
195
|
+
required: ["code", "message", ...Object.keys(fields)],
|
|
196
|
+
properties: {
|
|
197
|
+
code: error ? { type: "string", const: error } : { type: "string" },
|
|
198
|
+
message: { type: "string", ...(message ? { examples: [message] } : {}) },
|
|
199
|
+
...fields,
|
|
200
|
+
},
|
|
201
|
+
}),
|
|
202
|
+
discriminator: "code",
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
createApp({
|
|
206
|
+
hooks: { onError: [inOurFormat] },
|
|
207
|
+
routes: [api, docs({ info, errors })],
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`schema` describes one failure from what the generator knows of it: its
|
|
212
|
+
status, its code when known, an example message, and in `fields` what it
|
|
213
|
+
carries besides — `issues` for a validation failure, `retryAfter` for a
|
|
214
|
+
rate limit, a hook's own `fields`. It is used for the framework's failures
|
|
215
|
+
and the hooks' refusals alike.
|
|
216
|
+
|
|
217
|
+
`discriminator` names the top-level field that holds the code. Statuses of
|
|
218
|
+
envelopes are then discriminated on it, and a route's own envelope is
|
|
219
|
+
recognized by it — a `{ code: "ITEM_NOT_FOUND", … }` the route declares is
|
|
220
|
+
the same definition as a hook's. A format that nests its code has no
|
|
221
|
+
top-level field to name; it gives `code` instead, reading the code from a
|
|
222
|
+
route's schema:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
code: (schema) => {
|
|
226
|
+
const error = schema.properties?.error;
|
|
227
|
+
const code = typeof error === "object" ? error.properties?.code : undefined;
|
|
228
|
+
|
|
229
|
+
return typeof code === "object" && typeof code.const === "string" ? code.const : undefined;
|
|
230
|
+
},
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The hook and `errors` describe one format in two places — the core knows
|
|
234
|
+
nothing about documents, and a function cannot be read for the shape it
|
|
235
|
+
returns — so keep a test that holds them together: provoke a failure and
|
|
236
|
+
check its body against the document, with the JSON Schema validator of
|
|
237
|
+
your choice.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
test("a validation failure is what the document says", async () => {
|
|
241
|
+
const res = await request("/items", { method: "POST", body: "{}" });
|
|
242
|
+
const schema = document.components?.schemas?.ValidationFailed;
|
|
243
|
+
|
|
244
|
+
expect(validator.validate(schema, await res.json())).toBe(true);
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
154
248
|
## Documenting hooks
|
|
155
249
|
|
|
156
250
|
A hook that answers by itself — an auth check, a limiter — can say so, and
|
package/dist/src/controller.d.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import type { App, RouteDef, SchemaConfig, ValidatePath } from "@tetsujs/core";
|
|
16
16
|
import { onMount } from "@tetsujs/core";
|
|
17
17
|
import type { OpenApiDocument } from "./document.ts";
|
|
18
|
+
import type { ErrorFormat } from "./errors.ts";
|
|
18
19
|
import type { GeneratorWarning } from "./index.ts";
|
|
19
20
|
import type { DocsAssets, DocsUi } from "./page.ts";
|
|
20
21
|
/** What the documentation controller serves, and from where. */
|
|
@@ -23,6 +24,11 @@ export interface DocsOptions<Path extends string, UiPath extends string, Ui exte
|
|
|
23
24
|
readonly info: OpenApiDocument["info"];
|
|
24
25
|
/** Servers the API is reachable at. */
|
|
25
26
|
readonly servers?: OpenApiDocument["servers"];
|
|
27
|
+
/**
|
|
28
|
+
* How the application's errors look, when not like the framework's
|
|
29
|
+
* envelope — see `errors` on {@link openapi}.
|
|
30
|
+
*/
|
|
31
|
+
readonly errors?: ErrorFormat;
|
|
26
32
|
/** Where the document is served. Defaults to `/openapi.json`. */
|
|
27
33
|
readonly path?: Path & ValidatePath<Path>;
|
|
28
34
|
/** Where the page is served. Defaults to `/docs`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAS,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAGpD,gEAAgE;AAChE,MAAM,WAAW,WAAW,CAC1B,IAAI,SAAS,MAAM,EACnB,MAAM,SAAS,MAAM,EACrB,EAAE,SAAS,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK;IAE1C,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAEvC,uCAAuC;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;IAE9C,iEAAiE;IACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAE1C,qDAAqD;IACrD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAEhD;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAEjB,uEAAuE;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAE7B;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAEhC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC1D;AAED;;;;;;GAMG;AACH,qBAAa,cAAc,CACzB,IAAI,SAAS,MAAM,EACnB,MAAM,SAAS,MAAM,EACrB,IAAI,SAAS,OAAO,GAAG,IAAI;IAezB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAb1B,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IAErE,wDAAwD;IACxD,QAAQ,CAAC,EAAE,EAAE,IAAI,SAAS,IAAI,GAC1B,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,GACvD,SAAS,CAAC;IAEd,OAAO,CAAC,QAAQ,CAA8B;IAE9C,OAAO,CAAC,IAAI,CAAqB;IAEjC,YACmB,OAAO,EAAE,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,EACnD,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EAkCf;IAED;;;;;;;;OAQG;IACH,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/controller.ts"],"names":[],"mappings":"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"}
|
package/dist/src/emit.d.ts
CHANGED
package/dist/src/emit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/emit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B;AAED,yEAAyE;AACzE,KAAK,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEpC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,OAAO,CACrB,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EAC/B,SAAS,GAAE,SAAoB,GAC9B,gBAAgB,GAAG,SAAS,CAkB9B"}
|
|
1
|
+
{"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/emit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B;AAED,yEAAyE;AACzE,KAAK,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEpC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,OAAO,CACrB,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EAC/B,SAAS,GAAE,SAAoB,GAC9B,gBAAgB,GAAG,SAAS,CAkB9B"}
|
package/dist/src/envelopes.d.ts
CHANGED
|
@@ -20,14 +20,21 @@
|
|
|
20
20
|
* read first, before any operation is built, so the winner does not depend
|
|
21
21
|
* on which route happens to come first in the table.
|
|
22
22
|
*
|
|
23
|
+
* What an envelope looks like — how one is described, where its code is,
|
|
24
|
+
* what a client discriminates on — is the document's error format, which
|
|
25
|
+
* the registry carries for everything that builds or reads one.
|
|
26
|
+
*
|
|
23
27
|
* Internal to the package.
|
|
24
28
|
*
|
|
25
29
|
* @module
|
|
26
30
|
*/
|
|
27
31
|
import type { SchemaComponents, SchemaRef } from "./components.ts";
|
|
28
32
|
import type { JsonSchemaObject } from "./emit.ts";
|
|
33
|
+
import type { ResolvedFormat } from "./errors.ts";
|
|
29
34
|
/** The definitions of every envelope of one document. */
|
|
30
35
|
export interface Envelopes {
|
|
36
|
+
/** The document's error format. */
|
|
37
|
+
readonly format: ResolvedFormat;
|
|
31
38
|
/**
|
|
32
39
|
* Records a route's definition, before anything is referenced. The first
|
|
33
40
|
* route to declare a status and code defines it.
|
|
@@ -48,16 +55,7 @@ export interface Envelopes {
|
|
|
48
55
|
*/
|
|
49
56
|
codeOf(ref: string): string | undefined;
|
|
50
57
|
}
|
|
51
|
-
export declare function envelopes(components: SchemaComponents): Envelopes;
|
|
52
|
-
/**
|
|
53
|
-
* The error code of a schema that is an envelope: an object whose `error`
|
|
54
|
-
* is required and holds one string — a `const`, or an `enum` of one value.
|
|
55
|
-
*
|
|
56
|
-
* Read off what the validator emitted, not off a declaration: the route
|
|
57
|
-
* has no other way to say which of its responses is an envelope, and the
|
|
58
|
-
* shape is what a client would branch on anyway.
|
|
59
|
-
*/
|
|
60
|
-
export declare function envelopeCode(schema: JsonSchemaObject): string | undefined;
|
|
58
|
+
export declare function envelopes(components: SchemaComponents, format: ResolvedFormat): Envelopes;
|
|
61
59
|
/**
|
|
62
60
|
* The alternatives a schema stands for, as a list the status's own union
|
|
63
61
|
* can take in.
|
|
@@ -70,5 +68,5 @@ export declare function envelopeCode(schema: JsonSchemaObject): string | undefin
|
|
|
70
68
|
* beside its union, a `title` or a `$defs`, is left whole: those keywords
|
|
71
69
|
* belong to the union, and would be lost with it.
|
|
72
70
|
*/
|
|
73
|
-
export declare function branchesOf(schema: JsonSchemaObject): JsonSchemaObject[];
|
|
71
|
+
export declare function branchesOf(schema: JsonSchemaObject, code: (schema: JsonSchemaObject) => string | undefined): JsonSchemaObject[];
|
|
74
72
|
//# sourceMappingURL=envelopes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"envelopes.d.ts","sourceRoot":"","sources":["../../src/envelopes.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"envelopes.d.ts","sourceRoot":"","sources":["../../src/envelopes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,yDAAyD;AACzD,MAAM,WAAW,SAAS;IACxB,mCAAmC;IACnC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAEhC;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAEtE;;;;;;;OAOG;IACH,GAAG,CACD,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAC9B,SAAS,CAAC;IAEb;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACzC;AAED,wBAAgB,SAAS,CACvB,UAAU,EAAE,gBAAgB,EAC5B,MAAM,EAAE,cAAc,GACrB,SAAS,CAsEX;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,MAAM,GAAG,SAAS,GACrD,gBAAgB,EAAE,CA0BpB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape the document gives an error.
|
|
3
|
+
*
|
|
4
|
+
* Every failure an application answers with reaches its `onError` hooks,
|
|
5
|
+
* so one hook sets the format of all of them. The document is generated
|
|
6
|
+
* from the routes, not from that hook, and cannot see what it does: an
|
|
7
|
+
* application with a format of its own says so here too, and the framework's
|
|
8
|
+
* failures, the hooks' refusals and the routes' own envelopes are described
|
|
9
|
+
* in it. Keeping the two in step is the application's to do — the core knows
|
|
10
|
+
* nothing about documents, and a function cannot be read for the shape it
|
|
11
|
+
* returns.
|
|
12
|
+
*
|
|
13
|
+
* Without it the format is the framework's envelope,
|
|
14
|
+
* `{ status, message, error }`.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import type { JsonSchemaObject } from "./emit.ts";
|
|
19
|
+
import type { JsonSchema, JsonSchemaKeywords } from "./json-schema.ts";
|
|
20
|
+
/**
|
|
21
|
+
* One failure the document describes, as the generator knows it.
|
|
22
|
+
*
|
|
23
|
+
* This is everything known about a failure before it is answered: its
|
|
24
|
+
* status, its code when the framework or the hook knows it, an example of
|
|
25
|
+
* its message, and what else it carries — the `issues` of a validation
|
|
26
|
+
* failure, the `retryAfter` of a rate limit. Where each goes in the body
|
|
27
|
+
* is the format's to decide.
|
|
28
|
+
*/
|
|
29
|
+
export interface DocumentedFailure {
|
|
30
|
+
readonly status: number;
|
|
31
|
+
/** The code a client branches on, when it is known. */
|
|
32
|
+
readonly error?: string;
|
|
33
|
+
/** An example of the message; wording, never a contract. */
|
|
34
|
+
readonly message?: string;
|
|
35
|
+
/** What the failure carries besides, as JSON Schema by name. */
|
|
36
|
+
readonly fields: Readonly<Record<string, JsonSchema>>;
|
|
37
|
+
}
|
|
38
|
+
/** How the application's errors look, for the document to describe them. */
|
|
39
|
+
export interface ErrorFormat {
|
|
40
|
+
/** The body of one failure. */
|
|
41
|
+
readonly schema: (failure: DocumentedFailure) => JsonSchemaKeywords;
|
|
42
|
+
/**
|
|
43
|
+
* The top-level field that holds the code, for a client to discriminate
|
|
44
|
+
* on — `"code"` in `{ code, message }`. A status whose alternatives are
|
|
45
|
+
* all envelopes then carries a `discriminator` on it.
|
|
46
|
+
*
|
|
47
|
+
* None unless named: OpenAPI discriminates on a top-level field only, and
|
|
48
|
+
* a format that nests its code — `{ error: { code } }` — has none to
|
|
49
|
+
* name.
|
|
50
|
+
*/
|
|
51
|
+
readonly discriminator?: string;
|
|
52
|
+
/**
|
|
53
|
+
* The code of a schema a route or a hook declared, when it is an
|
|
54
|
+
* envelope in this format — how the document knows a route's own `404`
|
|
55
|
+
* is the same failure as a hook's, and gives both one definition.
|
|
56
|
+
*
|
|
57
|
+
* Read off the JSON Schema the validator emitted. By default, the
|
|
58
|
+
* `const` of the discriminator's field, when there is one; a nested
|
|
59
|
+
* format says where its code is.
|
|
60
|
+
*/
|
|
61
|
+
readonly code?: (schema: JsonSchemaKeywords) => string | undefined;
|
|
62
|
+
}
|
|
63
|
+
/** What the generator works with: every part of a format, filled in. */
|
|
64
|
+
export interface ResolvedFormat {
|
|
65
|
+
describe(failure: DocumentedFailure): JsonSchemaObject;
|
|
66
|
+
code(schema: JsonSchemaObject): string | undefined;
|
|
67
|
+
readonly discriminator: string | undefined;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A format as the generator uses it. Absent, the framework's envelope,
|
|
71
|
+
* discriminated on `error`.
|
|
72
|
+
*/
|
|
73
|
+
export declare function resolveFormat(format: ErrorFormat | undefined): ResolvedFormat;
|
|
74
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEvE;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,uDAAuD;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAE1B,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;CACvD;AAED,4EAA4E;AAC5E,MAAM,WAAW,WAAW;IAC1B,+BAA+B;IAC/B,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,kBAAkB,CAAC;IAEpE;;;;;;;;OAQG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAEhC;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,MAAM,GAAG,SAAS,CAAC;CACpE;AAED,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAAC;IACvD,IAAI,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAAC;IACnD,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,cAAc,CAqB7E"}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -20,11 +20,13 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import type { App } from "@tetsujs/core";
|
|
22
22
|
import type { OpenApiDocument } from "./document.ts";
|
|
23
|
+
import type { ErrorFormat } from "./errors.ts";
|
|
23
24
|
export type { DocumentedHeader, DocumentedResponse, HookContributions, HookDocs, SecurityAlternatives, SecurityRequirement, SecurityScheme, } from "./annotations.ts";
|
|
24
25
|
export { documented, secured } from "./annotations.ts";
|
|
25
26
|
export type { DocsOptions } from "./controller.ts";
|
|
26
27
|
export { DocsController, docs } from "./controller.ts";
|
|
27
28
|
export type { ContentMap, DocumentInfo, DocumentServer, HeaderObject, OpenApiDocument, OperationObject, ParameterObject, PathItemObject, ResponseObject, } from "./document.ts";
|
|
29
|
+
export type { DocumentedFailure, ErrorFormat } from "./errors.ts";
|
|
28
30
|
export type { JsonSchema, JsonSchemaKeywords, JsonSchemaType, } from "./json-schema.ts";
|
|
29
31
|
export type { DocsAssets, DocsPageOptions, DocsUi } from "./page.ts";
|
|
30
32
|
export { docsPage } from "./page.ts";
|
|
@@ -36,6 +38,32 @@ export interface OpenApiOptions {
|
|
|
36
38
|
readonly info: OpenApiDocument["info"];
|
|
37
39
|
/** Servers the API is reachable at. */
|
|
38
40
|
readonly servers?: OpenApiDocument["servers"];
|
|
41
|
+
/**
|
|
42
|
+
* How the application's errors look, when not like the framework's
|
|
43
|
+
* envelope — for an application whose `onError` hook answers in a format
|
|
44
|
+
* of its own. The framework's failures, the hooks' refusals and the
|
|
45
|
+
* routes' own envelopes are then described in it.
|
|
46
|
+
*
|
|
47
|
+
* @example `{ code, message }`, discriminated on `code`
|
|
48
|
+
* ```ts
|
|
49
|
+
* openapi(app, {
|
|
50
|
+
* info,
|
|
51
|
+
* errors: {
|
|
52
|
+
* schema: ({ error, message, fields }) => ({
|
|
53
|
+
* type: "object",
|
|
54
|
+
* required: ["code", "message", ...Object.keys(fields)],
|
|
55
|
+
* properties: {
|
|
56
|
+
* code: error ? { type: "string", const: error } : { type: "string" },
|
|
57
|
+
* message: { type: "string", ...(message ? { examples: [message] } : {}) },
|
|
58
|
+
* ...fields,
|
|
59
|
+
* },
|
|
60
|
+
* }),
|
|
61
|
+
* discriminator: "code",
|
|
62
|
+
* },
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
readonly errors?: ErrorFormat;
|
|
39
67
|
}
|
|
40
68
|
/**
|
|
41
69
|
* What the generator could not describe faithfully.
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,eAAe,CAAC;AAErE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACvD,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvD,YAAY,EACV,UAAU,EACV,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,eAAe,EACf,eAAe,EACf,eAAe,EACf,cAAc,EACd,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClE,YAAY,EACV,UAAU,EACV,kBAAkB,EAClB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAEvC,uCAAuC;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;IAE9C;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,sEAAsE;AACtE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,cAAc,GAAG,eAAe,CA8F1E"}
|