@moostjs/swagger 0.5.31 → 0.5.33
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/index.cjs +330 -304
- package/dist/index.d.ts +4 -2
- package/dist/index.mjs +330 -304
- package/package.json +6 -8
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { Const, Controller, Moost, getMoostMate, useControllerContext, useEventLogger } from "moost";
|
|
2
2
|
import { Get, HeaderHook, SetHeader, StatusHook, Url } from "@moostjs/event-http";
|
|
3
|
-
import { ZodSkip, getZodType, getZodTypeForProp, z } from "@moostjs/zod";
|
|
4
3
|
import { THeaderHook, useSetHeaders } from "@wooksjs/event-http";
|
|
5
4
|
import { serveFile } from "@wooksjs/http-static";
|
|
6
5
|
import Path from "path";
|
|
7
6
|
import { getAbsoluteFSPath } from "swagger-ui-dist";
|
|
8
|
-
import { parseZodType } from "zod-parser";
|
|
9
7
|
|
|
10
8
|
//#region packages/swagger/src/swagger.mate.ts
|
|
11
9
|
function getSwaggerMate() {
|
|
@@ -53,7 +51,11 @@ function SwaggerExample(example) {
|
|
|
53
51
|
|
|
54
52
|
//#endregion
|
|
55
53
|
//#region packages/swagger/src/mapping.ts
|
|
54
|
+
const globalSchemas = {};
|
|
55
|
+
let schemaRefs = /* @__PURE__ */ new WeakMap();
|
|
56
|
+
const nameToType = /* @__PURE__ */ new Map();
|
|
56
57
|
function mapToSwaggerSpec(metadata, options, logger) {
|
|
58
|
+
resetSchemaRegistry();
|
|
57
59
|
const swaggerSpec = {
|
|
58
60
|
openapi: "3.0.0",
|
|
59
61
|
info: {
|
|
@@ -83,50 +85,30 @@ function mapToSwaggerSpec(metadata, options, logger) {
|
|
|
83
85
|
const newCode = code === "0" ? getDefaultStatusCode(handlerMethod) : code;
|
|
84
86
|
for (const [contentType, conf] of Object.entries(responseConfigs)) {
|
|
85
87
|
const { response, example } = conf;
|
|
86
|
-
const schema =
|
|
88
|
+
const schema = resolveSwaggerSchemaFromConfig(response);
|
|
87
89
|
if (schema) {
|
|
88
90
|
responses = responses || {};
|
|
89
|
-
|
|
91
|
+
const schemaWithExample = example !== void 0 ? {
|
|
90
92
|
...schema,
|
|
91
|
-
example
|
|
92
|
-
}
|
|
93
|
+
example
|
|
94
|
+
} : schema;
|
|
95
|
+
responses[newCode] = { content: { [contentType]: { schema: schemaWithExample } } };
|
|
93
96
|
}
|
|
94
97
|
}
|
|
95
98
|
}
|
|
96
99
|
else if (hmeta?.returnType) {
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
"
|
|
102
|
-
"ZodArray",
|
|
103
|
-
"ZodBoolean"
|
|
104
|
-
].includes(parsed.$type)) {
|
|
105
|
-
const schema = getSwaggerSchema(parsed);
|
|
106
|
-
if (schema) {
|
|
107
|
-
responses = responses || {};
|
|
108
|
-
responses[getDefaultStatusCode(handlerMethod)] = { content: { "*/*": { schema } } };
|
|
109
|
-
}
|
|
100
|
+
const ensured = ensureSchema(hmeta.returnType);
|
|
101
|
+
const schema = toSchemaOrRef(ensured);
|
|
102
|
+
if (schema) {
|
|
103
|
+
responses = responses || {};
|
|
104
|
+
responses[getDefaultStatusCode(handlerMethod)] = { content: { "*/*": { schema } } };
|
|
110
105
|
}
|
|
111
106
|
}
|
|
112
107
|
let reqBodyRequired = true;
|
|
113
|
-
const
|
|
108
|
+
const bodyContent = {};
|
|
114
109
|
if (hmeta?.swaggerRequestBody) for (const [contentType, type] of Object.entries(hmeta.swaggerRequestBody)) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (type instanceof z.ZodType) zt = type;
|
|
118
|
-
else if (typeof type === "function") zt = getZodType({ type });
|
|
119
|
-
if (zt) {
|
|
120
|
-
const parsed = myParseZod(zt);
|
|
121
|
-
if ([
|
|
122
|
-
"ZodString",
|
|
123
|
-
"ZodNumber",
|
|
124
|
-
"ZodObject",
|
|
125
|
-
"ZodArray",
|
|
126
|
-
"ZodBoolean"
|
|
127
|
-
].includes(parsed.$type)) schema = getSwaggerSchema(parsed);
|
|
128
|
-
}
|
|
129
|
-
bodyConfig[contentType] = { schema };
|
|
110
|
+
const schema = resolveSwaggerSchemaFromConfig(type);
|
|
111
|
+
if (schema) bodyContent[contentType] = { schema };
|
|
130
112
|
}
|
|
131
113
|
swaggerSpec.paths[handlerPath][handlerMethod] = {
|
|
132
114
|
summary: handlerDescription,
|
|
@@ -152,298 +134,360 @@ function mapToSwaggerSpec(metadata, options, logger) {
|
|
|
152
134
|
in: param.in,
|
|
153
135
|
description: param.description,
|
|
154
136
|
required: !!param.required,
|
|
155
|
-
schema:
|
|
137
|
+
schema: resolveSwaggerSchemaFromConfig(param.type) || { type: "string" }
|
|
156
138
|
});
|
|
157
139
|
for (const param of hmeta?.swaggerParams || []) addParam({
|
|
158
140
|
name: param.name,
|
|
159
141
|
in: param.in,
|
|
160
142
|
description: param.description,
|
|
161
143
|
required: !!param.required,
|
|
162
|
-
schema:
|
|
144
|
+
schema: resolveSwaggerSchemaFromConfig(param.type) || { type: "string" }
|
|
163
145
|
});
|
|
164
146
|
for (const paramName of handler.registeredAs[0].args) {
|
|
165
147
|
const paramIndex = handler.meta.params.findIndex((param) => param.paramSource === "ROUTE" && param.paramName === paramName);
|
|
166
148
|
const paramMeta = handler.meta.params[paramIndex];
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (paramMeta) {
|
|
170
|
-
const zodType = getZodTypeForProp({
|
|
171
|
-
type: controller.type,
|
|
172
|
-
key: handler.method,
|
|
173
|
-
index: paramIndex
|
|
174
|
-
}, {
|
|
175
|
-
type: paramMeta.type,
|
|
176
|
-
additionalMeta: paramMeta
|
|
177
|
-
}, void 0, logger);
|
|
178
|
-
parsed = myParseZod(zodType);
|
|
179
|
-
schema = getSwaggerSchema(parsed, true);
|
|
180
|
-
}
|
|
149
|
+
const ensured = ensureSchema(paramMeta?.type);
|
|
150
|
+
const schema = toSchemaOrRef(ensured) || { type: "string" };
|
|
181
151
|
addParam({
|
|
182
152
|
name: paramName,
|
|
183
153
|
in: "path",
|
|
184
154
|
description: paramMeta ? paramMeta.description : void 0,
|
|
185
|
-
required: !paramMeta
|
|
186
|
-
schema
|
|
155
|
+
required: !paramMeta?.optional,
|
|
156
|
+
schema
|
|
187
157
|
});
|
|
188
158
|
}
|
|
189
159
|
for (let i = 0; i < handler.meta.params.length; i++) {
|
|
190
160
|
const paramMeta = handler.meta.params[i];
|
|
191
161
|
if (paramMeta.paramSource && ["QUERY_ITEM", "QUERY"].includes(paramMeta.paramSource)) {
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
162
|
+
const ensured = ensureSchema(paramMeta.type);
|
|
163
|
+
if (paramMeta.paramSource === "QUERY_ITEM") {
|
|
164
|
+
const schema = toSchemaOrRef(ensured);
|
|
165
|
+
const normalized = schema ? normalizeQueryParamSchema(schema) : void 0;
|
|
166
|
+
endpointSpec.parameters.push({
|
|
167
|
+
name: paramMeta.paramName || "",
|
|
168
|
+
in: "query",
|
|
169
|
+
description: paramMeta.description,
|
|
170
|
+
required: !paramMeta.optional,
|
|
171
|
+
schema: normalized || { type: "string" }
|
|
172
|
+
});
|
|
173
|
+
} else if (paramMeta.paramSource === "QUERY") {
|
|
174
|
+
const schema = ensured?.schema;
|
|
175
|
+
if (schema?.type === "object" && schema.properties) {
|
|
176
|
+
const requiredProps = new Set(schema.required || []);
|
|
177
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
|
178
|
+
const propertySchema = cloneSchema(value);
|
|
179
|
+
const normalizedProperty = normalizeQueryParamSchema(propertySchema);
|
|
180
|
+
if (normalizedProperty) endpointSpec.parameters.push({
|
|
181
|
+
name: key,
|
|
182
|
+
in: "query",
|
|
183
|
+
description: normalizedProperty.description,
|
|
184
|
+
required: !paramMeta.optional && requiredProps.has(key),
|
|
185
|
+
schema: normalizedProperty
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
} else if (ensured) {
|
|
189
|
+
const schema$1 = toSchemaOrRef(ensured);
|
|
190
|
+
const normalized = schema$1 ? normalizeQueryParamSchema(schema$1) : void 0;
|
|
191
|
+
endpointSpec.parameters.push({
|
|
192
|
+
name: paramMeta.paramName || "",
|
|
214
193
|
in: "query",
|
|
215
|
-
description:
|
|
216
|
-
required: !
|
|
217
|
-
schema:
|
|
218
|
-
};
|
|
219
|
-
endpointSpec.parameters.push(swaggerSchema);
|
|
194
|
+
description: paramMeta.description,
|
|
195
|
+
required: !paramMeta.optional,
|
|
196
|
+
schema: normalized || { type: "string" }
|
|
197
|
+
});
|
|
220
198
|
}
|
|
221
199
|
}
|
|
222
200
|
}
|
|
223
201
|
if (paramMeta.paramSource === "BODY") {
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
additionalMeta: paramMeta
|
|
231
|
-
}, void 0, logger);
|
|
232
|
-
const parsed = myParseZod(zodType);
|
|
233
|
-
let contentType = "";
|
|
234
|
-
switch (parsed.$type) {
|
|
235
|
-
case "ZodString":
|
|
236
|
-
case "ZodNumber":
|
|
237
|
-
case "ZodBigInt":
|
|
238
|
-
case "ZodBoolean":
|
|
239
|
-
case "ZodDate":
|
|
240
|
-
case "ZodEnum":
|
|
241
|
-
case "ZodNativeEnum":
|
|
242
|
-
case "ZodLiteral": {
|
|
243
|
-
contentType = "text/plan";
|
|
244
|
-
break;
|
|
245
|
-
}
|
|
246
|
-
default: contentType = "application/json";
|
|
202
|
+
const ensured = ensureSchema(paramMeta.type);
|
|
203
|
+
const schema = toSchemaOrRef(ensured);
|
|
204
|
+
if (schema) {
|
|
205
|
+
const contentType = inferBodyContentType(schema, ensured?.schema);
|
|
206
|
+
if (!bodyContent[contentType]) bodyContent[contentType] = { schema };
|
|
207
|
+
reqBodyRequired = !paramMeta.optional;
|
|
247
208
|
}
|
|
248
|
-
if (!bodyConfig[contentType]) bodyConfig[contentType] = { schema: getSwaggerSchema(parsed) };
|
|
249
|
-
reqBodyRequired = !zodType.isOptional() && !paramMeta.optional;
|
|
250
209
|
}
|
|
251
210
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
211
|
+
const bodyEntries = Object.entries(bodyContent).filter((entry) => entry[1] && entry[1].schema !== void 0);
|
|
212
|
+
if (bodyEntries.length) {
|
|
213
|
+
const content = {};
|
|
214
|
+
for (const [contentType, { schema }] of bodyEntries) content[contentType] = { schema };
|
|
215
|
+
endpointSpec.requestBody = {
|
|
216
|
+
content,
|
|
217
|
+
required: reqBodyRequired
|
|
218
|
+
};
|
|
219
|
+
}
|
|
256
220
|
}
|
|
257
221
|
}
|
|
258
222
|
return swaggerSpec;
|
|
259
223
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
224
|
+
function resolveSwaggerSchemaFromConfig(type) {
|
|
225
|
+
if (type === void 0) return void 0;
|
|
226
|
+
const ensured = ensureSchema(type);
|
|
227
|
+
return toSchemaOrRef(ensured);
|
|
228
|
+
}
|
|
229
|
+
function toSchemaOrRef(result) {
|
|
230
|
+
if (!result) return void 0;
|
|
231
|
+
if (result.ref) return { $ref: result.ref };
|
|
232
|
+
return cloneSchema(result.schema);
|
|
233
|
+
}
|
|
234
|
+
function inferBodyContentType(schema, resolved) {
|
|
235
|
+
const target = resolved ?? resolveSchemaFromRef(schema);
|
|
236
|
+
const schemaType = target?.type ?? schema.type;
|
|
237
|
+
if (schemaType && [
|
|
238
|
+
"string",
|
|
239
|
+
"number",
|
|
240
|
+
"integer",
|
|
241
|
+
"boolean"
|
|
242
|
+
].includes(schemaType)) return "text/plain";
|
|
243
|
+
return "application/json";
|
|
244
|
+
}
|
|
245
|
+
const SIMPLE_QUERY_TYPES = new Set([
|
|
246
|
+
"string",
|
|
247
|
+
"number",
|
|
248
|
+
"integer",
|
|
249
|
+
"boolean"
|
|
250
|
+
]);
|
|
251
|
+
function normalizeQueryParamSchema(schema) {
|
|
252
|
+
const target = resolveSchemaFromRef(schema) || schema;
|
|
253
|
+
if (!target) return void 0;
|
|
254
|
+
if (target.type === "array") return isArrayOfSimpleItems(target.items) ? schema : void 0;
|
|
255
|
+
return isSimpleSchema(schema) ? schema : void 0;
|
|
256
|
+
}
|
|
257
|
+
function isArrayOfSimpleItems(items) {
|
|
258
|
+
if (!items) return false;
|
|
259
|
+
if (Array.isArray(items)) {
|
|
260
|
+
if (items.length === 0) return false;
|
|
261
|
+
return items.every((entry) => isSimpleSchema(entry));
|
|
271
262
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
263
|
+
return isSimpleSchema(items);
|
|
264
|
+
}
|
|
265
|
+
function isSimpleSchema(schema, seen = /* @__PURE__ */ new Set()) {
|
|
266
|
+
if (!schema) return false;
|
|
267
|
+
if (seen.has(schema)) return false;
|
|
268
|
+
seen.add(schema);
|
|
269
|
+
if (schema.$ref) {
|
|
270
|
+
const resolved = resolveSchemaFromRef(schema);
|
|
271
|
+
if (!resolved) return false;
|
|
272
|
+
return isSimpleSchema(resolved, seen);
|
|
279
273
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
274
|
+
if (typeof schema.type === "string" && SIMPLE_QUERY_TYPES.has(schema.type)) return true;
|
|
275
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) return true;
|
|
276
|
+
if (schema.const !== void 0) return true;
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
function resetSchemaRegistry() {
|
|
280
|
+
schemaRefs = /* @__PURE__ */ new WeakMap();
|
|
281
|
+
nameToType.clear();
|
|
282
|
+
for (const key of Object.keys(globalSchemas)) delete globalSchemas[key];
|
|
283
|
+
}
|
|
284
|
+
function ensureSchema(type) {
|
|
285
|
+
if (type === void 0 || type === null) return void 0;
|
|
286
|
+
const resolution = createSchemaResolution(type);
|
|
287
|
+
if (!resolution) return void 0;
|
|
288
|
+
if (resolution.kind === "inline") return {
|
|
289
|
+
schema: cloneSchema(resolution.schema),
|
|
290
|
+
isComponent: false
|
|
291
|
+
};
|
|
292
|
+
const schemaClone = cloneSchema(resolution.schema);
|
|
293
|
+
const componentName = ensureComponentName(resolution.typeRef, schemaClone, resolution.suggestedName);
|
|
294
|
+
return {
|
|
295
|
+
schema: cloneSchema(globalSchemas[componentName]),
|
|
296
|
+
ref: `#/components/schemas/${componentName}`,
|
|
297
|
+
componentName,
|
|
298
|
+
isComponent: true
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function createSchemaResolution(type) {
|
|
302
|
+
if (type === void 0 || type === null) return void 0;
|
|
303
|
+
if (isSwaggerSchema(type)) return {
|
|
304
|
+
kind: "inline",
|
|
305
|
+
schema: cloneSchema(type)
|
|
306
|
+
};
|
|
307
|
+
if (Array.isArray(type)) {
|
|
308
|
+
if (type.length === 1) {
|
|
309
|
+
const itemEnsured = ensureSchema(type[0]);
|
|
310
|
+
const itemsSchema = toSchemaOrRef(itemEnsured);
|
|
311
|
+
return {
|
|
312
|
+
kind: "inline",
|
|
313
|
+
schema: {
|
|
314
|
+
type: "array",
|
|
315
|
+
items: itemsSchema
|
|
316
|
+
}
|
|
317
|
+
};
|
|
284
318
|
}
|
|
319
|
+
return {
|
|
320
|
+
kind: "inline",
|
|
321
|
+
schema: { type: "array" }
|
|
322
|
+
};
|
|
285
323
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
324
|
+
if (isLiteralValue(type)) return {
|
|
325
|
+
kind: "inline",
|
|
326
|
+
schema: schemaFromLiteral(type)
|
|
327
|
+
};
|
|
328
|
+
if (isPrimitiveConstructor(type)) return {
|
|
329
|
+
kind: "inline",
|
|
330
|
+
schema: schemaFromPrimitiveCtor(type)
|
|
331
|
+
};
|
|
332
|
+
if (typeof type === "function") {
|
|
333
|
+
const resolution = schemaFromFunction(type);
|
|
334
|
+
if (resolution) return resolution;
|
|
291
335
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
schema.enum = Object.keys(parsed.$value);
|
|
296
|
-
}
|
|
336
|
+
if (typeof type === "object") {
|
|
337
|
+
const resolution = schemaFromInstance(type);
|
|
338
|
+
if (resolution) return resolution;
|
|
297
339
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
schema
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
schema.type = "integer";
|
|
311
|
-
break;
|
|
312
|
-
}
|
|
313
|
-
case "ZodBoolean": {
|
|
314
|
-
schema.type = "boolean";
|
|
315
|
-
break;
|
|
316
|
-
}
|
|
317
|
-
case "ZodLiteral": {
|
|
318
|
-
asLiteral();
|
|
319
|
-
break;
|
|
320
|
-
}
|
|
321
|
-
case "ZodEnum": {
|
|
322
|
-
asEnum();
|
|
323
|
-
break;
|
|
324
|
-
}
|
|
325
|
-
case "ZodNativeEnum": {
|
|
326
|
-
asNativeEnum();
|
|
327
|
-
break;
|
|
328
|
-
}
|
|
329
|
-
case "ZodDate": {
|
|
330
|
-
schema.type = "string";
|
|
331
|
-
break;
|
|
332
|
-
}
|
|
333
|
-
case "ZodNull": {
|
|
334
|
-
schema.type = "null";
|
|
335
|
-
break;
|
|
336
|
-
}
|
|
337
|
-
default: return void 0;
|
|
340
|
+
return void 0;
|
|
341
|
+
}
|
|
342
|
+
function schemaFromFunction(fn) {
|
|
343
|
+
const ctor = fn;
|
|
344
|
+
if (typeof ctor.toJsonSchema === "function") {
|
|
345
|
+
const schema = asSwaggerSchema(ctor.toJsonSchema());
|
|
346
|
+
return {
|
|
347
|
+
kind: "component",
|
|
348
|
+
schema,
|
|
349
|
+
typeRef: ctor,
|
|
350
|
+
suggestedName: ctor.name
|
|
351
|
+
};
|
|
338
352
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
case "ZodEnum": {
|
|
361
|
-
asEnum();
|
|
362
|
-
break;
|
|
363
|
-
}
|
|
364
|
-
case "ZodNativeEnum": {
|
|
365
|
-
asNativeEnum();
|
|
366
|
-
break;
|
|
367
|
-
}
|
|
368
|
-
case "ZodDate": {
|
|
369
|
-
schema.type = "string";
|
|
370
|
-
break;
|
|
371
|
-
}
|
|
372
|
-
case "ZodNull": {
|
|
373
|
-
schema.type = "null";
|
|
374
|
-
break;
|
|
375
|
-
}
|
|
376
|
-
case "ZodFunction":
|
|
377
|
-
case "ZodSymbol":
|
|
378
|
-
case "ZodUndefined":
|
|
379
|
-
case "ZodUnknown":
|
|
380
|
-
case "ZodNever":
|
|
381
|
-
case "ZodVoid":
|
|
382
|
-
case "ZodNaN": return void 0;
|
|
383
|
-
case "ZodArray": {
|
|
384
|
-
schema.type = "array";
|
|
385
|
-
schema.minItems = parsed.$checks?.minLength || void 0;
|
|
386
|
-
schema.maxItems = parsed.$checks?.maxLength || void 0;
|
|
387
|
-
schema.items = getSwaggerSchema(parsed.$inner);
|
|
388
|
-
break;
|
|
389
|
-
}
|
|
390
|
-
case "ZodTuple": {
|
|
391
|
-
schema.type = "array";
|
|
392
|
-
schema.items = parsed.$inner.map((t) => getSwaggerSchema(t)).filter((t) => !!t);
|
|
393
|
-
break;
|
|
394
|
-
}
|
|
395
|
-
case "ZodObject": {
|
|
396
|
-
schema.type = "object";
|
|
397
|
-
schema.properties = {};
|
|
398
|
-
schema.required = [];
|
|
399
|
-
if (zodType._def.unknownKeys === "passthrough") schema.additionalProperties = {};
|
|
400
|
-
for (const [key, val] of Object.entries(parsed.$inner)) {
|
|
401
|
-
const prop = getSwaggerSchema(val);
|
|
402
|
-
if (prop) {
|
|
403
|
-
schema.properties[key] = prop;
|
|
404
|
-
if (!val.$optional) schema.required.push(key);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
break;
|
|
408
|
-
}
|
|
409
|
-
case "ZodPromise":
|
|
410
|
-
case "ZodRecord":
|
|
411
|
-
case "ZodMap":
|
|
412
|
-
case "ZodSet": {
|
|
413
|
-
schema.type = "object";
|
|
414
|
-
schema.properties = {};
|
|
415
|
-
schema.additionalProperties = parsed.$type === "ZodRecord" ? {} : void 0;
|
|
416
|
-
break;
|
|
417
|
-
}
|
|
418
|
-
case "ZodUnion":
|
|
419
|
-
case "ZodDiscriminatedUnion": {
|
|
420
|
-
schema.oneOf = parsed.$inner.map((t) => getSwaggerSchema(t)).filter((t) => !!t);
|
|
421
|
-
break;
|
|
422
|
-
}
|
|
423
|
-
case "ZodIntersection": {
|
|
424
|
-
schema.allOf = parsed.$inner.map((t) => getSwaggerSchema(t)).filter((t) => !!t);
|
|
425
|
-
break;
|
|
426
|
-
}
|
|
427
|
-
case "ZodLazy": return getSwaggerSchema(parsed.$get());
|
|
428
|
-
default: return void 0;
|
|
353
|
+
if (fn.length === 0) try {
|
|
354
|
+
const result = fn();
|
|
355
|
+
if (result && result !== fn) return createSchemaResolution(result);
|
|
356
|
+
} catch {}
|
|
357
|
+
return void 0;
|
|
358
|
+
}
|
|
359
|
+
function schemaFromInstance(obj) {
|
|
360
|
+
if (isSwaggerSchema(obj)) return {
|
|
361
|
+
kind: "inline",
|
|
362
|
+
schema: cloneSchema(obj)
|
|
363
|
+
};
|
|
364
|
+
const ctor = obj.constructor;
|
|
365
|
+
if (ctor && typeof ctor.toJsonSchema === "function") {
|
|
366
|
+
const schema = asSwaggerSchema(ctor.toJsonSchema());
|
|
367
|
+
return {
|
|
368
|
+
kind: "component",
|
|
369
|
+
schema,
|
|
370
|
+
typeRef: ctor,
|
|
371
|
+
suggestedName: ctor.name
|
|
372
|
+
};
|
|
429
373
|
}
|
|
430
|
-
if (
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
374
|
+
if (typeof obj.toJsonSchema === "function") {
|
|
375
|
+
const schema = asSwaggerSchema(obj.toJsonSchema());
|
|
376
|
+
return {
|
|
377
|
+
kind: "component",
|
|
378
|
+
schema,
|
|
379
|
+
typeRef: obj,
|
|
380
|
+
suggestedName: getTypeName(obj)
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return void 0;
|
|
384
|
+
}
|
|
385
|
+
function asSwaggerSchema(schema) {
|
|
386
|
+
if (!schema || typeof schema !== "object") return {};
|
|
387
|
+
return cloneSchema(schema);
|
|
388
|
+
}
|
|
389
|
+
function ensureComponentName(typeRef, schema, suggestedName) {
|
|
390
|
+
const existing = schemaRefs.get(typeRef);
|
|
391
|
+
if (existing) {
|
|
392
|
+
if (!globalSchemas[existing]) globalSchemas[existing] = cloneSchema(schema);
|
|
393
|
+
return existing;
|
|
441
394
|
}
|
|
442
|
-
|
|
443
|
-
|
|
395
|
+
const baseName = sanitizeComponentName(suggestedName || schema.title || getTypeName(typeRef) || "Schema");
|
|
396
|
+
let candidate = baseName || "Schema";
|
|
397
|
+
let counter = 1;
|
|
398
|
+
while (nameToType.has(candidate)) candidate = `${baseName}_${counter++}`;
|
|
399
|
+
nameToType.set(candidate, typeRef);
|
|
400
|
+
schemaRefs.set(typeRef, candidate);
|
|
401
|
+
applySwaggerMetadata(typeRef, schema);
|
|
402
|
+
globalSchemas[candidate] = cloneSchema(schema);
|
|
403
|
+
return candidate;
|
|
404
|
+
}
|
|
405
|
+
function applySwaggerMetadata(typeRef, schema) {
|
|
406
|
+
try {
|
|
407
|
+
const mate = getSwaggerMate();
|
|
408
|
+
const meta = mate.read(typeRef);
|
|
409
|
+
if (!meta) return;
|
|
410
|
+
if (meta.swaggerExample !== void 0 && schema.example === void 0) schema.example = meta.swaggerExample;
|
|
411
|
+
const title = meta.label || meta.id;
|
|
412
|
+
if (title && !schema.title) schema.title = title;
|
|
413
|
+
if (meta.swaggerDescription && !schema.description) schema.description = meta.swaggerDescription;
|
|
414
|
+
else if (meta.description && !schema.description) schema.description = meta.description;
|
|
415
|
+
} catch {}
|
|
416
|
+
}
|
|
417
|
+
function sanitizeComponentName(name) {
|
|
418
|
+
const sanitized = name.replace(/[^A-Za-z0-9_.-]/g, "_");
|
|
419
|
+
return sanitized || "Schema";
|
|
420
|
+
}
|
|
421
|
+
function getTypeName(typeRef) {
|
|
422
|
+
if (typeof typeRef === "function" && typeRef.name) return typeRef.name;
|
|
423
|
+
const ctor = typeRef.constructor;
|
|
424
|
+
if (ctor && ctor !== Object && ctor.name) return ctor.name;
|
|
425
|
+
return void 0;
|
|
426
|
+
}
|
|
427
|
+
function isSwaggerSchema(candidate) {
|
|
428
|
+
if (!candidate || typeof candidate !== "object") return false;
|
|
429
|
+
const obj = candidate;
|
|
430
|
+
return "$ref" in obj || "type" in obj || "properties" in obj || "items" in obj || "allOf" in obj || "anyOf" in obj || "oneOf" in obj;
|
|
444
431
|
}
|
|
445
|
-
function
|
|
446
|
-
|
|
432
|
+
function isLiteralValue(value) {
|
|
433
|
+
const type = typeof value;
|
|
434
|
+
return type === "string" || type === "number" || type === "boolean" || type === "bigint";
|
|
435
|
+
}
|
|
436
|
+
function schemaFromLiteral(value) {
|
|
437
|
+
if (typeof value === "string") {
|
|
438
|
+
if ([
|
|
439
|
+
"string",
|
|
440
|
+
"number",
|
|
441
|
+
"boolean",
|
|
442
|
+
"integer",
|
|
443
|
+
"object",
|
|
444
|
+
"array"
|
|
445
|
+
].includes(value)) return { type: value };
|
|
446
|
+
return {
|
|
447
|
+
const: value,
|
|
448
|
+
type: "string"
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
if (typeof value === "number") return {
|
|
452
|
+
const: value,
|
|
453
|
+
type: Number.isInteger(value) ? "integer" : "number"
|
|
454
|
+
};
|
|
455
|
+
if (typeof value === "boolean") return {
|
|
456
|
+
const: value,
|
|
457
|
+
type: "boolean"
|
|
458
|
+
};
|
|
459
|
+
return {
|
|
460
|
+
const: value.toString(),
|
|
461
|
+
type: "integer"
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
function isPrimitiveConstructor(value) {
|
|
465
|
+
if (typeof value !== "function") return false;
|
|
466
|
+
return value === String || value === Number || value === Boolean || value === BigInt || value === Date || value === Array || value === Object || value === Symbol;
|
|
467
|
+
}
|
|
468
|
+
function schemaFromPrimitiveCtor(fn) {
|
|
469
|
+
switch (fn) {
|
|
470
|
+
case String: return { type: "string" };
|
|
471
|
+
case Number: return { type: "number" };
|
|
472
|
+
case Boolean: return { type: "boolean" };
|
|
473
|
+
case BigInt: return { type: "integer" };
|
|
474
|
+
case Date: return {
|
|
475
|
+
type: "string",
|
|
476
|
+
format: "date-time"
|
|
477
|
+
};
|
|
478
|
+
case Array: return { type: "array" };
|
|
479
|
+
case Object: return { type: "object" };
|
|
480
|
+
case Symbol: return { type: "string" };
|
|
481
|
+
default: return { type: "object" };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
function resolveSchemaFromRef(schema) {
|
|
485
|
+
if (!schema.$ref) return schema;
|
|
486
|
+
const refName = schema.$ref.replace("#/components/schemas/", "");
|
|
487
|
+
return globalSchemas[refName];
|
|
488
|
+
}
|
|
489
|
+
function cloneSchema(schema) {
|
|
490
|
+
return JSON.parse(JSON.stringify(schema));
|
|
447
491
|
}
|
|
448
492
|
function getDefaultStatusCode(httpMethod) {
|
|
449
493
|
const defaultStatusCodes = {
|
|
@@ -454,23 +498,6 @@ function getDefaultStatusCode(httpMethod) {
|
|
|
454
498
|
};
|
|
455
499
|
return defaultStatusCodes[httpMethod.toUpperCase()] || 200;
|
|
456
500
|
}
|
|
457
|
-
function getSwaggerSchemaFromSwaggerConfigType(type) {
|
|
458
|
-
let schema;
|
|
459
|
-
let zt;
|
|
460
|
-
if (type instanceof z.ZodType) zt = type;
|
|
461
|
-
else if (typeof type === "function") zt = getZodType({ type });
|
|
462
|
-
if (zt) {
|
|
463
|
-
const parsed = myParseZod(zt);
|
|
464
|
-
if ([
|
|
465
|
-
"ZodString",
|
|
466
|
-
"ZodNumber",
|
|
467
|
-
"ZodObject",
|
|
468
|
-
"ZodArray",
|
|
469
|
-
"ZodBoolean"
|
|
470
|
-
].includes(parsed.$type)) schema = getSwaggerSchema(parsed);
|
|
471
|
-
} else if (type.type || type.$ref) schema = type;
|
|
472
|
-
return schema;
|
|
473
|
-
}
|
|
474
501
|
|
|
475
502
|
//#endregion
|
|
476
503
|
//#region packages/swagger/src/swagger.controller.ts
|
|
@@ -621,7 +648,6 @@ _ts_decorate([
|
|
|
621
648
|
], SwaggerController.prototype, "files", null);
|
|
622
649
|
SwaggerController = _ts_decorate([
|
|
623
650
|
SwaggerExclude(),
|
|
624
|
-
ZodSkip(),
|
|
625
651
|
Controller("api-docs"),
|
|
626
652
|
_ts_param(0, Const({ title: "Moost API" })),
|
|
627
653
|
_ts_metadata("design:type", Function),
|