adorn-api 1.0.12 → 1.0.14
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/adapter/express/auth.d.ts +8 -0
- package/dist/adapter/express/auth.d.ts.map +1 -1
- package/dist/adapter/express/bootstrap.d.ts +12 -0
- package/dist/adapter/express/bootstrap.d.ts.map +1 -1
- package/dist/adapter/express/coercion.d.ts +81 -1
- package/dist/adapter/express/coercion.d.ts.map +1 -1
- package/dist/adapter/express/index.d.ts +1 -0
- package/dist/adapter/express/index.d.ts.map +1 -1
- package/dist/adapter/express/merge.d.ts +17 -0
- package/dist/adapter/express/merge.d.ts.map +1 -1
- package/dist/adapter/express/openapi.d.ts +55 -0
- package/dist/adapter/express/openapi.d.ts.map +1 -1
- package/dist/adapter/express/router.d.ts +6 -0
- package/dist/adapter/express/router.d.ts.map +1 -1
- package/dist/adapter/express/swagger.d.ts +6 -0
- package/dist/adapter/express/swagger.d.ts.map +1 -1
- package/dist/adapter/express/types.d.ts +26 -0
- package/dist/adapter/express/types.d.ts.map +1 -1
- package/dist/adapter/express/validation.d.ts +19 -2
- package/dist/adapter/express/validation.d.ts.map +1 -1
- package/dist/cli.cjs +1016 -445
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1016 -445
- package/dist/cli.js.map +1 -1
- package/dist/compiler/analyze/index.d.ts +5 -0
- package/dist/compiler/analyze/index.d.ts.map +1 -0
- package/dist/compiler/analyze/scanControllers.d.ts +52 -0
- package/dist/compiler/analyze/scanControllers.d.ts.map +1 -1
- package/dist/compiler/cache/isStale.d.ts +26 -0
- package/dist/compiler/cache/isStale.d.ts.map +1 -1
- package/dist/compiler/cache/loadArtifacts.d.ts +36 -0
- package/dist/compiler/cache/loadArtifacts.d.ts.map +1 -1
- package/dist/compiler/cache/schema.d.ts +14 -0
- package/dist/compiler/cache/schema.d.ts.map +1 -1
- package/dist/compiler/cache/writeCache.d.ts +6 -0
- package/dist/compiler/cache/writeCache.d.ts.map +1 -1
- package/dist/compiler/gems.d.ts +75 -0
- package/dist/compiler/gems.d.ts.map +1 -0
- package/dist/compiler/generator/index.d.ts +7 -0
- package/dist/compiler/generator/index.d.ts.map +1 -0
- package/dist/compiler/generator/manifest.d.ts +23 -0
- package/dist/compiler/generator/manifest.d.ts.map +1 -0
- package/dist/compiler/generator/openapi.d.ts +118 -0
- package/dist/compiler/generator/openapi.d.ts.map +1 -0
- package/dist/compiler/graph/builder.d.ts +24 -0
- package/dist/compiler/graph/builder.d.ts.map +1 -0
- package/dist/compiler/graph/index.d.ts +7 -0
- package/dist/compiler/graph/index.d.ts.map +1 -0
- package/dist/compiler/graph/schemaGraph.d.ts +67 -0
- package/dist/compiler/graph/schemaGraph.d.ts.map +1 -0
- package/dist/compiler/graph/types.d.ts +203 -0
- package/dist/compiler/graph/types.d.ts.map +1 -0
- package/dist/compiler/index.d.ts +12 -0
- package/dist/compiler/index.d.ts.map +1 -0
- package/dist/compiler/ir/index.d.ts +7 -0
- package/dist/compiler/ir/index.d.ts.map +1 -0
- package/dist/compiler/ir/pipeline.d.ts +82 -0
- package/dist/compiler/ir/pipeline.d.ts.map +1 -0
- package/dist/compiler/ir/stages.d.ts +40 -0
- package/dist/compiler/ir/stages.d.ts.map +1 -0
- package/dist/compiler/ir/visitor.d.ts +98 -0
- package/dist/compiler/ir/visitor.d.ts.map +1 -0
- package/dist/compiler/manifest/emit.d.ts +14 -0
- package/dist/compiler/manifest/emit.d.ts.map +1 -1
- package/dist/compiler/manifest/format.d.ts +42 -0
- package/dist/compiler/manifest/format.d.ts.map +1 -1
- package/dist/compiler/manifest/index.d.ts +6 -0
- package/dist/compiler/manifest/index.d.ts.map +1 -0
- package/dist/compiler/runner/createProgram.d.ts +16 -0
- package/dist/compiler/runner/createProgram.d.ts.map +1 -1
- package/dist/compiler/runner/index.d.ts +5 -0
- package/dist/compiler/runner/index.d.ts.map +1 -0
- package/dist/compiler/schema/extractAnnotations.d.ts +47 -0
- package/dist/compiler/schema/extractAnnotations.d.ts.map +1 -1
- package/dist/compiler/schema/index.d.ts +6 -0
- package/dist/compiler/schema/index.d.ts.map +1 -0
- package/dist/compiler/schema/intersectionHandler.d.ts +44 -0
- package/dist/compiler/schema/intersectionHandler.d.ts.map +1 -0
- package/dist/compiler/schema/objectHandler.d.ts +106 -0
- package/dist/compiler/schema/objectHandler.d.ts.map +1 -0
- package/dist/compiler/schema/openapi.d.ts +16 -1
- package/dist/compiler/schema/openapi.d.ts.map +1 -1
- package/dist/compiler/schema/parameters.d.ts +90 -0
- package/dist/compiler/schema/parameters.d.ts.map +1 -0
- package/dist/compiler/schema/primitives.d.ts +68 -0
- package/dist/compiler/schema/primitives.d.ts.map +1 -0
- package/dist/compiler/schema/typeToJsonSchema.d.ts +22 -51
- package/dist/compiler/schema/typeToJsonSchema.d.ts.map +1 -1
- package/dist/compiler/schema/types.d.ts +69 -0
- package/dist/compiler/schema/types.d.ts.map +1 -0
- package/dist/compiler/schema/unionHandler.d.ts +70 -0
- package/dist/compiler/schema/unionHandler.d.ts.map +1 -0
- package/dist/compiler/transform/dedup.d.ts +35 -0
- package/dist/compiler/transform/dedup.d.ts.map +1 -0
- package/dist/compiler/transform/flatten.d.ts +50 -0
- package/dist/compiler/transform/flatten.d.ts.map +1 -0
- package/dist/compiler/transform/index.d.ts +7 -0
- package/dist/compiler/transform/index.d.ts.map +1 -0
- package/dist/compiler/transform/inline.d.ts +46 -0
- package/dist/compiler/transform/inline.d.ts.map +1 -0
- package/dist/compiler/validation/emitPrecompiledValidators.d.ts +16 -0
- package/dist/compiler/validation/emitPrecompiledValidators.d.ts.map +1 -1
- package/dist/compiler/validation/index.d.ts +5 -0
- package/dist/compiler/validation/index.d.ts.map +1 -0
- package/dist/decorators/Auth.d.ts +17 -0
- package/dist/decorators/Auth.d.ts.map +1 -1
- package/dist/decorators/Controller.d.ts +15 -0
- package/dist/decorators/Controller.d.ts.map +1 -1
- package/dist/decorators/Public.d.ts +13 -0
- package/dist/decorators/Public.d.ts.map +1 -1
- package/dist/decorators/Use.d.ts +18 -0
- package/dist/decorators/Use.d.ts.map +1 -1
- package/dist/decorators/methods.d.ts +20 -0
- package/dist/decorators/methods.d.ts.map +1 -1
- package/dist/express.cjs +73 -54
- package/dist/express.cjs.map +1 -1
- package/dist/express.js +73 -54
- package/dist/express.js.map +1 -1
- package/dist/http.d.ts +1 -2
- package/dist/http.d.ts.map +1 -1
- package/dist/index.cjs +161 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +156 -3
- package/dist/index.js.map +1 -1
- package/dist/metal/applyListQuery.d.ts +73 -0
- package/dist/metal/applyListQuery.d.ts.map +1 -1
- package/dist/metal/index.cjs +2 -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 +2 -2
- package/dist/metal/index.js.map +1 -1
- package/dist/metal/listQuery.d.ts +19 -0
- package/dist/metal/listQuery.d.ts.map +1 -1
- package/dist/metal/queryOptions.d.ts +8 -0
- package/dist/metal/queryOptions.d.ts.map +1 -1
- package/dist/metal/readMetalBag.d.ts +36 -0
- package/dist/metal/readMetalBag.d.ts.map +1 -1
- package/dist/metal/registerMetalEntities.d.ts +20 -0
- package/dist/metal/registerMetalEntities.d.ts.map +1 -1
- package/dist/metal/schemaFromEntity.d.ts +30 -0
- package/dist/metal/schemaFromEntity.d.ts.map +1 -1
- package/dist/metal/searchWhere.d.ts +39 -0
- package/dist/metal/searchWhere.d.ts.map +1 -1
- package/dist/metal/symbolMetadata.d.ts +6 -0
- package/dist/metal/symbolMetadata.d.ts.map +1 -1
- package/dist/runtime/auth/runtime.d.ts +155 -6
- package/dist/runtime/auth/runtime.d.ts.map +1 -1
- package/dist/runtime/metadata/bucket.d.ts +1 -2
- package/dist/runtime/metadata/bucket.d.ts.map +1 -1
- package/dist/runtime/metadata/key.d.ts +1 -1
- package/dist/runtime/metadata/key.d.ts.map +1 -1
- package/dist/runtime/metadata/read.d.ts +1 -2
- package/dist/runtime/metadata/read.d.ts.map +1 -1
- package/dist/runtime/metadata/types.d.ts +74 -0
- package/dist/runtime/metadata/types.d.ts.map +1 -1
- package/dist/runtime/polyfill.d.ts +1 -1
- package/dist/runtime/polyfill.d.ts.map +1 -1
- package/dist/runtime/upload.d.ts +37 -0
- package/dist/runtime/upload.d.ts.map +1 -1
- package/dist/runtime/validation/ajv.d.ts +100 -0
- package/dist/runtime/validation/ajv.d.ts.map +1 -1
- package/dist/runtime/validation/index.d.ts +9 -0
- package/dist/runtime/validation/index.d.ts.map +1 -1
- package/dist/scripts/adorn-example.cjs +238 -6
- package/dist/scripts/adorn-example.cjs.map +1 -1
- package/dist/utils/port.d.ts +9 -0
- package/dist/utils/port.d.ts.map +1 -0
- package/package.json +4 -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;
|
|
@@ -301,7 +301,7 @@ function extractDecoratorStringArg(decorator) {
|
|
|
301
301
|
}
|
|
302
302
|
return null;
|
|
303
303
|
}
|
|
304
|
-
function unwrapPromise(type,
|
|
304
|
+
function unwrapPromise(type, _checker) {
|
|
305
305
|
const symbol = type.getSymbol();
|
|
306
306
|
if (symbol?.getName() === "Promise") {
|
|
307
307
|
const typeArgs = type.typeArguments;
|
|
@@ -322,12 +322,15 @@ function unwrapPromiseTypeNode(typeNode) {
|
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
// src/compiler/schema/openapi.ts
|
|
325
|
-
var
|
|
325
|
+
var import_typescript9 = __toESM(require("typescript"), 1);
|
|
326
326
|
|
|
327
327
|
// src/compiler/schema/typeToJsonSchema.ts
|
|
328
|
+
var import_typescript7 = __toESM(require("typescript"), 1);
|
|
329
|
+
|
|
330
|
+
// src/compiler/schema/primitives.ts
|
|
328
331
|
var import_typescript3 = __toESM(require("typescript"), 1);
|
|
329
|
-
function
|
|
330
|
-
const { checker } = ctx;
|
|
332
|
+
function handlePrimitiveType(type, ctx, typeNode) {
|
|
333
|
+
const { checker, propertyName } = ctx;
|
|
331
334
|
if (type.flags & import_typescript3.default.TypeFlags.Undefined) {
|
|
332
335
|
return {};
|
|
333
336
|
}
|
|
@@ -341,7 +344,7 @@ function typeToJsonSchema(type, ctx, typeNode) {
|
|
|
341
344
|
return { type: "string" };
|
|
342
345
|
}
|
|
343
346
|
if (type.flags & import_typescript3.default.TypeFlags.Number) {
|
|
344
|
-
return normalizeNumericType(type, checker, typeNode);
|
|
347
|
+
return normalizeNumericType(type, checker, typeNode, propertyName);
|
|
345
348
|
}
|
|
346
349
|
if (type.flags & import_typescript3.default.TypeFlags.Boolean) {
|
|
347
350
|
return { type: "boolean" };
|
|
@@ -365,26 +368,7 @@ function typeToJsonSchema(type, ctx, typeNode) {
|
|
|
365
368
|
const intrinsic = type.intrinsicName;
|
|
366
369
|
return { type: "boolean", enum: [intrinsic === "true"] };
|
|
367
370
|
}
|
|
368
|
-
|
|
369
|
-
return handleUnion(type, ctx, typeNode);
|
|
370
|
-
}
|
|
371
|
-
if (type.isIntersection()) {
|
|
372
|
-
return handleIntersection(type, ctx, typeNode);
|
|
373
|
-
}
|
|
374
|
-
if (checker.isArrayType(type)) {
|
|
375
|
-
const typeArgs = type.typeArguments;
|
|
376
|
-
const itemType = typeArgs?.[0];
|
|
377
|
-
const items = itemType ? typeToJsonSchema(itemType, ctx) : {};
|
|
378
|
-
return {
|
|
379
|
-
type: "array",
|
|
380
|
-
items,
|
|
381
|
-
uniqueItems: isSetType(type, checker) ? true : void 0
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
if (type.flags & import_typescript3.default.TypeFlags.Object) {
|
|
385
|
-
return handleObjectType(type, ctx, typeNode);
|
|
386
|
-
}
|
|
387
|
-
return {};
|
|
371
|
+
return null;
|
|
388
372
|
}
|
|
389
373
|
function isDateType(type, checker) {
|
|
390
374
|
const symbol = type.getSymbol();
|
|
@@ -399,53 +383,51 @@ function isDateType(type, checker) {
|
|
|
399
383
|
}
|
|
400
384
|
return symbol?.getName() === "Date";
|
|
401
385
|
}
|
|
402
|
-
function
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
const
|
|
406
|
-
if (
|
|
407
|
-
|
|
408
|
-
}
|
|
409
|
-
function getSchemaName(type, typeNode) {
|
|
410
|
-
const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
|
|
411
|
-
const aliasName = aliasSymbol?.getName();
|
|
412
|
-
if (aliasName && aliasName !== "__type") {
|
|
413
|
-
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" };
|
|
414
392
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
+
}
|
|
419
406
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
407
|
+
if (import_typescript3.default.isTypeAliasDeclaration(typeNode.parent)) {
|
|
408
|
+
if (import_typescript3.default.isIdentifier(typeNode.parent.name)) {
|
|
409
|
+
return typeNode.parent.name.text;
|
|
410
|
+
}
|
|
423
411
|
}
|
|
424
412
|
return null;
|
|
425
413
|
}
|
|
426
|
-
function
|
|
427
|
-
const
|
|
428
|
-
if (
|
|
429
|
-
return
|
|
430
|
-
}
|
|
431
|
-
const { components, typeStack } = ctx;
|
|
432
|
-
if (components.has(name) || typeStack.has(type)) {
|
|
433
|
-
return { $ref: `#/components/schemas/${name}` };
|
|
434
|
-
}
|
|
435
|
-
typeStack.add(type);
|
|
436
|
-
const schema = build();
|
|
437
|
-
typeStack.delete(type);
|
|
438
|
-
if (!components.has(name)) {
|
|
439
|
-
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);
|
|
440
418
|
}
|
|
441
|
-
return
|
|
419
|
+
return type.getSymbol() ?? null;
|
|
442
420
|
}
|
|
421
|
+
|
|
422
|
+
// src/compiler/schema/unionHandler.ts
|
|
423
|
+
var import_typescript4 = __toESM(require("typescript"), 1);
|
|
443
424
|
function handleUnion(type, ctx, typeNode) {
|
|
444
425
|
return buildNamedSchema(type, ctx, typeNode, () => {
|
|
445
426
|
const types = type.types;
|
|
446
|
-
const nullType = types.find((t) => t.flags &
|
|
447
|
-
const
|
|
448
|
-
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);
|
|
449
431
|
if (allStringLiterals && otherTypes.length > 0) {
|
|
450
432
|
const enumValues = otherTypes.map((t) => t.value);
|
|
451
433
|
const schema = { type: "string", enum: enumValues };
|
|
@@ -454,6 +436,14 @@ function handleUnion(type, ctx, typeNode) {
|
|
|
454
436
|
}
|
|
455
437
|
return schema;
|
|
456
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
|
+
}
|
|
457
447
|
if (otherTypes.length === 1 && nullType) {
|
|
458
448
|
const innerSchema = typeToJsonSchema(otherTypes[0], ctx);
|
|
459
449
|
if (typeof innerSchema.type === "string") {
|
|
@@ -483,49 +473,7 @@ function handleUnion(type, ctx, typeNode) {
|
|
|
483
473
|
return {};
|
|
484
474
|
});
|
|
485
475
|
}
|
|
486
|
-
function
|
|
487
|
-
return buildNamedSchema(type, ctx, typeNode, () => {
|
|
488
|
-
const types = type.types;
|
|
489
|
-
const brandCollapsed = tryCollapseBrandedIntersection(types, ctx, typeNode);
|
|
490
|
-
if (brandCollapsed) {
|
|
491
|
-
return brandCollapsed;
|
|
492
|
-
}
|
|
493
|
-
const allOf = [];
|
|
494
|
-
for (const t of types) {
|
|
495
|
-
allOf.push(typeToJsonSchema(t, ctx));
|
|
496
|
-
}
|
|
497
|
-
return { allOf };
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
function tryCollapseBrandedIntersection(types, ctx, typeNode) {
|
|
501
|
-
const { checker } = ctx;
|
|
502
|
-
const parts = [...types];
|
|
503
|
-
const prim = parts.find(isPrimitiveLike);
|
|
504
|
-
if (!prim) return null;
|
|
505
|
-
const rest = parts.filter((p) => p !== prim);
|
|
506
|
-
if (rest.every((r) => isBrandObject(checker, r, ctx))) {
|
|
507
|
-
return typeToJsonSchema(prim, ctx);
|
|
508
|
-
}
|
|
509
|
-
return null;
|
|
510
|
-
}
|
|
511
|
-
function isPrimitiveLike(t) {
|
|
512
|
-
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;
|
|
513
|
-
}
|
|
514
|
-
function isBrandObject(checker, t, ctx) {
|
|
515
|
-
if (!(t.flags & import_typescript3.default.TypeFlags.Object)) return false;
|
|
516
|
-
const props = t.getProperties();
|
|
517
|
-
if (props.length === 0) return false;
|
|
518
|
-
const allowed = /* @__PURE__ */ new Set(["__brand", "__type", "__tag", "brand"]);
|
|
519
|
-
for (const p of props) {
|
|
520
|
-
if (!allowed.has(p.getName())) return false;
|
|
521
|
-
}
|
|
522
|
-
const callSigs = t.getCallSignatures?.();
|
|
523
|
-
if (callSigs && callSigs.length > 0) return false;
|
|
524
|
-
const constructSigs = t.getConstructSignatures?.();
|
|
525
|
-
if (constructSigs && constructSigs.length > 0) return false;
|
|
526
|
-
return true;
|
|
527
|
-
}
|
|
528
|
-
function detectDiscriminatedUnion(types, ctx, branches) {
|
|
476
|
+
function detectDiscriminatedUnion(types, ctx, _branches) {
|
|
529
477
|
if (types.length < 2) return null;
|
|
530
478
|
const candidates = findCommonPropertyNames(ctx.checker, types);
|
|
531
479
|
for (const propName of candidates) {
|
|
@@ -556,10 +504,10 @@ function findCommonPropertyNames(checker, types) {
|
|
|
556
504
|
function isRequiredProperty(checker, type, propName) {
|
|
557
505
|
const sym = checker.getPropertyOfType(type, propName);
|
|
558
506
|
if (!sym) return false;
|
|
559
|
-
if (sym.flags &
|
|
507
|
+
if (sym.flags & import_typescript4.default.SymbolFlags.Optional) return false;
|
|
560
508
|
const propType = checker.getTypeOfSymbol(sym);
|
|
561
509
|
if (propType.isUnion?.()) {
|
|
562
|
-
const hasUndefined = propType.types.some((t) => (t.flags &
|
|
510
|
+
const hasUndefined = propType.types.some((t) => (t.flags & import_typescript4.default.TypeFlags.Undefined) !== 0);
|
|
563
511
|
if (hasUndefined) return false;
|
|
564
512
|
}
|
|
565
513
|
return true;
|
|
@@ -602,16 +550,186 @@ function getBranchSchemaName(type, ctx) {
|
|
|
602
550
|
}
|
|
603
551
|
return `Anonymous_${ctx.typeNameStack.length}`;
|
|
604
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);
|
|
605
720
|
function handleObjectType(type, ctx, typeNode) {
|
|
606
|
-
const { checker, components, typeStack
|
|
721
|
+
const { checker, components, typeStack } = ctx;
|
|
607
722
|
const symbol = type.getSymbol();
|
|
608
723
|
const typeName = symbol?.getName?.() ?? getTypeNameFromNode(typeNode, ctx);
|
|
609
724
|
if (isMetalOrmWrapperType(type, checker)) {
|
|
610
725
|
return handleMetalOrmWrapper(type, ctx);
|
|
611
726
|
}
|
|
612
727
|
if (typeName && typeName !== "__type") {
|
|
613
|
-
|
|
614
|
-
|
|
728
|
+
const isMetalOrmGeneric = METAL_ORM_WRAPPER_NAMES.some(
|
|
729
|
+
(name) => typeName === name || typeName.endsWith("Api")
|
|
730
|
+
);
|
|
731
|
+
if (isMetalOrmGeneric) {
|
|
732
|
+
return {};
|
|
615
733
|
}
|
|
616
734
|
if (typeStack.has(type)) {
|
|
617
735
|
return { $ref: `#/components/schemas/${typeName}` };
|
|
@@ -624,46 +742,18 @@ function handleObjectType(type, ctx, typeNode) {
|
|
|
624
742
|
const existing = components.get(typeName);
|
|
625
743
|
if (!existing) {
|
|
626
744
|
components.set(typeName, schema);
|
|
745
|
+
} else {
|
|
746
|
+
const merged = mergeSchemasIfNeeded(existing, schema);
|
|
747
|
+
if (merged !== existing) {
|
|
748
|
+
components.set(typeName, merged);
|
|
749
|
+
}
|
|
627
750
|
}
|
|
628
751
|
return { $ref: `#/components/schemas/${typeName}` };
|
|
629
752
|
}
|
|
630
753
|
typeStack.delete(type);
|
|
631
754
|
return schema;
|
|
632
755
|
}
|
|
633
|
-
function
|
|
634
|
-
if (!typeNode) return null;
|
|
635
|
-
if (import_typescript3.default.isTypeReferenceNode(typeNode)) {
|
|
636
|
-
if (import_typescript3.default.isIdentifier(typeNode.typeName)) {
|
|
637
|
-
return typeNode.typeName.text;
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
if (import_typescript3.default.isTypeAliasDeclaration(typeNode.parent)) {
|
|
641
|
-
if (import_typescript3.default.isIdentifier(typeNode.parent.name)) {
|
|
642
|
-
return typeNode.parent.name.text;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
return null;
|
|
646
|
-
}
|
|
647
|
-
function shouldBeIntegerType(typeName) {
|
|
648
|
-
if (!typeName) return false;
|
|
649
|
-
const lower = typeName.toLowerCase();
|
|
650
|
-
return lower === "id" || lower.endsWith("id") || lower === "primarykey" || lower === "pk";
|
|
651
|
-
}
|
|
652
|
-
function normalizeNumericType(type, checker, typeNode) {
|
|
653
|
-
const typeName = getExplicitTypeNameFromNode(typeNode) ?? null;
|
|
654
|
-
const symbol = getEffectiveSymbol(type, checker);
|
|
655
|
-
const symbolName = symbol?.getName() ?? null;
|
|
656
|
-
if (shouldBeIntegerType(typeName) || shouldBeIntegerType(symbolName)) {
|
|
657
|
-
return { type: "integer" };
|
|
658
|
-
}
|
|
659
|
-
return { type: "number" };
|
|
660
|
-
}
|
|
661
|
-
function getTypeNameFromNode(typeNode, ctx) {
|
|
662
|
-
const explicitName = getExplicitTypeNameFromNode(typeNode);
|
|
663
|
-
if (explicitName) return explicitName;
|
|
664
|
-
return `Anonymous_${ctx.typeNameStack.length}`;
|
|
665
|
-
}
|
|
666
|
-
function buildObjectSchema(type, ctx, typeNode) {
|
|
756
|
+
function buildObjectSchema(type, ctx, _typeNode) {
|
|
667
757
|
const { checker, mode } = ctx;
|
|
668
758
|
const properties = {};
|
|
669
759
|
const required = [];
|
|
@@ -677,9 +767,10 @@ function buildObjectSchema(type, ctx, typeNode) {
|
|
|
677
767
|
if (isMethodLike(propType)) {
|
|
678
768
|
continue;
|
|
679
769
|
}
|
|
680
|
-
const isOptional = !!(prop.flags &
|
|
770
|
+
const isOptional = !!(prop.flags & import_typescript6.default.SymbolFlags.Optional);
|
|
681
771
|
const isRelation = isMetalOrmWrapperType(propType, checker);
|
|
682
|
-
|
|
772
|
+
const propCtx = { ...ctx, propertyName: propName };
|
|
773
|
+
properties[propName] = typeToJsonSchema(propType, propCtx);
|
|
683
774
|
const shouldRequire = mode === "response" ? !isRelation && !isOptional : !isOptional;
|
|
684
775
|
if (shouldRequire) {
|
|
685
776
|
required.push(propName);
|
|
@@ -700,14 +791,14 @@ function buildObjectSchema(type, ctx, typeNode) {
|
|
|
700
791
|
}
|
|
701
792
|
return schema;
|
|
702
793
|
}
|
|
703
|
-
function isRecordType(type,
|
|
794
|
+
function isRecordType(type, _checker) {
|
|
704
795
|
const symbol = type.getSymbol();
|
|
705
796
|
if (!symbol) return false;
|
|
706
797
|
const name = symbol.getName();
|
|
707
798
|
if (name === "Record") return true;
|
|
708
799
|
return false;
|
|
709
800
|
}
|
|
710
|
-
function getRecordValueType(type,
|
|
801
|
+
function getRecordValueType(type, _checker) {
|
|
711
802
|
const symbol = type.getSymbol();
|
|
712
803
|
if (!symbol) return null;
|
|
713
804
|
const name = symbol.getName();
|
|
@@ -720,24 +811,8 @@ function getRecordValueType(type, checker) {
|
|
|
720
811
|
}
|
|
721
812
|
return null;
|
|
722
813
|
}
|
|
723
|
-
var METAL_ORM_WRAPPER_NAMES = ["HasManyCollection", "ManyToManyCollection", "BelongsToReference", "HasOneReference"];
|
|
724
814
|
function isMetalOrmWrapperType(type, checker) {
|
|
725
|
-
|
|
726
|
-
if (!aliasSymbol) return false;
|
|
727
|
-
return METAL_ORM_WRAPPER_NAMES.includes(aliasSymbol.getName());
|
|
728
|
-
}
|
|
729
|
-
function getWrapperTypeName(type, checker) {
|
|
730
|
-
const symbol = getEffectiveSymbol(type, checker);
|
|
731
|
-
if (!symbol) return null;
|
|
732
|
-
const name = symbol.getName();
|
|
733
|
-
return METAL_ORM_WRAPPER_NAMES.includes(name) ? name : null;
|
|
734
|
-
}
|
|
735
|
-
function getEffectiveSymbol(type, checker) {
|
|
736
|
-
const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
|
|
737
|
-
if (aliasSymbol && aliasSymbol.flags & import_typescript3.default.SymbolFlags.Alias) {
|
|
738
|
-
return checker.getAliasedSymbol(aliasSymbol);
|
|
739
|
-
}
|
|
740
|
-
return type.getSymbol() ?? null;
|
|
815
|
+
return !!findMetalOrmWrapper(type, checker);
|
|
741
816
|
}
|
|
742
817
|
function isMethodLike(type) {
|
|
743
818
|
const callSigs = type.getCallSignatures?.();
|
|
@@ -746,6 +821,159 @@ function isMethodLike(type) {
|
|
|
746
821
|
function isIteratorOrSymbolProperty(propName) {
|
|
747
822
|
return propName.startsWith("__@") || propName.startsWith("[") || propName === Symbol.iterator.toString();
|
|
748
823
|
}
|
|
824
|
+
function getTypeNameFromNode(typeNode, _ctx) {
|
|
825
|
+
const explicitName = getExplicitTypeNameFromNode4(typeNode);
|
|
826
|
+
if (explicitName) return explicitName;
|
|
827
|
+
return "Anonymous_${ctx.typeNameStack.length}";
|
|
828
|
+
}
|
|
829
|
+
function getExplicitTypeNameFromNode4(typeNode) {
|
|
830
|
+
if (!typeNode) return null;
|
|
831
|
+
if (import_typescript6.default.isTypeReferenceNode(typeNode)) {
|
|
832
|
+
if (import_typescript6.default.isIdentifier(typeNode.typeName)) {
|
|
833
|
+
return typeNode.typeName.text;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
if (import_typescript6.default.isTypeAliasDeclaration(typeNode.parent)) {
|
|
837
|
+
if (import_typescript6.default.isIdentifier(typeNode.parent.name)) {
|
|
838
|
+
return typeNode.parent.name.text;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
function mergeSchemasIfNeeded(existing, newSchema) {
|
|
844
|
+
if (existing.type === "array" && newSchema.type === "array") {
|
|
845
|
+
return mergeArraySchemas(existing, newSchema);
|
|
846
|
+
}
|
|
847
|
+
const result = { ...existing };
|
|
848
|
+
for (const [key, newValue] of Object.entries(newSchema)) {
|
|
849
|
+
if (key === "properties" && newValue) {
|
|
850
|
+
result.properties = mergePropertiesIfNeeded(existing.properties || {}, newValue);
|
|
851
|
+
} else if (key === "required" && newValue) {
|
|
852
|
+
result.required = mergeRequiredFields(existing.required || [], newValue);
|
|
853
|
+
} else if (!deepEqual(existing[key], newValue)) {
|
|
854
|
+
result[key] = newValue;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return result;
|
|
858
|
+
}
|
|
859
|
+
function mergePropertiesIfNeeded(existing, newProps) {
|
|
860
|
+
const result = { ...existing };
|
|
861
|
+
for (const [propName, newPropSchema] of Object.entries(newProps)) {
|
|
862
|
+
const existingProp = existing[propName];
|
|
863
|
+
if (!existingProp) {
|
|
864
|
+
result[propName] = newPropSchema;
|
|
865
|
+
} else if (deepEqual(existingProp, newPropSchema)) {
|
|
866
|
+
continue;
|
|
867
|
+
} else {
|
|
868
|
+
result[propName] = mergePropertySchemas(existingProp, newPropSchema);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
return result;
|
|
872
|
+
}
|
|
873
|
+
function mergePropertySchemas(schema1, schema2) {
|
|
874
|
+
if (deepEqual(schema1, schema2)) {
|
|
875
|
+
return schema1;
|
|
876
|
+
}
|
|
877
|
+
if (schema1.type === "array" && schema2.type === "array") {
|
|
878
|
+
return mergeArraySchemas(schema1, schema2);
|
|
879
|
+
}
|
|
880
|
+
const existingOneOf = schema1.oneOf || schema1.anyOf;
|
|
881
|
+
const newOneOf = schema2.oneOf || schema2.anyOf;
|
|
882
|
+
if (existingOneOf) {
|
|
883
|
+
const mergedOneOf = [...existingOneOf];
|
|
884
|
+
if (newOneOf) {
|
|
885
|
+
for (const newItem of newOneOf) {
|
|
886
|
+
if (!mergedOneOf.some((item) => deepEqual(item, newItem))) {
|
|
887
|
+
mergedOneOf.push(newItem);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
} else if (!mergedOneOf.some((item) => deepEqual(item, schema2))) {
|
|
891
|
+
mergedOneOf.push(schema2);
|
|
892
|
+
}
|
|
893
|
+
return { ...schema1, oneOf: mergedOneOf };
|
|
894
|
+
}
|
|
895
|
+
return {
|
|
896
|
+
oneOf: [schema1, schema2]
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
function mergeArraySchemas(schema1, schema2) {
|
|
900
|
+
const result = { type: "array" };
|
|
901
|
+
if (schema1.uniqueItems || schema2.uniqueItems) {
|
|
902
|
+
result.uniqueItems = true;
|
|
903
|
+
}
|
|
904
|
+
if (schema1.items && schema2.items) {
|
|
905
|
+
result.items = mergePropertySchemas(schema1.items, schema2.items);
|
|
906
|
+
} else if (schema1.items) {
|
|
907
|
+
result.items = schema1.items;
|
|
908
|
+
} else if (schema2.items) {
|
|
909
|
+
result.items = schema2.items;
|
|
910
|
+
}
|
|
911
|
+
return result;
|
|
912
|
+
}
|
|
913
|
+
function mergeRequiredFields(existing, newFields) {
|
|
914
|
+
const merged = /* @__PURE__ */ new Set([...existing, ...newFields]);
|
|
915
|
+
return Array.from(merged);
|
|
916
|
+
}
|
|
917
|
+
function deepEqual(a, b) {
|
|
918
|
+
if (a === b) return true;
|
|
919
|
+
if (a == null || b == null) return false;
|
|
920
|
+
if (typeof a !== typeof b) return false;
|
|
921
|
+
if (typeof a === "object") {
|
|
922
|
+
const aKeys = Object.keys(a);
|
|
923
|
+
const bKeys = Object.keys(b);
|
|
924
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
925
|
+
for (const key of aKeys) {
|
|
926
|
+
if (!bKeys.includes(key)) return false;
|
|
927
|
+
if (!deepEqual(a[key], b[key])) return false;
|
|
928
|
+
}
|
|
929
|
+
return true;
|
|
930
|
+
}
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
933
|
+
var METAL_ORM_WRAPPER_NAMES = ["HasManyCollection", "ManyToManyCollection", "BelongsToReference", "HasOneReference"];
|
|
934
|
+
function findMetalOrmWrapper(type, checker) {
|
|
935
|
+
if (type.isIntersection()) {
|
|
936
|
+
let wrapperInfo = null;
|
|
937
|
+
let hasReadonlyArray = false;
|
|
938
|
+
for (const constituent of type.types) {
|
|
939
|
+
const result = findWrapperInType(constituent, checker);
|
|
940
|
+
if (result) {
|
|
941
|
+
wrapperInfo = result;
|
|
942
|
+
}
|
|
943
|
+
if (!(constituent.flags & import_typescript6.default.TypeFlags.Object)) continue;
|
|
944
|
+
const symbol = constituent.getSymbol();
|
|
945
|
+
if (symbol?.getName() === "ReadonlyArray") {
|
|
946
|
+
hasReadonlyArray = true;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
if (wrapperInfo) {
|
|
950
|
+
return { ...wrapperInfo, isReadonlyArray: hasReadonlyArray };
|
|
951
|
+
}
|
|
952
|
+
return null;
|
|
953
|
+
}
|
|
954
|
+
return findWrapperInType(type, checker);
|
|
955
|
+
}
|
|
956
|
+
function findWrapperInType(type, checker) {
|
|
957
|
+
const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
|
|
958
|
+
const symbol = type.getSymbol();
|
|
959
|
+
const effectiveSymbol = aliasSymbol && aliasSymbol.flags & import_typescript6.default.SymbolFlags.Alias ? checker.getAliasedSymbol(aliasSymbol) : symbol;
|
|
960
|
+
if (!effectiveSymbol) return null;
|
|
961
|
+
const name = effectiveSymbol.getName();
|
|
962
|
+
if (!METAL_ORM_WRAPPER_NAMES.includes(name)) return null;
|
|
963
|
+
const typeRef = type;
|
|
964
|
+
const typeArgs = typeRef.typeArguments || [];
|
|
965
|
+
return {
|
|
966
|
+
wrapperName: name,
|
|
967
|
+
targetTypeArgs: typeArgs,
|
|
968
|
+
isReadonlyArray: false
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
function getWrapperTypeName(type, _checker) {
|
|
972
|
+
const symbol = type.getSymbol();
|
|
973
|
+
if (!symbol) return null;
|
|
974
|
+
const name = symbol.getName();
|
|
975
|
+
return METAL_ORM_WRAPPER_NAMES.includes(name) ? name : null;
|
|
976
|
+
}
|
|
749
977
|
function handleMetalOrmWrapper(type, ctx) {
|
|
750
978
|
const typeRef = type;
|
|
751
979
|
const typeArgs = typeRef.typeArguments;
|
|
@@ -753,7 +981,27 @@ function handleMetalOrmWrapper(type, ctx) {
|
|
|
753
981
|
const wrapperName = getWrapperTypeName(type, ctx.checker);
|
|
754
982
|
if (!wrapperName) return {};
|
|
755
983
|
const wrapperRel = { wrapper: wrapperName };
|
|
984
|
+
if (!targetType) {
|
|
985
|
+
return { "x-metal-orm-rel": wrapperRel };
|
|
986
|
+
}
|
|
756
987
|
if (wrapperName === "HasManyCollection" || wrapperName === "ManyToManyCollection") {
|
|
988
|
+
if (ctx.typeStack.has(targetType)) {
|
|
989
|
+
const items2 = {
|
|
990
|
+
type: "object",
|
|
991
|
+
properties: {
|
|
992
|
+
id: { type: "integer" }
|
|
993
|
+
},
|
|
994
|
+
required: ["id"]
|
|
995
|
+
};
|
|
996
|
+
if (wrapperName === "ManyToManyCollection" && typeArgs?.[1]) {
|
|
997
|
+
wrapperRel.pivot = typeArgs[1];
|
|
998
|
+
}
|
|
999
|
+
return {
|
|
1000
|
+
type: "array",
|
|
1001
|
+
items: items2,
|
|
1002
|
+
"x-metal-orm-rel": wrapperRel
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
757
1005
|
const items = targetType ? typeToJsonSchema(targetType, ctx) : {};
|
|
758
1006
|
if (wrapperName === "ManyToManyCollection" && typeArgs?.[1]) {
|
|
759
1007
|
wrapperRel.pivot = typeArgs[1];
|
|
@@ -764,30 +1012,149 @@ function handleMetalOrmWrapper(type, ctx) {
|
|
|
764
1012
|
"x-metal-orm-rel": wrapperRel
|
|
765
1013
|
};
|
|
766
1014
|
}
|
|
767
|
-
|
|
1015
|
+
if (wrapperName === "BelongsToReference" || wrapperName === "HasOneReference") {
|
|
1016
|
+
return handleBelongsToReference(targetType, ctx, wrapperRel);
|
|
1017
|
+
}
|
|
1018
|
+
const targetSchema = typeToJsonSchema(targetType, ctx);
|
|
768
1019
|
return {
|
|
769
1020
|
...targetSchema,
|
|
770
1021
|
"x-metal-orm-rel": wrapperRel
|
|
771
1022
|
};
|
|
772
1023
|
}
|
|
1024
|
+
function handleBelongsToReference(targetType, ctx, wrapperRel) {
|
|
1025
|
+
const { components, typeStack } = ctx;
|
|
1026
|
+
const targetSymbol = targetType.getSymbol();
|
|
1027
|
+
const typeName = targetSymbol?.getName();
|
|
1028
|
+
if (!typeName) {
|
|
1029
|
+
return {
|
|
1030
|
+
type: "object",
|
|
1031
|
+
properties: {},
|
|
1032
|
+
"x-metal-orm-rel": wrapperRel
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
const refSchemaName = `${typeName}Ref`;
|
|
1036
|
+
if (components.has(refSchemaName)) {
|
|
1037
|
+
return {
|
|
1038
|
+
$ref: `#/components/schemas/${refSchemaName}`,
|
|
1039
|
+
"x-metal-orm-rel": wrapperRel
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
if (typeStack.has(targetType)) {
|
|
1043
|
+
const circularRefSchema = {
|
|
1044
|
+
type: "object",
|
|
1045
|
+
properties: {
|
|
1046
|
+
id: { type: "integer" }
|
|
1047
|
+
},
|
|
1048
|
+
required: ["id"]
|
|
1049
|
+
};
|
|
1050
|
+
components.set(refSchemaName, circularRefSchema);
|
|
1051
|
+
return {
|
|
1052
|
+
$ref: `#/components/schemas/${refSchemaName}`,
|
|
1053
|
+
"x-metal-orm-rel": wrapperRel
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
const refSchema = buildRefSchema(targetType, ctx);
|
|
1057
|
+
components.set(refSchemaName, refSchema);
|
|
1058
|
+
return {
|
|
1059
|
+
$ref: `#/components/schemas/${refSchemaName}`,
|
|
1060
|
+
"x-metal-orm-rel": wrapperRel
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
function buildRefSchema(type, ctx) {
|
|
1064
|
+
const { checker } = ctx;
|
|
1065
|
+
if (!(type.flags & import_typescript6.default.TypeFlags.Object)) {
|
|
1066
|
+
return { type: "object", properties: {} };
|
|
1067
|
+
}
|
|
1068
|
+
const objectType = type;
|
|
1069
|
+
const properties = {};
|
|
1070
|
+
const required = [];
|
|
1071
|
+
const props = checker.getPropertiesOfType(objectType);
|
|
1072
|
+
for (const prop of props) {
|
|
1073
|
+
const propName = prop.getName();
|
|
1074
|
+
if (isIteratorOrSymbolProperty(propName)) {
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
const propType = checker.getTypeOfSymbol(prop);
|
|
1078
|
+
if (isMethodLike(propType)) {
|
|
1079
|
+
continue;
|
|
1080
|
+
}
|
|
1081
|
+
const isOptional = !!(prop.flags & import_typescript6.default.SymbolFlags.Optional);
|
|
1082
|
+
const isRelation = isMetalOrmWrapperType(propType, checker);
|
|
1083
|
+
if (isRelation) {
|
|
1084
|
+
continue;
|
|
1085
|
+
}
|
|
1086
|
+
const propCtx = { ...ctx, propertyName: propName };
|
|
1087
|
+
properties[propName] = typeToJsonSchema(propType, propCtx);
|
|
1088
|
+
if (!isOptional) {
|
|
1089
|
+
required.push(propName);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
const schema = {
|
|
1093
|
+
type: "object",
|
|
1094
|
+
properties
|
|
1095
|
+
};
|
|
1096
|
+
if (required.length > 0) {
|
|
1097
|
+
schema.required = required;
|
|
1098
|
+
}
|
|
1099
|
+
return schema;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// src/compiler/schema/typeToJsonSchema.ts
|
|
1103
|
+
function typeToJsonSchema(type, ctx, typeNode) {
|
|
1104
|
+
const primitiveResult = handlePrimitiveType(type, ctx, typeNode);
|
|
1105
|
+
if (primitiveResult) {
|
|
1106
|
+
return primitiveResult;
|
|
1107
|
+
}
|
|
1108
|
+
if (type.isUnion()) {
|
|
1109
|
+
return handleUnion(type, ctx, typeNode);
|
|
1110
|
+
}
|
|
1111
|
+
if (type.isIntersection()) {
|
|
1112
|
+
return handleIntersection(type, ctx, typeNode);
|
|
1113
|
+
}
|
|
1114
|
+
if (ctx.checker.isArrayType(type)) {
|
|
1115
|
+
const typeArgs = type.typeArguments;
|
|
1116
|
+
const itemType = typeArgs?.[0];
|
|
1117
|
+
const items = itemType ? typeToJsonSchema(itemType, ctx) : {};
|
|
1118
|
+
return {
|
|
1119
|
+
type: "array",
|
|
1120
|
+
items,
|
|
1121
|
+
uniqueItems: isSetType(type, ctx.checker) ? true : void 0
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
if (type.flags & import_typescript7.default.TypeFlags.Object) {
|
|
1125
|
+
const objectType = type;
|
|
1126
|
+
if (isMetalOrmWrapperType(type, ctx.checker)) {
|
|
1127
|
+
return handleMetalOrmWrapper(objectType, ctx);
|
|
1128
|
+
}
|
|
1129
|
+
return handleObjectType(objectType, ctx, typeNode);
|
|
1130
|
+
}
|
|
1131
|
+
return {};
|
|
1132
|
+
}
|
|
1133
|
+
function isSetType(type, _checker) {
|
|
1134
|
+
const symbol = type.getSymbol();
|
|
1135
|
+
if (!symbol) return false;
|
|
1136
|
+
const name = symbol.getName();
|
|
1137
|
+
if (name === "Set") return true;
|
|
1138
|
+
return false;
|
|
1139
|
+
}
|
|
773
1140
|
|
|
774
1141
|
// src/compiler/schema/extractAnnotations.ts
|
|
775
|
-
var
|
|
1142
|
+
var import_typescript8 = __toESM(require("typescript"), 1);
|
|
776
1143
|
function extractPropertySchemaFragments(checker, prop) {
|
|
777
|
-
if (!
|
|
778
|
-
const decs =
|
|
1144
|
+
if (!import_typescript8.default.canHaveDecorators(prop)) return [];
|
|
1145
|
+
const decs = import_typescript8.default.getDecorators(prop);
|
|
779
1146
|
if (!decs || decs.length === 0) return [];
|
|
780
1147
|
const frags = [];
|
|
781
1148
|
for (const d of decs) {
|
|
782
1149
|
const expr = d.expression;
|
|
783
1150
|
let callee;
|
|
784
1151
|
let args;
|
|
785
|
-
if (
|
|
1152
|
+
if (import_typescript8.default.isCallExpression(expr)) {
|
|
786
1153
|
callee = expr.expression;
|
|
787
1154
|
args = expr.arguments;
|
|
788
1155
|
} else {
|
|
789
1156
|
callee = expr;
|
|
790
|
-
args =
|
|
1157
|
+
args = import_typescript8.default.factory.createNodeArray([]);
|
|
791
1158
|
}
|
|
792
1159
|
const sym = checker.getSymbolAtLocation(callee);
|
|
793
1160
|
if (!sym) continue;
|
|
@@ -796,7 +1163,7 @@ function extractPropertySchemaFragments(checker, prop) {
|
|
|
796
1163
|
const name = resolved.name;
|
|
797
1164
|
if (name === "Schema") {
|
|
798
1165
|
const obj = args[0];
|
|
799
|
-
if (obj &&
|
|
1166
|
+
if (obj && import_typescript8.default.isObjectLiteralExpression(obj)) {
|
|
800
1167
|
const frag = objectLiteralToJson(obj);
|
|
801
1168
|
if (frag) frags.push(frag);
|
|
802
1169
|
}
|
|
@@ -818,7 +1185,7 @@ function extractPropertySchemaFragments(checker, prop) {
|
|
|
818
1185
|
frags.push({ format: args[0].text });
|
|
819
1186
|
} else if (name === "Pattern") {
|
|
820
1187
|
const arg = args[0];
|
|
821
|
-
if (arg &&
|
|
1188
|
+
if (arg && import_typescript8.default.isRegularExpressionLiteral(arg)) {
|
|
822
1189
|
frags.push({ pattern: extractRegexPattern(arg.text) });
|
|
823
1190
|
} else if (isStringLiteral(arg)) {
|
|
824
1191
|
frags.push({ pattern: arg.text });
|
|
@@ -835,11 +1202,11 @@ function extractPropertySchemaFragments(checker, prop) {
|
|
|
835
1202
|
frags.push({ multipleOf: Number(args[0].text) });
|
|
836
1203
|
} else if (name === "Example") {
|
|
837
1204
|
frags.push({ example: literalToJson(args[0]) });
|
|
838
|
-
} else if (name === "Examples" &&
|
|
1205
|
+
} else if (name === "Examples" && import_typescript8.default.isArrayLiteralExpression(args[0])) {
|
|
839
1206
|
frags.push({ examples: args[0].elements.map((e) => literalToJson(e)) });
|
|
840
1207
|
} else if (name === "Description" && isStringLiteral(args[0])) {
|
|
841
1208
|
frags.push({ description: args[0].text });
|
|
842
|
-
} else if (name === "Enum" &&
|
|
1209
|
+
} else if (name === "Enum" && import_typescript8.default.isArrayLiteralExpression(args[0])) {
|
|
843
1210
|
frags.push({ enum: args[0].elements.map((e) => literalToJson(e)) });
|
|
844
1211
|
} else if (name === "Const") {
|
|
845
1212
|
frags.push({ const: literalToJson(args[0]) });
|
|
@@ -847,9 +1214,9 @@ function extractPropertySchemaFragments(checker, prop) {
|
|
|
847
1214
|
frags.push({ default: literalToJson(args[0]) });
|
|
848
1215
|
} else if (name === "AdditionalProperties") {
|
|
849
1216
|
const arg = args[0];
|
|
850
|
-
if (arg && (arg.kind ===
|
|
851
|
-
frags.push({ additionalProperties: arg.kind ===
|
|
852
|
-
} else if (arg &&
|
|
1217
|
+
if (arg && (arg.kind === import_typescript8.default.SyntaxKind.FalseKeyword || arg.kind === import_typescript8.default.SyntaxKind.TrueKeyword)) {
|
|
1218
|
+
frags.push({ additionalProperties: arg.kind === import_typescript8.default.SyntaxKind.TrueKeyword });
|
|
1219
|
+
} else if (arg && import_typescript8.default.isObjectLiteralExpression(arg)) {
|
|
853
1220
|
const obj = objectLiteralToJson(arg);
|
|
854
1221
|
if (obj) frags.push({ additionalProperties: obj });
|
|
855
1222
|
}
|
|
@@ -862,7 +1229,7 @@ function extractPropertySchemaFragments(checker, prop) {
|
|
|
862
1229
|
return frags;
|
|
863
1230
|
}
|
|
864
1231
|
function resolveImportedDecorator(checker, sym) {
|
|
865
|
-
const target = sym.flags &
|
|
1232
|
+
const target = sym.flags & import_typescript8.default.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
|
|
866
1233
|
const name = target.getName();
|
|
867
1234
|
const decl = target.declarations?.[0];
|
|
868
1235
|
if (!decl) return null;
|
|
@@ -873,10 +1240,10 @@ function resolveImportedDecorator(checker, sym) {
|
|
|
873
1240
|
return null;
|
|
874
1241
|
}
|
|
875
1242
|
function isNumberLiteral(node) {
|
|
876
|
-
return !!node &&
|
|
1243
|
+
return !!node && import_typescript8.default.isNumericLiteral(node);
|
|
877
1244
|
}
|
|
878
1245
|
function isStringLiteral(node) {
|
|
879
|
-
return !!node &&
|
|
1246
|
+
return !!node && import_typescript8.default.isStringLiteral(node);
|
|
880
1247
|
}
|
|
881
1248
|
function extractRegexPattern(text) {
|
|
882
1249
|
const match = text.match(/^\/(.+)\/[gimsuy]*$/);
|
|
@@ -885,12 +1252,12 @@ function extractRegexPattern(text) {
|
|
|
885
1252
|
function objectLiteralToJson(obj) {
|
|
886
1253
|
const out = {};
|
|
887
1254
|
for (const prop of obj.properties) {
|
|
888
|
-
if (!
|
|
1255
|
+
if (!import_typescript8.default.isPropertyAssignment(prop)) continue;
|
|
889
1256
|
const name = prop.name;
|
|
890
1257
|
let key;
|
|
891
|
-
if (
|
|
1258
|
+
if (import_typescript8.default.isIdentifier(name)) {
|
|
892
1259
|
key = name.text;
|
|
893
|
-
} else if (
|
|
1260
|
+
} else if (import_typescript8.default.isStringLiteral(name)) {
|
|
894
1261
|
key = name.text;
|
|
895
1262
|
} else {
|
|
896
1263
|
continue;
|
|
@@ -900,13 +1267,13 @@ function objectLiteralToJson(obj) {
|
|
|
900
1267
|
return out;
|
|
901
1268
|
}
|
|
902
1269
|
function literalToJson(node) {
|
|
903
|
-
if (
|
|
904
|
-
if (
|
|
905
|
-
if (node.kind ===
|
|
906
|
-
if (node.kind ===
|
|
907
|
-
if (node.kind ===
|
|
908
|
-
if (
|
|
909
|
-
if (
|
|
1270
|
+
if (import_typescript8.default.isStringLiteral(node)) return node.text;
|
|
1271
|
+
if (import_typescript8.default.isNumericLiteral(node)) return Number(node.text);
|
|
1272
|
+
if (node.kind === import_typescript8.default.SyntaxKind.TrueKeyword) return true;
|
|
1273
|
+
if (node.kind === import_typescript8.default.SyntaxKind.FalseKeyword) return false;
|
|
1274
|
+
if (node.kind === import_typescript8.default.SyntaxKind.NullKeyword) return null;
|
|
1275
|
+
if (import_typescript8.default.isObjectLiteralExpression(node)) return objectLiteralToJson(node);
|
|
1276
|
+
if (import_typescript8.default.isArrayLiteralExpression(node)) return node.elements.map((e) => literalToJson(e));
|
|
910
1277
|
return void 0;
|
|
911
1278
|
}
|
|
912
1279
|
function mergeFragments(base, ...frags) {
|
|
@@ -917,7 +1284,302 @@ function mergeFragments(base, ...frags) {
|
|
|
917
1284
|
return result;
|
|
918
1285
|
}
|
|
919
1286
|
|
|
1287
|
+
// src/compiler/schema/parameters.ts
|
|
1288
|
+
function buildPathParameters(operation, ctx, parameters) {
|
|
1289
|
+
for (const paramIndex of operation.pathParamIndices) {
|
|
1290
|
+
const param = operation.parameters[paramIndex];
|
|
1291
|
+
if (param) {
|
|
1292
|
+
let paramSchema = typeToJsonSchema(param.type, ctx);
|
|
1293
|
+
if (param.paramNode) {
|
|
1294
|
+
const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
|
|
1295
|
+
if (frags.length > 0) {
|
|
1296
|
+
paramSchema = mergeFragments(paramSchema, ...frags);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
const schema = paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema;
|
|
1300
|
+
const paramName = param.name.toLowerCase();
|
|
1301
|
+
const isIdParam = paramName === "id" || paramName.endsWith("id");
|
|
1302
|
+
if (!schema.$ref && schema.type === "number" && isIdParam) {
|
|
1303
|
+
schema.type = "integer";
|
|
1304
|
+
if (!schema.minimum) {
|
|
1305
|
+
schema.minimum = 1;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
parameters.push({
|
|
1309
|
+
name: param.name,
|
|
1310
|
+
in: "path",
|
|
1311
|
+
required: !param.isOptional,
|
|
1312
|
+
schema
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
function buildQueryParameters(operation, ctx, parameters) {
|
|
1318
|
+
if (operation.queryObjectParamIndex !== null) {
|
|
1319
|
+
const queryParam = operation.parameters[operation.queryObjectParamIndex];
|
|
1320
|
+
if (!queryParam) return;
|
|
1321
|
+
const querySchema = typeToJsonSchema(queryParam.type, ctx);
|
|
1322
|
+
const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps(querySchema, ctx.components);
|
|
1323
|
+
for (const [propName, propSchema] of Object.entries(queryObjProps)) {
|
|
1324
|
+
const isRequired = queryRequired.includes(propName);
|
|
1325
|
+
const isDeepObject = isDeepObjectSchema(propSchema, ctx);
|
|
1326
|
+
const isObjectLike = isObjectLikeSchema(propSchema, ctx);
|
|
1327
|
+
const serialization = determineQuerySerialization(propSchema.type);
|
|
1328
|
+
const exampleValue = generateExampleValue(propSchema, propName);
|
|
1329
|
+
if (isDeepObject) {
|
|
1330
|
+
parameters.push({
|
|
1331
|
+
name: propName,
|
|
1332
|
+
in: "query",
|
|
1333
|
+
required: isRequired,
|
|
1334
|
+
style: "deepObject",
|
|
1335
|
+
explode: true,
|
|
1336
|
+
schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
|
|
1337
|
+
});
|
|
1338
|
+
} else if (isObjectLike) {
|
|
1339
|
+
const schemaRef = propSchema.$ref || "#/components/schemas/InlineQueryParam";
|
|
1340
|
+
parameters.push({
|
|
1341
|
+
name: propName,
|
|
1342
|
+
in: "query",
|
|
1343
|
+
required: isRequired,
|
|
1344
|
+
schema: { type: "string" },
|
|
1345
|
+
description: `JSON-encoded object. ${exampleValue}`,
|
|
1346
|
+
examples: {
|
|
1347
|
+
default: { value: parseExampleValue(exampleValue) }
|
|
1348
|
+
},
|
|
1349
|
+
"x-adorn-jsonSchemaRef": schemaRef
|
|
1350
|
+
});
|
|
1351
|
+
} else {
|
|
1352
|
+
const paramDef = {
|
|
1353
|
+
name: propName,
|
|
1354
|
+
in: "query",
|
|
1355
|
+
required: isRequired,
|
|
1356
|
+
schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
|
|
1357
|
+
};
|
|
1358
|
+
if (propName === "page") {
|
|
1359
|
+
paramDef.schema = { type: "integer", default: 1, minimum: 1 };
|
|
1360
|
+
} else if (propName === "pageSize") {
|
|
1361
|
+
paramDef.schema = { type: "integer", default: 10, minimum: 1 };
|
|
1362
|
+
} else if (propName === "totalItems") {
|
|
1363
|
+
paramDef.schema = { type: "integer", minimum: 0 };
|
|
1364
|
+
} else if (propName === "sort") {
|
|
1365
|
+
paramDef.schema = {
|
|
1366
|
+
oneOf: [
|
|
1367
|
+
{ type: "string" },
|
|
1368
|
+
{ type: "array", items: { type: "string" } }
|
|
1369
|
+
]
|
|
1370
|
+
};
|
|
1371
|
+
} else if (propName === "q") {
|
|
1372
|
+
paramDef.schema = { type: "string" };
|
|
1373
|
+
} else if (propName === "hasComments") {
|
|
1374
|
+
paramDef.schema = { type: "boolean" };
|
|
1375
|
+
}
|
|
1376
|
+
if (Object.keys(serialization).length > 0) {
|
|
1377
|
+
Object.assign(paramDef, serialization);
|
|
1378
|
+
}
|
|
1379
|
+
parameters.push(paramDef);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
for (const paramIndex of operation.queryParamIndices) {
|
|
1384
|
+
const param = operation.parameters[paramIndex];
|
|
1385
|
+
if (param) {
|
|
1386
|
+
let paramSchema = typeToJsonSchema(param.type, ctx);
|
|
1387
|
+
if (param.paramNode) {
|
|
1388
|
+
const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
|
|
1389
|
+
if (frags.length > 0) {
|
|
1390
|
+
paramSchema = mergeFragments(paramSchema, ...frags);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
const isDeepObject = isDeepObjectSchema(paramSchema, ctx);
|
|
1394
|
+
const isObjectLike = isObjectLikeSchema(paramSchema, ctx);
|
|
1395
|
+
if (isDeepObject) {
|
|
1396
|
+
parameters.push({
|
|
1397
|
+
name: param.name,
|
|
1398
|
+
in: "query",
|
|
1399
|
+
required: !param.isOptional,
|
|
1400
|
+
style: "deepObject",
|
|
1401
|
+
explode: true,
|
|
1402
|
+
schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema
|
|
1403
|
+
});
|
|
1404
|
+
} else if (isObjectLike) {
|
|
1405
|
+
const schemaRef = paramSchema.$ref || "#/components/schemas/InlineQueryParam";
|
|
1406
|
+
const exampleValue = generateExampleValue(paramSchema, param.name);
|
|
1407
|
+
parameters.push({
|
|
1408
|
+
name: param.name,
|
|
1409
|
+
in: "query",
|
|
1410
|
+
required: !param.isOptional,
|
|
1411
|
+
schema: { type: "string" },
|
|
1412
|
+
description: `JSON-encoded object. ${exampleValue}`,
|
|
1413
|
+
examples: {
|
|
1414
|
+
default: { value: parseExampleValue(exampleValue) }
|
|
1415
|
+
},
|
|
1416
|
+
"x-adorn-jsonSchemaRef": schemaRef
|
|
1417
|
+
});
|
|
1418
|
+
} else {
|
|
1419
|
+
const serialization = determineQuerySerialization(paramSchema.type);
|
|
1420
|
+
parameters.push({
|
|
1421
|
+
name: param.name,
|
|
1422
|
+
in: "query",
|
|
1423
|
+
required: !param.isOptional,
|
|
1424
|
+
schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema,
|
|
1425
|
+
...Object.keys(serialization).length > 0 ? serialization : {}
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
function buildHeaderParameters(operation, ctx, parameters) {
|
|
1432
|
+
if (operation.headerObjectParamIndex === null) return;
|
|
1433
|
+
const headerParam = operation.parameters[operation.headerObjectParamIndex];
|
|
1434
|
+
if (!headerParam) return;
|
|
1435
|
+
const headerSchema = typeToJsonSchema(headerParam.type, ctx);
|
|
1436
|
+
if (!headerSchema.properties) return;
|
|
1437
|
+
const headerObjProps = headerSchema.properties;
|
|
1438
|
+
for (const [propName, propSchema] of Object.entries(headerObjProps)) {
|
|
1439
|
+
const isRequired = headerSchema.required?.includes(propName) ?? false;
|
|
1440
|
+
parameters.push({
|
|
1441
|
+
name: propName,
|
|
1442
|
+
in: "header",
|
|
1443
|
+
required: isRequired,
|
|
1444
|
+
schema: propSchema
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
function buildCookieParameters(operation, ctx, parameters) {
|
|
1449
|
+
if (operation.cookieObjectParamIndex === null) return;
|
|
1450
|
+
const cookieParam = operation.parameters[operation.cookieObjectParamIndex];
|
|
1451
|
+
if (!cookieParam) return;
|
|
1452
|
+
const cookieSchema = typeToJsonSchema(cookieParam.type, ctx);
|
|
1453
|
+
if (!cookieSchema.properties) return;
|
|
1454
|
+
const cookieObjProps = cookieSchema.properties;
|
|
1455
|
+
for (const [propName, propSchema] of Object.entries(cookieObjProps)) {
|
|
1456
|
+
const isRequired = cookieSchema.required?.includes(propName) ?? false;
|
|
1457
|
+
parameters.push({
|
|
1458
|
+
name: propName,
|
|
1459
|
+
in: "cookie",
|
|
1460
|
+
required: isRequired,
|
|
1461
|
+
schema: propSchema,
|
|
1462
|
+
style: "form",
|
|
1463
|
+
explode: true
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
function determineQuerySerialization(schemaType) {
|
|
1468
|
+
const typeArray = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
|
|
1469
|
+
const isArray = typeArray.includes("array");
|
|
1470
|
+
if (isArray) {
|
|
1471
|
+
return { style: "form", explode: true };
|
|
1472
|
+
}
|
|
1473
|
+
return {};
|
|
1474
|
+
}
|
|
1475
|
+
function generateExampleValue(schema, propName) {
|
|
1476
|
+
const resolved = resolveSchemaRef(schema, /* @__PURE__ */ new Map());
|
|
1477
|
+
if (resolved.type === "object" && resolved.properties) {
|
|
1478
|
+
const example = {};
|
|
1479
|
+
for (const [key, prop] of Object.entries(resolved.properties)) {
|
|
1480
|
+
const propResolved = resolveSchemaRef(prop, /* @__PURE__ */ new Map());
|
|
1481
|
+
if (propResolved.type === "string") {
|
|
1482
|
+
example[key] = "value";
|
|
1483
|
+
} else if (propResolved.type === "number" || propResolved.type === "integer") {
|
|
1484
|
+
example[key] = 1;
|
|
1485
|
+
} else if (propResolved.type === "boolean") {
|
|
1486
|
+
example[key] = true;
|
|
1487
|
+
} else if (Array.isArray(propResolved.type) && propResolved.type.includes("null")) {
|
|
1488
|
+
example[key] = null;
|
|
1489
|
+
} else if (propResolved.enum) {
|
|
1490
|
+
example[key] = propResolved.enum[0];
|
|
1491
|
+
} else {
|
|
1492
|
+
example[key] = "value";
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
return `Example: ${propName}=${JSON.stringify(example)}`;
|
|
1496
|
+
}
|
|
1497
|
+
return `Example: ${propName}=${JSON.stringify({ key: "value" })}`;
|
|
1498
|
+
}
|
|
1499
|
+
function parseExampleValue(description) {
|
|
1500
|
+
const match = description.match(/Example:\s*\w+=(\{[^}]+\})/);
|
|
1501
|
+
if (match) {
|
|
1502
|
+
return match[1];
|
|
1503
|
+
}
|
|
1504
|
+
return JSON.stringify({ key: "value" });
|
|
1505
|
+
}
|
|
1506
|
+
function isDeepObjectSchema(schema, ctx) {
|
|
1507
|
+
const resolved = resolveSchemaRef(schema, ctx.components);
|
|
1508
|
+
if (resolved.type === "array") {
|
|
1509
|
+
return false;
|
|
1510
|
+
}
|
|
1511
|
+
if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
|
|
1512
|
+
return true;
|
|
1513
|
+
}
|
|
1514
|
+
if (resolved.allOf) {
|
|
1515
|
+
for (const branch of resolved.allOf) {
|
|
1516
|
+
if (isDeepObjectSchema(branch, ctx)) {
|
|
1517
|
+
return true;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
return false;
|
|
1522
|
+
}
|
|
1523
|
+
function isObjectLikeSchema(schema, ctx) {
|
|
1524
|
+
const resolved = resolveSchemaRef(schema, ctx.components);
|
|
1525
|
+
if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
|
|
1526
|
+
return true;
|
|
1527
|
+
}
|
|
1528
|
+
if (resolved.allOf) {
|
|
1529
|
+
for (const branch of resolved.allOf) {
|
|
1530
|
+
if (isObjectLikeSchema(branch, ctx)) {
|
|
1531
|
+
return true;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
if (resolved.type === "array" && resolved.items) {
|
|
1536
|
+
const itemsSchema = resolveSchemaRef(resolved.items, ctx.components);
|
|
1537
|
+
return isObjectLikeSchema(itemsSchema, ctx);
|
|
1538
|
+
}
|
|
1539
|
+
return false;
|
|
1540
|
+
}
|
|
1541
|
+
function resolveSchemaRef(schema, components) {
|
|
1542
|
+
const ref = schema.$ref;
|
|
1543
|
+
if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
|
|
1544
|
+
return schema;
|
|
1545
|
+
}
|
|
1546
|
+
const name = ref.replace("#/components/schemas/", "");
|
|
1547
|
+
const next = components.get(name);
|
|
1548
|
+
if (!next) return schema;
|
|
1549
|
+
return resolveSchemaRef(next, components);
|
|
1550
|
+
}
|
|
1551
|
+
function resolveAndCollectObjectProps(schema, components) {
|
|
1552
|
+
const resolved = resolveSchemaRef(schema, components);
|
|
1553
|
+
const properties = {};
|
|
1554
|
+
const required = [];
|
|
1555
|
+
const processSchema = (s) => {
|
|
1556
|
+
const current = resolveSchemaRef(s, components);
|
|
1557
|
+
if (current.properties) {
|
|
1558
|
+
for (const [key, val] of Object.entries(current.properties)) {
|
|
1559
|
+
if (!properties[key]) {
|
|
1560
|
+
properties[key] = val;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
if (current.required) {
|
|
1565
|
+
for (const req of current.required) {
|
|
1566
|
+
if (!required.includes(req)) {
|
|
1567
|
+
required.push(req);
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
if (current.allOf) {
|
|
1572
|
+
for (const branch of current.allOf) {
|
|
1573
|
+
processSchema(branch);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
processSchema(resolved);
|
|
1578
|
+
return { properties, required };
|
|
1579
|
+
}
|
|
1580
|
+
|
|
920
1581
|
// src/compiler/schema/openapi.ts
|
|
1582
|
+
var METAL_ORM_WRAPPER_NAMES2 = ["BelongsToReference", "HasOneReference", "HasManyCollection", "ManyToManyCollection"];
|
|
921
1583
|
function generateOpenAPI(controllers, checker, options = {}) {
|
|
922
1584
|
const components = /* @__PURE__ */ new Map();
|
|
923
1585
|
const ctx = {
|
|
@@ -934,21 +1596,124 @@ function generateOpenAPI(controllers, checker, options = {}) {
|
|
|
934
1596
|
if (!paths[fullPath]) {
|
|
935
1597
|
paths[fullPath] = {};
|
|
936
1598
|
}
|
|
937
|
-
const method = operation.httpMethod.toLowerCase();
|
|
938
|
-
paths[fullPath][method] = buildOperation(operation, ctx, controller.consumes);
|
|
1599
|
+
const method = operation.httpMethod.toLowerCase();
|
|
1600
|
+
paths[fullPath][method] = buildOperation(operation, ctx, controller.consumes);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
const schemas = Object.fromEntries(components);
|
|
1604
|
+
cleanupMetalOrmWrappers(schemas, paths);
|
|
1605
|
+
return {
|
|
1606
|
+
openapi: "3.1.0",
|
|
1607
|
+
info: {
|
|
1608
|
+
title: options.title ?? "API",
|
|
1609
|
+
version: options.version ?? "1.0.0"
|
|
1610
|
+
},
|
|
1611
|
+
components: {
|
|
1612
|
+
schemas
|
|
1613
|
+
},
|
|
1614
|
+
paths
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
function cleanupMetalOrmWrappers(schemas, paths) {
|
|
1618
|
+
const schemasToDelete = /* @__PURE__ */ new Set();
|
|
1619
|
+
for (const wrapperName of METAL_ORM_WRAPPER_NAMES2) {
|
|
1620
|
+
if (schemas[wrapperName]) {
|
|
1621
|
+
schemasToDelete.add(wrapperName);
|
|
1622
|
+
}
|
|
1623
|
+
if (schemas[`${wrapperName}Api`]) {
|
|
1624
|
+
schemasToDelete.add(`${wrapperName}Api`);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
for (const schema of Object.values(schemas)) {
|
|
1628
|
+
cleanupSchemaRefs(schema, schemasToDelete);
|
|
1629
|
+
}
|
|
1630
|
+
for (const pathItem of Object.values(paths)) {
|
|
1631
|
+
cleanupPathItemRefs(pathItem, schemasToDelete);
|
|
1632
|
+
}
|
|
1633
|
+
for (const schemaName of schemasToDelete) {
|
|
1634
|
+
delete schemas[schemaName];
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
function cleanupSchemaRefs(schema, schemasToDelete) {
|
|
1638
|
+
if (typeof schema !== "object" || schema === null) {
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
if (schema.properties) {
|
|
1642
|
+
for (const propName of Object.keys(schema.properties)) {
|
|
1643
|
+
const propSchema = schema.properties[propName];
|
|
1644
|
+
if (propSchema.$ref && typeof propSchema.$ref === "string") {
|
|
1645
|
+
const refName = propSchema.$ref.replace("#/components/schemas/", "");
|
|
1646
|
+
if (schemasToDelete.has(refName)) {
|
|
1647
|
+
delete schema.properties[propName];
|
|
1648
|
+
if (schema.required && Array.isArray(schema.required)) {
|
|
1649
|
+
schema.required = schema.required.filter((r) => r !== propName);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
} else {
|
|
1653
|
+
cleanupSchemaRefs(propSchema, schemasToDelete);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
if (schema.items) {
|
|
1658
|
+
cleanupSchemaRefs(schema.items, schemasToDelete);
|
|
1659
|
+
}
|
|
1660
|
+
if (schema.allOf) {
|
|
1661
|
+
for (const item of schema.allOf) {
|
|
1662
|
+
cleanupSchemaRefs(item, schemasToDelete);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
function cleanupPathItemRefs(pathItem, schemasToDelete) {
|
|
1667
|
+
if (typeof pathItem !== "object" || pathItem === null) {
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
for (const method of Object.keys(pathItem)) {
|
|
1671
|
+
const operation = pathItem[method];
|
|
1672
|
+
if (typeof operation !== "object" || operation === null) continue;
|
|
1673
|
+
if (operation.requestBody) {
|
|
1674
|
+
cleanupRequestBodyRefs(operation.requestBody, schemasToDelete);
|
|
1675
|
+
}
|
|
1676
|
+
if (operation.responses) {
|
|
1677
|
+
const responses = Object.values(operation.responses);
|
|
1678
|
+
for (const response of responses) {
|
|
1679
|
+
if (response.content) {
|
|
1680
|
+
const contentTypes = Object.values(response.content);
|
|
1681
|
+
for (const contentType of contentTypes) {
|
|
1682
|
+
if (contentType.schema) {
|
|
1683
|
+
cleanupSchemaRefs(contentType.schema, schemasToDelete);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
function cleanupRequestBodyRefs(requestBody, schemasToDelete) {
|
|
1692
|
+
if (typeof requestBody !== "object" || requestBody === null) return;
|
|
1693
|
+
if (requestBody.content) {
|
|
1694
|
+
const contentTypes = Object.values(requestBody.content);
|
|
1695
|
+
for (const contentType of contentTypes) {
|
|
1696
|
+
if (contentType.schema) {
|
|
1697
|
+
cleanupSchemaRefs(contentType.schema, schemasToDelete);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
if (requestBody.properties) {
|
|
1702
|
+
for (const propName of Object.keys(requestBody.properties)) {
|
|
1703
|
+
const propSchema = requestBody.properties[propName];
|
|
1704
|
+
if (propSchema.$ref && typeof propSchema.$ref === "string") {
|
|
1705
|
+
const refName = propSchema.$ref.replace("#/components/schemas/", "");
|
|
1706
|
+
if (schemasToDelete.has(refName)) {
|
|
1707
|
+
delete requestBody.properties[propName];
|
|
1708
|
+
if (requestBody.required && Array.isArray(requestBody.required)) {
|
|
1709
|
+
requestBody.required = requestBody.required.filter((r) => r !== propName);
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
} else {
|
|
1713
|
+
cleanupSchemaRefs(propSchema, schemasToDelete);
|
|
1714
|
+
}
|
|
939
1715
|
}
|
|
940
1716
|
}
|
|
941
|
-
return {
|
|
942
|
-
openapi: "3.1.0",
|
|
943
|
-
info: {
|
|
944
|
-
title: options.title ?? "API",
|
|
945
|
-
version: options.version ?? "1.0.0"
|
|
946
|
-
},
|
|
947
|
-
components: {
|
|
948
|
-
schemas: Object.fromEntries(components)
|
|
949
|
-
},
|
|
950
|
-
paths
|
|
951
|
-
};
|
|
952
1717
|
}
|
|
953
1718
|
function convertToOpenApiPath(basePath, path4) {
|
|
954
1719
|
const base = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
@@ -1015,12 +1780,12 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
|
|
|
1015
1780
|
const declarations = typeSymbol.getDeclarations();
|
|
1016
1781
|
if (!declarations || declarations.length === 0) return schema;
|
|
1017
1782
|
const classDecl = declarations[0];
|
|
1018
|
-
if (!
|
|
1783
|
+
if (!import_typescript9.default.isClassDeclaration(classDecl)) return schema;
|
|
1019
1784
|
const result = { ...schema };
|
|
1020
1785
|
const props = { ...result.properties };
|
|
1021
1786
|
for (const member of classDecl.members) {
|
|
1022
|
-
if (!
|
|
1023
|
-
const propName =
|
|
1787
|
+
if (!import_typescript9.default.isPropertyDeclaration(member) || !member.name) continue;
|
|
1788
|
+
const propName = import_typescript9.default.isIdentifier(member.name) ? member.name.text : null;
|
|
1024
1789
|
if (!propName) continue;
|
|
1025
1790
|
if (!props[propName]) continue;
|
|
1026
1791
|
const frags = extractPropertySchemaFragments(ctx.checker, member);
|
|
@@ -1031,220 +1796,9 @@ function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
|
|
|
1031
1796
|
result.properties = props;
|
|
1032
1797
|
return result;
|
|
1033
1798
|
}
|
|
1034
|
-
function buildPathParameters(operation, ctx, parameters) {
|
|
1035
|
-
for (const paramIndex of operation.pathParamIndices) {
|
|
1036
|
-
const param = operation.parameters[paramIndex];
|
|
1037
|
-
if (param) {
|
|
1038
|
-
let paramSchema = typeToJsonSchema(param.type, ctx);
|
|
1039
|
-
if (param.paramNode) {
|
|
1040
|
-
const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
|
|
1041
|
-
if (frags.length > 0) {
|
|
1042
|
-
paramSchema = mergeFragments(paramSchema, ...frags);
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
const schema = paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema;
|
|
1046
|
-
parameters.push({
|
|
1047
|
-
name: param.name,
|
|
1048
|
-
in: "path",
|
|
1049
|
-
required: !param.isOptional,
|
|
1050
|
-
schema
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
function isObjectLikeSchema(schema, ctx) {
|
|
1056
|
-
const resolved = resolveSchemaRef(schema, ctx.components);
|
|
1057
|
-
if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
|
|
1058
|
-
return true;
|
|
1059
|
-
}
|
|
1060
|
-
if (resolved.allOf) {
|
|
1061
|
-
for (const branch of resolved.allOf) {
|
|
1062
|
-
if (isObjectLikeSchema(branch, ctx)) {
|
|
1063
|
-
return true;
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
if (resolved.type === "array" && resolved.items) {
|
|
1068
|
-
const itemsSchema = resolveSchemaRef(resolved.items, ctx.components);
|
|
1069
|
-
return isObjectLikeSchema(itemsSchema, ctx);
|
|
1070
|
-
}
|
|
1071
|
-
return false;
|
|
1072
|
-
}
|
|
1073
|
-
function resolveSchemaRef(schema, components) {
|
|
1074
|
-
const ref = schema.$ref;
|
|
1075
|
-
if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
|
|
1076
|
-
return schema;
|
|
1077
|
-
}
|
|
1078
|
-
const name = ref.replace("#/components/schemas/", "");
|
|
1079
|
-
const next = components.get(name);
|
|
1080
|
-
if (!next) return schema;
|
|
1081
|
-
return resolveSchemaRef(next, components);
|
|
1082
|
-
}
|
|
1083
|
-
function resolveAndCollectObjectProps(schema, components) {
|
|
1084
|
-
const resolved = resolveSchemaRef(schema, components);
|
|
1085
|
-
const properties = {};
|
|
1086
|
-
const required = [];
|
|
1087
|
-
const processSchema = (s) => {
|
|
1088
|
-
const current = resolveSchemaRef(s, components);
|
|
1089
|
-
if (current.properties) {
|
|
1090
|
-
for (const [key, val] of Object.entries(current.properties)) {
|
|
1091
|
-
if (!properties[key]) {
|
|
1092
|
-
properties[key] = val;
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
if (current.required) {
|
|
1097
|
-
for (const req of current.required) {
|
|
1098
|
-
if (!required.includes(req)) {
|
|
1099
|
-
required.push(req);
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
if (current.allOf) {
|
|
1104
|
-
for (const branch of current.allOf) {
|
|
1105
|
-
processSchema(branch);
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
};
|
|
1109
|
-
processSchema(resolved);
|
|
1110
|
-
return { properties, required };
|
|
1111
|
-
}
|
|
1112
|
-
function buildQueryParameters(operation, ctx, parameters) {
|
|
1113
|
-
if (operation.queryObjectParamIndex !== null) {
|
|
1114
|
-
const queryParam = operation.parameters[operation.queryObjectParamIndex];
|
|
1115
|
-
if (!queryParam) return;
|
|
1116
|
-
const querySchema = typeToJsonSchema(queryParam.type, ctx);
|
|
1117
|
-
const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps(querySchema, ctx.components);
|
|
1118
|
-
for (const [propName, propSchema] of Object.entries(queryObjProps)) {
|
|
1119
|
-
const isRequired = queryRequired.includes(propName) ?? false;
|
|
1120
|
-
const isObjectLike = isObjectLikeSchema(propSchema, ctx);
|
|
1121
|
-
const serialization = determineQuerySerialization(propSchema.type);
|
|
1122
|
-
if (isObjectLike) {
|
|
1123
|
-
parameters.push({
|
|
1124
|
-
name: propName,
|
|
1125
|
-
in: "query",
|
|
1126
|
-
required: isRequired,
|
|
1127
|
-
content: {
|
|
1128
|
-
"application/json": {
|
|
1129
|
-
schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
|
|
1130
|
-
}
|
|
1131
|
-
},
|
|
1132
|
-
description: `URL-encoded JSON. Example: ${propName}=${encodeURIComponent(JSON.stringify({ example: "value" }))}`
|
|
1133
|
-
});
|
|
1134
|
-
} else {
|
|
1135
|
-
const paramDef = {
|
|
1136
|
-
name: propName,
|
|
1137
|
-
in: "query",
|
|
1138
|
-
required: isRequired,
|
|
1139
|
-
schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
|
|
1140
|
-
};
|
|
1141
|
-
if (propName === "page") {
|
|
1142
|
-
paramDef.schema = { type: "integer", default: 1, minimum: 1 };
|
|
1143
|
-
} else if (propName === "pageSize") {
|
|
1144
|
-
paramDef.schema = { type: "integer", default: 10, minimum: 1 };
|
|
1145
|
-
} else if (propName === "totalItems") {
|
|
1146
|
-
paramDef.schema = { type: "integer", minimum: 0 };
|
|
1147
|
-
} else if (propName === "sort") {
|
|
1148
|
-
paramDef.schema = {
|
|
1149
|
-
oneOf: [
|
|
1150
|
-
{ type: "string" },
|
|
1151
|
-
{ type: "array", items: { type: "string" } }
|
|
1152
|
-
]
|
|
1153
|
-
};
|
|
1154
|
-
} else if (propName === "q") {
|
|
1155
|
-
paramDef.schema = { type: "string" };
|
|
1156
|
-
} else if (propName === "hasComments") {
|
|
1157
|
-
paramDef.schema = { type: "boolean" };
|
|
1158
|
-
}
|
|
1159
|
-
if (Object.keys(serialization).length > 0) {
|
|
1160
|
-
Object.assign(paramDef, serialization);
|
|
1161
|
-
}
|
|
1162
|
-
parameters.push(paramDef);
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
for (const paramIndex of operation.queryParamIndices) {
|
|
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 isObjectLike = isObjectLikeSchema(paramSchema, ctx);
|
|
1177
|
-
if (isObjectLike) {
|
|
1178
|
-
parameters.push({
|
|
1179
|
-
name: param.name,
|
|
1180
|
-
in: "query",
|
|
1181
|
-
required: !param.isOptional,
|
|
1182
|
-
content: {
|
|
1183
|
-
"application/json": {
|
|
1184
|
-
schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
});
|
|
1188
|
-
} else {
|
|
1189
|
-
const serialization = determineQuerySerialization(paramSchema.type);
|
|
1190
|
-
parameters.push({
|
|
1191
|
-
name: param.name,
|
|
1192
|
-
in: "query",
|
|
1193
|
-
required: !param.isOptional,
|
|
1194
|
-
schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema,
|
|
1195
|
-
...Object.keys(serialization).length > 0 ? serialization : {}
|
|
1196
|
-
});
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
function determineQuerySerialization(schemaType) {
|
|
1202
|
-
const typeArray = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
|
|
1203
|
-
const isArray = typeArray.includes("array");
|
|
1204
|
-
if (isArray) {
|
|
1205
|
-
return { style: "form", explode: true };
|
|
1206
|
-
}
|
|
1207
|
-
return {};
|
|
1208
|
-
}
|
|
1209
|
-
function buildHeaderParameters(operation, ctx, parameters) {
|
|
1210
|
-
if (operation.headerObjectParamIndex === null) return;
|
|
1211
|
-
const headerParam = operation.parameters[operation.headerObjectParamIndex];
|
|
1212
|
-
if (!headerParam) return;
|
|
1213
|
-
const headerSchema = typeToJsonSchema(headerParam.type, ctx);
|
|
1214
|
-
if (!headerSchema.properties) return;
|
|
1215
|
-
const headerObjProps = headerSchema.properties;
|
|
1216
|
-
for (const [propName, propSchema] of Object.entries(headerObjProps)) {
|
|
1217
|
-
const isRequired = headerSchema.required?.includes(propName) ?? false;
|
|
1218
|
-
parameters.push({
|
|
1219
|
-
name: propName,
|
|
1220
|
-
in: "header",
|
|
1221
|
-
required: isRequired,
|
|
1222
|
-
schema: propSchema
|
|
1223
|
-
});
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
function buildCookieParameters(operation, ctx, parameters) {
|
|
1227
|
-
if (operation.cookieObjectParamIndex === null) return;
|
|
1228
|
-
const cookieParam = operation.parameters[operation.cookieObjectParamIndex];
|
|
1229
|
-
if (!cookieParam) return;
|
|
1230
|
-
const cookieSchema = typeToJsonSchema(cookieParam.type, ctx);
|
|
1231
|
-
if (!cookieSchema.properties) return;
|
|
1232
|
-
const cookieObjProps = cookieSchema.properties;
|
|
1233
|
-
for (const [propName, propSchema] of Object.entries(cookieObjProps)) {
|
|
1234
|
-
const isRequired = cookieSchema.required?.includes(propName) ?? false;
|
|
1235
|
-
parameters.push({
|
|
1236
|
-
name: propName,
|
|
1237
|
-
in: "cookie",
|
|
1238
|
-
required: isRequired,
|
|
1239
|
-
schema: propSchema,
|
|
1240
|
-
style: "form",
|
|
1241
|
-
explode: true
|
|
1242
|
-
});
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
1799
|
|
|
1246
1800
|
// src/compiler/manifest/emit.ts
|
|
1247
|
-
var
|
|
1801
|
+
var import_typescript10 = __toESM(require("typescript"), 1);
|
|
1248
1802
|
function generateManifest(controllers, checker, version, validationMode = "ajv-runtime") {
|
|
1249
1803
|
const components = /* @__PURE__ */ new Map();
|
|
1250
1804
|
const ctx = {
|
|
@@ -1266,7 +1820,7 @@ function generateManifest(controllers, checker, version, validationMode = "ajv-r
|
|
|
1266
1820
|
generator: {
|
|
1267
1821
|
name: "adorn-api",
|
|
1268
1822
|
version,
|
|
1269
|
-
typescript:
|
|
1823
|
+
typescript: import_typescript10.default.version
|
|
1270
1824
|
},
|
|
1271
1825
|
schemas: {
|
|
1272
1826
|
kind: "openapi-3.1",
|
|
@@ -1316,6 +1870,23 @@ function resolveAndCollectObjectProps2(schema, components) {
|
|
|
1316
1870
|
processSchema(resolved);
|
|
1317
1871
|
return { properties, required };
|
|
1318
1872
|
}
|
|
1873
|
+
function isDeepObjectSchema2(schema, components) {
|
|
1874
|
+
const resolved = resolveSchemaRef2(schema, components);
|
|
1875
|
+
if (resolved.type === "array") {
|
|
1876
|
+
return false;
|
|
1877
|
+
}
|
|
1878
|
+
if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
|
|
1879
|
+
return true;
|
|
1880
|
+
}
|
|
1881
|
+
if (resolved.allOf) {
|
|
1882
|
+
for (const branch of resolved.allOf) {
|
|
1883
|
+
if (isDeepObjectSchema2(branch, components)) {
|
|
1884
|
+
return true;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
return false;
|
|
1889
|
+
}
|
|
1319
1890
|
function isObjectLikeSchema2(schema, components) {
|
|
1320
1891
|
const resolved = resolveSchemaRef2(schema, components);
|
|
1321
1892
|
if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
|
|
@@ -1412,6 +1983,7 @@ function buildQueryArgs(op, ctx, args) {
|
|
|
1412
1983
|
const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps2(querySchema, ctx.components);
|
|
1413
1984
|
for (const [propName, propSchema] of Object.entries(queryObjProps)) {
|
|
1414
1985
|
const isRequired = queryRequired.includes(propName) ?? false;
|
|
1986
|
+
const isDeepObject = isDeepObjectSchema2(propSchema, ctx.components);
|
|
1415
1987
|
const isObjectLike = isObjectLikeSchema2(propSchema, ctx.components);
|
|
1416
1988
|
let schemaRef = propSchema.$ref;
|
|
1417
1989
|
if (!schemaRef) {
|
|
@@ -1420,10 +1992,11 @@ function buildQueryArgs(op, ctx, args) {
|
|
|
1420
1992
|
args.query.push({
|
|
1421
1993
|
name: propName,
|
|
1422
1994
|
index: queryParam.index,
|
|
1423
|
-
required:
|
|
1995
|
+
required: isRequired,
|
|
1424
1996
|
schemaRef,
|
|
1425
1997
|
schemaType: propSchema.type,
|
|
1426
|
-
|
|
1998
|
+
serialization: isDeepObject ? { style: "deepObject", explode: true } : void 0,
|
|
1999
|
+
content: !isDeepObject && isObjectLike ? "application/json" : void 0
|
|
1427
2000
|
});
|
|
1428
2001
|
}
|
|
1429
2002
|
}
|
|
@@ -1431,6 +2004,7 @@ function buildQueryArgs(op, ctx, args) {
|
|
|
1431
2004
|
const param = op.parameters[paramIndex];
|
|
1432
2005
|
if (param) {
|
|
1433
2006
|
const paramSchema = typeToJsonSchema(param.type, ctx);
|
|
2007
|
+
const isDeepObject = isDeepObjectSchema2(paramSchema, ctx.components);
|
|
1434
2008
|
const isObjectLike = isObjectLikeSchema2(paramSchema, ctx.components);
|
|
1435
2009
|
const schemaRef = paramSchema.$ref ?? "#/components/schemas/InlineQueryParam";
|
|
1436
2010
|
args.query.push({
|
|
@@ -1439,7 +2013,8 @@ function buildQueryArgs(op, ctx, args) {
|
|
|
1439
2013
|
required: !param.isOptional,
|
|
1440
2014
|
schemaRef,
|
|
1441
2015
|
schemaType: paramSchema.type,
|
|
1442
|
-
|
|
2016
|
+
serialization: isDeepObject ? { style: "deepObject", explode: true } : void 0,
|
|
2017
|
+
content: !isDeepObject && isObjectLike ? "application/json" : void 0
|
|
1443
2018
|
});
|
|
1444
2019
|
}
|
|
1445
2020
|
}
|
|
@@ -1459,7 +2034,7 @@ function buildHeaderArgs(op, ctx, args) {
|
|
|
1459
2034
|
args.headers.push({
|
|
1460
2035
|
name: propName,
|
|
1461
2036
|
index: headerParam.index,
|
|
1462
|
-
required:
|
|
2037
|
+
required: isRequired,
|
|
1463
2038
|
schemaRef,
|
|
1464
2039
|
schemaType: propSchema.type
|
|
1465
2040
|
});
|
|
@@ -1480,7 +2055,7 @@ function buildCookieArgs(op, ctx, args) {
|
|
|
1480
2055
|
args.cookies.push({
|
|
1481
2056
|
name: propName,
|
|
1482
2057
|
index: cookieParam.index,
|
|
1483
|
-
required:
|
|
2058
|
+
required: isRequired,
|
|
1484
2059
|
schemaRef,
|
|
1485
2060
|
schemaType: propSchema.type,
|
|
1486
2061
|
serialization: { style: "form", explode: true }
|
|
@@ -1496,7 +2071,7 @@ var import_ajv = __toESM(require("ajv"), 1);
|
|
|
1496
2071
|
var import_ajv_formats = __toESM(require("ajv-formats"), 1);
|
|
1497
2072
|
var OAS_SCHEMA_ONLY = /* @__PURE__ */ new Set(["discriminator", "xml", "externalDocs", "example"]);
|
|
1498
2073
|
function sanitizeSchemaForAjv(schema) {
|
|
1499
|
-
if (schema
|
|
2074
|
+
if (schema === null || typeof schema !== "object") return schema;
|
|
1500
2075
|
if (Array.isArray(schema)) return schema.map(sanitizeSchemaForAjv);
|
|
1501
2076
|
const out = {};
|
|
1502
2077
|
for (const [k, v] of Object.entries(schema)) {
|
|
@@ -1507,7 +2082,7 @@ function sanitizeSchemaForAjv(schema) {
|
|
|
1507
2082
|
return out;
|
|
1508
2083
|
}
|
|
1509
2084
|
function rewriteComponentRefs(schema) {
|
|
1510
|
-
if (schema
|
|
2085
|
+
if (schema === null || typeof schema !== "object") return schema;
|
|
1511
2086
|
if (Array.isArray(schema)) return schema.map(rewriteComponentRefs);
|
|
1512
2087
|
if (typeof schema.$ref === "string") {
|
|
1513
2088
|
const ref = schema.$ref;
|
|
@@ -1584,16 +2159,13 @@ async function emitPrecompiledValidators(opts) {
|
|
|
1584
2159
|
`;
|
|
1585
2160
|
cjs += ` body: ${v.body ? `exports[${JSON.stringify(v.body)}]` : "undefined"},
|
|
1586
2161
|
`;
|
|
1587
|
-
cjs +=
|
|
1588
|
-
`;
|
|
2162
|
+
cjs += " response: {\n";
|
|
1589
2163
|
for (const [key, id] of Object.entries(v.response)) {
|
|
1590
2164
|
cjs += ` ${JSON.stringify(key)}: exports[${JSON.stringify(id)}],
|
|
1591
2165
|
`;
|
|
1592
2166
|
}
|
|
1593
|
-
cjs +=
|
|
1594
|
-
|
|
1595
|
-
cjs += ` },
|
|
1596
|
-
`;
|
|
2167
|
+
cjs += " }\n";
|
|
2168
|
+
cjs += " },\n";
|
|
1597
2169
|
}
|
|
1598
2170
|
cjs += "};\n";
|
|
1599
2171
|
import_node_fs2.default.writeFileSync(cjsPath, cjs, "utf8");
|
|
@@ -1628,7 +2200,6 @@ export function validateResponse(operationId, status, contentType, data) {
|
|
|
1628
2200
|
// src/compiler/cache/isStale.ts
|
|
1629
2201
|
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
1630
2202
|
var import_node_path3 = __toESM(require("path"), 1);
|
|
1631
|
-
var import_typescript7 = require("typescript");
|
|
1632
2203
|
var import_meta = {};
|
|
1633
2204
|
function readJson(p) {
|
|
1634
2205
|
try {
|
|
@@ -1686,7 +2257,7 @@ function findLockfile(startDir) {
|
|
|
1686
2257
|
for (const n of names) {
|
|
1687
2258
|
const p = import_node_path3.default.join(dir, n);
|
|
1688
2259
|
const mt = statMtimeMs(p);
|
|
1689
|
-
if (mt
|
|
2260
|
+
if (mt !== null) return { path: p, mtimeMs: mt };
|
|
1690
2261
|
}
|
|
1691
2262
|
const parent = import_node_path3.default.dirname(dir);
|
|
1692
2263
|
if (parent === dir) break;
|
|
@@ -1711,22 +2282,22 @@ async function isStale(params) {
|
|
|
1711
2282
|
const chain = collectTsconfigChain(tsconfigAbs);
|
|
1712
2283
|
for (const cfg of chain) {
|
|
1713
2284
|
const mt = statMtimeMs(cfg);
|
|
1714
|
-
if (mt
|
|
2285
|
+
if (mt === null) return { stale: true, reason: "config-missing", detail: cfg };
|
|
1715
2286
|
const cachedMt = cache.project.configFiles[cfg];
|
|
1716
|
-
if (cachedMt
|
|
2287
|
+
if (cachedMt === null || Math.abs(cachedMt - mt) > 1e-4) {
|
|
1717
2288
|
return { stale: true, reason: "config-updated", detail: cfg };
|
|
1718
2289
|
}
|
|
1719
2290
|
}
|
|
1720
2291
|
if (cache.project.lockfile?.path) {
|
|
1721
2292
|
const mt = statMtimeMs(cache.project.lockfile.path);
|
|
1722
|
-
if (mt
|
|
2293
|
+
if (mt === null) return { stale: true, reason: "lockfile-missing", detail: cache.project.lockfile.path };
|
|
1723
2294
|
if (Math.abs(cache.project.lockfile.mtimeMs - mt) > 1e-4) {
|
|
1724
2295
|
return { stale: true, reason: "lockfile-updated", detail: cache.project.lockfile.path };
|
|
1725
2296
|
}
|
|
1726
2297
|
}
|
|
1727
2298
|
for (const [file, cachedMt] of Object.entries(cache.inputs)) {
|
|
1728
2299
|
const mt = statMtimeMs(file);
|
|
1729
|
-
if (mt
|
|
2300
|
+
if (mt === null) return { stale: true, reason: "input-missing", detail: file };
|
|
1730
2301
|
if (Math.abs(cachedMt - mt) > 1e-4) return { stale: true, reason: "input-updated", detail: file };
|
|
1731
2302
|
}
|
|
1732
2303
|
return { stale: false, reason: "up-to-date" };
|
|
@@ -1735,7 +2306,7 @@ async function isStale(params) {
|
|
|
1735
2306
|
// src/compiler/cache/writeCache.ts
|
|
1736
2307
|
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
1737
2308
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
1738
|
-
var
|
|
2309
|
+
var import_typescript11 = __toESM(require("typescript"), 1);
|
|
1739
2310
|
function statMtimeMs2(p) {
|
|
1740
2311
|
return import_node_fs4.default.statSync(p).mtimeMs;
|
|
1741
2312
|
}
|
|
@@ -1768,7 +2339,7 @@ function writeCache(params) {
|
|
|
1768
2339
|
generator: {
|
|
1769
2340
|
name: "adorn-api",
|
|
1770
2341
|
version: params.adornVersion,
|
|
1771
|
-
typescript:
|
|
2342
|
+
typescript: import_typescript11.default.version
|
|
1772
2343
|
},
|
|
1773
2344
|
project: {
|
|
1774
2345
|
tsconfigPath: params.tsconfigAbs,
|
|
@@ -1781,7 +2352,7 @@ function writeCache(params) {
|
|
|
1781
2352
|
}
|
|
1782
2353
|
|
|
1783
2354
|
// src/cli.ts
|
|
1784
|
-
var
|
|
2355
|
+
var import_typescript12 = __toESM(require("typescript"), 1);
|
|
1785
2356
|
var import_node_process = __toESM(require("process"), 1);
|
|
1786
2357
|
var ADORN_VERSION = "0.1.0";
|
|
1787
2358
|
function log(msg) {
|
|
@@ -1829,7 +2400,7 @@ async function buildCommand(args) {
|
|
|
1829
2400
|
outDir: outputDir,
|
|
1830
2401
|
project: projectPath,
|
|
1831
2402
|
adornVersion: ADORN_VERSION,
|
|
1832
|
-
typescriptVersion:
|
|
2403
|
+
typescriptVersion: import_typescript12.default.version
|
|
1833
2404
|
});
|
|
1834
2405
|
if (!stale.stale) {
|
|
1835
2406
|
log("adorn-api: artifacts up-to-date");
|