adorn-api 1.0.11 → 1.0.13
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 +318 -620
- package/dist/adapter/express/auth.d.ts +5 -0
- package/dist/adapter/express/auth.d.ts.map +1 -0
- package/dist/adapter/express/bootstrap.d.ts.map +1 -1
- package/dist/adapter/express/coercion.d.ts +22 -0
- package/dist/adapter/express/coercion.d.ts.map +1 -0
- package/dist/adapter/express/index.d.ts +3 -50
- package/dist/adapter/express/index.d.ts.map +1 -1
- package/dist/adapter/express/merge.d.ts +0 -3
- package/dist/adapter/express/merge.d.ts.map +1 -1
- package/dist/adapter/express/openapi.d.ts +11 -0
- package/dist/adapter/express/openapi.d.ts.map +1 -0
- package/dist/adapter/express/router.d.ts +4 -0
- package/dist/adapter/express/router.d.ts.map +1 -0
- package/dist/adapter/express/swagger.d.ts +4 -0
- package/dist/adapter/express/swagger.d.ts.map +1 -0
- package/dist/adapter/express/types.d.ts +64 -0
- package/dist/adapter/express/types.d.ts.map +1 -0
- package/dist/adapter/express/validation.d.ts +10 -0
- package/dist/adapter/express/validation.d.ts.map +1 -0
- package/dist/cli.cjs +1003 -434
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1003 -434
- package/dist/cli.js.map +1 -1
- package/dist/compiler/analyze/scanControllers.d.ts +0 -1
- package/dist/compiler/analyze/scanControllers.d.ts.map +1 -1
- package/dist/compiler/cache/isStale.d.ts.map +1 -1
- package/dist/compiler/cache/writeCache.d.ts.map +1 -1
- package/dist/compiler/manifest/emit.d.ts.map +1 -1
- package/dist/compiler/manifest/format.d.ts +1 -1
- package/dist/compiler/manifest/format.d.ts.map +1 -1
- package/dist/compiler/schema/intersectionHandler.d.ts +7 -0
- package/dist/compiler/schema/intersectionHandler.d.ts.map +1 -0
- package/dist/compiler/schema/objectHandler.d.ts +20 -0
- package/dist/compiler/schema/objectHandler.d.ts.map +1 -0
- package/dist/compiler/schema/openapi.d.ts +1 -1
- package/dist/compiler/schema/openapi.d.ts.map +1 -1
- package/dist/compiler/schema/parameters.d.ts +18 -0
- package/dist/compiler/schema/parameters.d.ts.map +1 -0
- package/dist/compiler/schema/primitives.d.ts +10 -0
- package/dist/compiler/schema/primitives.d.ts.map +1 -0
- package/dist/compiler/schema/typeToJsonSchema.d.ts +3 -46
- package/dist/compiler/schema/typeToJsonSchema.d.ts.map +1 -1
- package/dist/compiler/schema/types.d.ts +54 -0
- package/dist/compiler/schema/types.d.ts.map +1 -0
- package/dist/compiler/schema/unionHandler.d.ts +10 -0
- package/dist/compiler/schema/unionHandler.d.ts.map +1 -0
- package/dist/decorators/index.d.ts +0 -1
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/express.cjs +522 -502
- package/dist/express.cjs.map +1 -1
- package/dist/express.js +522 -502
- package/dist/express.js.map +1 -1
- package/dist/http.d.ts +1 -10
- package/dist/http.d.ts.map +1 -1
- package/dist/index.cjs +3 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -34
- package/dist/index.js.map +1 -1
- package/dist/metal/applyListQuery.d.ts +27 -0
- package/dist/metal/applyListQuery.d.ts.map +1 -0
- package/dist/metal/index.cjs +61 -2
- package/dist/metal/index.cjs.map +1 -1
- package/dist/metal/index.d.ts +4 -0
- package/dist/metal/index.d.ts.map +1 -1
- package/dist/metal/index.js +57 -2
- package/dist/metal/index.js.map +1 -1
- package/dist/metal/listQuery.d.ts +7 -0
- package/dist/metal/listQuery.d.ts.map +1 -0
- package/dist/metal/queryOptions.d.ts +8 -0
- package/dist/metal/queryOptions.d.ts.map +1 -0
- package/dist/metal/registerMetalEntities.d.ts.map +1 -1
- package/dist/runtime/metadata/types.d.ts +0 -3
- package/dist/runtime/metadata/types.d.ts.map +1 -1
- package/package.json +4 -1
- package/dist/compiler/analyze/extractQueryStyle.d.ts +0 -8
- package/dist/compiler/analyze/extractQueryStyle.d.ts.map +0 -1
- package/dist/decorators/Paginated.d.ts +0 -5
- package/dist/decorators/Paginated.d.ts.map +0 -1
package/dist/cli.cjs
CHANGED
|
@@ -103,7 +103,7 @@ function analyzeClass(node, sourceFile, checker) {
|
|
|
103
103
|
produces
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
|
-
function extractClassConsumes(node,
|
|
106
|
+
function extractClassConsumes(node, _checker) {
|
|
107
107
|
const decorator = findDecorator(node, "Consumes");
|
|
108
108
|
if (!decorator) return void 0;
|
|
109
109
|
const callExpr = decorator.expression;
|
|
@@ -119,7 +119,7 @@ function extractClassConsumes(node, checker) {
|
|
|
119
119
|
}
|
|
120
120
|
return void 0;
|
|
121
121
|
}
|
|
122
|
-
function extractClassProduces(node,
|
|
122
|
+
function extractClassProduces(node, _checker) {
|
|
123
123
|
const decorator = findDecorator(node, "Produces");
|
|
124
124
|
if (!decorator) return void 0;
|
|
125
125
|
const callExpr = decorator.expression;
|
|
@@ -171,7 +171,7 @@ function analyzeMethod(node, className, checker) {
|
|
|
171
171
|
}
|
|
172
172
|
const pathParamNames = extractPathParams(path4);
|
|
173
173
|
const pathParamIndices = matchPathParamsToIndices(pathParamNames, parameters);
|
|
174
|
-
const { bodyParamIndex, queryParamIndices, queryObjectParamIndex, headerObjectParamIndex, cookieObjectParamIndex,
|
|
174
|
+
const { bodyParamIndex, queryParamIndices, queryObjectParamIndex, headerObjectParamIndex, cookieObjectParamIndex, bodyContentType } = classifyParameters(parameters, httpMethod, pathParamIndices, checker);
|
|
175
175
|
return {
|
|
176
176
|
methodName,
|
|
177
177
|
httpMethod,
|
|
@@ -187,7 +187,6 @@ function analyzeMethod(node, className, checker) {
|
|
|
187
187
|
queryObjectParamIndex,
|
|
188
188
|
headerObjectParamIndex,
|
|
189
189
|
cookieObjectParamIndex,
|
|
190
|
-
paginationParamIndex,
|
|
191
190
|
bodyContentType
|
|
192
191
|
};
|
|
193
192
|
}
|
|
@@ -213,7 +212,6 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
|
|
|
213
212
|
let queryObjectParamIndex = null;
|
|
214
213
|
let headerObjectParamIndex = null;
|
|
215
214
|
let cookieObjectParamIndex = null;
|
|
216
|
-
let paginationParamIndex = null;
|
|
217
215
|
const isBodyMethod = ["POST", "PUT", "PATCH"].includes(httpMethod);
|
|
218
216
|
for (let i = 0; i < parameters.length; i++) {
|
|
219
217
|
const param = parameters[i];
|
|
@@ -240,11 +238,6 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
|
|
|
240
238
|
usedIndices.add(i);
|
|
241
239
|
continue;
|
|
242
240
|
}
|
|
243
|
-
if (typeStr === "PaginationParams") {
|
|
244
|
-
paginationParamIndex = i;
|
|
245
|
-
usedIndices.add(i);
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
241
|
if (isBodyMethod && bodyParamIndex === null) {
|
|
249
242
|
bodyParamIndex = i;
|
|
250
243
|
usedIndices.add(i);
|
|
@@ -265,19 +258,19 @@ function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
|
|
|
265
258
|
queryObjectParamIndex,
|
|
266
259
|
headerObjectParamIndex,
|
|
267
260
|
cookieObjectParamIndex,
|
|
268
|
-
paginationParamIndex,
|
|
269
261
|
bodyContentType: void 0
|
|
270
262
|
};
|
|
271
263
|
}
|
|
272
264
|
function isObjectType(type, checker) {
|
|
273
265
|
const objectFlags = (type.flags & import_typescript2.default.TypeFlags.Object) !== 0;
|
|
274
|
-
|
|
266
|
+
const intersectionFlags = (type.flags & import_typescript2.default.TypeFlags.Intersection) !== 0;
|
|
267
|
+
if (!objectFlags && !intersectionFlags) return false;
|
|
275
268
|
const symbol = type.getSymbol();
|
|
276
269
|
if (symbol?.getName() === "__object") return true;
|
|
277
270
|
const properties = checker.getPropertiesOfType(type);
|
|
278
271
|
if (properties.length > 0) return true;
|
|
279
|
-
const
|
|
280
|
-
if (
|
|
272
|
+
const callSigs = type.getCallSignatures?.();
|
|
273
|
+
if (callSigs && callSigs.length > 0) return false;
|
|
281
274
|
return true;
|
|
282
275
|
}
|
|
283
276
|
function getTypeName(type) {
|
|
@@ -308,7 +301,7 @@ function extractDecoratorStringArg(decorator) {
|
|
|
308
301
|
}
|
|
309
302
|
return null;
|
|
310
303
|
}
|
|
311
|
-
function unwrapPromise(type,
|
|
304
|
+
function unwrapPromise(type, _checker) {
|
|
312
305
|
const symbol = type.getSymbol();
|
|
313
306
|
if (symbol?.getName() === "Promise") {
|
|
314
307
|
const typeArgs = type.typeArguments;
|
|
@@ -329,12 +322,15 @@ function unwrapPromiseTypeNode(typeNode) {
|
|
|
329
322
|
}
|
|
330
323
|
|
|
331
324
|
// src/compiler/schema/openapi.ts
|
|
332
|
-
var
|
|
325
|
+
var import_typescript9 = __toESM(require("typescript"), 1);
|
|
333
326
|
|
|
334
327
|
// src/compiler/schema/typeToJsonSchema.ts
|
|
328
|
+
var import_typescript7 = __toESM(require("typescript"), 1);
|
|
329
|
+
|
|
330
|
+
// src/compiler/schema/primitives.ts
|
|
335
331
|
var import_typescript3 = __toESM(require("typescript"), 1);
|
|
336
|
-
function
|
|
337
|
-
const { checker } = ctx;
|
|
332
|
+
function handlePrimitiveType(type, ctx, typeNode) {
|
|
333
|
+
const { checker, propertyName } = ctx;
|
|
338
334
|
if (type.flags & import_typescript3.default.TypeFlags.Undefined) {
|
|
339
335
|
return {};
|
|
340
336
|
}
|
|
@@ -348,7 +344,7 @@ function typeToJsonSchema(type, ctx, typeNode) {
|
|
|
348
344
|
return { type: "string" };
|
|
349
345
|
}
|
|
350
346
|
if (type.flags & import_typescript3.default.TypeFlags.Number) {
|
|
351
|
-
return
|
|
347
|
+
return normalizeNumericType(type, checker, typeNode, propertyName);
|
|
352
348
|
}
|
|
353
349
|
if (type.flags & import_typescript3.default.TypeFlags.Boolean) {
|
|
354
350
|
return { type: "boolean" };
|
|
@@ -372,26 +368,7 @@ function typeToJsonSchema(type, ctx, typeNode) {
|
|
|
372
368
|
const intrinsic = type.intrinsicName;
|
|
373
369
|
return { type: "boolean", enum: [intrinsic === "true"] };
|
|
374
370
|
}
|
|
375
|
-
|
|
376
|
-
return handleUnion(type, ctx, typeNode);
|
|
377
|
-
}
|
|
378
|
-
if (type.isIntersection()) {
|
|
379
|
-
return handleIntersection(type, ctx, typeNode);
|
|
380
|
-
}
|
|
381
|
-
if (checker.isArrayType(type)) {
|
|
382
|
-
const typeArgs = type.typeArguments;
|
|
383
|
-
const itemType = typeArgs?.[0];
|
|
384
|
-
const items = itemType ? typeToJsonSchema(itemType, ctx) : {};
|
|
385
|
-
return {
|
|
386
|
-
type: "array",
|
|
387
|
-
items,
|
|
388
|
-
uniqueItems: isSetType(type, checker) ? true : void 0
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
if (type.flags & import_typescript3.default.TypeFlags.Object) {
|
|
392
|
-
return handleObjectType(type, ctx, typeNode);
|
|
393
|
-
}
|
|
394
|
-
return {};
|
|
371
|
+
return null;
|
|
395
372
|
}
|
|
396
373
|
function isDateType(type, checker) {
|
|
397
374
|
const symbol = type.getSymbol();
|
|
@@ -406,53 +383,51 @@ function isDateType(type, checker) {
|
|
|
406
383
|
}
|
|
407
384
|
return symbol?.getName() === "Date";
|
|
408
385
|
}
|
|
409
|
-
function
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
const
|
|
413
|
-
if (
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
function getSchemaName(type, typeNode) {
|
|
417
|
-
const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
|
|
418
|
-
const aliasName = aliasSymbol?.getName();
|
|
419
|
-
if (aliasName && aliasName !== "__type") {
|
|
420
|
-
return aliasName;
|
|
386
|
+
function normalizeNumericType(type, checker, typeNode, propertyName) {
|
|
387
|
+
const typeName = getExplicitTypeNameFromNode(typeNode) ?? null;
|
|
388
|
+
const symbol = getEffectiveSymbol(type, checker);
|
|
389
|
+
const symbolName = symbol?.getName() ?? null;
|
|
390
|
+
if (shouldBeIntegerType(typeName) || shouldBeIntegerType(symbolName) || shouldBeIntegerType(propertyName ?? null)) {
|
|
391
|
+
return { type: "integer" };
|
|
421
392
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
393
|
+
return { type: "number" };
|
|
394
|
+
}
|
|
395
|
+
function shouldBeIntegerType(typeName) {
|
|
396
|
+
if (!typeName) return false;
|
|
397
|
+
const lower = typeName.toLowerCase();
|
|
398
|
+
return lower === "id" || lower.endsWith("id") || lower === "primarykey" || lower === "pk" || lower === "page" || lower === "pagesize" || lower === "totalitems" || lower === "limit" || lower === "offset";
|
|
399
|
+
}
|
|
400
|
+
function getExplicitTypeNameFromNode(typeNode) {
|
|
401
|
+
if (!typeNode) return null;
|
|
402
|
+
if (import_typescript3.default.isTypeReferenceNode(typeNode)) {
|
|
403
|
+
if (import_typescript3.default.isIdentifier(typeNode.typeName)) {
|
|
404
|
+
return typeNode.typeName.text;
|
|
405
|
+
}
|
|
426
406
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
407
|
+
if (import_typescript3.default.isTypeAliasDeclaration(typeNode.parent)) {
|
|
408
|
+
if (import_typescript3.default.isIdentifier(typeNode.parent.name)) {
|
|
409
|
+
return typeNode.parent.name.text;
|
|
410
|
+
}
|
|
430
411
|
}
|
|
431
412
|
return null;
|
|
432
413
|
}
|
|
433
|
-
function
|
|
434
|
-
const
|
|
435
|
-
if (
|
|
436
|
-
return
|
|
437
|
-
}
|
|
438
|
-
const { components, typeStack } = ctx;
|
|
439
|
-
if (components.has(name) || typeStack.has(type)) {
|
|
440
|
-
return { $ref: `#/components/schemas/${name}` };
|
|
441
|
-
}
|
|
442
|
-
typeStack.add(type);
|
|
443
|
-
const schema = build();
|
|
444
|
-
typeStack.delete(type);
|
|
445
|
-
if (!components.has(name)) {
|
|
446
|
-
components.set(name, schema);
|
|
414
|
+
function getEffectiveSymbol(type, checker) {
|
|
415
|
+
const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
|
|
416
|
+
if (aliasSymbol && aliasSymbol.flags & import_typescript3.default.SymbolFlags.Alias) {
|
|
417
|
+
return checker.getAliasedSymbol(aliasSymbol);
|
|
447
418
|
}
|
|
448
|
-
return
|
|
419
|
+
return type.getSymbol() ?? null;
|
|
449
420
|
}
|
|
421
|
+
|
|
422
|
+
// src/compiler/schema/unionHandler.ts
|
|
423
|
+
var import_typescript4 = __toESM(require("typescript"), 1);
|
|
450
424
|
function handleUnion(type, ctx, typeNode) {
|
|
451
425
|
return buildNamedSchema(type, ctx, typeNode, () => {
|
|
452
426
|
const types = type.types;
|
|
453
|
-
const nullType = types.find((t) => t.flags &
|
|
454
|
-
const
|
|
455
|
-
const
|
|
427
|
+
const nullType = types.find((t) => t.flags & import_typescript4.default.TypeFlags.Null);
|
|
428
|
+
const undefinedType = types.find((t) => t.flags & import_typescript4.default.TypeFlags.Undefined);
|
|
429
|
+
const otherTypes = types.filter((t) => !(t.flags & import_typescript4.default.TypeFlags.Null) && !(t.flags & import_typescript4.default.TypeFlags.Undefined));
|
|
430
|
+
const allStringLiterals = otherTypes.every((t) => t.flags & import_typescript4.default.TypeFlags.StringLiteral);
|
|
456
431
|
if (allStringLiterals && otherTypes.length > 0) {
|
|
457
432
|
const enumValues = otherTypes.map((t) => t.value);
|
|
458
433
|
const schema = { type: "string", enum: enumValues };
|
|
@@ -461,6 +436,14 @@ function handleUnion(type, ctx, typeNode) {
|
|
|
461
436
|
}
|
|
462
437
|
return schema;
|
|
463
438
|
}
|
|
439
|
+
const allBooleanLiterals = otherTypes.length > 0 && otherTypes.every((t) => t.flags & import_typescript4.default.TypeFlags.BooleanLiteral);
|
|
440
|
+
if (allBooleanLiterals) {
|
|
441
|
+
const schema = { type: "boolean" };
|
|
442
|
+
if (nullType || undefinedType) {
|
|
443
|
+
schema.type = ["boolean", "null"];
|
|
444
|
+
}
|
|
445
|
+
return schema;
|
|
446
|
+
}
|
|
464
447
|
if (otherTypes.length === 1 && nullType) {
|
|
465
448
|
const innerSchema = typeToJsonSchema(otherTypes[0], ctx);
|
|
466
449
|
if (typeof innerSchema.type === "string") {
|
|
@@ -490,49 +473,7 @@ function handleUnion(type, ctx, typeNode) {
|
|
|
490
473
|
return {};
|
|
491
474
|
});
|
|
492
475
|
}
|
|
493
|
-
function
|
|
494
|
-
return buildNamedSchema(type, ctx, typeNode, () => {
|
|
495
|
-
const types = type.types;
|
|
496
|
-
const brandCollapsed = tryCollapseBrandedIntersection(types, ctx, typeNode);
|
|
497
|
-
if (brandCollapsed) {
|
|
498
|
-
return brandCollapsed;
|
|
499
|
-
}
|
|
500
|
-
const allOf = [];
|
|
501
|
-
for (const t of types) {
|
|
502
|
-
allOf.push(typeToJsonSchema(t, ctx));
|
|
503
|
-
}
|
|
504
|
-
return { allOf };
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
function tryCollapseBrandedIntersection(types, ctx, typeNode) {
|
|
508
|
-
const { checker } = ctx;
|
|
509
|
-
const parts = [...types];
|
|
510
|
-
const prim = parts.find(isPrimitiveLike);
|
|
511
|
-
if (!prim) return null;
|
|
512
|
-
const rest = parts.filter((p) => p !== prim);
|
|
513
|
-
if (rest.every((r) => isBrandObject(checker, r, ctx))) {
|
|
514
|
-
return typeToJsonSchema(prim, ctx);
|
|
515
|
-
}
|
|
516
|
-
return null;
|
|
517
|
-
}
|
|
518
|
-
function isPrimitiveLike(t) {
|
|
519
|
-
return (t.flags & (import_typescript3.default.TypeFlags.String | import_typescript3.default.TypeFlags.Number | import_typescript3.default.TypeFlags.Boolean | import_typescript3.default.TypeFlags.BigInt)) !== 0 || (t.flags & import_typescript3.default.TypeFlags.StringLiteral) !== 0 || (t.flags & import_typescript3.default.TypeFlags.NumberLiteral) !== 0;
|
|
520
|
-
}
|
|
521
|
-
function isBrandObject(checker, t, ctx) {
|
|
522
|
-
if (!(t.flags & import_typescript3.default.TypeFlags.Object)) return false;
|
|
523
|
-
const props = t.getProperties();
|
|
524
|
-
if (props.length === 0) return false;
|
|
525
|
-
const allowed = /* @__PURE__ */ new Set(["__brand", "__type", "__tag", "brand"]);
|
|
526
|
-
for (const p of props) {
|
|
527
|
-
if (!allowed.has(p.getName())) return false;
|
|
528
|
-
}
|
|
529
|
-
const callSigs = t.getCallSignatures?.();
|
|
530
|
-
if (callSigs && callSigs.length > 0) return false;
|
|
531
|
-
const constructSigs = t.getConstructSignatures?.();
|
|
532
|
-
if (constructSigs && constructSigs.length > 0) return false;
|
|
533
|
-
return true;
|
|
534
|
-
}
|
|
535
|
-
function detectDiscriminatedUnion(types, ctx, branches) {
|
|
476
|
+
function detectDiscriminatedUnion(types, ctx, _branches) {
|
|
536
477
|
if (types.length < 2) return null;
|
|
537
478
|
const candidates = findCommonPropertyNames(ctx.checker, types);
|
|
538
479
|
for (const propName of candidates) {
|
|
@@ -563,10 +504,10 @@ function findCommonPropertyNames(checker, types) {
|
|
|
563
504
|
function isRequiredProperty(checker, type, propName) {
|
|
564
505
|
const sym = checker.getPropertyOfType(type, propName);
|
|
565
506
|
if (!sym) return false;
|
|
566
|
-
if (sym.flags &
|
|
507
|
+
if (sym.flags & import_typescript4.default.SymbolFlags.Optional) return false;
|
|
567
508
|
const propType = checker.getTypeOfSymbol(sym);
|
|
568
509
|
if (propType.isUnion?.()) {
|
|
569
|
-
const hasUndefined = propType.types.some((t) => (t.flags &
|
|
510
|
+
const hasUndefined = propType.types.some((t) => (t.flags & import_typescript4.default.TypeFlags.Undefined) !== 0);
|
|
570
511
|
if (hasUndefined) return false;
|
|
571
512
|
}
|
|
572
513
|
return true;
|
|
@@ -609,11 +550,187 @@ function getBranchSchemaName(type, ctx) {
|
|
|
609
550
|
}
|
|
610
551
|
return `Anonymous_${ctx.typeNameStack.length}`;
|
|
611
552
|
}
|
|
553
|
+
function buildNamedSchema(type, ctx, typeNode, build) {
|
|
554
|
+
const name = getSchemaName(type, typeNode);
|
|
555
|
+
if (!name) {
|
|
556
|
+
return build();
|
|
557
|
+
}
|
|
558
|
+
const { components, typeStack } = ctx;
|
|
559
|
+
if (components.has(name) || typeStack.has(type)) {
|
|
560
|
+
return { $ref: `#/components/schemas/${name}` };
|
|
561
|
+
}
|
|
562
|
+
typeStack.add(type);
|
|
563
|
+
const schema = build();
|
|
564
|
+
typeStack.delete(type);
|
|
565
|
+
if (!components.has(name)) {
|
|
566
|
+
components.set(name, schema);
|
|
567
|
+
}
|
|
568
|
+
return { $ref: `#/components/schemas/${name}` };
|
|
569
|
+
}
|
|
570
|
+
function getSchemaName(type, typeNode) {
|
|
571
|
+
const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
|
|
572
|
+
const aliasName = aliasSymbol?.getName();
|
|
573
|
+
if (aliasName && aliasName !== "__type") {
|
|
574
|
+
return aliasName;
|
|
575
|
+
}
|
|
576
|
+
const symbol = type.getSymbol();
|
|
577
|
+
const symbolName = symbol?.getName?.();
|
|
578
|
+
if (symbolName && symbolName !== "__type") {
|
|
579
|
+
return symbolName;
|
|
580
|
+
}
|
|
581
|
+
const nodeName = getExplicitTypeNameFromNode2(typeNode);
|
|
582
|
+
if (nodeName && nodeName !== "__type") {
|
|
583
|
+
return nodeName;
|
|
584
|
+
}
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
function getExplicitTypeNameFromNode2(typeNode) {
|
|
588
|
+
if (!typeNode) return null;
|
|
589
|
+
if (import_typescript4.default.isTypeReferenceNode(typeNode)) {
|
|
590
|
+
if (import_typescript4.default.isIdentifier(typeNode.typeName)) {
|
|
591
|
+
return typeNode.typeName.text;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (import_typescript4.default.isTypeAliasDeclaration(typeNode.parent)) {
|
|
595
|
+
if (import_typescript4.default.isIdentifier(typeNode.parent.name)) {
|
|
596
|
+
return typeNode.parent.name.text;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// src/compiler/schema/intersectionHandler.ts
|
|
603
|
+
var import_typescript5 = __toESM(require("typescript"), 1);
|
|
604
|
+
function handleIntersection(type, ctx, typeNode) {
|
|
605
|
+
return buildNamedSchema2(type, ctx, typeNode, () => {
|
|
606
|
+
const types = type.types;
|
|
607
|
+
const brandCollapsed = tryCollapseBrandedIntersection(types, ctx, typeNode);
|
|
608
|
+
if (brandCollapsed) {
|
|
609
|
+
return brandCollapsed;
|
|
610
|
+
}
|
|
611
|
+
const allOf = [];
|
|
612
|
+
for (const t of types) {
|
|
613
|
+
const schema = typeToJsonSchema(t, ctx);
|
|
614
|
+
if (Object.keys(schema).length > 0) {
|
|
615
|
+
if (isEmptyObjectSchema(schema)) {
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
allOf.push(schema);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (allOf.length === 0) {
|
|
622
|
+
return {};
|
|
623
|
+
}
|
|
624
|
+
if (allOf.length === 1) {
|
|
625
|
+
return allOf[0];
|
|
626
|
+
}
|
|
627
|
+
return { allOf };
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
function tryCollapseBrandedIntersection(types, ctx, _typeNode) {
|
|
631
|
+
const { checker } = ctx;
|
|
632
|
+
const parts = [...types];
|
|
633
|
+
const prim = parts.find(isPrimitiveLike);
|
|
634
|
+
if (!prim) return null;
|
|
635
|
+
const rest = parts.filter((p) => p !== prim);
|
|
636
|
+
if (rest.every((r) => isBrandObject(checker, r, ctx))) {
|
|
637
|
+
return typeToJsonSchema(prim, ctx);
|
|
638
|
+
}
|
|
639
|
+
return null;
|
|
640
|
+
}
|
|
641
|
+
function isPrimitiveLike(t) {
|
|
642
|
+
return (t.flags & (import_typescript5.default.TypeFlags.String | import_typescript5.default.TypeFlags.Number | import_typescript5.default.TypeFlags.Boolean | import_typescript5.default.TypeFlags.BigInt)) !== 0 || (t.flags & import_typescript5.default.TypeFlags.StringLiteral) !== 0 || (t.flags & import_typescript5.default.TypeFlags.NumberLiteral) !== 0;
|
|
643
|
+
}
|
|
644
|
+
function isBrandObject(checker, t, _ctx) {
|
|
645
|
+
if (!(t.flags & import_typescript5.default.TypeFlags.Object)) return false;
|
|
646
|
+
const props = t.getProperties();
|
|
647
|
+
if (props.length === 0) return false;
|
|
648
|
+
const allowed = /* @__PURE__ */ new Set(["__brand", "__type", "__tag", "brand"]);
|
|
649
|
+
for (const p of props) {
|
|
650
|
+
if (!allowed.has(p.getName())) return false;
|
|
651
|
+
}
|
|
652
|
+
const callSigs = t.getCallSignatures?.();
|
|
653
|
+
if (callSigs && callSigs.length > 0) return false;
|
|
654
|
+
const constructSigs = t.getConstructSignatures?.();
|
|
655
|
+
if (constructSigs && constructSigs.length > 0) return false;
|
|
656
|
+
return true;
|
|
657
|
+
}
|
|
658
|
+
function isEmptyObjectSchema(schema) {
|
|
659
|
+
if (schema.type !== "object") {
|
|
660
|
+
return false;
|
|
661
|
+
}
|
|
662
|
+
if (!schema.properties || Object.keys(schema.properties).length === 0) {
|
|
663
|
+
if (!schema.additionalProperties) {
|
|
664
|
+
return true;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
669
|
+
function buildNamedSchema2(type, ctx, typeNode, build) {
|
|
670
|
+
const name = getSchemaName2(type, typeNode);
|
|
671
|
+
if (!name) {
|
|
672
|
+
return build();
|
|
673
|
+
}
|
|
674
|
+
const { components, typeStack } = ctx;
|
|
675
|
+
if (components.has(name) || typeStack.has(type)) {
|
|
676
|
+
return { $ref: `#/components/schemas/${name}` };
|
|
677
|
+
}
|
|
678
|
+
typeStack.add(type);
|
|
679
|
+
const schema = build();
|
|
680
|
+
typeStack.delete(type);
|
|
681
|
+
if (!components.has(name)) {
|
|
682
|
+
components.set(name, schema);
|
|
683
|
+
}
|
|
684
|
+
return { $ref: `#/components/schemas/${name}` };
|
|
685
|
+
}
|
|
686
|
+
function getSchemaName2(type, typeNode) {
|
|
687
|
+
const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
|
|
688
|
+
const aliasName = aliasSymbol?.getName();
|
|
689
|
+
if (aliasName && aliasName !== "__type") {
|
|
690
|
+
return aliasName;
|
|
691
|
+
}
|
|
692
|
+
const symbol = type.getSymbol();
|
|
693
|
+
const symbolName = symbol?.getName?.();
|
|
694
|
+
if (symbolName && symbolName !== "__type") {
|
|
695
|
+
return symbolName;
|
|
696
|
+
}
|
|
697
|
+
const nodeName = getExplicitTypeNameFromNode3(typeNode);
|
|
698
|
+
if (nodeName && nodeName !== "__type") {
|
|
699
|
+
return nodeName;
|
|
700
|
+
}
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
function getExplicitTypeNameFromNode3(typeNode) {
|
|
704
|
+
if (!typeNode) return null;
|
|
705
|
+
if (import_typescript5.default.isTypeReferenceNode(typeNode)) {
|
|
706
|
+
if (import_typescript5.default.isIdentifier(typeNode.typeName)) {
|
|
707
|
+
return typeNode.typeName.text;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
if (import_typescript5.default.isTypeAliasDeclaration(typeNode.parent)) {
|
|
711
|
+
if (import_typescript5.default.isIdentifier(typeNode.parent.name)) {
|
|
712
|
+
return typeNode.parent.name.text;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// src/compiler/schema/objectHandler.ts
|
|
719
|
+
var import_typescript6 = __toESM(require("typescript"), 1);
|
|
612
720
|
function handleObjectType(type, ctx, typeNode) {
|
|
613
721
|
const { checker, components, typeStack } = ctx;
|
|
614
722
|
const symbol = type.getSymbol();
|
|
615
723
|
const typeName = symbol?.getName?.() ?? getTypeNameFromNode(typeNode, ctx);
|
|
724
|
+
if (isMetalOrmWrapperType(type, checker)) {
|
|
725
|
+
return handleMetalOrmWrapper(type, ctx);
|
|
726
|
+
}
|
|
616
727
|
if (typeName && typeName !== "__type") {
|
|
728
|
+
const isMetalOrmGeneric = METAL_ORM_WRAPPER_NAMES.some(
|
|
729
|
+
(name) => typeName === name || typeName.endsWith("Api")
|
|
730
|
+
);
|
|
731
|
+
if (isMetalOrmGeneric) {
|
|
732
|
+
return {};
|
|
733
|
+
}
|
|
617
734
|
if (components.has(typeName)) {
|
|
618
735
|
return { $ref: `#/components/schemas/${typeName}` };
|
|
619
736
|
}
|
|
@@ -625,7 +742,8 @@ function handleObjectType(type, ctx, typeNode) {
|
|
|
625
742
|
const schema = buildObjectSchema(type, ctx, typeNode);
|
|
626
743
|
if (typeName && typeName !== "__type") {
|
|
627
744
|
typeStack.delete(type);
|
|
628
|
-
|
|
745
|
+
const existing = components.get(typeName);
|
|
746
|
+
if (!existing) {
|
|
629
747
|
components.set(typeName, schema);
|
|
630
748
|
}
|
|
631
749
|
return { $ref: `#/components/schemas/${typeName}` };
|
|
@@ -633,36 +751,26 @@ function handleObjectType(type, ctx, typeNode) {
|
|
|
633
751
|
typeStack.delete(type);
|
|
634
752
|
return schema;
|
|
635
753
|
}
|
|
636
|
-
function
|
|
637
|
-
|
|
638
|
-
if (import_typescript3.default.isTypeReferenceNode(typeNode)) {
|
|
639
|
-
if (import_typescript3.default.isIdentifier(typeNode.typeName)) {
|
|
640
|
-
return typeNode.typeName.text;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
if (import_typescript3.default.isTypeAliasDeclaration(typeNode.parent)) {
|
|
644
|
-
if (import_typescript3.default.isIdentifier(typeNode.parent.name)) {
|
|
645
|
-
return typeNode.parent.name.text;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
return null;
|
|
649
|
-
}
|
|
650
|
-
function getTypeNameFromNode(typeNode, ctx) {
|
|
651
|
-
const explicitName = getExplicitTypeNameFromNode(typeNode);
|
|
652
|
-
if (explicitName) return explicitName;
|
|
653
|
-
return `Anonymous_${ctx.typeNameStack.length}`;
|
|
654
|
-
}
|
|
655
|
-
function buildObjectSchema(type, ctx, typeNode) {
|
|
656
|
-
const { checker } = ctx;
|
|
754
|
+
function buildObjectSchema(type, ctx, _typeNode) {
|
|
755
|
+
const { checker, mode } = ctx;
|
|
657
756
|
const properties = {};
|
|
658
757
|
const required = [];
|
|
659
758
|
const props = checker.getPropertiesOfType(type);
|
|
660
759
|
for (const prop of props) {
|
|
661
760
|
const propName = prop.getName();
|
|
761
|
+
if (isIteratorOrSymbolProperty(propName)) {
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
662
764
|
const propType = checker.getTypeOfSymbol(prop);
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
765
|
+
if (isMethodLike(propType)) {
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
const isOptional = !!(prop.flags & import_typescript6.default.SymbolFlags.Optional);
|
|
769
|
+
const isRelation = isMetalOrmWrapperType(propType, checker);
|
|
770
|
+
const propCtx = { ...ctx, propertyName: propName };
|
|
771
|
+
properties[propName] = typeToJsonSchema(propType, propCtx);
|
|
772
|
+
const shouldRequire = mode === "response" ? !isRelation && !isOptional : !isOptional;
|
|
773
|
+
if (shouldRequire) {
|
|
666
774
|
required.push(propName);
|
|
667
775
|
}
|
|
668
776
|
}
|
|
@@ -681,14 +789,14 @@ function buildObjectSchema(type, ctx, typeNode) {
|
|
|
681
789
|
}
|
|
682
790
|
return schema;
|
|
683
791
|
}
|
|
684
|
-
function isRecordType(type,
|
|
792
|
+
function isRecordType(type, _checker) {
|
|
685
793
|
const symbol = type.getSymbol();
|
|
686
794
|
if (!symbol) return false;
|
|
687
795
|
const name = symbol.getName();
|
|
688
796
|
if (name === "Record") return true;
|
|
689
797
|
return false;
|
|
690
798
|
}
|
|
691
|
-
function getRecordValueType(type,
|
|
799
|
+
function getRecordValueType(type, _checker) {
|
|
692
800
|
const symbol = type.getSymbol();
|
|
693
801
|
if (!symbol) return null;
|
|
694
802
|
const name = symbol.getName();
|
|
@@ -701,24 +809,229 @@ function getRecordValueType(type, checker) {
|
|
|
701
809
|
}
|
|
702
810
|
return null;
|
|
703
811
|
}
|
|
812
|
+
function isMetalOrmWrapperType(type, checker) {
|
|
813
|
+
return !!findMetalOrmWrapper(type, checker);
|
|
814
|
+
}
|
|
815
|
+
function isMethodLike(type) {
|
|
816
|
+
const callSigs = type.getCallSignatures?.();
|
|
817
|
+
return !!(callSigs && callSigs.length > 0);
|
|
818
|
+
}
|
|
819
|
+
function isIteratorOrSymbolProperty(propName) {
|
|
820
|
+
return propName.startsWith("__@") || propName.startsWith("[") || propName === Symbol.iterator.toString();
|
|
821
|
+
}
|
|
822
|
+
function getTypeNameFromNode(typeNode, _ctx) {
|
|
823
|
+
const explicitName = getExplicitTypeNameFromNode4(typeNode);
|
|
824
|
+
if (explicitName) return explicitName;
|
|
825
|
+
return "Anonymous_${ctx.typeNameStack.length}";
|
|
826
|
+
}
|
|
827
|
+
function getExplicitTypeNameFromNode4(typeNode) {
|
|
828
|
+
if (!typeNode) return null;
|
|
829
|
+
if (import_typescript6.default.isTypeReferenceNode(typeNode)) {
|
|
830
|
+
if (import_typescript6.default.isIdentifier(typeNode.typeName)) {
|
|
831
|
+
return typeNode.typeName.text;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
if (import_typescript6.default.isTypeAliasDeclaration(typeNode.parent)) {
|
|
835
|
+
if (import_typescript6.default.isIdentifier(typeNode.parent.name)) {
|
|
836
|
+
return typeNode.parent.name.text;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
return null;
|
|
840
|
+
}
|
|
841
|
+
var METAL_ORM_WRAPPER_NAMES = ["HasManyCollection", "ManyToManyCollection", "BelongsToReference", "HasOneReference"];
|
|
842
|
+
function findMetalOrmWrapper(type, checker) {
|
|
843
|
+
if (type.isIntersection()) {
|
|
844
|
+
let wrapperInfo = null;
|
|
845
|
+
let hasReadonlyArray = false;
|
|
846
|
+
for (const constituent of type.types) {
|
|
847
|
+
const result = findWrapperInType(constituent, checker);
|
|
848
|
+
if (result) {
|
|
849
|
+
wrapperInfo = result;
|
|
850
|
+
}
|
|
851
|
+
if (!(constituent.flags & import_typescript6.default.TypeFlags.Object)) continue;
|
|
852
|
+
const symbol = constituent.getSymbol();
|
|
853
|
+
if (symbol?.getName() === "ReadonlyArray") {
|
|
854
|
+
hasReadonlyArray = true;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
if (wrapperInfo) {
|
|
858
|
+
return { ...wrapperInfo, isReadonlyArray: hasReadonlyArray };
|
|
859
|
+
}
|
|
860
|
+
return null;
|
|
861
|
+
}
|
|
862
|
+
return findWrapperInType(type, checker);
|
|
863
|
+
}
|
|
864
|
+
function findWrapperInType(type, checker) {
|
|
865
|
+
const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
|
|
866
|
+
const symbol = type.getSymbol();
|
|
867
|
+
const effectiveSymbol = aliasSymbol && aliasSymbol.flags & import_typescript6.default.SymbolFlags.Alias ? checker.getAliasedSymbol(aliasSymbol) : symbol;
|
|
868
|
+
if (!effectiveSymbol) return null;
|
|
869
|
+
const name = effectiveSymbol.getName();
|
|
870
|
+
if (!METAL_ORM_WRAPPER_NAMES.includes(name)) return null;
|
|
871
|
+
const typeRef = type;
|
|
872
|
+
const typeArgs = typeRef.typeArguments || [];
|
|
873
|
+
return {
|
|
874
|
+
wrapperName: name,
|
|
875
|
+
targetTypeArgs: typeArgs,
|
|
876
|
+
isReadonlyArray: false
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
function getWrapperTypeName(type, _checker) {
|
|
880
|
+
const symbol = type.getSymbol();
|
|
881
|
+
if (!symbol) return null;
|
|
882
|
+
const name = symbol.getName();
|
|
883
|
+
return METAL_ORM_WRAPPER_NAMES.includes(name) ? name : null;
|
|
884
|
+
}
|
|
885
|
+
function handleMetalOrmWrapper(type, ctx) {
|
|
886
|
+
const typeRef = type;
|
|
887
|
+
const typeArgs = typeRef.typeArguments;
|
|
888
|
+
const targetType = typeArgs?.[0] ?? null;
|
|
889
|
+
const wrapperName = getWrapperTypeName(type, ctx.checker);
|
|
890
|
+
if (!wrapperName) return {};
|
|
891
|
+
const wrapperRel = { wrapper: wrapperName };
|
|
892
|
+
if (wrapperName === "HasManyCollection" || wrapperName === "ManyToManyCollection") {
|
|
893
|
+
const items = targetType ? typeToJsonSchema(targetType, ctx) : {};
|
|
894
|
+
if (wrapperName === "ManyToManyCollection" && typeArgs?.[1]) {
|
|
895
|
+
wrapperRel.pivot = typeArgs[1];
|
|
896
|
+
}
|
|
897
|
+
return {
|
|
898
|
+
type: "array",
|
|
899
|
+
items,
|
|
900
|
+
"x-metal-orm-rel": wrapperRel
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
if (!targetType) {
|
|
904
|
+
return { "x-metal-orm-rel": wrapperRel };
|
|
905
|
+
}
|
|
906
|
+
if (wrapperName === "BelongsToReference" || wrapperName === "HasOneReference") {
|
|
907
|
+
return handleBelongsToReference(targetType, ctx, wrapperRel);
|
|
908
|
+
}
|
|
909
|
+
const targetSchema = typeToJsonSchema(targetType, ctx);
|
|
910
|
+
return {
|
|
911
|
+
...targetSchema,
|
|
912
|
+
"x-metal-orm-rel": wrapperRel
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
function handleBelongsToReference(targetType, ctx, wrapperRel) {
|
|
916
|
+
const { components } = ctx;
|
|
917
|
+
const targetSymbol = targetType.getSymbol();
|
|
918
|
+
const typeName = targetSymbol?.getName();
|
|
919
|
+
if (!typeName) {
|
|
920
|
+
return {
|
|
921
|
+
type: "object",
|
|
922
|
+
properties: {},
|
|
923
|
+
"x-metal-orm-rel": wrapperRel
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
const refSchemaName = `${typeName}Ref`;
|
|
927
|
+
if (components.has(refSchemaName)) {
|
|
928
|
+
return {
|
|
929
|
+
$ref: `#/components/schemas/${refSchemaName}`,
|
|
930
|
+
"x-metal-orm-rel": wrapperRel
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
const refSchema = buildRefSchema(targetType, ctx);
|
|
934
|
+
components.set(refSchemaName, refSchema);
|
|
935
|
+
return {
|
|
936
|
+
$ref: `#/components/schemas/${refSchemaName}`,
|
|
937
|
+
"x-metal-orm-rel": wrapperRel
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
function buildRefSchema(type, ctx) {
|
|
941
|
+
const { checker } = ctx;
|
|
942
|
+
if (!(type.flags & import_typescript6.default.TypeFlags.Object)) {
|
|
943
|
+
return { type: "object", properties: {} };
|
|
944
|
+
}
|
|
945
|
+
const objectType = type;
|
|
946
|
+
const properties = {};
|
|
947
|
+
const required = [];
|
|
948
|
+
const props = checker.getPropertiesOfType(objectType);
|
|
949
|
+
for (const prop of props) {
|
|
950
|
+
const propName = prop.getName();
|
|
951
|
+
if (isIteratorOrSymbolProperty(propName)) {
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
const propType = checker.getTypeOfSymbol(prop);
|
|
955
|
+
if (isMethodLike(propType)) {
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
const isOptional = !!(prop.flags & import_typescript6.default.SymbolFlags.Optional);
|
|
959
|
+
const isRelation = isMetalOrmWrapperType(propType, checker);
|
|
960
|
+
if (isRelation) {
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
const propCtx = { ...ctx, propertyName: propName };
|
|
964
|
+
properties[propName] = typeToJsonSchema(propType, propCtx);
|
|
965
|
+
if (!isOptional) {
|
|
966
|
+
required.push(propName);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
const schema = {
|
|
970
|
+
type: "object",
|
|
971
|
+
properties
|
|
972
|
+
};
|
|
973
|
+
if (required.length > 0) {
|
|
974
|
+
schema.required = required;
|
|
975
|
+
}
|
|
976
|
+
return schema;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// src/compiler/schema/typeToJsonSchema.ts
|
|
980
|
+
function typeToJsonSchema(type, ctx, typeNode) {
|
|
981
|
+
const primitiveResult = handlePrimitiveType(type, ctx, typeNode);
|
|
982
|
+
if (primitiveResult) {
|
|
983
|
+
return primitiveResult;
|
|
984
|
+
}
|
|
985
|
+
if (type.isUnion()) {
|
|
986
|
+
return handleUnion(type, ctx, typeNode);
|
|
987
|
+
}
|
|
988
|
+
if (type.isIntersection()) {
|
|
989
|
+
return handleIntersection(type, ctx, typeNode);
|
|
990
|
+
}
|
|
991
|
+
if (ctx.checker.isArrayType(type)) {
|
|
992
|
+
const typeArgs = type.typeArguments;
|
|
993
|
+
const itemType = typeArgs?.[0];
|
|
994
|
+
const items = itemType ? typeToJsonSchema(itemType, ctx) : {};
|
|
995
|
+
return {
|
|
996
|
+
type: "array",
|
|
997
|
+
items,
|
|
998
|
+
uniqueItems: isSetType(type, ctx.checker) ? true : void 0
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
if (type.flags & import_typescript7.default.TypeFlags.Object) {
|
|
1002
|
+
const objectType = type;
|
|
1003
|
+
if (isMetalOrmWrapperType(type, ctx.checker)) {
|
|
1004
|
+
return handleMetalOrmWrapper(objectType, ctx);
|
|
1005
|
+
}
|
|
1006
|
+
return handleObjectType(objectType, ctx, typeNode);
|
|
1007
|
+
}
|
|
1008
|
+
return {};
|
|
1009
|
+
}
|
|
1010
|
+
function isSetType(type, _checker) {
|
|
1011
|
+
const symbol = type.getSymbol();
|
|
1012
|
+
if (!symbol) return false;
|
|
1013
|
+
const name = symbol.getName();
|
|
1014
|
+
if (name === "Set") return true;
|
|
1015
|
+
return false;
|
|
1016
|
+
}
|
|
704
1017
|
|
|
705
1018
|
// src/compiler/schema/extractAnnotations.ts
|
|
706
|
-
var
|
|
1019
|
+
var import_typescript8 = __toESM(require("typescript"), 1);
|
|
707
1020
|
function extractPropertySchemaFragments(checker, prop) {
|
|
708
|
-
if (!
|
|
709
|
-
const decs =
|
|
1021
|
+
if (!import_typescript8.default.canHaveDecorators(prop)) return [];
|
|
1022
|
+
const decs = import_typescript8.default.getDecorators(prop);
|
|
710
1023
|
if (!decs || decs.length === 0) return [];
|
|
711
1024
|
const frags = [];
|
|
712
1025
|
for (const d of decs) {
|
|
713
1026
|
const expr = d.expression;
|
|
714
1027
|
let callee;
|
|
715
1028
|
let args;
|
|
716
|
-
if (
|
|
1029
|
+
if (import_typescript8.default.isCallExpression(expr)) {
|
|
717
1030
|
callee = expr.expression;
|
|
718
1031
|
args = expr.arguments;
|
|
719
1032
|
} else {
|
|
720
1033
|
callee = expr;
|
|
721
|
-
args =
|
|
1034
|
+
args = import_typescript8.default.factory.createNodeArray([]);
|
|
722
1035
|
}
|
|
723
1036
|
const sym = checker.getSymbolAtLocation(callee);
|
|
724
1037
|
if (!sym) continue;
|
|
@@ -727,7 +1040,7 @@ function extractPropertySchemaFragments(checker, prop) {
|
|
|
727
1040
|
const name = resolved.name;
|
|
728
1041
|
if (name === "Schema") {
|
|
729
1042
|
const obj = args[0];
|
|
730
|
-
if (obj &&
|
|
1043
|
+
if (obj && import_typescript8.default.isObjectLiteralExpression(obj)) {
|
|
731
1044
|
const frag = objectLiteralToJson(obj);
|
|
732
1045
|
if (frag) frags.push(frag);
|
|
733
1046
|
}
|
|
@@ -749,7 +1062,7 @@ function extractPropertySchemaFragments(checker, prop) {
|
|
|
749
1062
|
frags.push({ format: args[0].text });
|
|
750
1063
|
} else if (name === "Pattern") {
|
|
751
1064
|
const arg = args[0];
|
|
752
|
-
if (arg &&
|
|
1065
|
+
if (arg && import_typescript8.default.isRegularExpressionLiteral(arg)) {
|
|
753
1066
|
frags.push({ pattern: extractRegexPattern(arg.text) });
|
|
754
1067
|
} else if (isStringLiteral(arg)) {
|
|
755
1068
|
frags.push({ pattern: arg.text });
|
|
@@ -766,11 +1079,11 @@ function extractPropertySchemaFragments(checker, prop) {
|
|
|
766
1079
|
frags.push({ multipleOf: Number(args[0].text) });
|
|
767
1080
|
} else if (name === "Example") {
|
|
768
1081
|
frags.push({ example: literalToJson(args[0]) });
|
|
769
|
-
} else if (name === "Examples" &&
|
|
1082
|
+
} else if (name === "Examples" && import_typescript8.default.isArrayLiteralExpression(args[0])) {
|
|
770
1083
|
frags.push({ examples: args[0].elements.map((e) => literalToJson(e)) });
|
|
771
1084
|
} else if (name === "Description" && isStringLiteral(args[0])) {
|
|
772
1085
|
frags.push({ description: args[0].text });
|
|
773
|
-
} else if (name === "Enum" &&
|
|
1086
|
+
} else if (name === "Enum" && import_typescript8.default.isArrayLiteralExpression(args[0])) {
|
|
774
1087
|
frags.push({ enum: args[0].elements.map((e) => literalToJson(e)) });
|
|
775
1088
|
} else if (name === "Const") {
|
|
776
1089
|
frags.push({ const: literalToJson(args[0]) });
|
|
@@ -778,9 +1091,9 @@ function extractPropertySchemaFragments(checker, prop) {
|
|
|
778
1091
|
frags.push({ default: literalToJson(args[0]) });
|
|
779
1092
|
} else if (name === "AdditionalProperties") {
|
|
780
1093
|
const arg = args[0];
|
|
781
|
-
if (arg && (arg.kind ===
|
|
782
|
-
frags.push({ additionalProperties: arg.kind ===
|
|
783
|
-
} else if (arg &&
|
|
1094
|
+
if (arg && (arg.kind === import_typescript8.default.SyntaxKind.FalseKeyword || arg.kind === import_typescript8.default.SyntaxKind.TrueKeyword)) {
|
|
1095
|
+
frags.push({ additionalProperties: arg.kind === import_typescript8.default.SyntaxKind.TrueKeyword });
|
|
1096
|
+
} else if (arg && import_typescript8.default.isObjectLiteralExpression(arg)) {
|
|
784
1097
|
const obj = objectLiteralToJson(arg);
|
|
785
1098
|
if (obj) frags.push({ additionalProperties: obj });
|
|
786
1099
|
}
|
|
@@ -793,7 +1106,7 @@ function extractPropertySchemaFragments(checker, prop) {
|
|
|
793
1106
|
return frags;
|
|
794
1107
|
}
|
|
795
1108
|
function resolveImportedDecorator(checker, sym) {
|
|
796
|
-
const target = sym.flags &
|
|
1109
|
+
const target = sym.flags & import_typescript8.default.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
|
|
797
1110
|
const name = target.getName();
|
|
798
1111
|
const decl = target.declarations?.[0];
|
|
799
1112
|
if (!decl) return null;
|
|
@@ -801,111 +1114,320 @@ function resolveImportedDecorator(checker, sym) {
|
|
|
801
1114
|
if (fileName.includes("/node_modules/adorn-api/") || fileName.includes("/src/schema/")) {
|
|
802
1115
|
return { module: "adorn-api/schema", name };
|
|
803
1116
|
}
|
|
804
|
-
return null;
|
|
805
|
-
}
|
|
806
|
-
function isNumberLiteral(node) {
|
|
807
|
-
return !!node &&
|
|
1117
|
+
return null;
|
|
1118
|
+
}
|
|
1119
|
+
function isNumberLiteral(node) {
|
|
1120
|
+
return !!node && import_typescript8.default.isNumericLiteral(node);
|
|
1121
|
+
}
|
|
1122
|
+
function isStringLiteral(node) {
|
|
1123
|
+
return !!node && import_typescript8.default.isStringLiteral(node);
|
|
1124
|
+
}
|
|
1125
|
+
function extractRegexPattern(text) {
|
|
1126
|
+
const match = text.match(/^\/(.+)\/[gimsuy]*$/);
|
|
1127
|
+
return match ? match[1] : text;
|
|
1128
|
+
}
|
|
1129
|
+
function objectLiteralToJson(obj) {
|
|
1130
|
+
const out = {};
|
|
1131
|
+
for (const prop of obj.properties) {
|
|
1132
|
+
if (!import_typescript8.default.isPropertyAssignment(prop)) continue;
|
|
1133
|
+
const name = prop.name;
|
|
1134
|
+
let key;
|
|
1135
|
+
if (import_typescript8.default.isIdentifier(name)) {
|
|
1136
|
+
key = name.text;
|
|
1137
|
+
} else if (import_typescript8.default.isStringLiteral(name)) {
|
|
1138
|
+
key = name.text;
|
|
1139
|
+
} else {
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
out[key] = literalToJson(prop.initializer);
|
|
1143
|
+
}
|
|
1144
|
+
return out;
|
|
1145
|
+
}
|
|
1146
|
+
function literalToJson(node) {
|
|
1147
|
+
if (import_typescript8.default.isStringLiteral(node)) return node.text;
|
|
1148
|
+
if (import_typescript8.default.isNumericLiteral(node)) return Number(node.text);
|
|
1149
|
+
if (node.kind === import_typescript8.default.SyntaxKind.TrueKeyword) return true;
|
|
1150
|
+
if (node.kind === import_typescript8.default.SyntaxKind.FalseKeyword) return false;
|
|
1151
|
+
if (node.kind === import_typescript8.default.SyntaxKind.NullKeyword) return null;
|
|
1152
|
+
if (import_typescript8.default.isObjectLiteralExpression(node)) return objectLiteralToJson(node);
|
|
1153
|
+
if (import_typescript8.default.isArrayLiteralExpression(node)) return node.elements.map((e) => literalToJson(e));
|
|
1154
|
+
return void 0;
|
|
1155
|
+
}
|
|
1156
|
+
function mergeFragments(base, ...frags) {
|
|
1157
|
+
const result = { ...base };
|
|
1158
|
+
for (const frag of frags) {
|
|
1159
|
+
Object.assign(result, frag);
|
|
1160
|
+
}
|
|
1161
|
+
return result;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// src/compiler/schema/parameters.ts
|
|
1165
|
+
function buildPathParameters(operation, ctx, parameters) {
|
|
1166
|
+
for (const paramIndex of operation.pathParamIndices) {
|
|
1167
|
+
const param = operation.parameters[paramIndex];
|
|
1168
|
+
if (param) {
|
|
1169
|
+
let paramSchema = typeToJsonSchema(param.type, ctx);
|
|
1170
|
+
if (param.paramNode) {
|
|
1171
|
+
const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
|
|
1172
|
+
if (frags.length > 0) {
|
|
1173
|
+
paramSchema = mergeFragments(paramSchema, ...frags);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
const schema = paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema;
|
|
1177
|
+
const paramName = param.name.toLowerCase();
|
|
1178
|
+
const isIdParam = paramName === "id" || paramName.endsWith("id");
|
|
1179
|
+
if (!schema.$ref && schema.type === "number" && isIdParam) {
|
|
1180
|
+
schema.type = "integer";
|
|
1181
|
+
if (!schema.minimum) {
|
|
1182
|
+
schema.minimum = 1;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
parameters.push({
|
|
1186
|
+
name: param.name,
|
|
1187
|
+
in: "path",
|
|
1188
|
+
required: !param.isOptional,
|
|
1189
|
+
schema
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
function buildQueryParameters(operation, ctx, parameters) {
|
|
1195
|
+
if (operation.queryObjectParamIndex !== null) {
|
|
1196
|
+
const queryParam = operation.parameters[operation.queryObjectParamIndex];
|
|
1197
|
+
if (!queryParam) return;
|
|
1198
|
+
const querySchema = typeToJsonSchema(queryParam.type, ctx);
|
|
1199
|
+
const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps(querySchema, ctx.components);
|
|
1200
|
+
for (const [propName, propSchema] of Object.entries(queryObjProps)) {
|
|
1201
|
+
const isRequired = queryRequired.includes(propName);
|
|
1202
|
+
const isObjectLike = isObjectLikeSchema(propSchema, ctx);
|
|
1203
|
+
const serialization = determineQuerySerialization(propSchema.type);
|
|
1204
|
+
const exampleValue = generateExampleValue(propSchema, propName);
|
|
1205
|
+
if (isObjectLike) {
|
|
1206
|
+
const schemaRef = propSchema.$ref || "#/components/schemas/InlineQueryParam";
|
|
1207
|
+
parameters.push({
|
|
1208
|
+
name: propName,
|
|
1209
|
+
in: "query",
|
|
1210
|
+
required: isRequired,
|
|
1211
|
+
schema: { type: "string" },
|
|
1212
|
+
description: `JSON-encoded object. ${exampleValue}`,
|
|
1213
|
+
examples: {
|
|
1214
|
+
default: { value: parseExampleValue(exampleValue) }
|
|
1215
|
+
},
|
|
1216
|
+
"x-adorn-jsonSchemaRef": schemaRef
|
|
1217
|
+
});
|
|
1218
|
+
} else {
|
|
1219
|
+
const paramDef = {
|
|
1220
|
+
name: propName,
|
|
1221
|
+
in: "query",
|
|
1222
|
+
required: isRequired,
|
|
1223
|
+
schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
|
|
1224
|
+
};
|
|
1225
|
+
if (propName === "page") {
|
|
1226
|
+
paramDef.schema = { type: "integer", default: 1, minimum: 1 };
|
|
1227
|
+
} else if (propName === "pageSize") {
|
|
1228
|
+
paramDef.schema = { type: "integer", default: 10, minimum: 1 };
|
|
1229
|
+
} else if (propName === "totalItems") {
|
|
1230
|
+
paramDef.schema = { type: "integer", minimum: 0 };
|
|
1231
|
+
} else if (propName === "sort") {
|
|
1232
|
+
paramDef.schema = {
|
|
1233
|
+
oneOf: [
|
|
1234
|
+
{ type: "string" },
|
|
1235
|
+
{ type: "array", items: { type: "string" } }
|
|
1236
|
+
]
|
|
1237
|
+
};
|
|
1238
|
+
} else if (propName === "q") {
|
|
1239
|
+
paramDef.schema = { type: "string" };
|
|
1240
|
+
} else if (propName === "hasComments") {
|
|
1241
|
+
paramDef.schema = { type: "boolean" };
|
|
1242
|
+
}
|
|
1243
|
+
if (Object.keys(serialization).length > 0) {
|
|
1244
|
+
Object.assign(paramDef, serialization);
|
|
1245
|
+
}
|
|
1246
|
+
parameters.push(paramDef);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
for (const paramIndex of operation.queryParamIndices) {
|
|
1251
|
+
const param = operation.parameters[paramIndex];
|
|
1252
|
+
if (param) {
|
|
1253
|
+
let paramSchema = typeToJsonSchema(param.type, ctx);
|
|
1254
|
+
if (param.paramNode) {
|
|
1255
|
+
const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
|
|
1256
|
+
if (frags.length > 0) {
|
|
1257
|
+
paramSchema = mergeFragments(paramSchema, ...frags);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
const isObjectLike = isObjectLikeSchema(paramSchema, ctx);
|
|
1261
|
+
if (isObjectLike) {
|
|
1262
|
+
const schemaRef = paramSchema.$ref || "#/components/schemas/InlineQueryParam";
|
|
1263
|
+
const exampleValue = generateExampleValue(paramSchema, param.name);
|
|
1264
|
+
parameters.push({
|
|
1265
|
+
name: param.name,
|
|
1266
|
+
in: "query",
|
|
1267
|
+
required: !param.isOptional,
|
|
1268
|
+
schema: { type: "string" },
|
|
1269
|
+
description: `JSON-encoded object. ${exampleValue}`,
|
|
1270
|
+
examples: {
|
|
1271
|
+
default: { value: parseExampleValue(exampleValue) }
|
|
1272
|
+
},
|
|
1273
|
+
"x-adorn-jsonSchemaRef": schemaRef
|
|
1274
|
+
});
|
|
1275
|
+
} else {
|
|
1276
|
+
const serialization = determineQuerySerialization(paramSchema.type);
|
|
1277
|
+
parameters.push({
|
|
1278
|
+
name: param.name,
|
|
1279
|
+
in: "query",
|
|
1280
|
+
required: !param.isOptional,
|
|
1281
|
+
schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema,
|
|
1282
|
+
...Object.keys(serialization).length > 0 ? serialization : {}
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
function buildHeaderParameters(operation, ctx, parameters) {
|
|
1289
|
+
if (operation.headerObjectParamIndex === null) return;
|
|
1290
|
+
const headerParam = operation.parameters[operation.headerObjectParamIndex];
|
|
1291
|
+
if (!headerParam) return;
|
|
1292
|
+
const headerSchema = typeToJsonSchema(headerParam.type, ctx);
|
|
1293
|
+
if (!headerSchema.properties) return;
|
|
1294
|
+
const headerObjProps = headerSchema.properties;
|
|
1295
|
+
for (const [propName, propSchema] of Object.entries(headerObjProps)) {
|
|
1296
|
+
const isRequired = headerSchema.required?.includes(propName) ?? false;
|
|
1297
|
+
parameters.push({
|
|
1298
|
+
name: propName,
|
|
1299
|
+
in: "header",
|
|
1300
|
+
required: isRequired,
|
|
1301
|
+
schema: propSchema
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
808
1304
|
}
|
|
809
|
-
function
|
|
810
|
-
|
|
1305
|
+
function buildCookieParameters(operation, ctx, parameters) {
|
|
1306
|
+
if (operation.cookieObjectParamIndex === null) return;
|
|
1307
|
+
const cookieParam = operation.parameters[operation.cookieObjectParamIndex];
|
|
1308
|
+
if (!cookieParam) return;
|
|
1309
|
+
const cookieSchema = typeToJsonSchema(cookieParam.type, ctx);
|
|
1310
|
+
if (!cookieSchema.properties) return;
|
|
1311
|
+
const cookieObjProps = cookieSchema.properties;
|
|
1312
|
+
for (const [propName, propSchema] of Object.entries(cookieObjProps)) {
|
|
1313
|
+
const isRequired = cookieSchema.required?.includes(propName) ?? false;
|
|
1314
|
+
parameters.push({
|
|
1315
|
+
name: propName,
|
|
1316
|
+
in: "cookie",
|
|
1317
|
+
required: isRequired,
|
|
1318
|
+
schema: propSchema,
|
|
1319
|
+
style: "form",
|
|
1320
|
+
explode: true
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
811
1323
|
}
|
|
812
|
-
function
|
|
813
|
-
const
|
|
814
|
-
|
|
1324
|
+
function determineQuerySerialization(schemaType) {
|
|
1325
|
+
const typeArray = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
|
|
1326
|
+
const isArray = typeArray.includes("array");
|
|
1327
|
+
if (isArray) {
|
|
1328
|
+
return { style: "form", explode: true };
|
|
1329
|
+
}
|
|
1330
|
+
return {};
|
|
815
1331
|
}
|
|
816
|
-
function
|
|
817
|
-
const
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1332
|
+
function generateExampleValue(schema, propName) {
|
|
1333
|
+
const resolved = resolveSchemaRef(schema, /* @__PURE__ */ new Map());
|
|
1334
|
+
if (resolved.type === "object" && resolved.properties) {
|
|
1335
|
+
const example = {};
|
|
1336
|
+
for (const [key, prop] of Object.entries(resolved.properties)) {
|
|
1337
|
+
const propResolved = resolveSchemaRef(prop, /* @__PURE__ */ new Map());
|
|
1338
|
+
if (propResolved.type === "string") {
|
|
1339
|
+
example[key] = "value";
|
|
1340
|
+
} else if (propResolved.type === "number" || propResolved.type === "integer") {
|
|
1341
|
+
example[key] = 1;
|
|
1342
|
+
} else if (propResolved.type === "boolean") {
|
|
1343
|
+
example[key] = true;
|
|
1344
|
+
} else if (Array.isArray(propResolved.type) && propResolved.type.includes("null")) {
|
|
1345
|
+
example[key] = null;
|
|
1346
|
+
} else if (propResolved.enum) {
|
|
1347
|
+
example[key] = propResolved.enum[0];
|
|
1348
|
+
} else {
|
|
1349
|
+
example[key] = "value";
|
|
1350
|
+
}
|
|
828
1351
|
}
|
|
829
|
-
|
|
1352
|
+
return `Example: ${propName}=${JSON.stringify(example)}`;
|
|
830
1353
|
}
|
|
831
|
-
return
|
|
832
|
-
}
|
|
833
|
-
function literalToJson(node) {
|
|
834
|
-
if (import_typescript4.default.isStringLiteral(node)) return node.text;
|
|
835
|
-
if (import_typescript4.default.isNumericLiteral(node)) return Number(node.text);
|
|
836
|
-
if (node.kind === import_typescript4.default.SyntaxKind.TrueKeyword) return true;
|
|
837
|
-
if (node.kind === import_typescript4.default.SyntaxKind.FalseKeyword) return false;
|
|
838
|
-
if (node.kind === import_typescript4.default.SyntaxKind.NullKeyword) return null;
|
|
839
|
-
if (import_typescript4.default.isObjectLiteralExpression(node)) return objectLiteralToJson(node);
|
|
840
|
-
if (import_typescript4.default.isArrayLiteralExpression(node)) return node.elements.map((e) => literalToJson(e));
|
|
841
|
-
return void 0;
|
|
1354
|
+
return `Example: ${propName}=${JSON.stringify({ key: "value" })}`;
|
|
842
1355
|
}
|
|
843
|
-
function
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
|
|
1356
|
+
function parseExampleValue(description) {
|
|
1357
|
+
const match = description.match(/Example:\s*\w+=(\{[^}]+\})/);
|
|
1358
|
+
if (match) {
|
|
1359
|
+
return match[1];
|
|
847
1360
|
}
|
|
848
|
-
return
|
|
1361
|
+
return JSON.stringify({ key: "value" });
|
|
849
1362
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
const callee = isCall ? expr.expression : expr;
|
|
861
|
-
const args = isCall ? expr.arguments : import_typescript5.default.factory.createNodeArray([]);
|
|
862
|
-
const sym = checker.getSymbolAtLocation(callee);
|
|
863
|
-
if (!sym) continue;
|
|
864
|
-
const resolved = sym.flags & import_typescript5.default.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
|
|
865
|
-
const name = resolved.getName();
|
|
866
|
-
if (name !== "QueryStyle") continue;
|
|
867
|
-
const optsNode = args[0];
|
|
868
|
-
if (!optsNode || !import_typescript5.default.isObjectLiteralExpression(optsNode)) {
|
|
869
|
-
return {};
|
|
1363
|
+
function isObjectLikeSchema(schema, ctx) {
|
|
1364
|
+
const resolved = resolveSchemaRef(schema, ctx.components);
|
|
1365
|
+
if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
|
|
1366
|
+
return true;
|
|
1367
|
+
}
|
|
1368
|
+
if (resolved.allOf) {
|
|
1369
|
+
for (const branch of resolved.allOf) {
|
|
1370
|
+
if (isObjectLikeSchema(branch, ctx)) {
|
|
1371
|
+
return true;
|
|
1372
|
+
}
|
|
870
1373
|
}
|
|
871
|
-
return parseQueryStyleOptions(optsNode);
|
|
872
1374
|
}
|
|
873
|
-
|
|
1375
|
+
if (resolved.type === "array" && resolved.items) {
|
|
1376
|
+
const itemsSchema = resolveSchemaRef(resolved.items, ctx.components);
|
|
1377
|
+
return isObjectLikeSchema(itemsSchema, ctx);
|
|
1378
|
+
}
|
|
1379
|
+
return false;
|
|
874
1380
|
}
|
|
875
|
-
function
|
|
876
|
-
const
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
} else if (name === "explode" && isBooleanLiteral(prop.initializer)) {
|
|
885
|
-
opts.explode = prop.initializer.kind === import_typescript5.default.SyntaxKind.TrueKeyword;
|
|
886
|
-
} else if (name === "allowReserved" && isBooleanLiteral(prop.initializer)) {
|
|
887
|
-
opts.allowReserved = prop.initializer.kind === import_typescript5.default.SyntaxKind.TrueKeyword;
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
return opts;
|
|
891
|
-
}
|
|
892
|
-
function getPropName(name) {
|
|
893
|
-
if (import_typescript5.default.isIdentifier(name)) return name.text;
|
|
894
|
-
if (import_typescript5.default.isStringLiteral(name)) return name.text;
|
|
895
|
-
return null;
|
|
1381
|
+
function resolveSchemaRef(schema, components) {
|
|
1382
|
+
const ref = schema.$ref;
|
|
1383
|
+
if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
|
|
1384
|
+
return schema;
|
|
1385
|
+
}
|
|
1386
|
+
const name = ref.replace("#/components/schemas/", "");
|
|
1387
|
+
const next = components.get(name);
|
|
1388
|
+
if (!next) return schema;
|
|
1389
|
+
return resolveSchemaRef(next, components);
|
|
896
1390
|
}
|
|
897
|
-
function
|
|
898
|
-
|
|
1391
|
+
function resolveAndCollectObjectProps(schema, components) {
|
|
1392
|
+
const resolved = resolveSchemaRef(schema, components);
|
|
1393
|
+
const properties = {};
|
|
1394
|
+
const required = [];
|
|
1395
|
+
const processSchema = (s) => {
|
|
1396
|
+
const current = resolveSchemaRef(s, components);
|
|
1397
|
+
if (current.properties) {
|
|
1398
|
+
for (const [key, val] of Object.entries(current.properties)) {
|
|
1399
|
+
if (!properties[key]) {
|
|
1400
|
+
properties[key] = val;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
if (current.required) {
|
|
1405
|
+
for (const req of current.required) {
|
|
1406
|
+
if (!required.includes(req)) {
|
|
1407
|
+
required.push(req);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
if (current.allOf) {
|
|
1412
|
+
for (const branch of current.allOf) {
|
|
1413
|
+
processSchema(branch);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
};
|
|
1417
|
+
processSchema(resolved);
|
|
1418
|
+
return { properties, required };
|
|
899
1419
|
}
|
|
900
1420
|
|
|
901
1421
|
// src/compiler/schema/openapi.ts
|
|
1422
|
+
var METAL_ORM_WRAPPER_NAMES2 = ["BelongsToReference", "HasOneReference", "HasManyCollection", "ManyToManyCollection"];
|
|
902
1423
|
function generateOpenAPI(controllers, checker, options = {}) {
|
|
903
1424
|
const components = /* @__PURE__ */ new Map();
|
|
904
1425
|
const ctx = {
|
|
905
1426
|
checker,
|
|
906
1427
|
components,
|
|
907
1428
|
typeStack: /* @__PURE__ */ new Set(),
|
|
908
|
-
typeNameStack: []
|
|
1429
|
+
typeNameStack: [],
|
|
1430
|
+
mode: "response"
|
|
909
1431
|
};
|
|
910
1432
|
const paths = {};
|
|
911
1433
|
for (const controller of controllers) {
|
|
@@ -918,6 +1440,8 @@ function generateOpenAPI(controllers, checker, options = {}) {
|
|
|
918
1440
|
paths[fullPath][method] = buildOperation(operation, ctx, controller.consumes);
|
|
919
1441
|
}
|
|
920
1442
|
}
|
|
1443
|
+
const schemas = Object.fromEntries(components);
|
|
1444
|
+
cleanupMetalOrmWrappers(schemas, paths);
|
|
921
1445
|
return {
|
|
922
1446
|
openapi: "3.1.0",
|
|
923
1447
|
info: {
|
|
@@ -925,15 +1449,120 @@ function generateOpenAPI(controllers, checker, options = {}) {
|
|
|
925
1449
|
version: options.version ?? "1.0.0"
|
|
926
1450
|
},
|
|
927
1451
|
components: {
|
|
928
|
-
schemas
|
|
1452
|
+
schemas
|
|
929
1453
|
},
|
|
930
1454
|
paths
|
|
931
1455
|
};
|
|
932
1456
|
}
|
|
1457
|
+
function cleanupMetalOrmWrappers(schemas, paths) {
|
|
1458
|
+
const schemasToDelete = /* @__PURE__ */ new Set();
|
|
1459
|
+
for (const wrapperName of METAL_ORM_WRAPPER_NAMES2) {
|
|
1460
|
+
if (schemas[wrapperName]) {
|
|
1461
|
+
schemasToDelete.add(wrapperName);
|
|
1462
|
+
}
|
|
1463
|
+
if (schemas[`${wrapperName}Api`]) {
|
|
1464
|
+
schemasToDelete.add(`${wrapperName}Api`);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
for (const schema of Object.values(schemas)) {
|
|
1468
|
+
cleanupSchemaRefs(schema, schemasToDelete);
|
|
1469
|
+
}
|
|
1470
|
+
for (const pathItem of Object.values(paths)) {
|
|
1471
|
+
cleanupPathItemRefs(pathItem, schemasToDelete);
|
|
1472
|
+
}
|
|
1473
|
+
for (const schemaName of schemasToDelete) {
|
|
1474
|
+
delete schemas[schemaName];
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
function cleanupSchemaRefs(schema, schemasToDelete) {
|
|
1478
|
+
if (typeof schema !== "object" || schema === null) {
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
if (schema.properties) {
|
|
1482
|
+
for (const propName of Object.keys(schema.properties)) {
|
|
1483
|
+
const propSchema = schema.properties[propName];
|
|
1484
|
+
if (propSchema.$ref && typeof propSchema.$ref === "string") {
|
|
1485
|
+
const refName = propSchema.$ref.replace("#/components/schemas/", "");
|
|
1486
|
+
if (schemasToDelete.has(refName)) {
|
|
1487
|
+
delete schema.properties[propName];
|
|
1488
|
+
if (schema.required && Array.isArray(schema.required)) {
|
|
1489
|
+
schema.required = schema.required.filter((r) => r !== propName);
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
} else {
|
|
1493
|
+
cleanupSchemaRefs(propSchema, schemasToDelete);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
if (schema.items) {
|
|
1498
|
+
cleanupSchemaRefs(schema.items, schemasToDelete);
|
|
1499
|
+
}
|
|
1500
|
+
if (schema.allOf) {
|
|
1501
|
+
for (const item of schema.allOf) {
|
|
1502
|
+
cleanupSchemaRefs(item, schemasToDelete);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
function cleanupPathItemRefs(pathItem, schemasToDelete) {
|
|
1507
|
+
if (typeof pathItem !== "object" || pathItem === null) {
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
for (const method of Object.keys(pathItem)) {
|
|
1511
|
+
const operation = pathItem[method];
|
|
1512
|
+
if (typeof operation !== "object" || operation === null) continue;
|
|
1513
|
+
if (operation.requestBody) {
|
|
1514
|
+
cleanupRequestBodyRefs(operation.requestBody, schemasToDelete);
|
|
1515
|
+
}
|
|
1516
|
+
if (operation.responses) {
|
|
1517
|
+
const responses = Object.values(operation.responses);
|
|
1518
|
+
for (const response of responses) {
|
|
1519
|
+
if (response.content) {
|
|
1520
|
+
const contentTypes = Object.values(response.content);
|
|
1521
|
+
for (const contentType of contentTypes) {
|
|
1522
|
+
if (contentType.schema) {
|
|
1523
|
+
cleanupSchemaRefs(contentType.schema, schemasToDelete);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
function cleanupRequestBodyRefs(requestBody, schemasToDelete) {
|
|
1532
|
+
if (typeof requestBody !== "object" || requestBody === null) return;
|
|
1533
|
+
if (requestBody.content) {
|
|
1534
|
+
const contentTypes = Object.values(requestBody.content);
|
|
1535
|
+
for (const contentType of contentTypes) {
|
|
1536
|
+
if (contentType.schema) {
|
|
1537
|
+
cleanupSchemaRefs(contentType.schema, schemasToDelete);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
if (requestBody.properties) {
|
|
1542
|
+
for (const propName of Object.keys(requestBody.properties)) {
|
|
1543
|
+
const propSchema = requestBody.properties[propName];
|
|
1544
|
+
if (propSchema.$ref && typeof propSchema.$ref === "string") {
|
|
1545
|
+
const refName = propSchema.$ref.replace("#/components/schemas/", "");
|
|
1546
|
+
if (schemasToDelete.has(refName)) {
|
|
1547
|
+
delete requestBody.properties[propName];
|
|
1548
|
+
if (requestBody.required && Array.isArray(requestBody.required)) {
|
|
1549
|
+
requestBody.required = requestBody.required.filter((r) => r !== propName);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
} else {
|
|
1553
|
+
cleanupSchemaRefs(propSchema, schemasToDelete);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
933
1558
|
function convertToOpenApiPath(basePath, path4) {
|
|
934
1559
|
const base = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
935
1560
|
const converted = path4.replace(/:([^/]+)/g, "{$1}");
|
|
936
|
-
|
|
1561
|
+
let fullPath = base + converted || "/";
|
|
1562
|
+
if (fullPath.endsWith("/") && fullPath !== "/") {
|
|
1563
|
+
fullPath = fullPath.slice(0, -1);
|
|
1564
|
+
}
|
|
1565
|
+
return fullPath;
|
|
937
1566
|
}
|
|
938
1567
|
function buildOperation(operation, ctx, controllerConsumes) {
|
|
939
1568
|
const op = {
|
|
@@ -948,7 +1577,8 @@ function buildOperation(operation, ctx, controllerConsumes) {
|
|
|
948
1577
|
if (parameters.length > 0) {
|
|
949
1578
|
op.parameters = parameters;
|
|
950
1579
|
}
|
|
951
|
-
const
|
|
1580
|
+
const responseCtx = { ...ctx, mode: "response" };
|
|
1581
|
+
const responseSchema = typeToJsonSchema(operation.returnType, responseCtx, operation.returnTypeNode);
|
|
952
1582
|
const status = operation.httpMethod === "POST" ? 201 : 200;
|
|
953
1583
|
op.responses[status] = {
|
|
954
1584
|
description: status === 201 ? "Created" : "OK",
|
|
@@ -961,8 +1591,9 @@ function buildOperation(operation, ctx, controllerConsumes) {
|
|
|
961
1591
|
if (["POST", "PUT", "PATCH"].includes(operation.httpMethod) && operation.bodyParamIndex !== null) {
|
|
962
1592
|
const bodyParam = operation.parameters[operation.bodyParamIndex];
|
|
963
1593
|
if (bodyParam) {
|
|
964
|
-
|
|
965
|
-
bodySchema =
|
|
1594
|
+
const requestCtx = { ...ctx, mode: "request" };
|
|
1595
|
+
let bodySchema = typeToJsonSchema(bodyParam.type, requestCtx);
|
|
1596
|
+
bodySchema = mergeBodySchemaAnnotations(bodyParam, requestCtx, bodySchema);
|
|
966
1597
|
const contentType = operation.bodyContentType ?? controllerConsumes?.[0] ?? "application/json";
|
|
967
1598
|
const requestBody = {
|
|
968
1599
|
required: !bodyParam.isOptional,
|
|
@@ -989,12 +1620,12 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
|
|
|
989
1620
|
const declarations = typeSymbol.getDeclarations();
|
|
990
1621
|
if (!declarations || declarations.length === 0) return schema;
|
|
991
1622
|
const classDecl = declarations[0];
|
|
992
|
-
if (!
|
|
1623
|
+
if (!import_typescript9.default.isClassDeclaration(classDecl)) return schema;
|
|
993
1624
|
const result = { ...schema };
|
|
994
1625
|
const props = { ...result.properties };
|
|
995
1626
|
for (const member of classDecl.members) {
|
|
996
|
-
if (!
|
|
997
|
-
const propName =
|
|
1627
|
+
if (!import_typescript9.default.isPropertyDeclaration(member) || !member.name) continue;
|
|
1628
|
+
const propName = import_typescript9.default.isIdentifier(member.name) ? member.name.text : null;
|
|
998
1629
|
if (!propName) continue;
|
|
999
1630
|
if (!props[propName]) continue;
|
|
1000
1631
|
const frags = extractPropertySchemaFragments(ctx.checker, member);
|
|
@@ -1005,137 +1636,17 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
|
|
|
1005
1636
|
result.properties = props;
|
|
1006
1637
|
return result;
|
|
1007
1638
|
}
|
|
1008
|
-
function buildPathParameters(operation, ctx, parameters) {
|
|
1009
|
-
for (const paramIndex of operation.pathParamIndices) {
|
|
1010
|
-
const param = operation.parameters[paramIndex];
|
|
1011
|
-
if (param) {
|
|
1012
|
-
let paramSchema = typeToJsonSchema(param.type, ctx);
|
|
1013
|
-
if (param.paramNode) {
|
|
1014
|
-
const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
|
|
1015
|
-
if (frags.length > 0) {
|
|
1016
|
-
paramSchema = mergeFragments(paramSchema, ...frags);
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
parameters.push({
|
|
1020
|
-
name: param.name,
|
|
1021
|
-
in: "path",
|
|
1022
|
-
required: !param.isOptional,
|
|
1023
|
-
schema: paramSchema.$ref ? { type: "string", $ref: paramSchema.$ref } : paramSchema
|
|
1024
|
-
});
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
function buildQueryParameters(operation, ctx, parameters) {
|
|
1029
|
-
if (operation.queryObjectParamIndex !== null) {
|
|
1030
|
-
const queryParam = operation.parameters[operation.queryObjectParamIndex];
|
|
1031
|
-
if (!queryParam) return;
|
|
1032
|
-
const queryStyle = extractQueryStyleOptions(ctx.checker, operation.methodDeclaration);
|
|
1033
|
-
const querySchema = typeToJsonSchema(queryParam.type, ctx);
|
|
1034
|
-
if (queryStyle?.style === "deepObject") {
|
|
1035
|
-
const explode = queryStyle.explode ?? true;
|
|
1036
|
-
const deepParam = {
|
|
1037
|
-
name: queryParam.name,
|
|
1038
|
-
in: "query",
|
|
1039
|
-
required: !queryParam.isOptional,
|
|
1040
|
-
schema: querySchema.$ref ? { $ref: querySchema.$ref } : querySchema,
|
|
1041
|
-
style: "deepObject",
|
|
1042
|
-
explode
|
|
1043
|
-
};
|
|
1044
|
-
if (queryStyle.allowReserved !== void 0) {
|
|
1045
|
-
deepParam.allowReserved = queryStyle.allowReserved;
|
|
1046
|
-
}
|
|
1047
|
-
parameters.push(deepParam);
|
|
1048
|
-
} else {
|
|
1049
|
-
if (!querySchema.properties) return;
|
|
1050
|
-
const queryObjProps = querySchema.properties;
|
|
1051
|
-
for (const [propName, propSchema] of Object.entries(queryObjProps)) {
|
|
1052
|
-
const isRequired = querySchema.required?.includes(propName) ?? false;
|
|
1053
|
-
const serialization = determineQuerySerialization(propSchema.type);
|
|
1054
|
-
parameters.push({
|
|
1055
|
-
name: propName,
|
|
1056
|
-
in: "query",
|
|
1057
|
-
required: isRequired,
|
|
1058
|
-
schema: propSchema,
|
|
1059
|
-
...Object.keys(serialization).length > 0 ? serialization : {}
|
|
1060
|
-
});
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
for (const paramIndex of operation.queryParamIndices) {
|
|
1065
|
-
const param = operation.parameters[paramIndex];
|
|
1066
|
-
if (param) {
|
|
1067
|
-
let paramSchema = typeToJsonSchema(param.type, ctx);
|
|
1068
|
-
if (param.paramNode) {
|
|
1069
|
-
const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
|
|
1070
|
-
if (frags.length > 0) {
|
|
1071
|
-
paramSchema = mergeFragments(paramSchema, ...frags);
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
const serialization = determineQuerySerialization(paramSchema.type);
|
|
1075
|
-
parameters.push({
|
|
1076
|
-
name: param.name,
|
|
1077
|
-
in: "query",
|
|
1078
|
-
required: !param.isOptional,
|
|
1079
|
-
schema: paramSchema.$ref ? { type: "string", $ref: paramSchema.$ref } : paramSchema,
|
|
1080
|
-
...Object.keys(serialization).length > 0 ? serialization : {}
|
|
1081
|
-
});
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
function determineQuerySerialization(schemaType) {
|
|
1086
|
-
const typeArray = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
|
|
1087
|
-
const isArray = typeArray.includes("array");
|
|
1088
|
-
if (isArray) {
|
|
1089
|
-
return { style: "form", explode: true };
|
|
1090
|
-
}
|
|
1091
|
-
return {};
|
|
1092
|
-
}
|
|
1093
|
-
function buildHeaderParameters(operation, ctx, parameters) {
|
|
1094
|
-
if (operation.headerObjectParamIndex === null) return;
|
|
1095
|
-
const headerParam = operation.parameters[operation.headerObjectParamIndex];
|
|
1096
|
-
if (!headerParam) return;
|
|
1097
|
-
const headerSchema = typeToJsonSchema(headerParam.type, ctx);
|
|
1098
|
-
if (!headerSchema.properties) return;
|
|
1099
|
-
const headerObjProps = headerSchema.properties;
|
|
1100
|
-
for (const [propName, propSchema] of Object.entries(headerObjProps)) {
|
|
1101
|
-
const isRequired = headerSchema.required?.includes(propName) ?? false;
|
|
1102
|
-
parameters.push({
|
|
1103
|
-
name: propName,
|
|
1104
|
-
in: "header",
|
|
1105
|
-
required: isRequired,
|
|
1106
|
-
schema: propSchema
|
|
1107
|
-
});
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
function buildCookieParameters(operation, ctx, parameters) {
|
|
1111
|
-
if (operation.cookieObjectParamIndex === null) return;
|
|
1112
|
-
const cookieParam = operation.parameters[operation.cookieObjectParamIndex];
|
|
1113
|
-
if (!cookieParam) return;
|
|
1114
|
-
const cookieSchema = typeToJsonSchema(cookieParam.type, ctx);
|
|
1115
|
-
if (!cookieSchema.properties) return;
|
|
1116
|
-
const cookieObjProps = cookieSchema.properties;
|
|
1117
|
-
for (const [propName, propSchema] of Object.entries(cookieObjProps)) {
|
|
1118
|
-
const isRequired = cookieSchema.required?.includes(propName) ?? false;
|
|
1119
|
-
parameters.push({
|
|
1120
|
-
name: propName,
|
|
1121
|
-
in: "cookie",
|
|
1122
|
-
required: isRequired,
|
|
1123
|
-
schema: propSchema,
|
|
1124
|
-
style: "form",
|
|
1125
|
-
explode: true
|
|
1126
|
-
});
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
1639
|
|
|
1130
1640
|
// src/compiler/manifest/emit.ts
|
|
1131
|
-
var
|
|
1641
|
+
var import_typescript10 = __toESM(require("typescript"), 1);
|
|
1132
1642
|
function generateManifest(controllers, checker, version, validationMode = "ajv-runtime") {
|
|
1133
1643
|
const components = /* @__PURE__ */ new Map();
|
|
1134
1644
|
const ctx = {
|
|
1135
1645
|
checker,
|
|
1136
1646
|
components,
|
|
1137
1647
|
typeStack: /* @__PURE__ */ new Set(),
|
|
1138
|
-
typeNameStack: []
|
|
1648
|
+
typeNameStack: [],
|
|
1649
|
+
mode: "request"
|
|
1139
1650
|
};
|
|
1140
1651
|
const controllerEntries = controllers.map((ctrl) => ({
|
|
1141
1652
|
controllerId: ctrl.className,
|
|
@@ -1149,7 +1660,7 @@ function generateManifest(controllers, checker, version, validationMode = "ajv-r
|
|
|
1149
1660
|
generator: {
|
|
1150
1661
|
name: "adorn-api",
|
|
1151
1662
|
version,
|
|
1152
|
-
typescript:
|
|
1663
|
+
typescript: import_typescript10.default.version
|
|
1153
1664
|
},
|
|
1154
1665
|
schemas: {
|
|
1155
1666
|
kind: "openapi-3.1",
|
|
@@ -1160,14 +1671,70 @@ function generateManifest(controllers, checker, version, validationMode = "ajv-r
|
|
|
1160
1671
|
controllers: controllerEntries
|
|
1161
1672
|
};
|
|
1162
1673
|
}
|
|
1674
|
+
function resolveSchemaRef2(schema, components) {
|
|
1675
|
+
const ref = schema.$ref;
|
|
1676
|
+
if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
|
|
1677
|
+
return schema;
|
|
1678
|
+
}
|
|
1679
|
+
const name = ref.replace("#/components/schemas/", "");
|
|
1680
|
+
const next = components.get(name);
|
|
1681
|
+
if (!next) return schema;
|
|
1682
|
+
return resolveSchemaRef2(next, components);
|
|
1683
|
+
}
|
|
1684
|
+
function resolveAndCollectObjectProps2(schema, components) {
|
|
1685
|
+
const resolved = resolveSchemaRef2(schema, components);
|
|
1686
|
+
const properties = {};
|
|
1687
|
+
const required = [];
|
|
1688
|
+
const processSchema = (s) => {
|
|
1689
|
+
const current = resolveSchemaRef2(s, components);
|
|
1690
|
+
if (current.properties) {
|
|
1691
|
+
for (const [key, val] of Object.entries(current.properties)) {
|
|
1692
|
+
if (!properties[key]) {
|
|
1693
|
+
properties[key] = val;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
if (current.required) {
|
|
1698
|
+
for (const req of current.required) {
|
|
1699
|
+
if (!required.includes(req)) {
|
|
1700
|
+
required.push(req);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
if (current.allOf) {
|
|
1705
|
+
for (const branch of current.allOf) {
|
|
1706
|
+
processSchema(branch);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
};
|
|
1710
|
+
processSchema(resolved);
|
|
1711
|
+
return { properties, required };
|
|
1712
|
+
}
|
|
1713
|
+
function isObjectLikeSchema2(schema, components) {
|
|
1714
|
+
const resolved = resolveSchemaRef2(schema, components);
|
|
1715
|
+
if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
|
|
1716
|
+
return true;
|
|
1717
|
+
}
|
|
1718
|
+
if (resolved.allOf) {
|
|
1719
|
+
for (const branch of resolved.allOf) {
|
|
1720
|
+
if (isObjectLikeSchema2(branch, components)) {
|
|
1721
|
+
return true;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
if (resolved.type === "array" && resolved.items) {
|
|
1726
|
+
const itemsSchema = resolveSchemaRef2(resolved.items, components);
|
|
1727
|
+
return isObjectLikeSchema2(itemsSchema, components);
|
|
1728
|
+
}
|
|
1729
|
+
return false;
|
|
1730
|
+
}
|
|
1163
1731
|
function buildOperationEntry(op, ctx) {
|
|
1164
1732
|
const args = {
|
|
1165
1733
|
body: null,
|
|
1166
1734
|
path: [],
|
|
1167
1735
|
query: [],
|
|
1168
1736
|
headers: [],
|
|
1169
|
-
cookies: []
|
|
1170
|
-
paginationParamIndex: op.paginationParamIndex
|
|
1737
|
+
cookies: []
|
|
1171
1738
|
};
|
|
1172
1739
|
buildPathArgs(op, ctx, args);
|
|
1173
1740
|
buildQueryArgs(op, ctx, args);
|
|
@@ -1234,53 +1801,39 @@ function buildPathArgs(op, ctx, args) {
|
|
|
1234
1801
|
function buildQueryArgs(op, ctx, args) {
|
|
1235
1802
|
if (op.queryObjectParamIndex !== null) {
|
|
1236
1803
|
const queryParam = op.parameters[op.queryObjectParamIndex];
|
|
1237
|
-
if (queryParam)
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
schemaRef,
|
|
1247
|
-
schemaType: querySchema.type,
|
|
1248
|
-
serialization: {
|
|
1249
|
-
style: "deepObject",
|
|
1250
|
-
explode: queryStyle.explode ?? true,
|
|
1251
|
-
allowReserved: queryStyle.allowReserved
|
|
1252
|
-
}
|
|
1253
|
-
});
|
|
1254
|
-
} else {
|
|
1255
|
-
if (!querySchema.properties) return;
|
|
1256
|
-
for (const [propName, propSchema] of Object.entries(querySchema.properties)) {
|
|
1257
|
-
const isRequired = querySchema.required?.includes(propName) ?? false;
|
|
1258
|
-
let schemaRef = propSchema.$ref;
|
|
1259
|
-
if (!schemaRef) {
|
|
1260
|
-
schemaRef = "#/components/schemas/InlineQueryParam";
|
|
1261
|
-
}
|
|
1262
|
-
args.query.push({
|
|
1263
|
-
name: propName,
|
|
1264
|
-
index: queryParam.index,
|
|
1265
|
-
required: !isRequired,
|
|
1266
|
-
schemaRef,
|
|
1267
|
-
schemaType: propSchema.type
|
|
1268
|
-
});
|
|
1269
|
-
}
|
|
1804
|
+
if (!queryParam) return;
|
|
1805
|
+
const querySchema = typeToJsonSchema(queryParam.type, ctx);
|
|
1806
|
+
const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps2(querySchema, ctx.components);
|
|
1807
|
+
for (const [propName, propSchema] of Object.entries(queryObjProps)) {
|
|
1808
|
+
const isRequired = queryRequired.includes(propName) ?? false;
|
|
1809
|
+
const isObjectLike = isObjectLikeSchema2(propSchema, ctx.components);
|
|
1810
|
+
let schemaRef = propSchema.$ref;
|
|
1811
|
+
if (!schemaRef) {
|
|
1812
|
+
schemaRef = "#/components/schemas/InlineQueryParam";
|
|
1270
1813
|
}
|
|
1814
|
+
args.query.push({
|
|
1815
|
+
name: propName,
|
|
1816
|
+
index: queryParam.index,
|
|
1817
|
+
required: isRequired,
|
|
1818
|
+
schemaRef,
|
|
1819
|
+
schemaType: propSchema.type,
|
|
1820
|
+
content: isObjectLike ? "application/json" : void 0
|
|
1821
|
+
});
|
|
1271
1822
|
}
|
|
1272
1823
|
}
|
|
1273
1824
|
for (const paramIndex of op.queryParamIndices) {
|
|
1274
1825
|
const param = op.parameters[paramIndex];
|
|
1275
1826
|
if (param) {
|
|
1276
1827
|
const paramSchema = typeToJsonSchema(param.type, ctx);
|
|
1828
|
+
const isObjectLike = isObjectLikeSchema2(paramSchema, ctx.components);
|
|
1277
1829
|
const schemaRef = paramSchema.$ref ?? "#/components/schemas/InlineQueryParam";
|
|
1278
1830
|
args.query.push({
|
|
1279
1831
|
name: param.name,
|
|
1280
1832
|
index: param.index,
|
|
1281
1833
|
required: !param.isOptional,
|
|
1282
1834
|
schemaRef,
|
|
1283
|
-
schemaType: paramSchema.type
|
|
1835
|
+
schemaType: paramSchema.type,
|
|
1836
|
+
content: isObjectLike ? "application/json" : void 0
|
|
1284
1837
|
});
|
|
1285
1838
|
}
|
|
1286
1839
|
}
|
|
@@ -1300,7 +1853,7 @@ function buildHeaderArgs(op, ctx, args) {
|
|
|
1300
1853
|
args.headers.push({
|
|
1301
1854
|
name: propName,
|
|
1302
1855
|
index: headerParam.index,
|
|
1303
|
-
required:
|
|
1856
|
+
required: isRequired,
|
|
1304
1857
|
schemaRef,
|
|
1305
1858
|
schemaType: propSchema.type
|
|
1306
1859
|
});
|
|
@@ -1321,7 +1874,7 @@ function buildCookieArgs(op, ctx, args) {
|
|
|
1321
1874
|
args.cookies.push({
|
|
1322
1875
|
name: propName,
|
|
1323
1876
|
index: cookieParam.index,
|
|
1324
|
-
required:
|
|
1877
|
+
required: isRequired,
|
|
1325
1878
|
schemaRef,
|
|
1326
1879
|
schemaType: propSchema.type,
|
|
1327
1880
|
serialization: { style: "form", explode: true }
|
|
@@ -1337,7 +1890,7 @@ var import_ajv = __toESM(require("ajv"), 1);
|
|
|
1337
1890
|
var import_ajv_formats = __toESM(require("ajv-formats"), 1);
|
|
1338
1891
|
var OAS_SCHEMA_ONLY = /* @__PURE__ */ new Set(["discriminator", "xml", "externalDocs", "example"]);
|
|
1339
1892
|
function sanitizeSchemaForAjv(schema) {
|
|
1340
|
-
if (schema
|
|
1893
|
+
if (schema === null || typeof schema !== "object") return schema;
|
|
1341
1894
|
if (Array.isArray(schema)) return schema.map(sanitizeSchemaForAjv);
|
|
1342
1895
|
const out = {};
|
|
1343
1896
|
for (const [k, v] of Object.entries(schema)) {
|
|
@@ -1348,7 +1901,7 @@ function sanitizeSchemaForAjv(schema) {
|
|
|
1348
1901
|
return out;
|
|
1349
1902
|
}
|
|
1350
1903
|
function rewriteComponentRefs(schema) {
|
|
1351
|
-
if (schema
|
|
1904
|
+
if (schema === null || typeof schema !== "object") return schema;
|
|
1352
1905
|
if (Array.isArray(schema)) return schema.map(rewriteComponentRefs);
|
|
1353
1906
|
if (typeof schema.$ref === "string") {
|
|
1354
1907
|
const ref = schema.$ref;
|
|
@@ -1425,16 +1978,13 @@ async function emitPrecompiledValidators(opts) {
|
|
|
1425
1978
|
`;
|
|
1426
1979
|
cjs += ` body: ${v.body ? `exports[${JSON.stringify(v.body)}]` : "undefined"},
|
|
1427
1980
|
`;
|
|
1428
|
-
cjs +=
|
|
1429
|
-
`;
|
|
1981
|
+
cjs += " response: {\n";
|
|
1430
1982
|
for (const [key, id] of Object.entries(v.response)) {
|
|
1431
1983
|
cjs += ` ${JSON.stringify(key)}: exports[${JSON.stringify(id)}],
|
|
1432
1984
|
`;
|
|
1433
1985
|
}
|
|
1434
|
-
cjs +=
|
|
1435
|
-
|
|
1436
|
-
cjs += ` },
|
|
1437
|
-
`;
|
|
1986
|
+
cjs += " }\n";
|
|
1987
|
+
cjs += " },\n";
|
|
1438
1988
|
}
|
|
1439
1989
|
cjs += "};\n";
|
|
1440
1990
|
import_node_fs2.default.writeFileSync(cjsPath, cjs, "utf8");
|
|
@@ -1469,7 +2019,6 @@ export function validateResponse(operationId, status, contentType, data) {
|
|
|
1469
2019
|
// src/compiler/cache/isStale.ts
|
|
1470
2020
|
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
1471
2021
|
var import_node_path3 = __toESM(require("path"), 1);
|
|
1472
|
-
var import_typescript8 = require("typescript");
|
|
1473
2022
|
var import_meta = {};
|
|
1474
2023
|
function readJson(p) {
|
|
1475
2024
|
try {
|
|
@@ -1527,7 +2076,7 @@ function findLockfile(startDir) {
|
|
|
1527
2076
|
for (const n of names) {
|
|
1528
2077
|
const p = import_node_path3.default.join(dir, n);
|
|
1529
2078
|
const mt = statMtimeMs(p);
|
|
1530
|
-
if (mt
|
|
2079
|
+
if (mt !== null) return { path: p, mtimeMs: mt };
|
|
1531
2080
|
}
|
|
1532
2081
|
const parent = import_node_path3.default.dirname(dir);
|
|
1533
2082
|
if (parent === dir) break;
|
|
@@ -1552,22 +2101,22 @@ async function isStale(params) {
|
|
|
1552
2101
|
const chain = collectTsconfigChain(tsconfigAbs);
|
|
1553
2102
|
for (const cfg of chain) {
|
|
1554
2103
|
const mt = statMtimeMs(cfg);
|
|
1555
|
-
if (mt
|
|
2104
|
+
if (mt === null) return { stale: true, reason: "config-missing", detail: cfg };
|
|
1556
2105
|
const cachedMt = cache.project.configFiles[cfg];
|
|
1557
|
-
if (cachedMt
|
|
2106
|
+
if (cachedMt === null || Math.abs(cachedMt - mt) > 1e-4) {
|
|
1558
2107
|
return { stale: true, reason: "config-updated", detail: cfg };
|
|
1559
2108
|
}
|
|
1560
2109
|
}
|
|
1561
2110
|
if (cache.project.lockfile?.path) {
|
|
1562
2111
|
const mt = statMtimeMs(cache.project.lockfile.path);
|
|
1563
|
-
if (mt
|
|
2112
|
+
if (mt === null) return { stale: true, reason: "lockfile-missing", detail: cache.project.lockfile.path };
|
|
1564
2113
|
if (Math.abs(cache.project.lockfile.mtimeMs - mt) > 1e-4) {
|
|
1565
2114
|
return { stale: true, reason: "lockfile-updated", detail: cache.project.lockfile.path };
|
|
1566
2115
|
}
|
|
1567
2116
|
}
|
|
1568
2117
|
for (const [file, cachedMt] of Object.entries(cache.inputs)) {
|
|
1569
2118
|
const mt = statMtimeMs(file);
|
|
1570
|
-
if (mt
|
|
2119
|
+
if (mt === null) return { stale: true, reason: "input-missing", detail: file };
|
|
1571
2120
|
if (Math.abs(cachedMt - mt) > 1e-4) return { stale: true, reason: "input-updated", detail: file };
|
|
1572
2121
|
}
|
|
1573
2122
|
return { stale: false, reason: "up-to-date" };
|
|
@@ -1576,7 +2125,7 @@ async function isStale(params) {
|
|
|
1576
2125
|
// src/compiler/cache/writeCache.ts
|
|
1577
2126
|
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
1578
2127
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
1579
|
-
var
|
|
2128
|
+
var import_typescript11 = __toESM(require("typescript"), 1);
|
|
1580
2129
|
function statMtimeMs2(p) {
|
|
1581
2130
|
return import_node_fs4.default.statSync(p).mtimeMs;
|
|
1582
2131
|
}
|
|
@@ -1609,7 +2158,7 @@ function writeCache(params) {
|
|
|
1609
2158
|
generator: {
|
|
1610
2159
|
name: "adorn-api",
|
|
1611
2160
|
version: params.adornVersion,
|
|
1612
|
-
typescript:
|
|
2161
|
+
typescript: import_typescript11.default.version
|
|
1613
2162
|
},
|
|
1614
2163
|
project: {
|
|
1615
2164
|
tsconfigPath: params.tsconfigAbs,
|
|
@@ -1622,7 +2171,7 @@ function writeCache(params) {
|
|
|
1622
2171
|
}
|
|
1623
2172
|
|
|
1624
2173
|
// src/cli.ts
|
|
1625
|
-
var
|
|
2174
|
+
var import_typescript12 = __toESM(require("typescript"), 1);
|
|
1626
2175
|
var import_node_process = __toESM(require("process"), 1);
|
|
1627
2176
|
var ADORN_VERSION = "0.1.0";
|
|
1628
2177
|
function log(msg) {
|
|
@@ -1633,6 +2182,26 @@ function debug(...args) {
|
|
|
1633
2182
|
console.error("[adorn-api]", ...args);
|
|
1634
2183
|
}
|
|
1635
2184
|
}
|
|
2185
|
+
function sanitizeForJson(obj) {
|
|
2186
|
+
if (obj === null || obj === void 0) return obj;
|
|
2187
|
+
if (typeof obj !== "object") return obj;
|
|
2188
|
+
if (Array.isArray(obj)) {
|
|
2189
|
+
return obj.map((item) => sanitizeForJson(item));
|
|
2190
|
+
}
|
|
2191
|
+
const result = {};
|
|
2192
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
2193
|
+
if (key.startsWith("__@") || key.startsWith("[")) continue;
|
|
2194
|
+
if (typeof value === "function") continue;
|
|
2195
|
+
if (value !== null && typeof value === "object") {
|
|
2196
|
+
const typeName = value.constructor?.name;
|
|
2197
|
+
if (typeName && !["Object", "Array", "String", "Number", "Boolean", "Date", "RegExp"].includes(typeName)) {
|
|
2198
|
+
continue;
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
result[key] = sanitizeForJson(value);
|
|
2202
|
+
}
|
|
2203
|
+
return result;
|
|
2204
|
+
}
|
|
1636
2205
|
async function buildCommand(args) {
|
|
1637
2206
|
const projectIndex = args.indexOf("-p");
|
|
1638
2207
|
const projectPath = projectIndex !== -1 ? args[projectIndex + 1] : "./tsconfig.json";
|
|
@@ -1650,7 +2219,7 @@ async function buildCommand(args) {
|
|
|
1650
2219
|
outDir: outputDir,
|
|
1651
2220
|
project: projectPath,
|
|
1652
2221
|
adornVersion: ADORN_VERSION,
|
|
1653
|
-
typescriptVersion:
|
|
2222
|
+
typescriptVersion: import_typescript12.default.version
|
|
1654
2223
|
});
|
|
1655
2224
|
if (!stale.stale) {
|
|
1656
2225
|
log("adorn-api: artifacts up-to-date");
|
|
@@ -1671,7 +2240,7 @@ async function buildCommand(args) {
|
|
|
1671
2240
|
const openapi = generateOpenAPI(controllers, checker, { title: "API", version: "1.0.0" });
|
|
1672
2241
|
const manifest = generateManifest(controllers, checker, ADORN_VERSION, validationMode);
|
|
1673
2242
|
(0, import_node_fs5.mkdirSync)(outputPath, { recursive: true });
|
|
1674
|
-
(0, import_node_fs5.writeFileSync)((0, import_node_path5.resolve)(outputPath, "openapi.json"), JSON.stringify(openapi, null, 2));
|
|
2243
|
+
(0, import_node_fs5.writeFileSync)((0, import_node_path5.resolve)(outputPath, "openapi.json"), JSON.stringify(sanitizeForJson(openapi), null, 2));
|
|
1675
2244
|
(0, import_node_fs5.writeFileSync)((0, import_node_path5.resolve)(outputPath, "manifest.json"), JSON.stringify(manifest, null, 2));
|
|
1676
2245
|
if (validationMode === "precompiled") {
|
|
1677
2246
|
log("Generating precompiled validators...");
|