@trpc/openapi 11.15.1-alpha → 11.15.2-alpha
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 +1 -1
- package/dist/chunk-CJ2cON1m.mjs +33 -0
- package/dist/cli.js +219 -38
- package/dist/heyapi/index.mjs +2 -1
- package/dist/heyapi/index.mjs.map +1 -1
- package/dist/index.cjs +244 -39
- package/dist/index.d.cts +93 -35
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +94 -35
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +241 -41
- package/dist/index.mjs.map +1 -1
- package/dist/{objectSpread2-UxrN8MPM.mjs → objectSpread2-Blgb1OZh.mjs} +3 -27
- package/dist/{objectSpread2-UxrN8MPM.mjs.map → objectSpread2-Blgb1OZh.mjs.map} +1 -1
- package/package.json +8 -4
- package/skills/openapi/SKILL.md +1 -1
- package/src/generate.ts +393 -112
- package/src/index.ts +2 -1
- package/src/schemaExtraction.ts +99 -29
- package/src/types.ts +151 -0
package/src/index.ts
CHANGED
package/src/schemaExtraction.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
$ZodTypeDef,
|
|
14
14
|
GlobalMeta,
|
|
15
15
|
} from 'zod/v4/core';
|
|
16
|
-
import type {
|
|
16
|
+
import type { SchemaObject } from './types';
|
|
17
17
|
|
|
18
18
|
/** Description strings extracted from Zod `.describe()` calls, keyed by dot-delimited property path. */
|
|
19
19
|
export interface DescriptionMap {
|
|
@@ -81,7 +81,6 @@ const wrapperDefTypes: ReadonlySet<$ZodTypeDef['type']> = new Set([
|
|
|
81
81
|
'pipe',
|
|
82
82
|
'transform',
|
|
83
83
|
'promise',
|
|
84
|
-
'lazy',
|
|
85
84
|
]);
|
|
86
85
|
|
|
87
86
|
/**
|
|
@@ -129,7 +128,7 @@ export function extractZodDescriptions(schema: unknown): DescriptionMap | null {
|
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
// Walk object shape
|
|
132
|
-
walkZodShape(schema, '', { registry, map });
|
|
131
|
+
walkZodShape(schema, '', { registry, map, seenLazy: new Set() });
|
|
133
132
|
if (map.properties.size > 0) hasAny = true;
|
|
134
133
|
|
|
135
134
|
return hasAny ? map : null;
|
|
@@ -138,9 +137,26 @@ export function extractZodDescriptions(schema: unknown): DescriptionMap | null {
|
|
|
138
137
|
function walkZodShape(
|
|
139
138
|
schema: $ZodType,
|
|
140
139
|
prefix: string,
|
|
141
|
-
ctx: {
|
|
140
|
+
ctx: {
|
|
141
|
+
registry: $ZodRegistry<GlobalMeta>;
|
|
142
|
+
map: DescriptionMap;
|
|
143
|
+
seenLazy: Set<$ZodType>;
|
|
144
|
+
},
|
|
142
145
|
): void {
|
|
143
146
|
const unwrapped = unwrapZodSchema(schema);
|
|
147
|
+
const def = unwrapped._zod.def;
|
|
148
|
+
|
|
149
|
+
if (def.type === 'lazy' && 'getter' in def) {
|
|
150
|
+
if (ctx.seenLazy.has(unwrapped)) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
ctx.seenLazy.add(unwrapped);
|
|
154
|
+
const inner = (def as { getter: () => unknown }).getter();
|
|
155
|
+
if (isZodSchema(inner)) {
|
|
156
|
+
walkZodShape(inner, prefix, ctx);
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
144
160
|
|
|
145
161
|
// If this is an array, check for a description on the element schema itself
|
|
146
162
|
// (stored as `[]` in the path) and recurse into the element's shape.
|
|
@@ -325,23 +341,75 @@ function isProcedure(
|
|
|
325
341
|
* JSON schema produced by the TypeScript type checker. Mutates in place.
|
|
326
342
|
*/
|
|
327
343
|
export function applyDescriptions(
|
|
328
|
-
schema:
|
|
344
|
+
schema: SchemaObject,
|
|
329
345
|
descs: DescriptionMap,
|
|
346
|
+
schemas?: Record<string, SchemaObject>,
|
|
330
347
|
): void {
|
|
331
348
|
if (descs.self) {
|
|
332
349
|
schema.description = descs.self;
|
|
333
350
|
}
|
|
334
351
|
|
|
335
352
|
for (const [propPath, description] of descs.properties) {
|
|
336
|
-
setNestedDescription(
|
|
353
|
+
setNestedDescription({
|
|
354
|
+
schema,
|
|
355
|
+
pathParts: propPath.split('.'),
|
|
356
|
+
description,
|
|
357
|
+
schemas,
|
|
358
|
+
});
|
|
337
359
|
}
|
|
338
360
|
}
|
|
339
361
|
|
|
340
|
-
function
|
|
341
|
-
schema:
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
362
|
+
function resolveSchemaRef(
|
|
363
|
+
schema: SchemaObject,
|
|
364
|
+
schemas?: Record<string, SchemaObject>,
|
|
365
|
+
): SchemaObject | null {
|
|
366
|
+
const ref = schema.$ref;
|
|
367
|
+
if (!ref) {
|
|
368
|
+
return schema;
|
|
369
|
+
}
|
|
370
|
+
if (!schemas || !ref.startsWith('#/components/schemas/')) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const refName = ref.slice('#/components/schemas/'.length);
|
|
375
|
+
return refName ? (schemas[refName] ?? null) : null;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function getArrayItemsSchema(schema: SchemaObject): SchemaObject | null {
|
|
379
|
+
const items = schema.items;
|
|
380
|
+
if (schema.type !== 'array' || items == null || items === false) {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
return items;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function getPropertySchema(
|
|
387
|
+
schema: SchemaObject,
|
|
388
|
+
propertyName: string,
|
|
389
|
+
): SchemaObject | null {
|
|
390
|
+
return schema.properties?.[propertyName] ?? null;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function setLeafDescription(schema: SchemaObject, description: string): void {
|
|
394
|
+
if (schema.$ref) {
|
|
395
|
+
const ref = schema.$ref;
|
|
396
|
+
delete schema.$ref;
|
|
397
|
+
schema.allOf = [{ $ref: ref }, ...(schema.allOf ?? [])];
|
|
398
|
+
}
|
|
399
|
+
schema.description = description;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function setNestedDescription({
|
|
403
|
+
schema,
|
|
404
|
+
pathParts,
|
|
405
|
+
description,
|
|
406
|
+
schemas,
|
|
407
|
+
}: {
|
|
408
|
+
schema: SchemaObject;
|
|
409
|
+
pathParts: string[];
|
|
410
|
+
description: string;
|
|
411
|
+
schemas?: Record<string, SchemaObject>;
|
|
412
|
+
}): void {
|
|
345
413
|
if (pathParts.length === 0) return;
|
|
346
414
|
|
|
347
415
|
const [head, ...rest] = pathParts;
|
|
@@ -349,35 +417,37 @@ function setNestedDescription(
|
|
|
349
417
|
|
|
350
418
|
// `[]` means "array items" — navigate to the `items` sub-schema
|
|
351
419
|
if (head === '[]') {
|
|
352
|
-
const items =
|
|
353
|
-
schema.type === 'array' &&
|
|
354
|
-
schema.items &&
|
|
355
|
-
typeof schema.items === 'object'
|
|
356
|
-
? schema.items
|
|
357
|
-
: null;
|
|
420
|
+
const items = getArrayItemsSchema(schema);
|
|
358
421
|
if (!items) return;
|
|
359
422
|
if (rest.length === 0) {
|
|
360
|
-
items
|
|
423
|
+
setLeafDescription(items, description);
|
|
361
424
|
} else {
|
|
362
|
-
|
|
425
|
+
const target = resolveSchemaRef(items, schemas) ?? items;
|
|
426
|
+
setNestedDescription({
|
|
427
|
+
schema: target,
|
|
428
|
+
pathParts: rest,
|
|
429
|
+
description,
|
|
430
|
+
schemas,
|
|
431
|
+
});
|
|
363
432
|
}
|
|
364
433
|
return;
|
|
365
434
|
}
|
|
366
435
|
|
|
367
|
-
const propSchema = schema
|
|
368
|
-
if (!propSchema
|
|
436
|
+
const propSchema = getPropertySchema(schema, head);
|
|
437
|
+
if (!propSchema) return;
|
|
369
438
|
|
|
370
439
|
if (rest.length === 0) {
|
|
371
440
|
// Leaf — Zod .describe() takes priority over JSDoc
|
|
372
|
-
propSchema
|
|
441
|
+
setLeafDescription(propSchema, description);
|
|
373
442
|
} else {
|
|
374
443
|
// For arrays, step through `items` transparently
|
|
375
|
-
const target =
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
444
|
+
const target = getArrayItemsSchema(propSchema) ?? propSchema;
|
|
445
|
+
const resolvedTarget = resolveSchemaRef(target, schemas) ?? target;
|
|
446
|
+
setNestedDescription({
|
|
447
|
+
schema: resolvedTarget,
|
|
448
|
+
pathParts: rest,
|
|
449
|
+
description,
|
|
450
|
+
schemas,
|
|
451
|
+
});
|
|
382
452
|
}
|
|
383
453
|
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { OpenAPIV3_1 as BaseOpenAPIV3_1 } from 'openapi-types';
|
|
2
|
+
|
|
3
|
+
export type Replace<TTarget, TReplaceWith> = Omit<TTarget, keyof TReplaceWith> &
|
|
4
|
+
TReplaceWith;
|
|
5
|
+
|
|
6
|
+
export type SchemaType =
|
|
7
|
+
| 'array'
|
|
8
|
+
| 'boolean'
|
|
9
|
+
| 'integer'
|
|
10
|
+
| 'null'
|
|
11
|
+
| 'number'
|
|
12
|
+
| 'object'
|
|
13
|
+
| 'string';
|
|
14
|
+
|
|
15
|
+
export type PrimitiveSchemaType = SchemaType;
|
|
16
|
+
|
|
17
|
+
export type HttpMethods = BaseOpenAPIV3_1.HttpMethods;
|
|
18
|
+
export type ReferenceObject = BaseOpenAPIV3_1.ReferenceObject;
|
|
19
|
+
export type ExampleObject = BaseOpenAPIV3_1.ExampleObject;
|
|
20
|
+
export type DiscriminatorObject = BaseOpenAPIV3_1.DiscriminatorObject;
|
|
21
|
+
export type ExternalDocumentationObject =
|
|
22
|
+
BaseOpenAPIV3_1.ExternalDocumentationObject;
|
|
23
|
+
export type XMLObject = BaseOpenAPIV3_1.XMLObject;
|
|
24
|
+
export type LinkObject = BaseOpenAPIV3_1.LinkObject;
|
|
25
|
+
export type SecuritySchemeObject = BaseOpenAPIV3_1.SecuritySchemeObject;
|
|
26
|
+
|
|
27
|
+
export type SchemaObject = Replace<
|
|
28
|
+
BaseOpenAPIV3_1.BaseSchemaObject,
|
|
29
|
+
{
|
|
30
|
+
$ref?: string;
|
|
31
|
+
$defs?: Record<string, SchemaObject>;
|
|
32
|
+
$schema?: string;
|
|
33
|
+
type?: string | string[];
|
|
34
|
+
properties?: Record<string, SchemaObject>;
|
|
35
|
+
required?: string[];
|
|
36
|
+
items?: SchemaObject | false;
|
|
37
|
+
prefixItems?: SchemaObject[];
|
|
38
|
+
const?: string | number | boolean | null;
|
|
39
|
+
enum?: (string | number | boolean | null)[];
|
|
40
|
+
oneOf?: SchemaObject[];
|
|
41
|
+
anyOf?: SchemaObject[];
|
|
42
|
+
allOf?: SchemaObject[];
|
|
43
|
+
not?: SchemaObject;
|
|
44
|
+
additionalProperties?: boolean | SchemaObject;
|
|
45
|
+
discriminator?: DiscriminatorObject;
|
|
46
|
+
externalDocs?: ExternalDocumentationObject;
|
|
47
|
+
xml?: XMLObject;
|
|
48
|
+
contentMediaType?: string;
|
|
49
|
+
exclusiveMinimum?: boolean | number;
|
|
50
|
+
exclusiveMaximum?: boolean | number;
|
|
51
|
+
}
|
|
52
|
+
>;
|
|
53
|
+
|
|
54
|
+
export type SchemaLike = SchemaObject;
|
|
55
|
+
|
|
56
|
+
export interface ArraySchemaObject extends SchemaObject {
|
|
57
|
+
type: 'array';
|
|
58
|
+
items: SchemaObject | false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type MediaTypeObject = Replace<
|
|
62
|
+
BaseOpenAPIV3_1.MediaTypeObject,
|
|
63
|
+
{
|
|
64
|
+
schema?: SchemaObject | ReferenceObject;
|
|
65
|
+
examples?: Record<string, ReferenceObject | ExampleObject>;
|
|
66
|
+
}
|
|
67
|
+
>;
|
|
68
|
+
|
|
69
|
+
export interface ParameterBaseObject
|
|
70
|
+
extends Replace<
|
|
71
|
+
BaseOpenAPIV3_1.ParameterBaseObject,
|
|
72
|
+
{
|
|
73
|
+
schema?: SchemaObject | ReferenceObject;
|
|
74
|
+
examples?: Record<string, ReferenceObject | ExampleObject>;
|
|
75
|
+
content?: Record<string, MediaTypeObject>;
|
|
76
|
+
}
|
|
77
|
+
> {}
|
|
78
|
+
|
|
79
|
+
export interface ParameterObject extends ParameterBaseObject {
|
|
80
|
+
name: string;
|
|
81
|
+
in: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type HeaderObject = ParameterBaseObject;
|
|
85
|
+
|
|
86
|
+
export type RequestBodyObject = Replace<
|
|
87
|
+
BaseOpenAPIV3_1.RequestBodyObject,
|
|
88
|
+
{
|
|
89
|
+
content: Record<string, MediaTypeObject>;
|
|
90
|
+
}
|
|
91
|
+
>;
|
|
92
|
+
|
|
93
|
+
export type ResponseObject = Replace<
|
|
94
|
+
BaseOpenAPIV3_1.ResponseObject,
|
|
95
|
+
{
|
|
96
|
+
headers?: Record<string, ReferenceObject | HeaderObject>;
|
|
97
|
+
content?: Record<string, MediaTypeObject>;
|
|
98
|
+
links?: Record<string, ReferenceObject | LinkObject>;
|
|
99
|
+
}
|
|
100
|
+
>;
|
|
101
|
+
|
|
102
|
+
export type ResponsesObject = Record<string, ReferenceObject | ResponseObject>;
|
|
103
|
+
|
|
104
|
+
export type OperationObject<T extends {} = {}> = Replace<
|
|
105
|
+
BaseOpenAPIV3_1.OperationObject<T>,
|
|
106
|
+
{
|
|
107
|
+
parameters?: (ReferenceObject | ParameterObject)[];
|
|
108
|
+
requestBody?: ReferenceObject | RequestBodyObject;
|
|
109
|
+
responses?: ResponsesObject;
|
|
110
|
+
callbacks?: Record<string, ReferenceObject | CallbackObject>;
|
|
111
|
+
}
|
|
112
|
+
> &
|
|
113
|
+
T;
|
|
114
|
+
|
|
115
|
+
export type PathItemObject<T extends {} = {}> = Replace<
|
|
116
|
+
BaseOpenAPIV3_1.PathItemObject<T>,
|
|
117
|
+
{
|
|
118
|
+
parameters?: (ReferenceObject | ParameterObject)[];
|
|
119
|
+
}
|
|
120
|
+
> & {
|
|
121
|
+
[method in HttpMethods]?: OperationObject<T>;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export type PathsObject<T extends {} = {}, TPath extends {} = {}> = Record<
|
|
125
|
+
string,
|
|
126
|
+
(PathItemObject<T> & TPath) | undefined
|
|
127
|
+
>;
|
|
128
|
+
|
|
129
|
+
export type CallbackObject = Record<string, PathItemObject | ReferenceObject>;
|
|
130
|
+
|
|
131
|
+
export type ComponentsObject = Replace<
|
|
132
|
+
BaseOpenAPIV3_1.ComponentsObject,
|
|
133
|
+
{
|
|
134
|
+
schemas?: Record<string, SchemaObject>;
|
|
135
|
+
responses?: Record<string, ReferenceObject | ResponseObject>;
|
|
136
|
+
parameters?: Record<string, ReferenceObject | ParameterObject>;
|
|
137
|
+
requestBodies?: Record<string, ReferenceObject | RequestBodyObject>;
|
|
138
|
+
headers?: Record<string, ReferenceObject | HeaderObject>;
|
|
139
|
+
links?: Record<string, ReferenceObject | LinkObject>;
|
|
140
|
+
callbacks?: Record<string, ReferenceObject | CallbackObject>;
|
|
141
|
+
pathItems?: Record<string, ReferenceObject | PathItemObject>;
|
|
142
|
+
}
|
|
143
|
+
>;
|
|
144
|
+
|
|
145
|
+
export type Document<T extends {} = {}> = Replace<
|
|
146
|
+
BaseOpenAPIV3_1.Document<T>,
|
|
147
|
+
{
|
|
148
|
+
paths?: PathsObject<T>;
|
|
149
|
+
components?: ComponentsObject;
|
|
150
|
+
}
|
|
151
|
+
>;
|