@nifrajs/schema 1.3.0 → 1.4.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/dist/openapi.d.ts +1 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/openapi.js +36 -39
- package/dist/openapi.js.map +1 -1
- package/package.json +2 -2
- package/src/openapi.ts +52 -55
package/dist/openapi.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContractShape, Server } from "@nifrajs/core";
|
|
2
|
+
import { type JsonSchema } from "@nifrajs/core/reflection";
|
|
2
3
|
/**
|
|
3
4
|
* OpenAPI 3.1 generation. We model a practical slice of the spec — enough to feed Swagger UI / codegen
|
|
4
5
|
* and to validate structurally: paths, parameters, request bodies, responses (incl. non-200 and
|
|
@@ -44,7 +45,6 @@ export interface ToOpenAPIOptions {
|
|
|
44
45
|
*/
|
|
45
46
|
readonly operations?: Readonly<Record<string, Record<string, unknown>>>;
|
|
46
47
|
}
|
|
47
|
-
type JsonSchema = Record<string, unknown>;
|
|
48
48
|
interface OpenAPIParameter {
|
|
49
49
|
readonly name: string;
|
|
50
50
|
readonly in: "path" | "query";
|
package/dist/openapi.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}
|
package/dist/openapi.js
CHANGED
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
return ("jsonSchema" in schema &&
|
|
3
|
-
schema.jsonSchema !== undefined &&
|
|
4
|
-
schema.jsonSchema !== null);
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Extract clean JSON Schema from a schema, or `undefined` for a BYO Standard Schema with no JSON
|
|
8
|
-
* Schema. The `JSON` round-trip strips TypeBox's Symbol-keyed metadata; a string `$id` survives it and
|
|
9
|
-
* drives `$ref` reuse.
|
|
10
|
-
*/
|
|
11
|
-
function toJsonSchema(schema) {
|
|
12
|
-
if (!hasJsonSchema(schema))
|
|
13
|
-
return undefined;
|
|
14
|
-
return JSON.parse(JSON.stringify(schema.jsonSchema));
|
|
15
|
-
}
|
|
1
|
+
import { reflectRoutes, reflectSchema, } from "@nifrajs/core/reflection";
|
|
16
2
|
/**
|
|
17
3
|
* Collects schemas that carry a `$id` into `components.schemas`, returning a `$ref` in their place —
|
|
18
4
|
* so a schema used by N operations is emitted once. Schemas without a `$id` stay inline (the existing
|
|
@@ -24,6 +10,8 @@ class SchemaStore {
|
|
|
24
10
|
collect(schema) {
|
|
25
11
|
if (schema === undefined)
|
|
26
12
|
return undefined;
|
|
13
|
+
if (typeof schema === "boolean")
|
|
14
|
+
return schema;
|
|
27
15
|
const id = typeof schema.$id === "string" ? schema.$id : undefined;
|
|
28
16
|
if (id === undefined)
|
|
29
17
|
return schema;
|
|
@@ -66,17 +54,15 @@ function pathParameters(path) {
|
|
|
66
54
|
function queryParameters(schema) {
|
|
67
55
|
if (schema === undefined)
|
|
68
56
|
return [];
|
|
69
|
-
const
|
|
70
|
-
// Only an object schema decomposes into individual query parameters.
|
|
71
|
-
if (
|
|
57
|
+
const fields = schema.fields;
|
|
58
|
+
// Only an introspectable object schema decomposes into individual query parameters.
|
|
59
|
+
if (fields === undefined)
|
|
72
60
|
return [];
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return Object.entries(properties).map(([name, propSchema]) => ({
|
|
76
|
-
name,
|
|
61
|
+
return fields.map((field) => ({
|
|
62
|
+
name: field.name,
|
|
77
63
|
in: "query",
|
|
78
|
-
required: required
|
|
79
|
-
schema:
|
|
64
|
+
required: field.required,
|
|
65
|
+
schema: field.schema,
|
|
80
66
|
}));
|
|
81
67
|
}
|
|
82
68
|
const STATUS_TEXT = {
|
|
@@ -94,21 +80,21 @@ const STATUS_TEXT = {
|
|
|
94
80
|
"502": "Bad Gateway",
|
|
95
81
|
"503": "Service Unavailable",
|
|
96
82
|
};
|
|
97
|
-
|
|
98
|
-
* {@link buildResponses} emits as non-2xx OpenAPI responses. */
|
|
99
|
-
function errorsToResponses(errors) {
|
|
83
|
+
function reflectedErrorsToResponses(errors) {
|
|
100
84
|
if (errors === undefined)
|
|
101
85
|
return undefined;
|
|
102
86
|
const out = {};
|
|
103
|
-
for (const [status,
|
|
104
|
-
|
|
87
|
+
for (const [status, reflection] of Object.entries(errors)) {
|
|
88
|
+
if (reflection.jsonSchema !== undefined || reflection.standard !== undefined) {
|
|
89
|
+
out[status] = { description: STATUS_TEXT[status] ?? "Error", schema: reflection };
|
|
90
|
+
}
|
|
105
91
|
}
|
|
106
|
-
return out;
|
|
92
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
107
93
|
}
|
|
108
94
|
function buildResponses(input, store) {
|
|
109
95
|
const responses = {};
|
|
110
96
|
if (input.response !== undefined) {
|
|
111
|
-
const schema = store.collect(
|
|
97
|
+
const schema = store.collect(input.response.jsonSchema);
|
|
112
98
|
responses["200"] =
|
|
113
99
|
schema !== undefined
|
|
114
100
|
? {
|
|
@@ -124,7 +110,7 @@ function buildResponses(input, store) {
|
|
|
124
110
|
if (input.responses !== undefined) {
|
|
125
111
|
for (const [status, def] of Object.entries(input.responses)) {
|
|
126
112
|
const response = { description: def.description ?? "" };
|
|
127
|
-
const schema = def.schema !== undefined ? store.collect(
|
|
113
|
+
const schema = def.schema !== undefined ? store.collect(def.schema.jsonSchema) : undefined;
|
|
128
114
|
if (schema !== undefined)
|
|
129
115
|
response.content = { [def.contentType ?? "application/json"]: { schema } };
|
|
130
116
|
responses[status] = response;
|
|
@@ -150,7 +136,7 @@ function buildOperation(input, store) {
|
|
|
150
136
|
if (parameters.length > 0)
|
|
151
137
|
operation.parameters = parameters;
|
|
152
138
|
if (input.body !== undefined) {
|
|
153
|
-
const schema = store.collect(
|
|
139
|
+
const schema = store.collect(input.body.jsonSchema);
|
|
154
140
|
if (schema !== undefined) {
|
|
155
141
|
operation.requestBody = {
|
|
156
142
|
required: true,
|
|
@@ -181,7 +167,7 @@ export function toOpenAPI(input, options = {}) {
|
|
|
181
167
|
const paths = {};
|
|
182
168
|
const store = new SchemaStore();
|
|
183
169
|
if (isApp(input)) {
|
|
184
|
-
for (const route of input
|
|
170
|
+
for (const route of reflectRoutes(input)) {
|
|
185
171
|
addOperation(paths, route.method, {
|
|
186
172
|
path: route.path,
|
|
187
173
|
body: route.schema?.body,
|
|
@@ -189,7 +175,7 @@ export function toOpenAPI(input, options = {}) {
|
|
|
189
175
|
// A route may now declare a `response` contract — emit it as the 200 body schema.
|
|
190
176
|
response: route.schema?.response,
|
|
191
177
|
// …and an `errors` contract — emit each as a non-2xx response.
|
|
192
|
-
responses:
|
|
178
|
+
responses: reflectedErrorsToResponses(route.schema?.errors),
|
|
193
179
|
operationId: undefined,
|
|
194
180
|
}, store, options.operations);
|
|
195
181
|
}
|
|
@@ -198,9 +184,9 @@ export function toOpenAPI(input, options = {}) {
|
|
|
198
184
|
for (const [name, op] of Object.entries(input)) {
|
|
199
185
|
addOperation(paths, op.method, {
|
|
200
186
|
path: op.path,
|
|
201
|
-
body: op.body,
|
|
202
|
-
query: op.query,
|
|
203
|
-
response: op.response,
|
|
187
|
+
body: op.body === undefined ? undefined : reflectSchema(op.body),
|
|
188
|
+
query: op.query === undefined ? undefined : reflectSchema(op.query),
|
|
189
|
+
response: op.response === undefined ? undefined : reflectSchema(op.response),
|
|
204
190
|
operationId: name,
|
|
205
191
|
summary: op.summary,
|
|
206
192
|
description: op.description,
|
|
@@ -209,7 +195,18 @@ export function toOpenAPI(input, options = {}) {
|
|
|
209
195
|
security: op.security,
|
|
210
196
|
requestContentType: op.requestContentType,
|
|
211
197
|
responseContentType: op.responseContentType,
|
|
212
|
-
responses: op.responses
|
|
198
|
+
responses: op.responses === undefined
|
|
199
|
+
? undefined
|
|
200
|
+
: Object.fromEntries(Object.entries(op.responses).map(([status, response]) => {
|
|
201
|
+
const { schema, ...metadata } = response;
|
|
202
|
+
return [
|
|
203
|
+
status,
|
|
204
|
+
{
|
|
205
|
+
...metadata,
|
|
206
|
+
...(schema === undefined ? {} : { schema: reflectSchema(schema) }),
|
|
207
|
+
},
|
|
208
|
+
];
|
|
209
|
+
})),
|
|
213
210
|
}, store, options.operations);
|
|
214
211
|
}
|
|
215
212
|
}
|
package/dist/openapi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nifrajs/schema",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Built-in schema builder `t` — TypeBox-backed (free JSON Schema + compiled validators), implements Standard Schema so nifra routes validate it natively.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@sinclair/typebox": "^0.34.49"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@nifrajs/core": "^1.
|
|
51
|
+
"@nifrajs/core": "^1.4.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@nifrajs/core": "workspace:*",
|
package/src/openapi.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import type { ContractShape, Server
|
|
1
|
+
import type { ContractShape, Server } from "@nifrajs/core"
|
|
2
|
+
import {
|
|
3
|
+
type JsonSchema,
|
|
4
|
+
type ReflectedRouteSchema,
|
|
5
|
+
reflectRoutes,
|
|
6
|
+
reflectSchema,
|
|
7
|
+
type SchemaReflection,
|
|
8
|
+
} from "@nifrajs/core/reflection"
|
|
2
9
|
|
|
3
10
|
/**
|
|
4
11
|
* OpenAPI 3.1 generation. We model a practical slice of the spec — enough to feed Swagger UI / codegen
|
|
@@ -50,8 +57,6 @@ export interface ToOpenAPIOptions {
|
|
|
50
57
|
readonly operations?: Readonly<Record<string, Record<string, unknown>>>
|
|
51
58
|
}
|
|
52
59
|
|
|
53
|
-
type JsonSchema = Record<string, unknown>
|
|
54
|
-
|
|
55
60
|
interface OpenAPIParameter {
|
|
56
61
|
readonly name: string
|
|
57
62
|
readonly in: "path" | "query"
|
|
@@ -100,27 +105,6 @@ export interface OpenAPIDocument {
|
|
|
100
105
|
readonly components?: OpenAPIComponents
|
|
101
106
|
}
|
|
102
107
|
|
|
103
|
-
/** A Standard Schema that also carries a raw JSON Schema (i.e. a `t`/TypeBox schema). */
|
|
104
|
-
type WithJsonSchema = StandardSchemaV1 & { readonly jsonSchema: unknown }
|
|
105
|
-
|
|
106
|
-
function hasJsonSchema(schema: StandardSchemaV1): schema is WithJsonSchema {
|
|
107
|
-
return (
|
|
108
|
-
"jsonSchema" in schema &&
|
|
109
|
-
(schema as { jsonSchema?: unknown }).jsonSchema !== undefined &&
|
|
110
|
-
(schema as { jsonSchema?: unknown }).jsonSchema !== null
|
|
111
|
-
)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Extract clean JSON Schema from a schema, or `undefined` for a BYO Standard Schema with no JSON
|
|
116
|
-
* Schema. The `JSON` round-trip strips TypeBox's Symbol-keyed metadata; a string `$id` survives it and
|
|
117
|
-
* drives `$ref` reuse.
|
|
118
|
-
*/
|
|
119
|
-
function toJsonSchema(schema: StandardSchemaV1): JsonSchema | undefined {
|
|
120
|
-
if (!hasJsonSchema(schema)) return undefined
|
|
121
|
-
return JSON.parse(JSON.stringify(schema.jsonSchema)) as JsonSchema
|
|
122
|
-
}
|
|
123
|
-
|
|
124
108
|
/**
|
|
125
109
|
* Collects schemas that carry a `$id` into `components.schemas`, returning a `$ref` in their place —
|
|
126
110
|
* so a schema used by N operations is emitted once. Schemas without a `$id` stay inline (the existing
|
|
@@ -132,6 +116,7 @@ class SchemaStore {
|
|
|
132
116
|
/** Hoist a schema with a `$id` into components + return a `$ref`; otherwise return it inline. */
|
|
133
117
|
collect(schema: JsonSchema | undefined): JsonSchema | undefined {
|
|
134
118
|
if (schema === undefined) return undefined
|
|
119
|
+
if (typeof schema === "boolean") return schema
|
|
135
120
|
const id = typeof schema.$id === "string" ? schema.$id : undefined
|
|
136
121
|
if (id === undefined) return schema
|
|
137
122
|
if (this.schemas[id] === undefined) {
|
|
@@ -172,26 +157,24 @@ function pathParameters(path: string): OpenAPIParameter[] {
|
|
|
172
157
|
return params
|
|
173
158
|
}
|
|
174
159
|
|
|
175
|
-
function queryParameters(schema:
|
|
160
|
+
function queryParameters(schema: SchemaReflection | undefined): OpenAPIParameter[] {
|
|
176
161
|
if (schema === undefined) return []
|
|
177
|
-
const
|
|
178
|
-
// Only an object schema decomposes into individual query parameters.
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return Object.entries(properties).map(([name, propSchema]) => ({
|
|
183
|
-
name,
|
|
162
|
+
const fields = schema.fields
|
|
163
|
+
// Only an introspectable object schema decomposes into individual query parameters.
|
|
164
|
+
if (fields === undefined) return []
|
|
165
|
+
return fields.map((field) => ({
|
|
166
|
+
name: field.name,
|
|
184
167
|
in: "query",
|
|
185
|
-
required: required
|
|
186
|
-
schema:
|
|
168
|
+
required: field.required,
|
|
169
|
+
schema: field.schema,
|
|
187
170
|
}))
|
|
188
171
|
}
|
|
189
172
|
|
|
190
173
|
interface OperationInput {
|
|
191
174
|
readonly path: string
|
|
192
|
-
readonly body:
|
|
193
|
-
readonly query:
|
|
194
|
-
readonly response:
|
|
175
|
+
readonly body: SchemaReflection | undefined
|
|
176
|
+
readonly query: SchemaReflection | undefined
|
|
177
|
+
readonly response: SchemaReflection | undefined
|
|
195
178
|
readonly operationId: string | undefined
|
|
196
179
|
// `| undefined` (not just `?`) so a contract op's optional fields — `string | undefined` etc. — are
|
|
197
180
|
// assignable under `exactOptionalPropertyTypes` when spread into this literal.
|
|
@@ -204,7 +187,7 @@ interface OperationInput {
|
|
|
204
187
|
readonly responseContentType?: string | undefined
|
|
205
188
|
readonly responses?:
|
|
206
189
|
| Readonly<
|
|
207
|
-
Record<string, { description?: string; schema?:
|
|
190
|
+
Record<string, { description?: string; schema?: SchemaReflection; contentType?: string }>
|
|
208
191
|
>
|
|
209
192
|
| undefined
|
|
210
193
|
}
|
|
@@ -225,17 +208,17 @@ const STATUS_TEXT: Readonly<Record<string, string>> = {
|
|
|
225
208
|
"503": "Service Unavailable",
|
|
226
209
|
}
|
|
227
210
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
function errorsToResponses(
|
|
231
|
-
errors: Readonly<Record<number, StandardSchemaV1>> | undefined,
|
|
211
|
+
function reflectedErrorsToResponses(
|
|
212
|
+
errors: ReflectedRouteSchema["errors"],
|
|
232
213
|
): OperationInput["responses"] {
|
|
233
214
|
if (errors === undefined) return undefined
|
|
234
|
-
const out: Record<string, { description?: string; schema?:
|
|
235
|
-
for (const [status,
|
|
236
|
-
|
|
215
|
+
const out: Record<string, { description?: string; schema?: SchemaReflection }> = {}
|
|
216
|
+
for (const [status, reflection] of Object.entries(errors)) {
|
|
217
|
+
if (reflection.jsonSchema !== undefined || reflection.standard !== undefined) {
|
|
218
|
+
out[status] = { description: STATUS_TEXT[status] ?? "Error", schema: reflection }
|
|
219
|
+
}
|
|
237
220
|
}
|
|
238
|
-
return out
|
|
221
|
+
return Object.keys(out).length > 0 ? out : undefined
|
|
239
222
|
}
|
|
240
223
|
|
|
241
224
|
function buildResponses(
|
|
@@ -244,7 +227,7 @@ function buildResponses(
|
|
|
244
227
|
): Record<string, OpenAPIResponse> {
|
|
245
228
|
const responses: Record<string, OpenAPIResponse> = {}
|
|
246
229
|
if (input.response !== undefined) {
|
|
247
|
-
const schema = store.collect(
|
|
230
|
+
const schema = store.collect(input.response.jsonSchema)
|
|
248
231
|
responses["200"] =
|
|
249
232
|
schema !== undefined
|
|
250
233
|
? {
|
|
@@ -259,7 +242,7 @@ function buildResponses(
|
|
|
259
242
|
if (input.responses !== undefined) {
|
|
260
243
|
for (const [status, def] of Object.entries(input.responses)) {
|
|
261
244
|
const response: OpenAPIResponse = { description: def.description ?? "" }
|
|
262
|
-
const schema = def.schema !== undefined ? store.collect(
|
|
245
|
+
const schema = def.schema !== undefined ? store.collect(def.schema.jsonSchema) : undefined
|
|
263
246
|
if (schema !== undefined)
|
|
264
247
|
response.content = { [def.contentType ?? "application/json"]: { schema } }
|
|
265
248
|
responses[status] = response
|
|
@@ -281,7 +264,7 @@ function buildOperation(input: OperationInput, store: SchemaStore): OpenAPIOpera
|
|
|
281
264
|
if (parameters.length > 0) operation.parameters = parameters
|
|
282
265
|
|
|
283
266
|
if (input.body !== undefined) {
|
|
284
|
-
const schema = store.collect(
|
|
267
|
+
const schema = store.collect(input.body.jsonSchema)
|
|
285
268
|
if (schema !== undefined) {
|
|
286
269
|
operation.requestBody = {
|
|
287
270
|
required: true,
|
|
@@ -325,7 +308,7 @@ export function toOpenAPI(
|
|
|
325
308
|
const store = new SchemaStore()
|
|
326
309
|
|
|
327
310
|
if (isApp(input)) {
|
|
328
|
-
for (const route of input
|
|
311
|
+
for (const route of reflectRoutes(input)) {
|
|
329
312
|
addOperation(
|
|
330
313
|
paths,
|
|
331
314
|
route.method,
|
|
@@ -336,7 +319,7 @@ export function toOpenAPI(
|
|
|
336
319
|
// A route may now declare a `response` contract — emit it as the 200 body schema.
|
|
337
320
|
response: route.schema?.response,
|
|
338
321
|
// …and an `errors` contract — emit each as a non-2xx response.
|
|
339
|
-
responses:
|
|
322
|
+
responses: reflectedErrorsToResponses(route.schema?.errors),
|
|
340
323
|
operationId: undefined,
|
|
341
324
|
},
|
|
342
325
|
store,
|
|
@@ -350,9 +333,9 @@ export function toOpenAPI(
|
|
|
350
333
|
op.method,
|
|
351
334
|
{
|
|
352
335
|
path: op.path,
|
|
353
|
-
body: op.body,
|
|
354
|
-
query: op.query,
|
|
355
|
-
response: op.response,
|
|
336
|
+
body: op.body === undefined ? undefined : reflectSchema(op.body),
|
|
337
|
+
query: op.query === undefined ? undefined : reflectSchema(op.query),
|
|
338
|
+
response: op.response === undefined ? undefined : reflectSchema(op.response),
|
|
356
339
|
operationId: name,
|
|
357
340
|
summary: op.summary,
|
|
358
341
|
description: op.description,
|
|
@@ -361,7 +344,21 @@ export function toOpenAPI(
|
|
|
361
344
|
security: op.security,
|
|
362
345
|
requestContentType: op.requestContentType,
|
|
363
346
|
responseContentType: op.responseContentType,
|
|
364
|
-
responses:
|
|
347
|
+
responses:
|
|
348
|
+
op.responses === undefined
|
|
349
|
+
? undefined
|
|
350
|
+
: Object.fromEntries(
|
|
351
|
+
Object.entries(op.responses).map(([status, response]) => {
|
|
352
|
+
const { schema, ...metadata } = response
|
|
353
|
+
return [
|
|
354
|
+
status,
|
|
355
|
+
{
|
|
356
|
+
...metadata,
|
|
357
|
+
...(schema === undefined ? {} : { schema: reflectSchema(schema) }),
|
|
358
|
+
},
|
|
359
|
+
]
|
|
360
|
+
}),
|
|
361
|
+
),
|
|
365
362
|
},
|
|
366
363
|
store,
|
|
367
364
|
options.operations,
|