This commit is contained in:
2026-03-03 15:23:00 +00:00
parent 5e3726de39
commit 8e223bfbec
3689 changed files with 955330 additions and 1011 deletions

8
node_modules/@tsoa/cli/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright (c) 2016 Luke Autry
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

3
node_modules/@tsoa/cli/README.MD generated vendored Normal file
View File

@@ -0,0 +1,3 @@
This package contains the tsoa build time components.
For a comprehensive Readme, please refer to [the main readme](https://github.com/lukeautry/tsoa#readme).

44
node_modules/@tsoa/cli/dist/cli.d.ts generated vendored Normal file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/env node
import { Config, RoutesConfig, SpecConfig, Tsoa } from '@tsoa/runtime';
import { AbstractRouteGenerator } from './routeGeneration/routeGenerator';
export interface ExtendedSpecConfig extends SpecConfig {
entryFile: Config['entryFile'];
noImplicitAdditionalProperties: Exclude<Config['noImplicitAdditionalProperties'], undefined>;
controllerPathGlobs?: Config['controllerPathGlobs'];
}
export declare const validateSpecConfig: (config: Config) => Promise<ExtendedSpecConfig>;
type RouteGeneratorImpl = new (metadata: Tsoa.Metadata, options: ExtendedRoutesConfig) => AbstractRouteGenerator<any>;
export interface ExtendedRoutesConfig extends RoutesConfig {
entryFile: Config['entryFile'];
noImplicitAdditionalProperties: Exclude<Config['noImplicitAdditionalProperties'], undefined>;
bodyCoercion: Exclude<RoutesConfig['bodyCoercion'], undefined>;
controllerPathGlobs?: Config['controllerPathGlobs'];
multerOpts?: Config['multerOpts'];
rootSecurity?: Config['spec']['rootSecurity'];
routeGenerator?: string | RouteGeneratorImpl;
}
export interface ConfigArgs {
basePath?: string;
configuration?: string | Config;
}
export interface SwaggerArgs extends ConfigArgs {
host?: string;
json?: boolean;
yaml?: boolean;
}
export declare function runCLI(): {
[x: string]: unknown;
_: (string | number)[];
$0: string;
} | Promise<{
[x: string]: unknown;
_: (string | number)[];
$0: string;
}>;
export declare function generateSpecAndRoutes(args: SwaggerArgs, metadata?: Tsoa.Metadata): Promise<Tsoa.Metadata>;
export type RouteGeneratorModule<Config extends ExtendedRoutesConfig> = {
default: new (metadata: Tsoa.Metadata, routesConfig: Config) => {
GenerateCustomRoutes: () => Promise<void>;
};
};
export {};

356
node_modules/@tsoa/cli/dist/cli.js generated vendored Executable file
View File

@@ -0,0 +1,356 @@
#!/usr/bin/env node
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateSpecConfig = void 0;
exports.runCLI = runCLI;
exports.generateSpecAndRoutes = generateSpecAndRoutes;
const yaml_1 = __importDefault(require("yaml"));
const yargs_1 = __importDefault(require("yargs"));
const helpers_1 = require("yargs/helpers");
const metadataGenerator_1 = require("./metadataGeneration/metadataGenerator");
const generate_routes_1 = require("./module/generate-routes");
const generate_spec_1 = require("./module/generate-spec");
const fs_1 = require("./utils/fs");
const node_path_1 = require("node:path");
const workingDir = process.cwd();
let packageJson;
const getPackageJsonValue = async (key, defaultValue = '') => {
if (!packageJson) {
try {
const packageJsonRaw = await (0, fs_1.fsReadFile)(`${workingDir}/package.json`);
packageJson = JSON.parse(packageJsonRaw.toString('utf8'));
}
catch (err) {
return defaultValue;
}
}
return packageJson[key] || '';
};
const nameDefault = () => getPackageJsonValue('name', 'TSOA');
const versionDefault = () => getPackageJsonValue('version', '1.0.0');
const descriptionDefault = () => getPackageJsonValue('description', 'Build swagger-compliant REST APIs using TypeScript and Node');
const licenseDefault = () => getPackageJsonValue('license', 'MIT');
const determineNoImplicitAdditionalSetting = (noImplicitAdditionalProperties) => {
if (noImplicitAdditionalProperties === 'silently-remove-extras' || noImplicitAdditionalProperties === 'throw-on-extras' || noImplicitAdditionalProperties === 'ignore') {
return noImplicitAdditionalProperties;
}
else {
return 'ignore';
}
};
const authorInformation = getPackageJsonValue('author', 'unknown');
const isYamlExtension = (extension) => extension === '.yaml' || extension === '.yml';
const isJsExtension = (extension) => extension === '.js' || extension === '.cjs';
const getConfig = async (configPath = 'tsoa.json') => {
let config;
const ext = (0, node_path_1.extname)(configPath);
const configFullPath = (0, node_path_1.isAbsolute)(configPath) ? configPath : `${workingDir}/${configPath}`;
try {
if (isYamlExtension(ext)) {
const configRaw = await (0, fs_1.fsReadFile)(configFullPath);
config = yaml_1.default.parse(configRaw.toString('utf8'));
}
else if (isJsExtension(ext)) {
config = await Promise.resolve(`${configFullPath}`).then(s => __importStar(require(s)));
}
else {
const configRaw = await (0, fs_1.fsReadFile)(configFullPath);
config = JSON.parse(configRaw.toString('utf8'));
}
}
catch (err) {
if (!(err instanceof Error)) {
console.error(err);
throw Error(`Unhandled error encountered loading '${configPath}': ${String(err)}`);
}
else if ('code' in err && err.code === 'MODULE_NOT_FOUND') {
throw Error(`No config file found at '${configPath}'`);
}
else if (err.name === 'SyntaxError') {
console.error(err);
const errorType = isJsExtension(ext) ? 'JS' : 'JSON';
throw Error(`Invalid ${errorType} syntax in config at '${configPath}': ${err.message}`);
}
else {
console.error(err);
throw Error(`Unhandled error encountered loading '${configPath}': ${err.message}`);
}
}
return config;
};
const resolveConfig = async (config) => {
return typeof config === 'object' ? config : getConfig(config);
};
const validateCompilerOptions = (config) => {
return (config || {});
};
const validateSpecConfig = async (config) => {
if (!config.spec) {
throw new Error('Missing spec: configuration must contain spec. Spec used to be called swagger in previous versions of tsoa.');
}
if (!config.spec.outputDirectory) {
throw new Error('Missing outputDirectory: configuration must contain output directory.');
}
if (!config.entryFile && (!config.controllerPathGlobs || !config.controllerPathGlobs.length)) {
throw new Error('Missing entryFile and controllerPathGlobs: Configuration must contain an entry point file or controller path globals.');
}
if (!!config.entryFile && !(await (0, fs_1.fsExists)(config.entryFile))) {
throw new Error(`EntryFile not found: ${config.entryFile} - please check your tsoa config.`);
}
config.spec.version = config.spec.version || (await versionDefault());
config.spec.specVersion = config.spec.specVersion || 2;
const supportedVersions = [2, 3, 3.1];
if (!supportedVersions.includes(config.spec.specVersion)) {
throw new Error(`Unsupported Spec version: ${config.spec.specVersion}.`);
}
if (config.spec.spec && !['immediate', 'recursive', 'deepmerge', undefined].includes(config.spec.specMerging)) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
throw new Error(`Invalid specMerging config: ${config.spec.specMerging}`);
}
const noImplicitAdditionalProperties = determineNoImplicitAdditionalSetting(config.noImplicitAdditionalProperties);
config.spec.name = config.spec.name || (await nameDefault());
config.spec.description = config.spec.description || (await descriptionDefault());
config.spec.license = config.spec.license || (await licenseDefault());
config.spec.basePath = config.spec.basePath || '/';
// defaults to template that may generate non-unique operation ids.
// @see https://github.com/lukeautry/tsoa/issues/1005
config.spec.operationIdTemplate = config.spec.operationIdTemplate || '{{titleCase method.name}}';
if (!config.spec.contact) {
config.spec.contact = {};
}
const author = await authorInformation;
if (typeof author === 'string') {
const contact = /^([^<(]*)?\s*(?:<([^>(]*)>)?\s*(?:\(([^)]*)\)|$)/m.exec(author);
config.spec.contact.name = config.spec.contact.name || contact?.[1];
config.spec.contact.email = config.spec.contact.email || contact?.[2];
config.spec.contact.url = config.spec.contact.url || contact?.[3];
}
else if (typeof author === 'object') {
config.spec.contact.name = config.spec.contact.name || author?.name;
config.spec.contact.email = config.spec.contact.email || author?.email;
config.spec.contact.url = config.spec.contact.url || author?.url;
}
if (!config.defaultNumberType) {
config.defaultNumberType = 'double';
}
if (config.spec.rootSecurity) {
if (!Array.isArray(config.spec.rootSecurity)) {
throw new Error('spec.rootSecurity must be an array');
}
if (config.spec.rootSecurity) {
const ok = config.spec.rootSecurity.every(security => typeof security === 'object' && security !== null && Object.values(security).every(scope => Array.isArray(scope)));
if (!ok) {
throw new Error('spec.rootSecurity must be an array of objects whose keys are security scheme names and values are arrays of scopes');
}
}
}
return {
...config.spec,
noImplicitAdditionalProperties,
entryFile: config.entryFile,
controllerPathGlobs: config.controllerPathGlobs,
};
};
exports.validateSpecConfig = validateSpecConfig;
const validateRoutesConfig = async (config) => {
if (!config.entryFile && (!config.controllerPathGlobs || !config.controllerPathGlobs.length)) {
throw new Error('Missing entryFile and controllerPathGlobs: Configuration must contain an entry point file or controller path globals.');
}
if (!!config.entryFile && !(await (0, fs_1.fsExists)(config.entryFile))) {
throw new Error(`EntryFile not found: ${config.entryFile} - Please check your tsoa config.`);
}
if (!config.routes.routesDir) {
throw new Error('Missing routesDir: Configuration must contain a routes file output directory.');
}
if (config.routes.authenticationModule && !((await (0, fs_1.fsExists)(config.routes.authenticationModule)) || (await (0, fs_1.fsExists)(config.routes.authenticationModule + '.ts')))) {
throw new Error(`No authenticationModule file found at '${config.routes.authenticationModule}'`);
}
if (config.routes.iocModule && !((await (0, fs_1.fsExists)(config.routes.iocModule)) || (await (0, fs_1.fsExists)(config.routes.iocModule + '.ts')))) {
throw new Error(`No iocModule file found at '${config.routes.iocModule}'`);
}
const noImplicitAdditionalProperties = determineNoImplicitAdditionalSetting(config.noImplicitAdditionalProperties);
const bodyCoercion = config.routes.bodyCoercion ?? true;
config.routes.basePath = config.routes.basePath || '/';
return {
...config.routes,
entryFile: config.entryFile,
noImplicitAdditionalProperties,
bodyCoercion,
controllerPathGlobs: config.controllerPathGlobs,
multerOpts: config.multerOpts,
rootSecurity: config.spec.rootSecurity,
};
};
const configurationArgs = {
alias: 'c',
describe: 'tsoa configuration file; default is tsoa.json in the working directory',
required: false,
string: true,
};
const hostArgs = {
describe: 'API host',
required: false,
string: true,
};
const basePathArgs = {
describe: 'Base API path',
required: false,
string: true,
};
const yarmlArgs = {
describe: 'Swagger spec yaml format',
required: false,
boolean: true,
};
const jsonArgs = {
describe: 'Swagger spec json format',
required: false,
boolean: true,
};
function runCLI() {
return (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
.usage('Usage: $0 <command> [options]')
.command('spec', 'Generate OpenAPI spec', {
basePath: basePathArgs,
configuration: configurationArgs,
host: hostArgs,
json: jsonArgs,
yaml: yarmlArgs,
}, args => SpecGenerator(args))
.command('routes', 'Generate routes', {
basePath: basePathArgs,
configuration: configurationArgs,
}, args => routeGenerator(args))
.command('spec-and-routes', 'Generate OpenAPI spec and routes', {
basePath: basePathArgs,
configuration: configurationArgs,
host: hostArgs,
json: jsonArgs,
yaml: yarmlArgs,
}, args => void generateSpecAndRoutes(args))
.demandCommand(1, 1, 'Must provide a valid command.')
.help('help')
.alias('help', 'h')
.parse();
}
if (require.main === module) {
void (async () => {
try {
await runCLI();
}
catch (err) {
// eslint-disable-next-line no-console
console.error('tsoa cli error:\n', err);
process.exit(1);
}
})();
}
async function SpecGenerator(args) {
try {
const config = await resolveConfig(args.configuration);
if (args.basePath) {
config.spec.basePath = args.basePath;
}
if (args.host) {
config.spec.host = args.host;
}
if (args.yaml) {
config.spec.yaml = args.yaml;
}
if (args.json) {
config.spec.yaml = false;
}
const compilerOptions = validateCompilerOptions(config.compilerOptions);
const swaggerConfig = await (0, exports.validateSpecConfig)(config);
await (0, generate_spec_1.generateSpec)(swaggerConfig, compilerOptions, config.ignore);
}
catch (err) {
// eslint-disable-next-line no-console
console.error('Generate swagger error.\n', err);
process.exit(1);
}
}
async function routeGenerator(args) {
try {
const config = await resolveConfig(args.configuration);
if (args.basePath) {
config.routes.basePath = args.basePath;
}
const compilerOptions = validateCompilerOptions(config.compilerOptions);
const routesConfig = await validateRoutesConfig(config);
await (0, generate_routes_1.generateRoutes)(routesConfig, compilerOptions, config.ignore);
}
catch (err) {
// eslint-disable-next-line no-console
console.error('Generate routes error.\n', err);
process.exit(1);
}
}
async function generateSpecAndRoutes(args, metadata) {
try {
const config = await resolveConfig(args.configuration);
if (args.basePath) {
config.spec.basePath = args.basePath;
}
if (args.host) {
config.spec.host = args.host;
}
if (args.yaml) {
config.spec.yaml = args.yaml;
}
if (args.json) {
config.spec.yaml = false;
}
const compilerOptions = validateCompilerOptions(config.compilerOptions);
const routesConfig = await validateRoutesConfig(config);
const swaggerConfig = await (0, exports.validateSpecConfig)(config);
if (!metadata) {
metadata = new metadataGenerator_1.MetadataGenerator(config.entryFile, compilerOptions, config.ignore, config.controllerPathGlobs, config.spec.rootSecurity, config.defaultNumberType, config.routes.esm).Generate();
}
await Promise.all([(0, generate_routes_1.generateRoutes)(routesConfig, compilerOptions, config.ignore, metadata), (0, generate_spec_1.generateSpec)(swaggerConfig, compilerOptions, config.ignore, metadata)]);
return metadata;
}
catch (err) {
// eslint-disable-next-line no-console
console.error('Generate routes error.\n', err);
process.exit(1);
throw err;
}
}
//# sourceMappingURL=cli.js.map

1
node_modules/@tsoa/cli/dist/cli.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

6
node_modules/@tsoa/cli/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,6 @@
export * from './module/generate-spec';
export * from './module/generate-routes';
export { AbstractRouteGenerator } from './routeGeneration/routeGenerator';
export { DefaultRouteGenerator } from './routeGeneration/defaultRouteGenerator';
export { Config } from '@tsoa/runtime';
export * from './cli';

25
node_modules/@tsoa/cli/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,25 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultRouteGenerator = exports.AbstractRouteGenerator = void 0;
__exportStar(require("./module/generate-spec"), exports);
__exportStar(require("./module/generate-routes"), exports);
var routeGenerator_1 = require("./routeGeneration/routeGenerator");
Object.defineProperty(exports, "AbstractRouteGenerator", { enumerable: true, get: function () { return routeGenerator_1.AbstractRouteGenerator; } });
var defaultRouteGenerator_1 = require("./routeGeneration/defaultRouteGenerator");
Object.defineProperty(exports, "DefaultRouteGenerator", { enumerable: true, get: function () { return defaultRouteGenerator_1.DefaultRouteGenerator; } });
__exportStar(require("./cli"), exports);
//# sourceMappingURL=index.js.map

1
node_modules/@tsoa/cli/dist/index.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,yDAAuC;AACvC,2DAAyC;AACzC,mEAA0E;AAAjE,wHAAA,sBAAsB,OAAA;AAC/B,iFAAgF;AAAvE,8HAAA,qBAAqB,OAAA;AAE9B,wCAAsB"}

View File

@@ -0,0 +1,24 @@
import { MetadataGenerator } from './metadataGenerator';
import { Tsoa } from '@tsoa/runtime';
import { type ClassDeclaration } from 'typescript';
export declare class ControllerGenerator {
private readonly node;
private readonly current;
private readonly parentSecurity;
private readonly path?;
private readonly tags?;
private readonly security?;
private readonly isHidden?;
private readonly commonResponses;
private readonly produces?;
constructor(node: ClassDeclaration, current: MetadataGenerator, parentSecurity?: Tsoa.Security[]);
IsValid(): boolean;
Generate(): Tsoa.Controller;
private buildMethods;
private getPath;
private getCommonResponses;
private getTags;
private getSecurity;
private getIsHidden;
private getProduces;
}

View File

@@ -0,0 +1,123 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ControllerGenerator = void 0;
const decoratorUtils_1 = require("./../utils/decoratorUtils");
const exceptions_1 = require("./exceptions");
const methodGenerator_1 = require("./methodGenerator");
const typeResolver_1 = require("./typeResolver");
const headerTypeHelpers_1 = require("../utils/headerTypeHelpers");
const typescript_1 = require("typescript");
class ControllerGenerator {
constructor(node, current, parentSecurity = []) {
this.node = node;
this.current = current;
this.parentSecurity = parentSecurity;
this.path = this.getPath();
this.tags = this.getTags();
this.security = this.getSecurity();
this.isHidden = this.getIsHidden();
this.commonResponses = this.getCommonResponses();
this.produces = this.getProduces();
}
IsValid() {
return !!this.path || this.path === '';
}
Generate() {
if (!this.node.parent) {
throw new exceptions_1.GenerateMetadataError("Controller node doesn't have a valid parent source file.");
}
if (!this.node.name) {
throw new exceptions_1.GenerateMetadataError("Controller node doesn't have a valid name.");
}
const sourceFile = this.node.parent.getSourceFile();
return {
location: sourceFile.fileName,
methods: this.buildMethods(),
name: this.node.name.text,
path: this.path || '',
produces: this.produces,
};
}
buildMethods() {
return this.node.members
.filter(typescript_1.isMethodDeclaration)
.map(m => new methodGenerator_1.MethodGenerator(m, this.current, this.commonResponses, this.path, this.tags, this.security, this.isHidden))
.filter(generator => generator.IsValid())
.map(generator => generator.Generate());
}
getPath() {
const decorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => identifier.text === 'Route');
if (!decorators || !decorators.length) {
return;
}
if (decorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Route decorator allowed in '${this.node.name.text}' class.`);
}
const decorator = decorators[0];
const expression = decorator.parent;
const decoratorArgument = expression.arguments[0];
return decoratorArgument ? `${decoratorArgument.text}` : '';
}
getCommonResponses() {
const decorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => identifier.text === 'Response');
if (!decorators || !decorators.length) {
return [];
}
return decorators.map(decorator => {
const expression = decorator.parent;
const [name, description, example] = (0, decoratorUtils_1.getDecoratorValues)(decorator, this.current.typeChecker);
if (!name) {
throw new exceptions_1.GenerateMetadataError(`Controller's responses should have an explicit name.`);
}
return {
description: description || '',
examples: example === undefined ? undefined : [example],
name,
schema: expression.typeArguments && expression.typeArguments.length > 0 && !this.isHidden ? new typeResolver_1.TypeResolver(expression.typeArguments[0], this.current).resolve() : undefined,
headers: (0, headerTypeHelpers_1.getHeaderType)(expression.typeArguments, 1, this.current),
};
});
}
getTags() {
const decorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => identifier.text === 'Tags');
if (!decorators || !decorators.length) {
return;
}
if (decorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Tags decorator allowed in '${this.node.name.text}' class.`);
}
const decorator = decorators[0];
const expression = decorator.parent;
return expression.arguments.map((a) => a.text);
}
getSecurity() {
const noSecurityDecorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => identifier.text === 'NoSecurity');
const securityDecorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => identifier.text === 'Security');
if (noSecurityDecorators?.length && securityDecorators?.length) {
throw new exceptions_1.GenerateMetadataError(`NoSecurity decorator cannot be used in conjunction with Security decorator in '${this.node.name.text}' class.`);
}
if (noSecurityDecorators?.length) {
return [];
}
if (!securityDecorators || !securityDecorators.length) {
return this.parentSecurity;
}
return securityDecorators.map(d => (0, decoratorUtils_1.getSecurites)(d, this.current.typeChecker));
}
getIsHidden() {
const hiddenDecorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => identifier.text === 'Hidden');
if (!hiddenDecorators || !hiddenDecorators.length) {
return false;
}
if (hiddenDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Hidden decorator allowed in '${this.node.name.text}' class.`);
}
return true;
}
getProduces() {
const produces = (0, decoratorUtils_1.getProduces)(this.node, this.current.typeChecker);
return produces.length ? produces : undefined;
}
}
exports.ControllerGenerator = ControllerGenerator;
//# sourceMappingURL=controllerGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,13 @@
import { Node, TypeNode } from 'typescript';
export declare class GenerateMetadataError extends Error {
constructor(message?: string, node?: Node | TypeNode, onlyCurrent?: boolean);
}
export declare class GenerateMetaDataWarning {
private message;
private node;
private onlyCurrent;
constructor(message: string, node: Node | TypeNode, onlyCurrent?: boolean);
toString(): string;
}
export declare function prettyLocationOfNode(node: Node | TypeNode): string;
export declare function prettyTroubleCause(node: Node | TypeNode, onlyCurrent?: boolean): string;

View File

@@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GenerateMetaDataWarning = exports.GenerateMetadataError = void 0;
exports.prettyLocationOfNode = prettyLocationOfNode;
exports.prettyTroubleCause = prettyTroubleCause;
const path_1 = require("path");
class GenerateMetadataError extends Error {
constructor(message, node, onlyCurrent = false) {
super(message);
if (node) {
this.message = `${message}\n${prettyLocationOfNode(node)}\n${prettyTroubleCause(node, onlyCurrent)}`;
}
}
}
exports.GenerateMetadataError = GenerateMetadataError;
class GenerateMetaDataWarning {
constructor(message, node, onlyCurrent = false) {
this.message = message;
this.node = node;
this.onlyCurrent = onlyCurrent;
}
toString() {
return `Warning: ${this.message}\n${prettyLocationOfNode(this.node)}\n${prettyTroubleCause(this.node, this.onlyCurrent)}`;
}
}
exports.GenerateMetaDataWarning = GenerateMetaDataWarning;
function prettyLocationOfNode(node) {
const sourceFile = node.getSourceFile();
if (sourceFile) {
const token = node.getFirstToken() || node.parent.getFirstToken();
const start = token ? `:${sourceFile.getLineAndCharacterOfPosition(token.getStart()).line + 1}` : '';
const end = token ? `:${sourceFile.getLineAndCharacterOfPosition(token.getEnd()).line + 1}` : '';
const normalizedPath = (0, path_1.normalize)(`${sourceFile.fileName}${start}${end}`);
return `At: ${normalizedPath}.`;
}
else {
return `At unknown position...`;
}
}
function prettyTroubleCause(node, onlyCurrent = false) {
let name;
if (onlyCurrent || !node.parent) {
name = node.pos !== -1 && node.parent ? node.getText() : node.name?.text || '<unknown name>';
}
else {
name = node.parent.pos !== -1 ? node.parent.getText() : node.parent.name?.text || '<unknown name>';
}
return `This was caused by '${name}'`;
}
//# sourceMappingURL=exceptions.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"exceptions.js","sourceRoot":"","sources":["../../src/metadataGeneration/exceptions.ts"],"names":[],"mappings":";;;AAwBA,oDAWC;AAED,gDAQC;AA7CD,+BAAiC;AAGjC,MAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAgB,EAAE,IAAsB,EAAE,WAAW,GAAG,KAAK;QACvE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,GAAG,OAAQ,KAAK,oBAAoB,CAAC,IAAI,CAAC,KAAK,kBAAkB,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;QACxG,CAAC;IACH,CAAC;CACF;AAPD,sDAOC;AAED,MAAa,uBAAuB;IAClC,YACU,OAAe,EACf,IAAqB,EACrB,cAAc,KAAK;QAFnB,YAAO,GAAP,OAAO,CAAQ;QACf,SAAI,GAAJ,IAAI,CAAiB;QACrB,gBAAW,GAAX,WAAW,CAAQ;IAC1B,CAAC;IAEJ,QAAQ;QACN,OAAO,YAAY,IAAI,CAAC,OAAO,KAAK,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,kBAAkB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;IAC5H,CAAC;CACF;AAVD,0DAUC;AAED,SAAgB,oBAAoB,CAAC,IAAqB;IACxD,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;IACxC,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;QAClE,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,6BAA6B,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrG,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,6BAA6B,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjG,MAAM,cAAc,GAAG,IAAA,gBAAS,EAAC,GAAG,UAAU,CAAC,QAAQ,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC,CAAC;QACzE,OAAO,OAAO,cAAc,GAAG,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,OAAO,wBAAwB,CAAC;IAClC,CAAC;AACH,CAAC;AAED,SAAgB,kBAAkB,CAAC,IAAqB,EAAE,WAAW,GAAG,KAAK;IAC3E,IAAI,IAAY,CAAC;IACjB,IAAI,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,IAAI,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAE,IAAY,CAAC,IAAI,EAAE,IAAI,IAAI,gBAAgB,CAAC;IACxG,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAE,IAAY,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,gBAAgB,CAAC;IAC9G,CAAC;IACD,OAAO,uBAAuB,IAAI,GAAG,CAAC;AACxC,CAAC"}

View File

@@ -0,0 +1,5 @@
import * as ts from 'typescript';
import { MetadataGenerator } from './metadataGenerator';
import { Tsoa } from '@tsoa/runtime';
export declare function getExtensions(decorators: ts.Identifier[], metadataGenerator: MetadataGenerator): Tsoa.Extension[];
export declare function getExtensionsFromJSDocComments(comments: string[]): Tsoa.Extension[];

View File

@@ -0,0 +1,85 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getExtensions = getExtensions;
exports.getExtensionsFromJSDocComments = getExtensionsFromJSDocComments;
const ts = __importStar(require("typescript"));
const initializer_value_1 = require("./initializer-value");
const jsonUtils_1 = require("../utils/jsonUtils");
function getExtensions(decorators, metadataGenerator) {
const extensions = decorators.map(extensionDecorator => {
if (!ts.isCallExpression(extensionDecorator.parent)) {
throw new Error('The parent of the @Extension is not a CallExpression. Are you using it in the right place?');
}
const [decoratorKeyArg, decoratorValueArg] = extensionDecorator.parent.arguments;
if (!ts.isStringLiteral(decoratorKeyArg) && !ts.isIdentifier(decoratorKeyArg)) {
throw new Error('The first argument of @Extension must be a string');
}
const attributeKey = ts.isIdentifier(decoratorKeyArg) ? (0, initializer_value_1.getInitializerValue)(decoratorKeyArg, metadataGenerator.typeChecker) : decoratorKeyArg.text;
if (typeof attributeKey !== 'string') {
throw new Error('The first argument of @Extension must be a string');
}
if (!decoratorValueArg) {
throw new Error(`Extension '${attributeKey}' must contain a value`);
}
assertValidExtensionKey(attributeKey);
const attributeValue = (0, initializer_value_1.getInitializerValue)(decoratorValueArg, metadataGenerator.typeChecker);
if (!(0, initializer_value_1.isNonUndefinedInitializerValue)(attributeValue)) {
throw new Error(`Extension '${attributeKey}' cannot have an undefined initializer value`);
}
return { key: attributeKey, value: attributeValue };
});
return extensions;
}
function getExtensionsFromJSDocComments(comments) {
const extensions = [];
comments.forEach(comment => {
const extensionData = (0, jsonUtils_1.safeFromJson)(comment);
if (extensionData) {
const keys = Object.keys(extensionData);
keys.forEach(key => {
assertValidExtensionKey(key);
extensions.push({ key: key, value: extensionData[key] });
});
}
});
return extensions;
}
function assertValidExtensionKey(key) {
if (!key.startsWith('x-')) {
throw new Error('Extensions must begin with "x-" to be valid. Please see the following link for more information: https://swagger.io/docs/specification/openapi-extensions/');
}
}
//# sourceMappingURL=extension.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../../src/metadataGeneration/extension.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,sCAgCC;AAED,wEAcC;AAtDD,+CAAiC;AACjC,2DAA0F;AAG1F,kDAAkD;AAElD,SAAgB,aAAa,CAAC,UAA2B,EAAE,iBAAoC;IAC7F,MAAM,UAAU,GAAqB,UAAU,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE;QACvE,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CAAC,4FAA4F,CAAC,CAAC;QAChH,CAAC;QAED,MAAM,CAAC,eAAe,EAAE,iBAAiB,CAAC,GAAG,kBAAkB,CAAC,MAAM,CAAC,SAAS,CAAC;QAEjF,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,IAAA,uCAAmB,EAAC,eAAe,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC;QAEnJ,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;QAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,cAAc,YAAY,wBAAwB,CAAC,CAAC;QACtE,CAAC;QAED,uBAAuB,CAAC,YAAY,CAAC,CAAC;QAEtC,MAAM,cAAc,GAAG,IAAA,uCAAmB,EAAC,iBAAiB,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC;QAC7F,IAAI,CAAC,IAAA,kDAA8B,EAAC,cAAc,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CAAC,cAAc,YAAY,8CAA8C,CAAC,CAAC;QAC5F,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAgB,8BAA8B,CAAC,QAAkB;IAC/D,MAAM,UAAU,GAAqB,EAAE,CAAC;IACxC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QACzB,MAAM,aAAa,GAAG,IAAA,wBAAY,EAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACxC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;gBACjB,uBAAuB,CAAC,GAAG,CAAC,CAAC;gBAC7B,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC3D,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,uBAAuB,CAAC,GAAW;IAC1C,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,4JAA4J,CAAC,CAAC;IAChL,CAAC;AACH,CAAC"}

View File

@@ -0,0 +1,6 @@
import * as ts from 'typescript';
import { Tsoa } from '@tsoa/runtime';
export type InitializerValue = string | number | boolean | undefined | null | InitializerValue[];
export type DefinedInitializerValue = string | number | boolean | null | DefinedInitializerValue[];
export declare function isNonUndefinedInitializerValue(value: InitializerValue): value is DefinedInitializerValue;
export declare function getInitializerValue(initializer?: ts.Expression | ts.ImportSpecifier, typeChecker?: ts.TypeChecker, type?: Tsoa.Type): InitializerValue | DefinedInitializerValue;

View File

@@ -0,0 +1,154 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.isNonUndefinedInitializerValue = isNonUndefinedInitializerValue;
exports.getInitializerValue = getInitializerValue;
const ts = __importStar(require("typescript"));
const hasInitializer = (node) => Object.prototype.hasOwnProperty.call(node, 'initializer');
const extractInitializer = (decl) => (decl && hasInitializer(decl) && decl.initializer) || undefined;
const extractImportSpecifier = (symbol) => (symbol?.declarations && symbol.declarations.length > 0 && ts.isImportSpecifier(symbol.declarations[0]) && symbol.declarations[0]) || undefined;
const isIterable = (obj) => obj != null && typeof obj[Symbol.iterator] === 'function';
function isNonUndefinedInitializerValue(value) {
if (Array.isArray(value)) {
return value.every(isNonUndefinedInitializerValue);
}
else {
return value !== undefined;
}
}
function getInitializerValue(initializer, typeChecker, type) {
if (!initializer || !typeChecker) {
return;
}
switch (initializer.kind) {
case ts.SyntaxKind.ArrayLiteralExpression: {
const arrayLiteral = initializer;
return arrayLiteral.elements.reduce((acc, element) => {
if (ts.isSpreadElement(element)) {
const spreadValue = getInitializerValue(element.expression, typeChecker);
if (spreadValue && isIterable(spreadValue)) {
return acc.concat(...spreadValue);
}
else {
throw new Error(`${typeof spreadValue} is not iterable`);
}
}
else {
acc.push(getInitializerValue(element, typeChecker));
}
return acc;
}, []);
}
case ts.SyntaxKind.StringLiteral:
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
return initializer.text;
case ts.SyntaxKind.TrueKeyword:
return true;
case ts.SyntaxKind.FalseKeyword:
return false;
case ts.SyntaxKind.PrefixUnaryExpression: {
const prefixUnary = initializer;
switch (prefixUnary.operator) {
case ts.SyntaxKind.PlusToken:
return Number(prefixUnary.operand.text);
case ts.SyntaxKind.MinusToken:
return Number(`-${prefixUnary.operand.text}`);
default:
throw new Error(`Unsupport prefix operator token: ${prefixUnary.operator}`);
}
}
case ts.SyntaxKind.NumberKeyword:
case ts.SyntaxKind.FirstLiteralToken:
return Number(initializer.text);
case ts.SyntaxKind.NewExpression: {
const newExpression = initializer;
const ident = newExpression.expression;
if (ident.text === 'Date') {
let date = new Date();
if (newExpression.arguments) {
const newArguments = newExpression.arguments.filter(args => args.kind !== undefined);
const argsValue = newArguments.map(args => getInitializerValue(args, typeChecker));
if (argsValue.length > 0) {
date = new Date(argsValue);
}
}
const dateString = date.toISOString();
if (type && type.dataType === 'date') {
return dateString.split('T')[0];
}
return dateString;
}
return;
}
case ts.SyntaxKind.NullKeyword:
return null;
case ts.SyntaxKind.ObjectLiteralExpression: {
const objectLiteral = initializer;
const nestedObject = {};
objectLiteral.properties.forEach((p) => {
if (ts.isSpreadAssignment(p)) {
const spreadValue = getInitializerValue(p.expression, typeChecker);
if (spreadValue) {
if (typeof spreadValue === 'object') {
Object.assign(nestedObject, spreadValue);
}
else {
throw new Error(`Spread types may only be created from object types.`);
}
}
}
else {
nestedObject[p.name.text] = getInitializerValue(p.initializer, typeChecker);
}
});
return nestedObject;
}
case ts.SyntaxKind.ImportSpecifier: {
const importSpecifier = initializer;
const importSymbol = typeChecker.getSymbolAtLocation(importSpecifier.name);
if (!importSymbol)
return;
const aliasedSymbol = typeChecker.getAliasedSymbol(importSymbol);
const declarations = aliasedSymbol.getDeclarations();
const declaration = declarations && declarations.length > 0 ? declarations[0] : undefined;
return getInitializerValue(extractInitializer(declaration), typeChecker);
}
default: {
const symbol = typeChecker.getSymbolAtLocation(initializer);
return getInitializerValue(extractInitializer(symbol?.valueDeclaration) || extractImportSpecifier(symbol), typeChecker);
}
}
}
//# sourceMappingURL=initializer-value.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"initializer-value.js","sourceRoot":"","sources":["../../src/metadataGeneration/initializer-value.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,29 @@
import { Config, Tsoa } from '@tsoa/runtime';
import { type ClassDeclaration, type CompilerOptions, type TypeChecker } from 'typescript';
export declare class MetadataGenerator {
private readonly compilerOptions?;
private readonly ignorePaths?;
private readonly rootSecurity;
readonly defaultNumberType: NonNullable<Config['defaultNumberType']>;
readonly controllerNodes: ClassDeclaration[];
readonly typeChecker: TypeChecker;
private readonly program;
private referenceTypeMap;
private modelDefinitionPosMap;
private expressionOrigNameMap;
constructor(entryFile: string, compilerOptions?: CompilerOptions | undefined, ignorePaths?: string[] | undefined, controllers?: string[], rootSecurity?: Tsoa.Security[], defaultNumberType?: NonNullable<Config['defaultNumberType']>, esm?: boolean);
Generate(): Tsoa.Metadata;
private setProgramToDynamicControllersFiles;
private extractNodeFromProgramSourceFiles;
private checkForMethodSignatureDuplicates;
private checkForPathParamSignatureDuplicates;
TypeChecker(): TypeChecker;
AddReferenceType(referenceType: Tsoa.ReferenceType): void;
GetReferenceType(refName: string): Tsoa.ReferenceType;
CheckModelUnicity(refName: string, positions: Array<{
fileName: string;
pos: number;
}>): void;
CheckExpressionUnicity(formattedRefName: string, refName: string): void;
private buildControllers;
}

View File

@@ -0,0 +1,214 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MetadataGenerator = void 0;
const minimatch_1 = require("minimatch");
const typescript_1 = require("typescript");
const decoratorUtils_1 = require("../utils/decoratorUtils");
const importClassesFromDirectories_1 = require("../utils/importClassesFromDirectories");
const controllerGenerator_1 = require("./controllerGenerator");
const exceptions_1 = require("./exceptions");
const typeResolver_1 = require("./typeResolver");
class MetadataGenerator {
constructor(entryFile, compilerOptions, ignorePaths, controllers, rootSecurity = [], defaultNumberType = 'double', esm = false) {
this.compilerOptions = compilerOptions;
this.ignorePaths = ignorePaths;
this.rootSecurity = rootSecurity;
this.defaultNumberType = defaultNumberType;
this.controllerNodes = new Array();
this.referenceTypeMap = {};
this.modelDefinitionPosMap = {};
this.expressionOrigNameMap = {};
this.checkForMethodSignatureDuplicates = (controllers) => {
const map = {};
controllers.forEach(controller => {
controller.methods.forEach(method => {
const signature = method.path ? `@${method.method}(${controller.path}/${method.path})` : `@${method.method}(${controller.path})`;
const methodDescription = `${controller.name}#${method.name}`;
if (map[signature]) {
map[signature].push(methodDescription);
}
else {
map[signature] = [methodDescription];
}
});
});
let message = '';
Object.keys(map).forEach(signature => {
const controllers = map[signature];
if (controllers.length > 1) {
message += `Duplicate method signature ${signature} found in controllers: ${controllers.join(', ')}\n`;
}
});
if (message) {
throw new exceptions_1.GenerateMetadataError(message);
}
};
this.checkForPathParamSignatureDuplicates = (controllers) => {
const paramRegExp = new RegExp('{(\\w*)}|:(\\w+)', 'g');
let PathDuplicationType;
(function (PathDuplicationType) {
PathDuplicationType[PathDuplicationType["FULL"] = 0] = "FULL";
PathDuplicationType[PathDuplicationType["PARTIAL"] = 1] = "PARTIAL";
})(PathDuplicationType || (PathDuplicationType = {}));
const collisions = [];
function addCollision(type, method, controller, collidesWith) {
let existingCollision = collisions.find(collision => collision.type === type && collision.method === method && collision.controller === controller);
if (!existingCollision) {
existingCollision = {
type,
method,
controller,
collidesWith: [],
};
collisions.push(existingCollision);
}
existingCollision.collidesWith.push(collidesWith);
}
controllers.forEach(controller => {
const methodRouteGroup = {};
// Group all ts methods with HTTP method decorator into same object in same controller.
controller.methods.forEach(method => {
if (methodRouteGroup[method.method] === undefined) {
methodRouteGroup[method.method] = [];
}
const params = method.path.match(paramRegExp);
methodRouteGroup[method.method].push({
method, // method.name + ": " + method.path) as any,
path: params?.reduce((s, a) => {
// replace all params with {} placeholder for comparison
return s.replace(a, '{}');
}, method.path) || method.path,
});
});
Object.keys(methodRouteGroup).forEach((key) => {
const methodRoutes = methodRouteGroup[key];
// check each route with the routes that are defined before it
for (let i = 0; i < methodRoutes.length; i += 1) {
for (let j = 0; j < i; j += 1) {
if (methodRoutes[i].path === methodRoutes[j].path) {
// full match
addCollision(PathDuplicationType.FULL, methodRoutes[i].method, controller, methodRoutes[j].method);
}
else if (methodRoutes[i].path.split('/').length === methodRoutes[j].path.split('/').length &&
methodRoutes[j].path
.substr(methodRoutes[j].path.lastIndexOf('/')) // compare only the "last" part of the path
.split('/')
.some(v => !!v) && // ensure the comparison path has a value
methodRoutes[i].path.split('/').every((v, index) => {
const comparisonPathPart = methodRoutes[j].path.split('/')[index];
// if no params, compare values
if (!v.includes('{}')) {
return v === comparisonPathPart;
}
// otherwise check if route starts with comparison route
return v.startsWith(methodRoutes[j].path.split('/')[index]);
})) {
// partial match - reorder routes!
addCollision(PathDuplicationType.PARTIAL, methodRoutes[i].method, controller, methodRoutes[j].method);
}
}
}
});
});
// print warnings for each collision (grouped by route)
collisions.forEach(collision => {
let message = '';
if (collision.type === PathDuplicationType.FULL) {
message = `Duplicate path parameter definition signature found in controller `;
}
else if (collision.type === PathDuplicationType.PARTIAL) {
message = `Overlapping path parameter definition signature found in controller `;
}
message += collision.controller.name;
message += ` [ method ${collision.method.method.toUpperCase()} ${collision.method.name} route: ${collision.method.path} ] collides with `;
message += collision.collidesWith
.map((method) => {
return `[ method ${method.method.toUpperCase()} ${method.name} route: ${method.path} ]`;
})
.join(', ');
message += '\n';
console.warn(message);
});
};
typeResolver_1.TypeResolver.clearCache();
this.program = controllers ? this.setProgramToDynamicControllersFiles(controllers, esm) : (0, typescript_1.createProgram)([entryFile], compilerOptions || {});
this.typeChecker = this.program.getTypeChecker();
}
Generate() {
this.extractNodeFromProgramSourceFiles();
const controllers = this.buildControllers();
this.checkForMethodSignatureDuplicates(controllers);
this.checkForPathParamSignatureDuplicates(controllers);
return {
controllers,
referenceTypeMap: this.referenceTypeMap,
};
}
setProgramToDynamicControllersFiles(controllers, esm) {
const allGlobFiles = (0, importClassesFromDirectories_1.importClassesFromDirectories)(controllers, esm ? ['.mts', '.ts', '.cts'] : ['.ts']);
if (allGlobFiles.length === 0) {
throw new exceptions_1.GenerateMetadataError(`[${controllers.join(', ')}] globs found 0 controllers.`);
}
return (0, typescript_1.createProgram)(allGlobFiles, this.compilerOptions || {});
}
extractNodeFromProgramSourceFiles() {
this.program.getSourceFiles().forEach(sf => {
if (this.ignorePaths && this.ignorePaths.length) {
for (const path of this.ignorePaths) {
if ((0, minimatch_1.minimatch)(sf.fileName, path)) {
return;
}
}
}
(0, typescript_1.forEachChild)(sf, node => {
if ((0, typescript_1.isClassDeclaration)(node) && (0, decoratorUtils_1.getDecorators)(node, identifier => identifier.text === 'Route').length) {
this.controllerNodes.push(node);
}
});
});
}
TypeChecker() {
return this.typeChecker;
}
AddReferenceType(referenceType) {
if (!referenceType.refName) {
throw new Error('no reference type name found');
}
this.referenceTypeMap[referenceType.refName] = referenceType;
}
GetReferenceType(refName) {
return this.referenceTypeMap[refName];
}
CheckModelUnicity(refName, positions) {
if (!this.modelDefinitionPosMap[refName]) {
this.modelDefinitionPosMap[refName] = positions;
}
else {
const origPositions = this.modelDefinitionPosMap[refName];
if (!(origPositions.length === positions.length && positions.every(pos => origPositions.find(origPos => pos.pos === origPos.pos && pos.fileName === origPos.fileName)))) {
throw new Error(`Found 2 different model definitions for model ${refName}: orig: ${JSON.stringify(origPositions)}, act: ${JSON.stringify(positions)}`);
}
}
}
CheckExpressionUnicity(formattedRefName, refName) {
if (!this.expressionOrigNameMap[formattedRefName]) {
this.expressionOrigNameMap[formattedRefName] = refName;
}
else {
if (this.expressionOrigNameMap[formattedRefName] !== refName) {
throw new Error(`Found 2 different type expressions for formatted name "${formattedRefName}": orig: "${this.expressionOrigNameMap[formattedRefName]}", act: "${refName}"`);
}
}
}
buildControllers() {
if (this.controllerNodes.length === 0) {
throw new Error('no controllers found, check tsoa configuration');
}
return this.controllerNodes
.map(classDeclaration => new controllerGenerator_1.ControllerGenerator(classDeclaration, this, this.rootSecurity))
.filter(generator => generator.IsValid())
.map(generator => generator.Generate());
}
}
exports.MetadataGenerator = MetadataGenerator;
//# sourceMappingURL=metadataGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,45 @@
import * as ts from 'typescript';
import { MetadataGenerator } from './metadataGenerator';
import { Tsoa } from '@tsoa/runtime';
type HttpMethod = 'options' | 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head';
export declare class MethodGenerator {
private readonly node;
private readonly current;
private readonly commonResponses;
private readonly parentPath?;
private readonly parentTags?;
private readonly parentSecurity?;
private readonly isParentHidden?;
protected method?: HttpMethod;
protected path?: string;
private produces?;
private consumes?;
constructor(node: ts.MethodDeclaration, current: MetadataGenerator, commonResponses: Tsoa.Response[], parentPath?: string | undefined, parentTags?: string[] | undefined, parentSecurity?: Tsoa.Security[] | undefined, isParentHidden?: boolean | undefined);
IsValid(): this is {
method: HttpMethod;
path: string;
};
Generate(): Tsoa.Method;
private buildParameters;
private validateBodyParameters;
private validateQueryParameters;
private getExtensions;
private getCurrentLocation;
private processMethodDecorators;
private getProduces;
private getConsumes;
private getMethodResponses;
private getMethodSuccessResponse;
private getHeadersFromDecorator;
private getSchemaFromDecorator;
private getMethodSuccessExamples;
private supportsPathMethod;
private getIsDeprecated;
private getOperationId;
private getTags;
private getSecurity;
private getIsHidden;
private getDecoratorsByIdentifier;
private getProducesAdapter;
}
export {};

View File

@@ -0,0 +1,356 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.MethodGenerator = void 0;
const ts = __importStar(require("typescript"));
const path = __importStar(require("path"));
const isVoidType_1 = require("../utils/isVoidType");
const decoratorUtils_1 = require("./../utils/decoratorUtils");
const jsDocUtils_1 = require("./../utils/jsDocUtils");
const extension_1 = require("./extension");
const exceptions_1 = require("./exceptions");
const parameterGenerator_1 = require("./parameterGenerator");
const typeResolver_1 = require("./typeResolver");
const headerTypeHelpers_1 = require("../utils/headerTypeHelpers");
class MethodGenerator {
constructor(node, current, commonResponses, parentPath, parentTags, parentSecurity, isParentHidden) {
this.node = node;
this.current = current;
this.commonResponses = commonResponses;
this.parentPath = parentPath;
this.parentTags = parentTags;
this.parentSecurity = parentSecurity;
this.isParentHidden = isParentHidden;
this.processMethodDecorators();
}
IsValid() {
return this.method !== undefined && this.path !== undefined;
}
Generate() {
if (!this.IsValid()) {
throw new exceptions_1.GenerateMetadataError("This isn't a valid a controller method.");
}
let nodeType = this.node.type;
if (!nodeType) {
const typeChecker = this.current.typeChecker;
const signature = typeChecker.getSignatureFromDeclaration(this.node);
const implicitType = typeChecker.getReturnTypeOfSignature(signature);
nodeType = typeChecker.typeToTypeNode(implicitType, undefined, ts.NodeBuilderFlags.NoTruncation);
}
const type = new typeResolver_1.TypeResolver(nodeType, this.current).resolve();
const responses = this.commonResponses.concat(this.getMethodResponses());
const { response: successResponse, status: successStatus } = this.getMethodSuccessResponse(type);
responses.push(successResponse);
const parameters = this.buildParameters();
const additionalResponses = parameters.filter((p) => p.in === 'res');
responses.push(...additionalResponses);
return {
extensions: this.getExtensions(),
deprecated: this.getIsDeprecated(),
description: (0, jsDocUtils_1.getJSDocDescription)(this.node),
isHidden: this.getIsHidden(),
method: this.method,
name: this.node.name.text,
operationId: this.getOperationId(),
parameters,
path: this.path,
produces: this.produces,
consumes: this.consumes,
responses,
successStatus,
security: this.getSecurity(),
summary: (0, jsDocUtils_1.getJSDocComment)(this.node, 'summary'),
tags: this.getTags(),
type,
};
}
buildParameters() {
if (!this.IsValid()) {
throw new exceptions_1.GenerateMetadataError("This isn't a valid a controller method.");
}
const fullPath = path.join(this.parentPath || '', this.path);
const method = this.method;
const parameters = this.node.parameters
.map(p => {
try {
return new parameterGenerator_1.ParameterGenerator(p, method, fullPath, this.current).Generate();
}
catch (e) {
const methodId = this.node.name;
const controllerId = this.node.parent.name;
const message = e instanceof Error ? e.message : String(e);
throw new exceptions_1.GenerateMetadataError(`${message} \n in '${controllerId.text}.${methodId.text}'`);
}
})
.reduce((flattened, params) => [...flattened, ...params], []);
this.validateBodyParameters(parameters);
this.validateQueryParameters(parameters);
return parameters;
}
validateBodyParameters(parameters) {
const bodyParameters = parameters.filter(p => p.in === 'body');
const bodyProps = parameters.filter(p => p.in === 'body-prop');
const hasFormDataParameters = parameters.some(p => p.in === 'formData');
const hasBodyParameter = bodyProps.length + bodyParameters.length > 0;
if (bodyParameters.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one body parameter allowed in '${this.getCurrentLocation()}' method.`);
}
if (bodyParameters.length > 0 && bodyProps.length > 0) {
throw new exceptions_1.GenerateMetadataError(`Choose either during @Body or @BodyProp in '${this.getCurrentLocation()}' method.`);
}
if (hasBodyParameter && hasFormDataParameters) {
throw new Error(`@Body or @BodyProp cannot be used with @FormField, @UploadedFile, or @UploadedFiles in '${this.getCurrentLocation()}' method.`);
}
}
validateQueryParameters(parameters) {
const queryParameters = parameters.filter(p => p.in === 'query');
const queriesParameters = parameters.filter(p => p.in === 'queries');
if (queriesParameters.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one queries parameter allowed in '${this.getCurrentLocation()}' method.`);
}
if (queriesParameters.length > 0 && queryParameters.length > 0) {
throw new exceptions_1.GenerateMetadataError(`Choose either during @Query or @Queries in '${this.getCurrentLocation()}' method.`);
}
}
getExtensions() {
const extensionDecorators = this.getDecoratorsByIdentifier(this.node, 'Extension');
if (!extensionDecorators || !extensionDecorators.length) {
return [];
}
return (0, extension_1.getExtensions)(extensionDecorators, this.current);
}
getCurrentLocation() {
const methodId = this.node.name;
const controllerId = this.node.parent.name;
return `${controllerId.text}.${methodId.text}`;
}
processMethodDecorators() {
const pathDecorators = (0, decoratorUtils_1.getDecorators)(this.node, identifier => this.supportsPathMethod(identifier.text));
if (!pathDecorators || !pathDecorators.length) {
return;
}
if (pathDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one path decorator in '${this.getCurrentLocation()}' method, Found: ${pathDecorators.map(d => d.text).join(', ')}`);
}
const decorator = pathDecorators[0];
this.method = decorator.text.toLowerCase();
// if you don't pass in a path to the method decorator, we'll just use the base route
// todo: what if someone has multiple no argument methods of the same type in a single controller?
// we need to throw an error there
this.path = (0, decoratorUtils_1.getPath)(decorator, this.current.typeChecker);
this.produces = this.getProduces();
this.consumes = this.getConsumes();
}
getProduces() {
const produces = (0, decoratorUtils_1.getProduces)(this.node, this.current.typeChecker);
return produces.length ? produces : undefined;
}
getConsumes() {
const consumesDecorators = this.getDecoratorsByIdentifier(this.node, 'Consumes');
if (!consumesDecorators || !consumesDecorators.length) {
return;
}
if (consumesDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Consumes decorator in '${this.getCurrentLocation()}' method, Found: ${consumesDecorators.map(d => d.text).join(', ')}`);
}
const [decorator] = consumesDecorators;
const [consumes] = (0, decoratorUtils_1.getDecoratorValues)(decorator, this.current.typeChecker);
return consumes;
}
getMethodResponses() {
const responseExamplesByName = {};
const decorators = this.getDecoratorsByIdentifier(this.node, 'Response');
if (!decorators || !decorators.length) {
return [];
}
return decorators.map(decorator => {
const [name, description, example, produces] = (0, decoratorUtils_1.getDecoratorValues)(decorator, this.current.typeChecker);
if (example !== undefined) {
responseExamplesByName[name] = responseExamplesByName[name] ? [...responseExamplesByName[name], example] : [example];
}
return {
description: description || '',
examples: responseExamplesByName[name] || undefined,
name: name || '200',
produces: this.getProducesAdapter(produces),
schema: this.getSchemaFromDecorator(decorator, 0),
headers: this.getHeadersFromDecorator(decorator, 1),
};
});
}
getMethodSuccessResponse(type) {
const decorators = this.getDecoratorsByIdentifier(this.node, 'SuccessResponse');
const examplesWithLabels = this.getMethodSuccessExamples();
if (!decorators || !decorators.length) {
const returnsDescription = (0, jsDocUtils_1.getJSDocComment)(this.node, 'returns') || 'Ok';
return {
response: {
description: (0, isVoidType_1.isVoidType)(type) ? 'No content' : returnsDescription,
examples: examplesWithLabels?.map(ex => ex.example),
exampleLabels: examplesWithLabels?.map(ex => ex.label),
name: (0, isVoidType_1.isVoidType)(type) ? '204' : '200',
produces: this.produces,
schema: type,
},
};
}
if (decorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one SuccessResponse decorator allowed in '${this.getCurrentLocation()}' method.`);
}
const [firstDecorator] = decorators;
const [name, description, produces] = (0, decoratorUtils_1.getDecoratorValues)(firstDecorator, this.current.typeChecker);
const headers = this.getHeadersFromDecorator(firstDecorator, 0);
return {
response: {
description: description || '',
examples: examplesWithLabels?.map(ex => ex.example),
exampleLabels: examplesWithLabels?.map(ex => ex.label),
name: name || '200',
produces: this.getProducesAdapter(produces),
schema: type,
headers,
},
status: name && /^\d+$/.test(name) ? parseInt(name, 10) : undefined,
};
}
getHeadersFromDecorator({ parent: expression }, headersIndex) {
if (!ts.isCallExpression(expression)) {
return undefined;
}
return (0, headerTypeHelpers_1.getHeaderType)(expression.typeArguments, headersIndex, this.current);
}
getSchemaFromDecorator({ parent: expression }, schemaIndex) {
if (!ts.isCallExpression(expression) || !expression.typeArguments?.length) {
return undefined;
}
return new typeResolver_1.TypeResolver(expression.typeArguments[schemaIndex], this.current).resolve();
}
getMethodSuccessExamples() {
const exampleDecorators = this.getDecoratorsByIdentifier(this.node, 'Example');
if (!exampleDecorators || !exampleDecorators.length) {
return undefined;
}
const examples = exampleDecorators.map(exampleDecorator => {
const [example, label] = (0, decoratorUtils_1.getDecoratorValues)(exampleDecorator, this.current.typeChecker);
return { example, label };
});
return examples || undefined;
}
supportsPathMethod(method) {
return ['options', 'get', 'post', 'put', 'patch', 'delete', 'head'].some(m => m === method.toLowerCase());
}
getIsDeprecated() {
if ((0, jsDocUtils_1.isExistJSDocTag)(this.node, tag => tag.tagName.text === 'deprecated')) {
return true;
}
const depDecorators = this.getDecoratorsByIdentifier(this.node, 'Deprecated');
if (!depDecorators || !depDecorators.length) {
return false;
}
if (depDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Deprecated decorator allowed in '${this.getCurrentLocation()}' method.`);
}
return true;
}
getOperationId() {
const opDecorators = this.getDecoratorsByIdentifier(this.node, 'OperationId');
if (!opDecorators || !opDecorators.length) {
return undefined;
}
if (opDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one OperationId decorator allowed in '${this.getCurrentLocation()}' method.`);
}
const values = (0, decoratorUtils_1.getDecoratorValues)(opDecorators[0], this.current.typeChecker);
return values && values[0];
}
getTags() {
const tagsDecorators = this.getDecoratorsByIdentifier(this.node, 'Tags');
if (!tagsDecorators || !tagsDecorators.length) {
return this.parentTags;
}
if (tagsDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Tags decorator allowed in '${this.getCurrentLocation()}' method.`);
}
const tags = (0, decoratorUtils_1.getDecoratorValues)(tagsDecorators[0], this.current.typeChecker);
if (tags && this.parentTags) {
tags.push(...this.parentTags);
}
return tags;
}
getSecurity() {
const noSecurityDecorators = this.getDecoratorsByIdentifier(this.node, 'NoSecurity');
const securityDecorators = this.getDecoratorsByIdentifier(this.node, 'Security');
if (noSecurityDecorators?.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one NoSecurity decorator allowed in '${this.getCurrentLocation()}' method.`);
}
if (noSecurityDecorators?.length && securityDecorators?.length) {
throw new exceptions_1.GenerateMetadataError(`NoSecurity decorator cannot be used in conjunction with Security decorator in '${this.getCurrentLocation()}' method.`);
}
if (noSecurityDecorators?.length) {
return [];
}
if (!securityDecorators || !securityDecorators.length) {
return this.parentSecurity || [];
}
return securityDecorators.map(d => (0, decoratorUtils_1.getSecurites)(d, this.current.typeChecker));
}
getIsHidden() {
const hiddenDecorators = this.getDecoratorsByIdentifier(this.node, 'Hidden');
if (!hiddenDecorators || !hiddenDecorators.length) {
return !!this.isParentHidden;
}
if (this.isParentHidden) {
throw new exceptions_1.GenerateMetadataError(`Hidden decorator cannot be set on '${this.getCurrentLocation()}' it is already defined on the controller`);
}
if (hiddenDecorators.length > 1) {
throw new exceptions_1.GenerateMetadataError(`Only one Hidden decorator allowed in '${this.getCurrentLocation()}' method.`);
}
return true;
}
getDecoratorsByIdentifier(node, id) {
return (0, decoratorUtils_1.getDecorators)(node, identifier => identifier.text === id);
}
getProducesAdapter(produces) {
if (Array.isArray(produces)) {
return produces;
}
else if (typeof produces === 'string') {
return [produces];
}
return;
}
}
exports.MethodGenerator = MethodGenerator;
//# sourceMappingURL=methodGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,33 @@
import * as ts from 'typescript';
import { MetadataGenerator } from './metadataGenerator';
import { Tsoa } from '@tsoa/runtime';
export declare class ParameterGenerator {
private readonly parameter;
private readonly method;
private readonly path;
private readonly current;
constructor(parameter: ts.ParameterDeclaration, method: string, path: string, current: MetadataGenerator);
Generate(): Tsoa.Parameter[];
private getRequestParameter;
private getRequestPropParameter;
private extractTsoaResponse;
private getResParameters;
private getProducesFromResHeaders;
private getBodyPropParameter;
private getBodyParameter;
private getHeaderParameter;
private getUploadedFileParameter;
private getFormFieldParameter;
private getQueriesParameters;
private validateQueriesProperties;
private getQueryParameters;
private getPathParameter;
private getParameterDescription;
private getParameterDeprecation;
private getParameterExample;
private supportBodyMethod;
private supportParameterDecorator;
private supportPathDataType;
private getValidatedType;
private getQueryParameterIsHidden;
}

View File

@@ -0,0 +1,548 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParameterGenerator = void 0;
const ts = __importStar(require("typescript"));
const decoratorUtils_1 = require("./../utils/decoratorUtils");
const jsDocUtils_1 = require("./../utils/jsDocUtils");
const validatorUtils_1 = require("./../utils/validatorUtils");
const exceptions_1 = require("./exceptions");
const initializer_value_1 = require("./initializer-value");
const typeResolver_1 = require("./typeResolver");
const headerTypeHelpers_1 = require("../utils/headerTypeHelpers");
class ParameterGenerator {
constructor(parameter, method, path, current) {
this.parameter = parameter;
this.method = method;
this.path = path;
this.current = current;
}
Generate() {
const decoratorName = (0, decoratorUtils_1.getNodeFirstDecoratorName)(this.parameter, identifier => this.supportParameterDecorator(identifier.text));
switch (decoratorName) {
case 'Request':
return [this.getRequestParameter(this.parameter)];
case 'RequestProp':
return [this.getRequestPropParameter(this.parameter)];
case 'Body':
return [this.getBodyParameter(this.parameter)];
case 'BodyProp':
return [this.getBodyPropParameter(this.parameter)];
case 'FormField':
return [this.getFormFieldParameter(this.parameter)];
case 'Header':
return [this.getHeaderParameter(this.parameter)];
case 'Query':
return this.getQueryParameters(this.parameter);
case 'Queries':
return [this.getQueriesParameters(this.parameter)];
case 'Path':
return [this.getPathParameter(this.parameter)];
case 'Res':
return this.getResParameters(this.parameter);
case 'Inject':
return [];
case 'UploadedFile':
return [this.getUploadedFileParameter(this.parameter)];
case 'UploadedFiles':
return [this.getUploadedFileParameter(this.parameter, true)];
default:
return [this.getPathParameter(this.parameter)];
}
}
getRequestParameter(parameter) {
const parameterName = parameter.name.text;
return {
description: this.getParameterDescription(parameter),
in: 'request',
name: parameterName,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type: { dataType: 'object' },
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getRequestPropParameter(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
default: (0, initializer_value_1.getInitializerValue)(parameter.initializer, this.current.typeChecker, type),
description: this.getParameterDescription(parameter),
example,
exampleLabels,
in: 'request-prop',
name: (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'ParameterProp') || parameterName,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
extractTsoaResponse(typeNode) {
if (!typeNode || !ts.isTypeReferenceNode(typeNode)) {
return undefined;
}
if (typeNode.typeName.getText() === 'TsoaResponse') {
return typeNode;
}
const symbol = this.current.typeChecker.getTypeAtLocation(typeNode).aliasSymbol;
if (!symbol || !symbol.declarations) {
return undefined;
}
const declaration = symbol.declarations[0];
if (!ts.isTypeAliasDeclaration(declaration) || !ts.isTypeReferenceNode(declaration.type)) {
return undefined;
}
return declaration.type.typeName.getText() === 'TsoaResponse' ? declaration.type : undefined;
}
getResParameters(parameter) {
const parameterName = parameter.name.text;
const decorator = (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'Res') || parameterName;
if (!decorator) {
throw new exceptions_1.GenerateMetadataError('Could not find Decorator', parameter);
}
const typeNode = this.extractTsoaResponse(parameter.type);
if (!typeNode) {
throw new exceptions_1.GenerateMetadataError('@Res() requires the type to be TsoaResponse<HTTPStatusCode, ResBody>', parameter);
}
if (!typeNode.typeArguments || !typeNode.typeArguments[0]) {
throw new exceptions_1.GenerateMetadataError('@Res() requires the type to be TsoaResponse<HTTPStatusCode, ResBody>', parameter);
}
const statusArgument = typeNode.typeArguments[0];
const bodyArgument = typeNode.typeArguments[1];
// support a union of status codes, all with the same response body
const statusArguments = ts.isUnionTypeNode(statusArgument) ? [...statusArgument.types] : [statusArgument];
const statusArgumentTypes = statusArguments.map(a => this.current.typeChecker.getTypeAtLocation(a));
const isNumberLiteralType = (tsType) => {
// eslint-disable-next-line no-bitwise
return (tsType.getFlags() & ts.TypeFlags.NumberLiteral) !== 0;
};
const headers = (0, headerTypeHelpers_1.getHeaderType)(typeNode.typeArguments, 2, this.current);
return statusArgumentTypes.map(statusArgumentType => {
if (!isNumberLiteralType(statusArgumentType)) {
throw new exceptions_1.GenerateMetadataError('@Res() requires the type to be TsoaResponse<HTTPStatusCode, ResBody>', parameter);
}
const status = String(statusArgumentType.value);
const type = new typeResolver_1.TypeResolver(bodyArgument, this.current, typeNode).resolve();
const { examples, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
description: this.getParameterDescription(parameter) || '',
in: 'res',
name: status,
produces: headers ? this.getProducesFromResHeaders(headers) : undefined,
parameterName,
examples,
required: true,
type,
exampleLabels,
schema: type,
validators: {},
headers,
deprecated: this.getParameterDeprecation(parameter),
};
});
}
getProducesFromResHeaders(headers) {
const { properties } = headers;
const [contentTypeProp] = (properties || []).filter(p => p.name.toLowerCase() === 'content-type' && p.type.dataType === 'enum');
if (contentTypeProp) {
const type = contentTypeProp.type;
return type.enums;
}
return;
}
getBodyPropParameter(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
if (!this.supportBodyMethod(this.method)) {
throw new exceptions_1.GenerateMetadataError(`@BodyProp('${parameterName}') Can't support in ${this.method.toUpperCase()} method.`);
}
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
default: (0, initializer_value_1.getInitializerValue)(parameter.initializer, this.current.typeChecker, type),
description: this.getParameterDescription(parameter),
example,
exampleLabels,
in: 'body-prop',
name: (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'BodyProp') || parameterName,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getBodyParameter(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
if (!this.supportBodyMethod(this.method)) {
throw new exceptions_1.GenerateMetadataError(`@Body('${parameterName}') Can't support in ${this.method.toUpperCase()} method.`);
}
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
description: this.getParameterDescription(parameter),
in: 'body',
name: parameterName,
example,
exampleLabels,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getHeaderParameter(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
if (!this.supportPathDataType(type)) {
throw new exceptions_1.GenerateMetadataError(`@Header('${parameterName}') Can't support '${type.dataType}' type.`);
}
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
default: (0, initializer_value_1.getInitializerValue)(parameter.initializer, this.current.typeChecker, type),
description: this.getParameterDescription(parameter),
example,
exampleLabels,
in: 'header',
name: (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'Header') || parameterName,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getUploadedFileParameter(parameter, isArray) {
const parameterName = parameter.name.text;
const elementType = { dataType: 'file' };
let type;
if (isArray) {
type = { dataType: 'array', elementType };
}
else {
type = elementType;
}
if (!this.supportPathDataType(elementType)) {
throw new exceptions_1.GenerateMetadataError(`Parameter '${parameterName}:${type.dataType}' can't be passed as an uploaded file(s) parameter in '${this.method.toUpperCase()}'.`, parameter);
}
return {
description: this.getParameterDescription(parameter),
in: 'formData',
name: (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => {
if (isArray) {
return ident.text === 'UploadedFiles';
}
return ident.text === 'UploadedFile';
}) ?? parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
parameterName,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getFormFieldParameter(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
if (!this.supportPathDataType(type)) {
throw new exceptions_1.GenerateMetadataError(`Parameter '${parameterName}:${type.dataType}' can't be passed as form field parameter in '${this.method.toUpperCase()}'.`, parameter);
}
return {
description: this.getParameterDescription(parameter),
in: 'formData',
name: (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'FormField') ?? parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
parameterName,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getQueriesParameters(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
// Handle cases where TypeResolver doesn't properly resolve complex types
// like Zod's z.infer types to refObject or nestedObjectLiteral
if (type.dataType !== 'refObject' && type.dataType !== 'nestedObjectLiteral') {
// Try to resolve the type more aggressively for complex types
let typeNode = parameter.type;
if (!typeNode) {
const typeFromChecker = this.current.typeChecker.getTypeAtLocation(parameter);
typeNode = this.current.typeChecker.typeToTypeNode(typeFromChecker, undefined, ts.NodeBuilderFlags.NoTruncation);
}
// If it's a TypeReferenceNode (like z.infer), try to resolve it differently
if (ts.isTypeReferenceNode(typeNode)) {
try {
// Try to get the actual type from the type checker
const actualType = this.current.typeChecker.getTypeAtLocation(typeNode);
const typeNodeFromType = this.current.typeChecker.typeToTypeNode(actualType, undefined, ts.NodeBuilderFlags.NoTruncation);
const resolvedType = new typeResolver_1.TypeResolver(typeNodeFromType, this.current, parameter).resolve();
// Check if the resolved type is now acceptable
if (resolvedType.dataType === 'refObject' || resolvedType.dataType === 'nestedObjectLiteral') {
// Use the resolved type instead
for (const property of resolvedType.properties) {
this.validateQueriesProperties(property, parameterName);
}
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
description: this.getParameterDescription(parameter),
in: 'queries',
name: parameterName,
example,
exampleLabels,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type: resolvedType,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
}
catch (error) {
// If resolution fails, log the error for debugging but continue with the original error
// This helps developers understand why the type resolution failed
console.warn(`Failed to resolve complex type for @Queries('${parameterName}'):`, error);
// Continue with the original error below
}
}
throw new exceptions_1.GenerateMetadataError(`@Queries('${parameterName}') only support 'refObject' or 'nestedObjectLiteral' types. If you want only one query parameter, please use the '@Query' decorator.`);
}
for (const property of type.properties) {
this.validateQueriesProperties(property, parameterName);
}
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
description: this.getParameterDescription(parameter),
in: 'queries',
name: parameterName,
example,
exampleLabels,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
type,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
validateQueriesProperties(property, parentName) {
if (property.type.dataType === 'array') {
const arrayType = property.type;
if (arrayType.elementType.dataType === 'nestedObjectLiteral') {
// For arrays of nestedObjectLiteral, validate each property recursively
const nestedType = arrayType.elementType;
if (nestedType.properties) {
for (const nestedProperty of nestedType.properties) {
this.validateQueriesProperties(nestedProperty, `${parentName}.${property.name}[]`);
}
}
}
else if (!this.supportPathDataType(arrayType.elementType)) {
throw new exceptions_1.GenerateMetadataError(`@Queries('${parentName}') property '${property.name}' can't support array '${arrayType.elementType.dataType}' type.`);
}
}
else if (property.type.dataType === 'nestedObjectLiteral') {
// For nestedObjectLiteral, validate each property recursively
const nestedType = property.type;
if (nestedType.properties) {
for (const nestedProperty of nestedType.properties) {
this.validateQueriesProperties(nestedProperty, `${parentName}.${property.name}`);
}
}
}
else if (!this.supportPathDataType(property.type)) {
throw new exceptions_1.GenerateMetadataError(`@Queries('${parentName}') nested property '${property.name}' Can't support '${property.type.dataType}' type.`);
}
}
getQueryParameters(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
const { examples: example, exampleLabels } = this.getParameterExample(parameter, parameterName);
const commonProperties = {
default: (0, initializer_value_1.getInitializerValue)(parameter.initializer, this.current.typeChecker, type),
description: this.getParameterDescription(parameter),
example,
exampleLabels,
in: 'query',
name: (0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'Query') || parameterName,
parameterName,
required: !parameter.questionToken && !parameter.initializer,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
if (this.getQueryParameterIsHidden(parameter)) {
if (commonProperties.required) {
throw new exceptions_1.GenerateMetadataError(`@Query('${parameterName}') Can't support @Hidden because it is required (does not allow undefined and does not have a default value).`);
}
return [];
}
if (type.dataType === 'array') {
const arrayType = type;
if (!this.supportPathDataType(arrayType.elementType)) {
throw new exceptions_1.GenerateMetadataError(`@Query('${parameterName}') Can't support array '${arrayType.elementType.dataType}' type.`);
}
return [
{
...commonProperties,
collectionFormat: 'multi',
type: arrayType,
},
];
}
if (!this.supportPathDataType(type)) {
throw new exceptions_1.GenerateMetadataError(`@Query('${parameterName}') Can't support '${type.dataType}' type.`);
}
return [
{
...commonProperties,
type,
},
];
}
getPathParameter(parameter) {
const parameterName = parameter.name.text;
const type = this.getValidatedType(parameter);
const pathName = String((0, decoratorUtils_1.getNodeFirstDecoratorValue)(this.parameter, this.current.typeChecker, ident => ident.text === 'Path') || parameterName);
if (!this.supportPathDataType(type)) {
throw new exceptions_1.GenerateMetadataError(`@Path('${parameterName}') Can't support '${type.dataType}' type.`);
}
if (!this.path.includes(`{${pathName}}`) && !this.path.includes(`:${pathName}`)) {
throw new exceptions_1.GenerateMetadataError(`@Path('${parameterName}') Can't match in URL: '${this.path}'.`);
}
const { examples, exampleLabels } = this.getParameterExample(parameter, parameterName);
return {
default: (0, initializer_value_1.getInitializerValue)(parameter.initializer, this.current.typeChecker, type),
description: this.getParameterDescription(parameter),
example: examples,
exampleLabels,
in: 'path',
name: pathName,
parameterName,
required: true,
type,
validators: (0, validatorUtils_1.getParameterValidators)(this.parameter, parameterName),
deprecated: this.getParameterDeprecation(parameter),
};
}
getParameterDescription(node) {
const symbol = this.current.typeChecker.getSymbolAtLocation(node.name);
if (!symbol) {
return undefined;
}
const comments = symbol.getDocumentationComment(this.current.typeChecker);
if (comments.length) {
return ts.displayPartsToString(comments);
}
return undefined;
}
getParameterDeprecation(node) {
return (0, jsDocUtils_1.isExistJSDocTag)(node, tag => tag.tagName.text === 'deprecated') || (0, decoratorUtils_1.isDecorator)(node, identifier => identifier.text === 'Deprecated');
}
getParameterExample(node, parameterName) {
const exampleLabels = [];
const examples = (0, jsDocUtils_1.getJSDocTags)(node.parent, tag => {
const comment = (0, jsDocUtils_1.commentToString)(tag.comment);
const isExample = (tag.tagName.text === 'example' || tag.tagName.escapedText === 'example') && !!tag.comment && comment?.startsWith(parameterName);
if (isExample) {
const hasExampleLabel = (comment?.split(' ')[0].indexOf('.') || -1) > 0;
// custom example label is delimited by first '.' and the rest will all be included as example label
exampleLabels.push(hasExampleLabel ? comment?.split(' ')[0].split('.').slice(1).join('.') : undefined);
}
return isExample ?? false;
}).map(tag => ((0, jsDocUtils_1.commentToString)(tag.comment) || '').replace(`${(0, jsDocUtils_1.commentToString)(tag.comment)?.split(' ')[0] || ''}`, '').replace(/\r/g, ''));
if (examples.length === 0) {
return {
examples: undefined,
exampleLabels: undefined,
};
}
else {
try {
return {
examples: examples.map(example => JSON.parse(example)),
exampleLabels,
};
}
catch (e) {
const message = e instanceof Error ? e.message : String(e);
throw new exceptions_1.GenerateMetadataError(`JSON format is incorrect: ${message}`);
}
}
}
supportBodyMethod(method) {
return ['post', 'put', 'patch', 'delete'].some(m => m === method.toLowerCase());
}
supportParameterDecorator(decoratorName) {
return ['header', 'query', 'queries', 'path', 'body', 'bodyprop', 'request', 'requestprop', 'res', 'inject', 'uploadedfile', 'uploadedfiles', 'formfield'].some(d => d === decoratorName.toLocaleLowerCase());
}
supportPathDataType(parameterType) {
const supportedPathDataTypes = ['string', 'integer', 'long', 'float', 'double', 'date', 'datetime', 'buffer', 'boolean', 'enum', 'refEnum', 'file', 'any'];
if (supportedPathDataTypes.find(t => t === parameterType.dataType)) {
return true;
}
if (parameterType.dataType === 'refAlias') {
return this.supportPathDataType(parameterType.type);
}
if (parameterType.dataType === 'union') {
// skip undefined inside unions
return !parameterType.types.map(t => t.dataType === 'undefined' || this.supportPathDataType(t)).some(t => t === false);
}
return false;
}
getValidatedType(parameter) {
let typeNode = parameter.type;
if (!typeNode) {
const type = this.current.typeChecker.getTypeAtLocation(parameter);
typeNode = this.current.typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.NoTruncation);
}
return new typeResolver_1.TypeResolver(typeNode, this.current, parameter).resolve();
}
getQueryParameterIsHidden(parameter) {
const hiddenDecorators = (0, decoratorUtils_1.getDecorators)(parameter, identifier => identifier.text === 'Hidden');
if (!hiddenDecorators || !hiddenDecorators.length) {
return false;
}
if (hiddenDecorators.length > 1) {
const parameterName = parameter.name.text;
throw new exceptions_1.GenerateMetadataError(`Only one Hidden decorator allowed on @Query('${parameterName}').`);
}
return true;
}
}
exports.ParameterGenerator = ParameterGenerator;
//# sourceMappingURL=parameterGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
import type { Node } from 'typescript';
import { Tsoa } from '@tsoa/runtime';
import { Transformer } from './transformer';
export declare class DateTransformer extends Transformer {
transform(parentNode?: Node): Tsoa.DateType | Tsoa.DateTimeType;
}

View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DateTransformer = void 0;
const transformer_1 = require("./transformer");
const jsDocUtils_1 = require("../../utils/jsDocUtils");
class DateTransformer extends transformer_1.Transformer {
transform(parentNode) {
if (!parentNode) {
return { dataType: 'datetime' };
}
const tags = (0, jsDocUtils_1.getJSDocTagNames)(parentNode).filter(name => {
return ['isDate', 'isDateTime'].some(m => m === name);
});
if (tags.length === 0) {
return { dataType: 'datetime' };
}
switch (tags[0]) {
case 'isDate':
return { dataType: 'date' };
case 'isDateTime':
return { dataType: 'datetime' };
default:
return { dataType: 'datetime' };
}
}
}
exports.DateTransformer = DateTransformer;
//# sourceMappingURL=dateTransformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"dateTransformer.js","sourceRoot":"","sources":["../../../src/metadataGeneration/transformer/dateTransformer.ts"],"names":[],"mappings":";;;AAGA,+CAA4C;AAC5C,uDAA0D;AAE1D,MAAa,eAAgB,SAAQ,yBAAW;IACvC,SAAS,CAAC,UAAiB;QAChC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;QAClC,CAAC;QACD,MAAM,IAAI,GAAG,IAAA,6BAAgB,EAAC,UAAU,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;YACtD,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;QAClC,CAAC;QACD,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,KAAK,QAAQ;gBACX,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;YAC9B,KAAK,YAAY;gBACf,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;YAClC;gBACE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;QACpC,CAAC;IACH,CAAC;CACF;AArBD,0CAqBC"}

View File

@@ -0,0 +1,12 @@
import type { Node, EnumDeclaration, EnumMember } from 'typescript';
import { Tsoa } from '@tsoa/runtime';
import { Transformer } from './transformer';
import { TypeResolver } from '../typeResolver';
export declare class EnumTransformer extends Transformer {
static mergeMany(many: Tsoa.RefEnumType[]): Tsoa.RefEnumType;
static merge(first: Tsoa.RefEnumType, second: Tsoa.RefEnumType): Tsoa.RefEnumType;
static transformable(declaration: Node): declaration is EnumDeclaration | EnumMember;
transform(resolver: TypeResolver, declaration: EnumDeclaration | EnumMember, enumName: string): Tsoa.RefEnumType;
private transformDeclaration;
private transformMember;
}

View File

@@ -0,0 +1,71 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EnumTransformer = void 0;
const typescript_1 = require("typescript");
const transformer_1 = require("./transformer");
const jsDocUtils_1 = require("../../utils/jsDocUtils");
class EnumTransformer extends transformer_1.Transformer {
static mergeMany(many) {
let merged = this.merge(many[0], many[1]);
for (let i = 2; i < many.length; ++i) {
merged = this.merge(merged, many[i]);
}
return merged;
}
static merge(first, second) {
if (!first)
return second;
if (!second)
return first;
const description = first.description ? (second.description ? `${first.description}\n${second.description}` : first.description) : second.description;
const deprecated = first.deprecated || second.deprecated;
const enums = first.enums ? (second.enums ? [...first.enums, ...second.enums] : first.enums) : second.enums;
const enumVarnames = first.enumVarnames ? (second.enumVarnames ? [...first.enumVarnames, ...second.enumVarnames] : first.enumVarnames) : second.enumVarnames;
const example = first.example || second.example;
return {
dataType: 'refEnum',
description,
enums,
enumVarnames,
refName: first.refName,
deprecated,
example,
};
}
static transformable(declaration) {
return (0, typescript_1.isEnumDeclaration)(declaration) || (0, typescript_1.isEnumMember)(declaration);
}
transform(resolver, declaration, enumName) {
if ((0, typescript_1.isEnumDeclaration)(declaration)) {
return this.transformDeclaration(resolver, declaration, enumName);
}
return this.transformMember(resolver, declaration, enumName);
}
transformDeclaration(resolver, declaration, enumName) {
const isNotUndefined = (item) => {
return item === undefined ? false : true;
};
const enums = declaration.members.map(e => resolver.current.typeChecker.getConstantValue(e)).filter(isNotUndefined);
const enumVarnames = declaration.members.map(e => e.name.getText()).filter(isNotUndefined);
return {
dataType: 'refEnum',
description: resolver.getNodeDescription(declaration),
example: resolver.getNodeExample(declaration),
enums,
enumVarnames,
refName: enumName,
deprecated: (0, jsDocUtils_1.isExistJSDocTag)(declaration, tag => tag.tagName.text === 'deprecated'),
};
}
transformMember(resolver, declaration, enumName) {
return {
dataType: 'refEnum',
refName: enumName,
enums: [resolver.current.typeChecker.getConstantValue(declaration)],
enumVarnames: [declaration.name.getText()],
deprecated: (0, jsDocUtils_1.isExistJSDocTag)(declaration, tag => tag.tagName.text === 'deprecated'),
};
}
}
exports.EnumTransformer = EnumTransformer;
//# sourceMappingURL=enumTransformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"enumTransformer.js","sourceRoot":"","sources":["../../../src/metadataGeneration/transformer/enumTransformer.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,11 @@
import { type TypeNode, SyntaxKind } from 'typescript';
import { Tsoa } from '@tsoa/runtime';
import { Transformer } from './transformer';
export declare class PrimitiveTransformer extends Transformer {
static resolveKindToPrimitive(syntaxKind: SyntaxKind): ResolvesToPrimitive;
transform(defaultNumberType: DefaultNumberType, typeNode: TypeNode, partentJsDocTagNames?: string[]): Tsoa.Type | undefined;
private transformNumber;
}
type DefaultNumberType = NonNullable<'double' | 'float' | 'integer' | 'long' | undefined>;
type ResolvesToPrimitive = 'number' | 'string' | 'boolean' | 'void' | 'undefined' | 'null' | undefined;
export {};

View File

@@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PrimitiveTransformer = void 0;
const typescript_1 = require("typescript");
const runtime_1 = require("@tsoa/runtime");
const transformer_1 = require("./transformer");
class PrimitiveTransformer extends transformer_1.Transformer {
static resolveKindToPrimitive(syntaxKind) {
switch (syntaxKind) {
case typescript_1.SyntaxKind.NumberKeyword:
return 'number';
case typescript_1.SyntaxKind.StringKeyword:
return 'string';
case typescript_1.SyntaxKind.BooleanKeyword:
return 'boolean';
case typescript_1.SyntaxKind.VoidKeyword:
return 'void';
case typescript_1.SyntaxKind.UndefinedKeyword:
return 'undefined';
case typescript_1.SyntaxKind.NullKeyword:
return 'null';
default:
return undefined;
}
}
transform(defaultNumberType, typeNode, partentJsDocTagNames) {
const resolvedType = PrimitiveTransformer.resolveKindToPrimitive(typeNode.kind);
if (!resolvedType) {
return;
}
switch (resolvedType) {
case 'number':
return this.transformNumber(defaultNumberType, partentJsDocTagNames);
case 'string':
case 'boolean':
case 'void':
case 'undefined':
return { dataType: resolvedType };
case 'null':
return {
dataType: 'enum',
enums: [null],
};
default:
return (0, runtime_1.assertNever)(resolvedType);
}
}
transformNumber(defaultNumberType, partentJsDocTagNames) {
if (!partentJsDocTagNames || partentJsDocTagNames.length === 0) {
return { dataType: defaultNumberType };
}
const tags = partentJsDocTagNames.filter(name => {
return ['isInt', 'isLong', 'isFloat', 'isDouble'].some(m => m === name);
});
switch (tags[0]) {
case 'isInt':
return { dataType: 'integer' };
case 'isLong':
return { dataType: 'long' };
case 'isFloat':
return { dataType: 'float' };
case 'isDouble':
return { dataType: 'double' };
default:
return { dataType: defaultNumberType };
}
}
}
exports.PrimitiveTransformer = PrimitiveTransformer;
//# sourceMappingURL=primitiveTransformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"primitiveTransformer.js","sourceRoot":"","sources":["../../../src/metadataGeneration/transformer/primitiveTransformer.ts"],"names":[],"mappings":";;;AAAA,2CAAuD;AACvD,2CAAkD;AAElD,+CAA4C;AAE5C,MAAa,oBAAqB,SAAQ,yBAAW;IAC5C,MAAM,CAAC,sBAAsB,CAAC,UAAsB;QACzD,QAAQ,UAAU,EAAE,CAAC;YACnB,KAAK,uBAAU,CAAC,aAAa;gBAC3B,OAAO,QAAQ,CAAC;YAClB,KAAK,uBAAU,CAAC,aAAa;gBAC3B,OAAO,QAAQ,CAAC;YAClB,KAAK,uBAAU,CAAC,cAAc;gBAC5B,OAAO,SAAS,CAAC;YACnB,KAAK,uBAAU,CAAC,WAAW;gBACzB,OAAO,MAAM,CAAC;YAChB,KAAK,uBAAU,CAAC,gBAAgB;gBAC9B,OAAO,WAAW,CAAC;YACrB,KAAK,uBAAU,CAAC,WAAW;gBACzB,OAAO,MAAM,CAAC;YAChB;gBACE,OAAO,SAAS,CAAC;QACrB,CAAC;IACH,CAAC;IAEM,SAAS,CAAC,iBAAoC,EAAE,QAAkB,EAAE,oBAA+B;QACxG,MAAM,YAAY,GAAG,oBAAoB,CAAC,sBAAsB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAChF,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QAED,QAAQ,YAAY,EAAE,CAAC;YACrB,KAAK,QAAQ;gBACX,OAAO,IAAI,CAAC,eAAe,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,CAAC;YACvE,KAAK,QAAQ,CAAC;YACd,KAAK,SAAS,CAAC;YACf,KAAK,MAAM,CAAC;YACZ,KAAK,WAAW;gBACd,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;YACpC,KAAK,MAAM;gBACT,OAAO;oBACL,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE,CAAC,IAAI,CAAC;iBACd,CAAC;YACJ;gBACE,OAAO,IAAA,qBAAW,EAAC,YAAY,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,iBAAoC,EAAE,oBAA+B;QAC3F,IAAI,CAAC,oBAAoB,IAAI,oBAAoB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/D,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;QACzC,CAAC;QAED,MAAM,IAAI,GAAG,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;YAC9C,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QAC1E,CAAC,CAAC,CAAC;QAEH,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,KAAK,OAAO;gBACV,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;YACjC,KAAK,QAAQ;gBACX,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;YAC9B,KAAK,SAAS;gBACZ,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;YAC/B,KAAK,UAAU;gBACb,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;YAChC;gBACE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;QAC3C,CAAC;IACH,CAAC;CACF;AAlED,oDAkEC"}

View File

@@ -0,0 +1,12 @@
import type { Token, InterfaceDeclaration, ClassDeclaration } from 'typescript';
import { SyntaxKind } from 'typescript';
import { Tsoa } from '@tsoa/runtime';
import { Transformer } from './transformer';
import { TypeResolver } from '../typeResolver';
type OverrideToken = Token<SyntaxKind.QuestionToken> | Token<SyntaxKind.PlusToken> | Token<SyntaxKind.MinusToken> | undefined;
export declare class PropertyTransformer extends Transformer {
transform(resolver: TypeResolver, node: InterfaceDeclaration | ClassDeclaration, overrideToken?: OverrideToken): Tsoa.Property[];
private propertyFromSignature;
private propertyFromDeclaration;
}
export {};

View File

@@ -0,0 +1,99 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PropertyTransformer = void 0;
const typescript_1 = require("typescript");
const transformer_1 = require("./transformer");
const exceptions_1 = require("../exceptions");
const typeResolver_1 = require("../typeResolver");
const initializer_value_1 = require("../initializer-value");
const validatorUtils_1 = require("../../utils/validatorUtils");
const jsDocUtils_1 = require("../../utils/jsDocUtils");
const decoratorUtils_1 = require("../../utils/decoratorUtils");
const flowUtils_1 = require("../../utils/flowUtils");
class PropertyTransformer extends transformer_1.Transformer {
transform(resolver, node, overrideToken) {
const isIgnored = (e) => {
let ignore = (0, jsDocUtils_1.isExistJSDocTag)(e, tag => tag.tagName.text === 'ignore');
ignore = ignore || (e.flags & typescript_1.NodeFlags.ThisNodeHasError) > 0;
return ignore;
};
// Interface model
if ((0, typescript_1.isInterfaceDeclaration)(node)) {
return node.members
.filter((member) => !isIgnored(member) && (0, typescript_1.isPropertySignature)(member))
.map((member) => this.propertyFromSignature(resolver, member, overrideToken));
}
const properties = [];
for (const member of node.members) {
if (!isIgnored(member) && (0, typescript_1.isPropertyDeclaration)(member) && !this.hasStaticModifier(member) && this.hasPublicModifier(member)) {
properties.push(member);
}
}
const classConstructor = node.members.find(member => (0, typescript_1.isConstructorDeclaration)(member));
if (classConstructor && classConstructor.parameters) {
const constructorProperties = classConstructor.parameters.filter(parameter => this.isAccessibleParameter(parameter));
properties.push(...constructorProperties);
}
return properties.map(property => this.propertyFromDeclaration(resolver, property, overrideToken));
}
propertyFromSignature(resolver, propertySignature, overrideToken) {
(0, flowUtils_1.throwUnless)(propertySignature.type, new exceptions_1.GenerateMetadataError(`No valid type found for property declaration.`));
let required = !propertySignature.questionToken;
if (overrideToken && overrideToken.kind === typescript_1.SyntaxKind.MinusToken) {
required = true;
}
else if (overrideToken && overrideToken.kind === typescript_1.SyntaxKind.QuestionToken) {
required = false;
}
const def = typeResolver_1.TypeResolver.getDefault(propertySignature);
const property = {
default: def,
description: resolver.getNodeDescription(propertySignature),
example: resolver.getNodeExample(propertySignature),
format: resolver.getNodeFormat(propertySignature),
name: resolver.getPropertyName(propertySignature),
required,
type: new typeResolver_1.TypeResolver(propertySignature.type, resolver.current, propertySignature.type.parent, resolver.context).resolve(),
validators: (0, validatorUtils_1.getPropertyValidators)(propertySignature) || {},
deprecated: (0, jsDocUtils_1.isExistJSDocTag)(propertySignature, tag => tag.tagName.text === 'deprecated'),
extensions: resolver.getNodeExtension(propertySignature),
};
return property;
}
propertyFromDeclaration(resolver, propertyDeclaration, overrideToken) {
let typeNode = propertyDeclaration.type;
const tsType = resolver.current.typeChecker.getTypeAtLocation(propertyDeclaration);
if (!typeNode) {
// Type is from initializer
typeNode = resolver.current.typeChecker.typeToTypeNode(tsType, undefined, typescript_1.NodeBuilderFlags.NoTruncation);
}
const type = new typeResolver_1.TypeResolver(typeNode, resolver.current, propertyDeclaration, resolver.context, tsType).resolve();
let required = !propertyDeclaration.questionToken && !propertyDeclaration.initializer;
if (overrideToken && overrideToken.kind === typescript_1.SyntaxKind.MinusToken) {
required = true;
}
else if (overrideToken && overrideToken.kind === typescript_1.SyntaxKind.QuestionToken) {
required = false;
}
let def = (0, initializer_value_1.getInitializerValue)(propertyDeclaration.initializer, resolver.current.typeChecker);
if (def === undefined) {
def = typeResolver_1.TypeResolver.getDefault(propertyDeclaration);
}
const property = {
default: def,
description: resolver.getNodeDescription(propertyDeclaration),
example: resolver.getNodeExample(propertyDeclaration),
format: resolver.getNodeFormat(propertyDeclaration),
name: resolver.getPropertyName(propertyDeclaration),
required,
type,
validators: (0, validatorUtils_1.getPropertyValidators)(propertyDeclaration) || {},
// class properties and constructor parameters may be deprecated either via jsdoc annotation or decorator
deprecated: (0, jsDocUtils_1.isExistJSDocTag)(propertyDeclaration, tag => tag.tagName.text === 'deprecated') || (0, decoratorUtils_1.isDecorator)(propertyDeclaration, identifier => identifier.text === 'Deprecated'),
extensions: resolver.getNodeExtension(propertyDeclaration),
};
return property;
}
}
exports.PropertyTransformer = PropertyTransformer;
//# sourceMappingURL=propertyTransformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"propertyTransformer.js","sourceRoot":"","sources":["../../../src/metadataGeneration/transformer/propertyTransformer.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,10 @@
import type { TypeAliasDeclaration, Type } from 'typescript';
import { Tsoa } from '@tsoa/runtime';
import { Transformer } from './transformer';
import { TypeResolver } from '../typeResolver';
export declare class ReferenceTransformer extends Transformer {
static merge(referenceTypes: Tsoa.ReferenceType[]): Tsoa.ReferenceType;
static mergeManyRefObj(many: Tsoa.RefObjectType[]): Tsoa.RefObjectType;
static mergeRefObj(first: Tsoa.RefObjectType, second: Tsoa.RefObjectType): Tsoa.RefObjectType;
transform(declaration: TypeAliasDeclaration, refTypeName: string, resolver: TypeResolver, referencer?: Type): Tsoa.ReferenceType;
}

View File

@@ -0,0 +1,77 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReferenceTransformer = void 0;
const transformer_1 = require("./transformer");
const enumTransformer_1 = require("./enumTransformer");
const typeResolver_1 = require("../typeResolver");
const exceptions_1 = require("../exceptions");
const validatorUtils_1 = require("../../utils/validatorUtils");
class ReferenceTransformer extends transformer_1.Transformer {
static merge(referenceTypes) {
if (referenceTypes.length === 0) {
throw new exceptions_1.GenerateMetadataError('Cannot merge empty reference types array');
}
if (referenceTypes.length === 1) {
return referenceTypes[0];
}
if (referenceTypes.every(refType => refType.dataType === 'refEnum')) {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
return enumTransformer_1.EnumTransformer.mergeMany(referenceTypes);
}
if (referenceTypes.every(refType => refType.dataType === 'refObject')) {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
return this.mergeManyRefObj(referenceTypes);
}
throw new exceptions_1.GenerateMetadataError(`These resolved type merge rules are not defined: ${JSON.stringify(referenceTypes)}`);
}
static mergeManyRefObj(many) {
let merged = this.mergeRefObj(many[0], many[1]);
for (let i = 2; i < many.length; ++i) {
merged = this.mergeRefObj(merged, many[i]);
}
return merged;
}
static mergeRefObj(first, second) {
const description = first.description ? (second.description ? `${first.description}\n${second.description}` : first.description) : second.description;
const deprecated = first.deprecated || second.deprecated;
const example = first.example || second.example;
const properties = [...first.properties, ...second.properties.filter(prop => first.properties.every(firstProp => firstProp.name !== prop.name))];
const mergeAdditionalTypes = (first, second) => {
return {
dataType: 'union',
types: [first, second],
};
};
const additionalProperties = first.additionalProperties
? second.additionalProperties
? mergeAdditionalTypes(first.additionalProperties, second.additionalProperties)
: first.additionalProperties
: second.additionalProperties;
const result = {
dataType: 'refObject',
description,
properties,
additionalProperties,
refName: first.refName,
deprecated,
example,
};
return result;
}
transform(declaration, refTypeName, resolver, referencer) {
const example = resolver.getNodeExample(declaration);
const referenceType = {
dataType: 'refAlias',
default: typeResolver_1.TypeResolver.getDefault(declaration),
description: resolver.getNodeDescription(declaration),
refName: refTypeName,
format: resolver.getNodeFormat(declaration),
type: new typeResolver_1.TypeResolver(declaration.type, resolver.current, declaration, resolver.context, resolver.referencer || referencer).resolve(),
validators: (0, validatorUtils_1.getPropertyValidators)(declaration) || {},
...(example && { example }),
};
return referenceType;
}
}
exports.ReferenceTransformer = ReferenceTransformer;
//# sourceMappingURL=referenceTransformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"referenceTransformer.js","sourceRoot":"","sources":["../../../src/metadataGeneration/transformer/referenceTransformer.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,9 @@
import { type HasModifiers } from 'typescript';
/**
* Transformer responsible to transforming native ts node into tsoa type.
*/
export declare abstract class Transformer {
protected hasPublicModifier(node: HasModifiers): boolean;
protected hasStaticModifier(node: HasModifiers): boolean | undefined;
protected isAccessibleParameter(node: HasModifiers): boolean;
}

View File

@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Transformer = void 0;
const typescript_1 = require("typescript");
/**
* Transformer responsible to transforming native ts node into tsoa type.
*/
class Transformer {
hasPublicModifier(node) {
return (!node.modifiers ||
node.modifiers.every(modifier => {
return modifier.kind !== typescript_1.SyntaxKind.ProtectedKeyword && modifier.kind !== typescript_1.SyntaxKind.PrivateKeyword;
}));
}
hasStaticModifier(node) {
return (node.modifiers &&
node.modifiers.some(modifier => {
return modifier.kind === typescript_1.SyntaxKind.StaticKeyword;
}));
}
isAccessibleParameter(node) {
const modifiers = (0, typescript_1.getModifiers)(node);
if (modifiers == null || modifiers.length === 0) {
return false;
}
// public || public readonly
if (modifiers.some(modifier => modifier.kind === typescript_1.SyntaxKind.PublicKeyword)) {
return true;
}
// readonly, not private readonly, not public readonly
const isReadonly = modifiers.some(modifier => modifier.kind === typescript_1.SyntaxKind.ReadonlyKeyword);
const isProtectedOrPrivate = modifiers.some(modifier => {
return modifier.kind === typescript_1.SyntaxKind.ProtectedKeyword || modifier.kind === typescript_1.SyntaxKind.PrivateKeyword;
});
return isReadonly && !isProtectedOrPrivate;
}
}
exports.Transformer = Transformer;
//# sourceMappingURL=transformer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"transformer.js","sourceRoot":"","sources":["../../../src/metadataGeneration/transformer/transformer.ts"],"names":[],"mappings":";;;AAAA,2CAAyE;AAEzE;;GAEG;AACH,MAAsB,WAAW;IACrB,iBAAiB,CAAC,IAAkB;QAC5C,OAAO,CACL,CAAC,IAAI,CAAC,SAAS;YACf,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE;gBAC9B,OAAO,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,gBAAgB,IAAI,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,cAAc,CAAC;YACtG,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAES,iBAAiB,CAAC,IAAkB;QAC5C,OAAO,CACL,IAAI,CAAC,SAAS;YACd,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAC7B,OAAO,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,aAAa,CAAC;YACpD,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAES,qBAAqB,CAAC,IAAkB;QAChD,MAAM,SAAS,GAAG,IAAA,yBAAY,EAAC,IAAI,CAAC,CAAC;QAErC,IAAI,SAAS,IAAI,IAAI,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,4BAA4B;QAC5B,IAAI,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAC3E,OAAO,IAAI,CAAC;QACd,CAAC;QAED,sDAAsD;QACtD,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,eAAe,CAAC,CAAC;QAC5F,MAAM,oBAAoB,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;YACrD,OAAO,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,gBAAgB,IAAI,QAAQ,CAAC,IAAI,KAAK,uBAAU,CAAC,cAAc,CAAC;QACtG,CAAC,CAAC,CAAC;QACH,OAAO,UAAU,IAAI,CAAC,oBAAoB,CAAC;IAC7C,CAAC;CACF;AAtCD,kCAsCC"}

View File

@@ -0,0 +1,51 @@
import { Tsoa } from '@tsoa/runtime';
import * as ts from 'typescript';
import { MetadataGenerator } from './metadataGenerator';
type UsableDeclaration = ts.InterfaceDeclaration | ts.ClassDeclaration | ts.PropertySignature | ts.TypeAliasDeclaration | ts.EnumMember;
interface Context {
[name: string]: {
type: ts.TypeNode;
name: string;
};
}
export declare class TypeResolver {
private readonly typeNode;
readonly current: MetadataGenerator;
private readonly parentNode?;
context: Context;
readonly referencer?: ts.Type | undefined;
constructor(typeNode: ts.TypeNode, current: MetadataGenerator, parentNode?: ts.Node | undefined, context?: Context, referencer?: ts.Type | undefined);
static clearCache(): void;
resolve(): Tsoa.Type;
private resolveTypeOperatorNode;
private resolveIndexedAccessTypeNode;
private resolveTypeReferenceNode;
private getLiteralValue;
private getDesignatedModels;
private hasFlag;
private getReferencer;
private static typeReferenceToEntityName;
private calcRefTypeName;
private calcMemberJsDocProperties;
private calcTypeName;
private calcTypeReferenceTypeName;
private getReferenceType;
private addToLocalReferenceTypeCache;
private getModelReference;
private getRefTypeName;
private createCircularDependencyResolver;
private nodeIsUsable;
private getModelTypeDeclarations;
private getSymbolAtLocation;
private getModelAdditionalProperties;
private typeArgumentsToContext;
private getModelInheritedProperties;
getNodeDescription(node: UsableDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration | ts.EnumDeclaration): string | undefined;
getNodeFormat(node: ts.Node): string | undefined;
getPropertyName(prop: ts.PropertySignature | ts.PropertyDeclaration | ts.ParameterDeclaration): string;
getNodeExample(node: ts.Node): any;
getNodeExtension(node: ts.Node): Tsoa.Extension[];
private getDecoratorsByIdentifier;
static getDefault(node: ts.Node): any;
}
export {};

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
import * as ts from 'typescript';
import { ExtendedRoutesConfig } from '../cli';
import { Tsoa } from '@tsoa/runtime';
import { Config as BaseConfig } from '@tsoa/runtime';
export declare function generateRoutes<Config extends ExtendedRoutesConfig>(routesConfig: Config, compilerOptions?: ts.CompilerOptions, ignorePaths?: string[],
/**
* pass in cached metadata returned in a previous step to speed things up
*/
metadata?: Tsoa.Metadata, defaultNumberType?: BaseConfig['defaultNumberType']): Promise<Tsoa.Metadata>;

90
node_modules/@tsoa/cli/dist/module/generate-routes.js generated vendored Normal file
View File

@@ -0,0 +1,90 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateRoutes = generateRoutes;
const metadataGenerator_1 = require("../metadataGeneration/metadataGenerator");
const defaultRouteGenerator_1 = require("../routeGeneration/defaultRouteGenerator");
const fs_1 = require("../utils/fs");
const path = require("path");
async function generateRoutes(routesConfig, compilerOptions, ignorePaths,
/**
* pass in cached metadata returned in a previous step to speed things up
*/
metadata, defaultNumberType) {
if (!metadata) {
metadata = new metadataGenerator_1.MetadataGenerator(routesConfig.entryFile, compilerOptions, ignorePaths, routesConfig.controllerPathGlobs, routesConfig.rootSecurity, defaultNumberType).Generate();
}
const routeGenerator = await getRouteGenerator(metadata, routesConfig);
await (0, fs_1.fsMkDir)(routesConfig.routesDir, { recursive: true });
await routeGenerator.GenerateCustomRoutes();
if (routesConfig.multerOpts) {
console.warn('Config MulterOptions is deprecated since v6.4.0 instroduces RegisterRoutes can pass multerOptions,' +
'we will quickly remove this options soon at future version.' +
'(https://github.com/lukeautry/tsoa/issues/1587#issuecomment-2391291433)' +
'(https://github.com/lukeautry/tsoa/pull/1638)');
}
return metadata;
}
async function getRouteGenerator(metadata, routesConfig) {
// default route generator for express/koa/hapi
// custom route generator
const routeGenerator = routesConfig.routeGenerator;
if (routeGenerator !== undefined) {
if (typeof routeGenerator === 'string') {
try {
// try as a module import
const module = (await Promise.resolve(`${routeGenerator}`).then(s => __importStar(require(s))));
return new module.default(metadata, routesConfig);
}
catch (_err) {
// try to find a relative import path
const relativePath = path.relative(__dirname, routeGenerator);
const module = (await Promise.resolve(`${relativePath}`).then(s => __importStar(require(s))));
return new module.default(metadata, routesConfig);
}
}
else {
return new routeGenerator(metadata, routesConfig);
}
}
if (routesConfig.middleware !== undefined || routesConfig.middlewareTemplate !== undefined) {
return new defaultRouteGenerator_1.DefaultRouteGenerator(metadata, routesConfig);
}
else {
routesConfig.middleware = 'express';
return new defaultRouteGenerator_1.DefaultRouteGenerator(metadata, routesConfig);
}
}
//# sourceMappingURL=generate-routes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"generate-routes.js","sourceRoot":"","sources":["../../src/module/generate-routes.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,wCA6BC;AApCD,+EAA4E;AAE5E,oFAAiF;AACjF,oCAAsC;AACtC,6BAA8B;AAGvB,KAAK,UAAU,cAAc,CAClC,YAAoB,EACpB,eAAoC,EACpC,WAAsB;AACtB;;GAEG;AACH,QAAwB,EACxB,iBAAmD;IAEnD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,IAAI,qCAAiB,CAAC,YAAY,CAAC,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,CAAC,mBAAmB,EAAE,YAAY,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC,QAAQ,EAAE,CAAC;IACpL,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAEvE,MAAM,IAAA,YAAO,EAAC,YAAY,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,MAAM,cAAc,CAAC,oBAAoB,EAAE,CAAC;IAE5C,IAAI,YAAY,CAAC,UAAU,EAAE,CAAC;QAC5B,OAAO,CAAC,IAAI,CACV,oGAAoG;YAClG,6DAA6D;YAC7D,yEAAyE;YACzE,+CAA+C,CAClD,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAsC,QAAuB,EAAE,YAAoB;IACjH,+CAA+C;IAC/C,yBAAyB;IACzB,MAAM,cAAc,GAAG,YAAY,CAAC,cAAc,CAAC;IACnD,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC;gBACH,yBAAyB;gBACzB,MAAM,MAAM,GAAG,CAAC,yBAAa,cAAc,uCAAC,CAAiC,CAAC;gBAC9E,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YACpD,CAAC;YAAC,OAAO,IAAI,EAAE,CAAC;gBACd,qCAAqC;gBACrC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;gBAC9D,MAAM,MAAM,GAAG,CAAC,yBAAa,YAAY,uCAAC,CAAiC,CAAC;gBAC5E,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,cAAc,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IACD,IAAI,YAAY,CAAC,UAAU,KAAK,SAAS,IAAI,YAAY,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;QAC3F,OAAO,IAAI,6CAAqB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC3D,CAAC;SAAM,CAAC;QACN,YAAY,CAAC,UAAU,GAAG,SAAS,CAAC;QACpC,OAAO,IAAI,6CAAqB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC"}

View File

@@ -0,0 +1,9 @@
import * as ts from 'typescript';
import { ExtendedSpecConfig } from '../cli';
import { Tsoa, Config } from '@tsoa/runtime';
export declare const getSwaggerOutputPath: (swaggerConfig: ExtendedSpecConfig) => string;
export declare const generateSpec: (swaggerConfig: ExtendedSpecConfig, compilerOptions?: ts.CompilerOptions, ignorePaths?: string[],
/**
* pass in cached metadata returned in a previous step to speed things up
*/
metadata?: Tsoa.Metadata, defaultNumberType?: Config["defaultNumberType"]) => Promise<Tsoa.Metadata>;

79
node_modules/@tsoa/cli/dist/module/generate-spec.js generated vendored Normal file
View File

@@ -0,0 +1,79 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateSpec = exports.getSwaggerOutputPath = void 0;
const YAML = __importStar(require("yaml"));
const metadataGenerator_1 = require("../metadataGeneration/metadataGenerator");
const specGenerator2_1 = require("../swagger/specGenerator2");
const specGenerator3_1 = require("../swagger/specGenerator3");
const specGenerator31_1 = require("../swagger/specGenerator31");
const fs_1 = require("../utils/fs");
const getSwaggerOutputPath = (swaggerConfig) => {
const ext = swaggerConfig.yaml ? 'yaml' : 'json';
const specFileBaseName = swaggerConfig.specFileBaseName || 'swagger';
return `${swaggerConfig.outputDirectory}/${specFileBaseName}.${ext}`;
};
exports.getSwaggerOutputPath = getSwaggerOutputPath;
const generateSpec = async (swaggerConfig, compilerOptions, ignorePaths,
/**
* pass in cached metadata returned in a previous step to speed things up
*/
metadata, defaultNumberType) => {
if (!metadata) {
metadata = new metadataGenerator_1.MetadataGenerator(swaggerConfig.entryFile, compilerOptions, ignorePaths, swaggerConfig.controllerPathGlobs, swaggerConfig.rootSecurity, defaultNumberType).Generate();
}
let spec;
switch (swaggerConfig.specVersion) {
case 2:
spec = new specGenerator2_1.SpecGenerator2(metadata, swaggerConfig).GetSpec();
break;
case 3:
spec = new specGenerator3_1.SpecGenerator3(metadata, swaggerConfig).GetSpec();
break;
case 3.1:
default:
spec = new specGenerator31_1.SpecGenerator31(metadata, swaggerConfig).GetSpec();
}
await (0, fs_1.fsMkDir)(swaggerConfig.outputDirectory, { recursive: true });
let data = JSON.stringify(spec, null, '\t');
if (swaggerConfig.yaml) {
data = YAML.stringify(JSON.parse(data));
}
const outputPath = (0, exports.getSwaggerOutputPath)(swaggerConfig);
await (0, fs_1.fsWriteFile)(outputPath, data, { encoding: 'utf8' });
return metadata;
};
exports.generateSpec = generateSpec;
//# sourceMappingURL=generate-spec.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"generate-spec.js","sourceRoot":"","sources":["../../src/module/generate-spec.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,2CAA6B;AAE7B,+EAA4E;AAE5E,8DAA2D;AAC3D,8DAA2D;AAC3D,gEAA6D;AAC7D,oCAAmD;AAE5C,MAAM,oBAAoB,GAAG,CAAC,aAAiC,EAAE,EAAE;IACxE,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IACjD,MAAM,gBAAgB,GAAG,aAAa,CAAC,gBAAgB,IAAI,SAAS,CAAC;IAErE,OAAO,GAAG,aAAa,CAAC,eAAe,IAAI,gBAAgB,IAAI,GAAG,EAAE,CAAC;AACvE,CAAC,CAAC;AALW,QAAA,oBAAoB,wBAK/B;AAEK,MAAM,YAAY,GAAG,KAAK,EAC/B,aAAiC,EACjC,eAAoC,EACpC,WAAsB;AACtB;;GAEG;AACH,QAAwB,EACxB,iBAA+C,EAC/C,EAAE;IACF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,IAAI,qCAAiB,CAAC,aAAa,CAAC,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,CAAC,mBAAmB,EAAE,aAAa,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC,QAAQ,EAAE,CAAC;IACvL,CAAC;IAED,IAAI,IAAkB,CAAC;IAEvB,QAAQ,aAAa,CAAC,WAAW,EAAE,CAAC;QAClC,KAAK,CAAC;YACJ,IAAI,GAAG,IAAI,+BAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,OAAO,EAAE,CAAC;YAC7D,MAAM;QACR,KAAK,CAAC;YACJ,IAAI,GAAG,IAAI,+BAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,OAAO,EAAE,CAAC;YAC7D,MAAM;QACR,KAAK,GAAG,CAAC;QACT;YACE,IAAI,GAAG,IAAI,iCAAe,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,OAAO,EAAE,CAAC;IAClE,CAAC;IAED,MAAM,IAAA,YAAO,EAAC,aAAa,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAElE,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5C,IAAI,aAAa,CAAC,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,UAAU,GAAG,IAAA,4BAAoB,EAAC,aAAa,CAAC,CAAC;IACvD,MAAM,IAAA,gBAAW,EAAC,UAAU,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IAE1D,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAvCW,QAAA,YAAY,gBAuCvB"}

View File

@@ -0,0 +1,12 @@
import { ExtendedRoutesConfig } from '../cli';
import { Tsoa } from '@tsoa/runtime';
import { AbstractRouteGenerator } from './routeGenerator';
export declare class DefaultRouteGenerator extends AbstractRouteGenerator<ExtendedRoutesConfig> {
pathTransformerFn: (path: string) => string;
template: string;
constructor(metadata: Tsoa.Metadata, options: ExtendedRoutesConfig);
GenerateCustomRoutes(): Promise<void>;
GenerateRoutes(middlewareTemplate: string): Promise<void>;
protected pathTransformer(path: string): string;
buildContent(middlewareTemplate: string): string;
}

View File

@@ -0,0 +1,117 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultRouteGenerator = void 0;
const fs = __importStar(require("fs"));
const handlebars = __importStar(require("handlebars"));
const path = __importStar(require("path"));
const runtime_1 = require("@tsoa/runtime");
const fs_1 = require("../utils/fs");
const pathUtils_1 = require("../utils/pathUtils");
const routeGenerator_1 = require("./routeGenerator");
class DefaultRouteGenerator extends routeGenerator_1.AbstractRouteGenerator {
constructor(metadata, options) {
super(metadata, options);
this.pathTransformerFn = pathUtils_1.convertBracesPathParams;
switch (options.middleware) {
case 'hapi':
this.template = path.join(__dirname, '..', 'routeGeneration/templates/hapi.hbs');
this.pathTransformerFn = (path) => path;
break;
case 'koa':
this.template = path.join(__dirname, '..', 'routeGeneration/templates/koa.hbs');
break;
case 'express':
default:
this.template = path.join(__dirname, '..', 'routeGeneration/templates/express.hbs');
}
if (options.middlewareTemplate) {
this.template = options.middlewareTemplate;
}
}
async GenerateCustomRoutes() {
const data = await (0, fs_1.fsReadFile)(path.join(this.template));
const file = data.toString();
return await this.GenerateRoutes(file);
}
async GenerateRoutes(middlewareTemplate) {
const allowedExtensions = this.options.esm ? ['.ts', '.mts', '.cts'] : ['.ts'];
if (!fs.lstatSync(this.options.routesDir).isDirectory()) {
throw new Error(`routesDir should be a directory`);
}
else if (this.options.routesFileName !== undefined) {
const ext = path.extname(this.options.routesFileName);
if (!allowedExtensions.includes(ext)) {
throw new Error(`routesFileName should be a valid typescript file.`);
}
}
const fileName = `${this.options.routesDir}/${this.options.routesFileName || 'routes.ts'}`;
const content = this.buildContent(middlewareTemplate);
if (await this.shouldWriteFile(fileName, content)) {
await (0, fs_1.fsWriteFile)(fileName, content);
}
}
pathTransformer(path) {
return this.pathTransformerFn(path);
}
buildContent(middlewareTemplate) {
handlebars.registerHelper('json', (context) => {
return JSON.stringify(context);
});
const additionalPropsHelper = (additionalProperties) => {
if (additionalProperties) {
// Then the model for this type explicitly allows additional properties and thus we should assign that
return JSON.stringify(additionalProperties);
}
else if (this.options.noImplicitAdditionalProperties === 'silently-remove-extras') {
return JSON.stringify(false);
}
else if (this.options.noImplicitAdditionalProperties === 'throw-on-extras') {
return JSON.stringify(false);
}
else if (this.options.noImplicitAdditionalProperties === 'ignore') {
return JSON.stringify(true);
}
else {
return (0, runtime_1.assertNever)(this.options.noImplicitAdditionalProperties);
}
};
handlebars.registerHelper('additionalPropsHelper', additionalPropsHelper);
const routesTemplate = handlebars.compile(middlewareTemplate, { noEscape: true });
return routesTemplate(this.buildContext());
}
}
exports.DefaultRouteGenerator = DefaultRouteGenerator;
//# sourceMappingURL=defaultRouteGenerator.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"defaultRouteGenerator.js","sourceRoot":"","sources":["../../src/routeGeneration/defaultRouteGenerator.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,56 @@
import { ExtendedRoutesConfig } from '../cli';
import { Tsoa, TsoaRoute } from '@tsoa/runtime';
export declare abstract class AbstractRouteGenerator<Config extends ExtendedRoutesConfig> {
protected readonly metadata: Tsoa.Metadata;
protected readonly options: Config;
constructor(metadata: Tsoa.Metadata, options: Config);
/**
* This is the entrypoint for a generator to create a custom set of routes
*/
abstract GenerateCustomRoutes(): Promise<void>;
buildModels(): TsoaRoute.Models;
protected pathTransformer(path: string): string;
protected buildContext(): {
authenticationModule: string | undefined;
basePath: string;
canImportByAlias: boolean;
controllers: {
actions: {
fullPath: string;
method: string;
name: string;
parameters: {
[name: string]: TsoaRoute.ParameterSchema;
};
path: string;
uploadFile: boolean;
uploadFileName: {
name: string;
maxCount: number | undefined;
multiple: boolean;
}[];
security: Tsoa.Security[];
successStatus: string | number;
}[];
modulePath: string;
name: string;
path: string;
}[];
environment: NodeJS.ProcessEnv;
iocModule: string | undefined;
minimalSwaggerConfig: {
noImplicitAdditionalProperties: "ignore" | "throw-on-extras" | "silently-remove-extras";
bodyCoercion: boolean;
};
models: TsoaRoute.Models;
useFileUploads: boolean;
multerOpts: Config["multerOpts"];
useSecurity: boolean;
esm: boolean | undefined;
};
protected getRelativeImportPath(fileLocation: string): string;
protected buildPropertySchema(source: Tsoa.Property): TsoaRoute.PropertySchema;
protected buildParameterSchema(source: Tsoa.Parameter): TsoaRoute.ParameterSchema;
protected buildProperty(type: Tsoa.Type): TsoaRoute.PropertySchema;
protected shouldWriteFile(fileName: string, content: string): Promise<boolean>;
}

View File

@@ -0,0 +1,255 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbstractRouteGenerator = void 0;
const path = __importStar(require("path"));
const runtime_1 = require("@tsoa/runtime");
const internalTypeGuards_1 = require("../utils/internalTypeGuards");
const pathUtils_1 = require("../utils/pathUtils");
const fs_1 = require("../utils/fs");
class AbstractRouteGenerator {
constructor(metadata, options) {
this.metadata = metadata;
this.options = options;
}
buildModels() {
const models = {};
Object.keys(this.metadata.referenceTypeMap).forEach(name => {
const referenceType = this.metadata.referenceTypeMap[name];
let model;
if (referenceType.dataType === 'refEnum') {
const refEnumModel = {
dataType: 'refEnum',
enums: referenceType.enums,
};
model = refEnumModel;
}
else if (referenceType.dataType === 'refObject') {
const propertySchemaDictionary = {};
referenceType.properties.forEach(property => {
propertySchemaDictionary[property.name] = this.buildPropertySchema(property);
});
const refObjModel = {
dataType: 'refObject',
properties: propertySchemaDictionary,
};
if (referenceType.additionalProperties) {
refObjModel.additionalProperties = this.buildProperty(referenceType.additionalProperties);
}
else if (this.options.noImplicitAdditionalProperties !== 'ignore') {
refObjModel.additionalProperties = false;
}
else {
// Since Swagger allows "excess properties" (to use a TypeScript term) by default
refObjModel.additionalProperties = true;
}
model = refObjModel;
}
else if (referenceType.dataType === 'refAlias') {
const refType = {
dataType: 'refAlias',
type: {
...this.buildProperty(referenceType.type),
validators: referenceType.validators,
default: referenceType.default,
},
};
model = refType;
}
else {
model = (0, runtime_1.assertNever)(referenceType);
}
models[name] = model;
});
return models;
}
pathTransformer(path) {
return (0, pathUtils_1.convertBracesPathParams)(path);
}
buildContext() {
const authenticationModule = this.options.authenticationModule ? this.getRelativeImportPath(this.options.authenticationModule) : undefined;
const iocModule = this.options.iocModule ? this.getRelativeImportPath(this.options.iocModule) : undefined;
// Left in for backwards compatibility, previously if we're working locally then tsoa runtime code wasn't an importable module but now it is.
const canImportByAlias = true;
const normalisedBasePath = (0, pathUtils_1.normalisePath)(this.options.basePath, '/');
return {
authenticationModule,
basePath: normalisedBasePath,
canImportByAlias,
controllers: this.metadata.controllers.map(controller => {
const normalisedControllerPath = this.pathTransformer((0, pathUtils_1.normalisePath)(controller.path, '/'));
return {
actions: controller.methods.map(method => {
const parameterObjs = {};
method.parameters.forEach(parameter => {
parameterObjs[parameter.parameterName] = this.buildParameterSchema(parameter);
});
const normalisedMethodPath = this.pathTransformer((0, pathUtils_1.normalisePath)(method.path, '/'));
const normalisedFullPath = (0, pathUtils_1.normalisePath)(`${normalisedBasePath}${normalisedControllerPath}${normalisedMethodPath}`, '/', '', false);
const uploadFilesWithDifferentFieldParameter = method.parameters.filter(parameter => parameter.type.dataType === 'file' || (parameter.type.dataType === 'array' && parameter.type.elementType.dataType === 'file'));
return {
fullPath: normalisedFullPath,
method: method.method.toLowerCase(),
name: method.name,
parameters: parameterObjs,
path: normalisedMethodPath,
uploadFile: uploadFilesWithDifferentFieldParameter.length > 0,
uploadFileName: uploadFilesWithDifferentFieldParameter.map(parameter => ({
name: parameter.name,
maxCount: parameter.type.dataType === 'file' ? 1 : undefined,
multiple: parameter.type.dataType === 'array' && parameter.type.elementType.dataType === 'file',
})),
security: method.security,
successStatus: method.successStatus ? method.successStatus : 'undefined',
};
}),
modulePath: this.getRelativeImportPath(controller.location),
name: controller.name,
path: normalisedControllerPath,
};
}),
environment: process.env,
iocModule,
minimalSwaggerConfig: { noImplicitAdditionalProperties: this.options.noImplicitAdditionalProperties, bodyCoercion: this.options.bodyCoercion },
models: this.buildModels(),
useFileUploads: this.metadata.controllers.some(controller => controller.methods.some(method => !!method.parameters.find(parameter => {
if (parameter.type.dataType === 'file') {
return true;
}
else if (parameter.type.dataType === 'array' && parameter.type.elementType.dataType === 'file') {
return true;
}
return false;
}))),
multerOpts: {
limits: {
fileSize: 8388608, // 8mb
},
...this.options.multerOpts,
},
useSecurity: this.metadata.controllers.some(controller => controller.methods.some(method => !!method.security.length)),
esm: this.options.esm,
};
}
getRelativeImportPath(fileLocation) {
const currentExt = path.extname(fileLocation);
let newExtension = this.options.rewriteRelativeImportExtensions ? currentExt : '';
if (this.options.esm && !this.options.rewriteRelativeImportExtensions) {
switch (currentExt) {
case '.ts':
default:
newExtension = '.js';
break;
case '.mts':
newExtension = '.mjs';
break;
case '.cts':
newExtension = '.cjs';
break;
}
}
fileLocation = fileLocation.replace(/\.(ts|mts|cts)$/, ''); // no ts extension in import
return `./${path.relative(this.options.routesDir, fileLocation).replace(/\\/g, '/')}${newExtension}`;
}
buildPropertySchema(source) {
const propertySchema = this.buildProperty(source.type);
propertySchema.default = source.default;
propertySchema.required = source.required ? true : undefined;
if (Object.keys(source.validators).length > 0) {
propertySchema.validators = source.validators;
}
return propertySchema;
}
buildParameterSchema(source) {
const property = this.buildProperty(source.type);
const parameter = {
default: source.default,
in: source.in,
name: source.name,
required: source.required ? true : undefined,
};
const parameterSchema = Object.assign(parameter, property);
if (Object.keys(source.validators).length > 0) {
parameterSchema.validators = source.validators;
}
return parameterSchema;
}
buildProperty(type) {
const schema = {
dataType: type.dataType,
};
if ((0, internalTypeGuards_1.isRefType)(type)) {
schema.dataType = undefined;
schema.ref = type.refName;
}
if (type.dataType === 'array') {
const arrayType = type;
if ((0, internalTypeGuards_1.isRefType)(arrayType.elementType)) {
schema.array = {
dataType: arrayType.elementType.dataType,
ref: arrayType.elementType.refName,
};
}
else {
schema.array = this.buildProperty(arrayType.elementType);
}
}
if (type.dataType === 'enum') {
schema.enums = type.enums;
}
if (type.dataType === 'union' || type.dataType === 'intersection') {
schema.subSchemas = type.types.map(type => this.buildProperty(type));
}
if (type.dataType === 'nestedObjectLiteral') {
const objLiteral = type;
schema.nestedProperties = objLiteral.properties.reduce((acc, prop) => {
return { ...acc, [prop.name]: this.buildPropertySchema(prop) };
}, {});
schema.additionalProperties = objLiteral.additionalProperties && this.buildProperty(objLiteral.additionalProperties);
}
return schema;
}
async shouldWriteFile(fileName, content) {
if (this.options.noWriteIfUnchanged) {
if (await (0, fs_1.fsExists)(fileName)) {
const existingContent = (await (0, fs_1.fsReadFile)(fileName)).toString();
return content !== existingContent;
}
}
return true;
}
}
exports.AbstractRouteGenerator = AbstractRouteGenerator;
//# sourceMappingURL=routeGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,221 @@
/* tslint:disable */
/* eslint-disable */
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import type { TsoaRoute } from '@tsoa/runtime';
import { fetchMiddlewares, ExpressTemplateService } from '@tsoa/runtime';
{{#each controllers}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import { {{name}} } from '{{modulePath}}';
{{/each}}
{{#if authenticationModule}}
import { expressAuthentication } from '{{authenticationModule}}';
// @ts-ignore - no great way to install types from subpackage
{{/if}}
{{#if iocModule}}
import { iocContainer } from '{{iocModule}}';
import type { IocContainer, IocContainerFactory } from '@tsoa/runtime';
{{/if}}
import type { Request as ExRequest, Response as ExResponse, RequestHandler, Router } from 'express';
{{#if useFileUploads}}
{{#if esm}}
import multer from 'multer';
{{else}}
const multer = require('multer');
{{/if}}
{{/if}}
{{#if authenticationModule}}
const expressAuthenticationRecasted = expressAuthentication as (req: ExRequest, securityName: string, scopes?: string[], res?: ExResponse) => Promise<any>;
{{/if}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
const models: TsoaRoute.Models = {
{{#each models}}
"{{@key}}": {
{{#if enums}}
"dataType": "refEnum",
"enums": {{{json enums}}},
{{/if}}
{{#if properties}}
"dataType": "refObject",
"properties": {
{{#each properties}}
"{{@key}}": {{{json this}}},
{{/each}}
},
"additionalProperties": {{{json additionalProperties}}},
{{/if}}
{{#if type}}
"dataType": "refAlias",
"type": {{{json type}}},
{{/if}}
},
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
};
const templateService = new ExpressTemplateService(models, {{{ json minimalSwaggerConfig}}});
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{#if useFileUploads}}
export function RegisterRoutes(app: Router,opts?:{multer?:ReturnType<typeof multer>}) {
{{else}}
export function RegisterRoutes(app: Router) {
{{/if}}
// ###########################################################################################################
// NOTE: If you do not see routes for all of your controllers in this file, then you might not have informed tsoa of where to look
// Please look into the "controllerPathGlobs" config option described in the readme: https://github.com/lukeautry/tsoa
// ###########################################################################################################
{{#if useFileUploads}}
const upload = opts?.multer || multer({{{json multerOpts}}});
{{/if}}
{{#each controllers}}
{{#each actions}}
const args{{../name}}_{{name}}: Record<string, TsoaRoute.ParameterSchema> = {
{{#each parameters}}
{{@key}}: {{{json this}}},
{{/each}}
};
app.{{method}}('{{fullPath}}',
{{#if security.length}}
authenticateMiddleware({{json security}}),
{{/if}}
{{#if uploadFile}}
upload.fields([
{{#each uploadFileName}}
{
name: {{json name}},
{{#if maxCount}}
maxCount: {{maxCount}}
{{/if}}
}{{#if @last}}{{else}},{{/if}}
{{/each}}
]),
{{/if}}
...(fetchMiddlewares<RequestHandler>({{../name}})),
...(fetchMiddlewares<RequestHandler>({{../name}}.prototype.{{name}})),
async function {{../name}}_{{name}}(request: ExRequest, response: ExResponse, next: any) {
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
let validatedArgs: any[] = [];
try {
validatedArgs = templateService.getValidatedArgs({ args: args{{../name}}_{{name}}, request, response });
{{#if ../../iocModule}}
const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer;
const controller: any = await container.get<{{../name}}>({{../name}});
if (typeof controller['setStatus'] === 'function') {
controller.setStatus(undefined);
}
{{else}}
const controller = new {{../name}}();
{{/if}}
await templateService.apiHandler({
methodName: '{{name}}',
controller,
response,
next,
validatedArgs,
successStatus: {{successStatus}},
});
} catch (err) {
return next(err);
}
});
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
{{/each}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{#if useSecurity}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
function authenticateMiddleware(security: TsoaRoute.Security[] = []) {
return async function runAuthenticationMiddleware(request: any, response: any, next: any) {
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
// keep track of failed auth attempts so we can hand back the most
// recent one. This behavior was previously existing so preserving it
// here
const failedAttempts: any[] = [];
const pushAndRethrow = (error: any) => {
failedAttempts.push(error);
throw error;
};
const secMethodOrPromises: Promise<any>[] = [];
for (const secMethod of security) {
if (Object.keys(secMethod).length > 1) {
const secMethodAndPromises: Promise<any>[] = [];
for (const name in secMethod) {
secMethodAndPromises.push(
expressAuthenticationRecasted(request, name, secMethod[name], response)
.catch(pushAndRethrow)
);
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
secMethodOrPromises.push(Promise.all(secMethodAndPromises)
.then(users => { return users[0]; }));
} else {
for (const name in secMethod) {
secMethodOrPromises.push(
expressAuthenticationRecasted(request, name, secMethod[name], response)
.catch(pushAndRethrow)
);
}
}
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
try {
request['user'] = await Promise.any(secMethodOrPromises);
// Response was sent in middleware, abort
if (response.writableEnded) {
return;
}
next();
}
catch(err) {
// Show most recent error as response
const error = failedAttempts.pop();
error.status = error.status || 401;
// Response was sent in middleware, abort
if (response.writableEnded) {
return;
}
next(error);
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
}
{{/if}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa

View File

@@ -0,0 +1,267 @@
/* tslint:disable */
/* eslint-disable */
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import type { TsoaRoute } from '@tsoa/runtime';
import { fetchMiddlewares, HapiTemplateService } from '@tsoa/runtime';
{{#each controllers}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import { {{name}} } from '{{modulePath}}';
{{/each}}
{{#if authenticationModule}}
import { hapiAuthentication } from '{{authenticationModule}}';
// @ts-ignore - no great way to install types from subpackage
{{/if}}
{{#if iocModule}}
import { iocContainer } from '{{iocModule}}';
import type { IocContainer, IocContainerFactory } from '@tsoa/runtime';
{{/if}}
import { boomify, isBoom, type Payload } from '@hapi/boom';
import type { Request, ResponseToolkit, RouteOptionsPreAllOptions } from '@hapi/hapi';
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
const models: TsoaRoute.Models = {
{{#each models}}
"{{@key}}": {
{{#if enums}}
"dataType": "refEnum",
"enums": {{{json enums}}},
{{/if}}
{{#if properties}}
"dataType": "refObject",
"properties": {
{{#each properties}}
"{{@key}}": {{{json this}}},
{{/each}}
},
"additionalProperties": {{{json additionalProperties}}},
{{/if}}
{{#if type}}
"dataType": "refAlias",
"type": {{{json type}}},
{{/if}}
},
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
};
const templateService = new HapiTemplateService(
models,
{{{ json minimalSwaggerConfig }}},
{ boomify, isBoom },
);
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
export function RegisterRoutes(server: any) {
// ###########################################################################################################
// NOTE: If you do not see routes for all of your controllers in this file, then you might not have informed tsoa of where to look
// Please look into the "controllerPathGlobs" config option described in the readme: https://github.com/lukeautry/tsoa
// ###########################################################################################################
{{#each controllers}}
{{#each actions}}
const args{{../name}}_{{name}}: Record<string, TsoaRoute.ParameterSchema> = {
{{#each parameters}}
{{@key}}: {{{json this}}},
{{/each}}
};
server.route({
method: '{{method}}',
path: '{{fullPath}}',
options: {
pre: [
{{#if security.length}}
{
method: authenticateMiddleware({{json security}})
},
{{/if}}
{{#if uploadFile}}
{{#each uploadFileName}}
{
{{#if multiple}}
method: fileUploadMiddleware('{{name}}', true)
{{else}}
method: fileUploadMiddleware('{{name}}', false)
{{/if}}
},
{{/each}}
{{/if}}
...(fetchMiddlewares<RouteOptionsPreAllOptions>({{../name}})),
...(fetchMiddlewares<RouteOptionsPreAllOptions>({{../name}}.prototype.{{name}})),
],
{{#if uploadFile}}
payload: {
output: 'stream',
parse: true,
multipart: true,
allow: 'multipart/form-data'
},
{{/if}}
handler: {{#if ../../iocModule}}async {{/if}}function {{../name}}_{{name}}(request: Request, h: ResponseToolkit) {
let validatedArgs: any[] = [];
try {
validatedArgs = templateService.getValidatedArgs({ args: args{{../name}}_{{name}}, request, h });
} catch (err) {
const error = err as any;
if (isBoom(error)) {
throw error;
}
const boomErr = boomify(error instanceof Error ? error : new Error(error.message), { statusCode: error.status || 500 });
boomErr.output.statusCode = error.status || 500;
boomErr.output.payload = {
name: error.name,
fields: error.fields,
message: error.message,
} as unknown as Payload;
throw boomErr;
}
{{#if ../../iocModule}}
const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(request) : iocContainer;
const controller: any = await container.get<{{../name}}>({{../name}});
if (typeof controller['setStatus'] === 'function') {
controller.setStatus(undefined);
}
{{else}}
const controller = new {{../name}}();
{{/if}}
return templateService.apiHandler({
methodName: '{{name}}',
controller,
h,
validatedArgs,
successStatus: {{successStatus}},
});
}
}
});
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
{{/each}}
{{#if useSecurity}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
function authenticateMiddleware(security: TsoaRoute.Security[] = []) {
return async function runAuthenticationMiddleware(request: any, h: any) {
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
// keep track of failed auth attempts so we can hand back the most
// recent one. This behavior was previously existing so preserving it
// here
const failedAttempts: any[] = [];
const pushAndRethrow = (error: any) => {
failedAttempts.push(error);
throw error;
};
const secMethodOrPromises: Promise<any>[] = [];
for (const secMethod of security) {
if (Object.keys(secMethod).length > 1) {
const secMethodAndPromises: Promise<any>[] = [];
for (const name in secMethod) {
secMethodAndPromises.push(
hapiAuthentication(request, name, secMethod[name])
.catch(pushAndRethrow)
);
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
secMethodOrPromises.push(Promise.all(secMethodAndPromises)
.then(users => { return users[0]; }));
} else {
for (const name in secMethod) {
secMethodOrPromises.push(
hapiAuthentication(request, name, secMethod[name])
.catch(pushAndRethrow)
);
}
}
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
try {
request['user'] = await Promise.any(secMethodOrPromises);
return request['user'];
}
catch(err) {
// Show most recent error as response
const error = failedAttempts.pop();
if (isBoom(error)) {
throw error;
}
const boomErr = boomify(error instanceof Error ? error : new Error(error.message), { statusCode: error.status || 500 });
boomErr.output.statusCode = error.status || 401;
boomErr.output.payload = {
name: error.name,
message: error.message,
} as unknown as Payload;
throw boomErr;
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
}
{{/if}}
{{#if useFileUploads}}
function fileUploadMiddleware(fieldname: string, multiple: boolean = false) {
return (request: Request, h: any) => {
if (!request.payload[fieldname]) {
return h.response(`${fieldname} is a required file(s).`).code(400);
}
const calculateFileInfo = (reqFile: any) => new Promise((resolve, reject) => {
const originalname = reqFile.hapi.filename;
const headers = reqFile.hapi.headers;
const contentTransferEncoding = headers['content-transfer-encoding'];
const encoding = contentTransferEncoding &&
contentTransferEncoding[0] &&
contentTransferEncoding[0].toLowerCase() || '7bit';
const mimetype = headers['content-type'] || 'text/plain';
const buffer = reqFile._data
return resolve({
fieldname,
originalname,
buffer,
encoding,
mimetype,
filename: originalname,
size: buffer.toString().length,
})
});
if (!multiple) {
return calculateFileInfo(request.payload[fieldname])
.then(fileMetadata => {
request.payload[fieldname] = fileMetadata;
return h.continue;
})
.catch(err => h.response(err.toString()).code(500));
} else {
const promises = request.payload[fieldname].map((reqFile: any) => calculateFileInfo(reqFile));
return Promise.all(promises)
.then(filesMetadata => {
request.payload[fieldname] = filesMetadata;
return h.continue;
})
.catch(err => h.response(err.toString()).code(500));
}
};
}
{{/if}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa

View File

@@ -0,0 +1,218 @@
/* tslint:disable */
/* eslint-disable */
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import type { TsoaRoute } from '@tsoa/runtime';
import { fetchMiddlewares, KoaTemplateService } from '@tsoa/runtime';
{{#each controllers}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import { {{name}} } from '{{modulePath}}';
{{/each}}
{{#if authenticationModule}}
import { koaAuthentication } from '{{authenticationModule}}';
// @ts-ignore - no great way to install types from subpackage
{{/if}}
{{#if iocModule}}
import { iocContainer } from '{{iocModule}}';
import type { IocContainer, IocContainerFactory } from '@tsoa/runtime';
{{/if}}
import type { Context, Next, Middleware, Request as KRequest, Response as KResponse } from 'koa';
import type * as KoaRouter from '@koa/router';
{{#if useFileUploads}}
{{#if esm}}
import multer from '@koa/multer';
{{else}}
const multer = require('@koa/multer');
{{/if}}
{{/if}}
{{#if authenticationModule}}
const koaAuthenticationRecasted = koaAuthentication as (req: KRequest, securityName: string, scopes?: string[], res?: KResponse) => Promise<any>;
{{/if}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
const models: TsoaRoute.Models = {
{{#each models}}
"{{@key}}": {
{{#if enums}}
"dataType": "refEnum",
"enums": {{{json enums}}},
{{/if}}
{{#if properties}}
"dataType": "refObject",
"properties": {
{{#each properties}}
"{{@key}}": {{{json this}}},
{{/each}}
},
"additionalProperties": {{{json additionalProperties}}},
{{/if}}
{{#if type}}
"dataType": "refAlias",
"type": {{{json type}}},
{{/if}}
},
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
};
const templateService = new KoaTemplateService(models, {{{ json minimalSwaggerConfig }}});
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{#if useFileUploads}}
export function RegisterRoutes(router: KoaRouter,opts?:{multer?:ReturnType<typeof multer>}) {
{{else}}
export function RegisterRoutes(router: KoaRouter) {
{{/if}}
// ###########################################################################################################
// NOTE: If you do not see routes for all of your controllers in this file, then you might not have informed tsoa of where to look
// Please look into the "controllerPathGlobs" config option described in the readme: https://github.com/lukeautry/tsoa
// ###########################################################################################################
{{#if useFileUploads}}
const upload = opts?.multer || multer({{{json multerOpts}}});
{{/if}}
{{#each controllers}}
{{#each actions}}
const args{{../name}}_{{name}}: Record<string, TsoaRoute.ParameterSchema> = {
{{#each parameters}}
{{@key}}: {{{json this}}},
{{/each}}
};
router.{{method}}('{{fullPath}}',
{{#if security.length}}
authenticateMiddleware({{json security}}),
{{/if}}
{{#if uploadFile}}
upload.fields([
{{#each uploadFileName}}
{
name: {{json name}},
{{#if maxCount}}
maxCount: {{maxCount}}
{{/if}}
}{{#if @last}}{{else}},{{/if}}
{{/each}}
]),
{{/if}}
...(fetchMiddlewares<Middleware>({{../name}})),
...(fetchMiddlewares<Middleware>({{../name}}.prototype.{{name}})),
async function {{../name}}_{{name}}(context: Context, next: Next) {
let validatedArgs: any[] = [];
try {
validatedArgs = templateService.getValidatedArgs({ args: args{{../name}}_{{name}}, context, next });
} catch (err) {
const error = err as any;
error.message ||= JSON.stringify({ fields: error.fields });
context.status = error.status;
context.throw(context.status, error.message, error);
}
{{#if ../../iocModule}}
const container: IocContainer = typeof iocContainer === 'function' ? (iocContainer as IocContainerFactory)(context.request) : iocContainer;
const controller: any = await container.get<{{../name}}>({{../name}});
if (typeof controller['setStatus'] === 'function') {
controller.setStatus(undefined);
}
{{else}}
const controller = new {{../name}}();
{{/if}}
return templateService.apiHandler({
methodName: '{{name}}',
controller,
context,
validatedArgs,
successStatus: {{successStatus}},
});
});
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
{{/each}}
{{/each}}
{{#if useSecurity}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
function authenticateMiddleware(security: TsoaRoute.Security[] = []) {
return async function runAuthenticationMiddleware(context: any, next: any) {
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
// keep track of failed auth attempts so we can hand back the most
// recent one. This behavior was previously existing so preserving it
// here
const failedAttempts: any[] = [];
const pushAndRethrow = (error: any) => {
failedAttempts.push(error);
throw error;
};
const secMethodOrPromises: Promise<any>[] = [];
for (const secMethod of security) {
if (Object.keys(secMethod).length > 1) {
const secMethodAndPromises: Promise<any>[] = [];
for (const name in secMethod) {
secMethodAndPromises.push(
koaAuthenticationRecasted(context.request, name, secMethod[name], context.response)
.catch(pushAndRethrow)
);
}
secMethodOrPromises.push(Promise.all(secMethodAndPromises)
.then(users => { return users[0]; }));
} else {
for (const name in secMethod) {
secMethodOrPromises.push(
koaAuthenticationRecasted(context.request, name, secMethod[name], context.response)
.catch(pushAndRethrow)
);
}
}
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
let success;
try {
const user = await Promise.any(secMethodOrPromises);
success = true;
context.request['user'] = user;
}
catch(err) {
// Response was sent in middleware, abort
if(context.response.body) {
return;
}
// Show most recent error as response
const error = failedAttempts.pop();
context.status = error.status || 401;
context.throw(context.status, error.message, error);
}
// Response was sent in middleware, abort
if(context.response.body) {
return;
}
if (success) {
await next();
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
}
{{/if}}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
}
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa

33
node_modules/@tsoa/cli/dist/swagger/specGenerator.d.ts generated vendored Normal file
View File

@@ -0,0 +1,33 @@
import { ExtendedSpecConfig } from '../cli';
import { Tsoa, Swagger } from '@tsoa/runtime';
export declare abstract class SpecGenerator {
protected readonly metadata: Tsoa.Metadata;
protected readonly config: ExtendedSpecConfig;
constructor(metadata: Tsoa.Metadata, config: ExtendedSpecConfig);
protected buildAdditionalProperties(type: Tsoa.Type): Swagger.BaseSchema<unknown>;
protected buildOperationIdTemplate(inlineTemplate: string): HandlebarsTemplateDelegate<any>;
protected getOperationId(controllerName: string, method: Tsoa.Method): string;
throwIfNotDataFormat(strToTest: string): Swagger.DataFormat;
throwIfNotDataType(strToTest: string): Swagger.DataType;
protected getSwaggerType(type: Tsoa.Type, title?: string): Swagger.BaseSchema;
protected abstract getSwaggerTypeForUnionType(type: Tsoa.UnionType, title?: string): Swagger.BaseSchema;
protected abstract getSwaggerTypeForIntersectionType(type: Tsoa.IntersectionType, title?: string): Swagger.BaseSchema;
protected abstract buildProperties(properties: Tsoa.Property[]): {
[propertyName: string]: Swagger.Schema2;
} | {
[propertyName: string]: Swagger.Schema3;
} | {
[propertyName: string]: Swagger.Schema31;
};
getSwaggerTypeForObjectLiteral(objectLiteral: Tsoa.NestedObjectLiteralType, title?: string): Swagger.BaseSchema;
protected getSwaggerTypeForReferenceType(_referenceType: Tsoa.ReferenceType): Swagger.BaseSchema;
protected getSwaggerTypeForVoid(_dataType: 'void' | 'undefined'): Swagger.BaseSchema;
protected determineImplicitAdditionalPropertiesValue: () => boolean;
protected getSwaggerTypeForPrimitiveType(dataType: Tsoa.PrimitiveTypeLiteral): Swagger.BaseSchema;
protected getSwaggerTypeForArrayType(arrayType: Tsoa.ArrayType, title?: string): Swagger.BaseSchema;
protected determineTypesUsedInEnum(anEnum: Array<string | number | boolean | null>): Set<"string" | "number" | "boolean">;
protected abstract getSwaggerTypeForEnumType(enumType: Tsoa.EnumType, title?: string): Swagger.Schema2 | Swagger.Schema3;
protected hasUndefined(property: Tsoa.Property): boolean;
protected queriesPropertyToQueryParameter(property: Tsoa.Property): Tsoa.Parameter;
protected isRequiredWithoutDefault(prop: Tsoa.Property | Tsoa.Parameter): boolean | undefined;
}

251
node_modules/@tsoa/cli/dist/swagger/specGenerator.js generated vendored Normal file
View File

@@ -0,0 +1,251 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpecGenerator = void 0;
const runtime_1 = require("@tsoa/runtime");
const handlebars = __importStar(require("handlebars"));
class SpecGenerator {
constructor(metadata, config) {
this.metadata = metadata;
this.config = config;
this.determineImplicitAdditionalPropertiesValue = () => {
if (this.config.noImplicitAdditionalProperties === 'silently-remove-extras') {
return false;
}
else if (this.config.noImplicitAdditionalProperties === 'throw-on-extras') {
return false;
}
else if (this.config.noImplicitAdditionalProperties === 'ignore') {
return true;
}
else {
return (0, runtime_1.assertNever)(this.config.noImplicitAdditionalProperties);
}
};
}
buildAdditionalProperties(type) {
return this.getSwaggerType(type);
}
buildOperationIdTemplate(inlineTemplate) {
handlebars.registerHelper('titleCase', (value) => (value ? value.charAt(0).toUpperCase() + value.slice(1) : value));
handlebars.registerHelper('replace', (subject, searchValue, withValue = '') => (subject ? subject.replace(searchValue, withValue) : subject));
return handlebars.compile(inlineTemplate, { noEscape: true });
}
getOperationId(controllerName, method) {
return this.buildOperationIdTemplate(this.config.operationIdTemplate ?? '{{titleCase method.name}}')({
method,
controllerName,
});
}
throwIfNotDataFormat(strToTest) {
const guiltyUntilInnocent = strToTest;
if (guiltyUntilInnocent === 'int32' ||
guiltyUntilInnocent === 'int64' ||
guiltyUntilInnocent === 'float' ||
guiltyUntilInnocent === 'double' ||
guiltyUntilInnocent === 'byte' ||
guiltyUntilInnocent === 'binary' ||
guiltyUntilInnocent === 'date' ||
guiltyUntilInnocent === 'date-time' ||
guiltyUntilInnocent === 'password') {
return guiltyUntilInnocent;
}
else {
return (0, runtime_1.assertNever)(guiltyUntilInnocent);
}
}
throwIfNotDataType(strToTest) {
const guiltyUntilInnocent = strToTest;
if (guiltyUntilInnocent === 'array' ||
guiltyUntilInnocent === 'boolean' ||
guiltyUntilInnocent === 'integer' ||
guiltyUntilInnocent === 'file' ||
guiltyUntilInnocent === 'number' ||
guiltyUntilInnocent === 'object' ||
guiltyUntilInnocent === 'string' ||
guiltyUntilInnocent === 'undefined') {
return guiltyUntilInnocent;
}
else {
return (0, runtime_1.assertNever)(guiltyUntilInnocent);
}
}
getSwaggerType(type, title) {
if (type.dataType === 'void' || type.dataType === 'undefined') {
return this.getSwaggerTypeForVoid(type.dataType);
}
else if (type.dataType === 'refEnum' || type.dataType === 'refObject' || type.dataType === 'refAlias') {
return this.getSwaggerTypeForReferenceType(type);
}
else if (type.dataType === 'any' ||
type.dataType === 'binary' ||
type.dataType === 'boolean' ||
type.dataType === 'buffer' ||
type.dataType === 'byte' ||
type.dataType === 'date' ||
type.dataType === 'datetime' ||
type.dataType === 'double' ||
type.dataType === 'float' ||
type.dataType === 'file' ||
type.dataType === 'integer' ||
type.dataType === 'long' ||
type.dataType === 'object' ||
type.dataType === 'string') {
return this.getSwaggerTypeForPrimitiveType(type.dataType);
}
else if (type.dataType === 'array') {
return this.getSwaggerTypeForArrayType(type, title);
}
else if (type.dataType === 'enum') {
return this.getSwaggerTypeForEnumType(type, title);
}
else if (type.dataType === 'union') {
return this.getSwaggerTypeForUnionType(type, title);
}
else if (type.dataType === 'intersection') {
return this.getSwaggerTypeForIntersectionType(type, title);
}
else if (type.dataType === 'nestedObjectLiteral') {
return this.getSwaggerTypeForObjectLiteral(type, title);
}
else if (type.dataType === 'tuple') {
throw new Error('Tuple types are only supported in OpenAPI 3.1+');
}
else {
return (0, runtime_1.assertNever)(type);
}
}
getSwaggerTypeForObjectLiteral(objectLiteral, title) {
const properties = this.buildProperties(objectLiteral.properties);
const additionalProperties = objectLiteral.additionalProperties && this.getSwaggerType(objectLiteral.additionalProperties);
const required = objectLiteral.properties.filter(prop => this.isRequiredWithoutDefault(prop) && !this.hasUndefined(prop)).map(prop => prop.name);
// An empty list required: [] is not valid.
// If all properties are optional, do not specify the required keyword.
return {
...(title && { title }),
properties,
...(additionalProperties && { additionalProperties }),
...(required && required.length && { required }),
type: 'object',
};
}
getSwaggerTypeForReferenceType(_referenceType) {
return {
// Don't set additionalProperties value here since it will be set within the $ref's model when that $ref gets created
};
}
getSwaggerTypeForVoid(_dataType) {
// Described here: https://swagger.io/docs/specification/describing-responses/#empty
const voidSchema = {
// isn't allowed to have additionalProperties at all (meaning not a boolean or object)
};
return voidSchema;
}
getSwaggerTypeForPrimitiveType(dataType) {
if (dataType === 'object') {
if (process.env.NODE_ENV !== 'tsoa_test') {
// eslint-disable-next-line no-console
console.warn(`The type Object is discouraged. Please consider using an interface such as:
export interface IStringToStringDictionary {
[key: string]: string;
}
// or
export interface IRecordOfAny {
[key: string]: any;
}
`);
}
}
const map = {
any: {
// While the any type is discouraged, it does explicitly allows anything, so it should always allow additionalProperties
additionalProperties: true,
},
binary: { type: 'string', format: 'binary' },
boolean: { type: 'boolean' },
buffer: { type: 'string', format: 'byte' },
byte: { type: 'string', format: 'byte' },
date: { type: 'string', format: 'date' },
datetime: { type: 'string', format: 'date-time' },
double: { type: 'number', format: 'double' },
file: { type: 'file' },
float: { type: 'number', format: 'float' },
integer: { type: 'integer', format: 'int32' },
long: { type: 'integer', format: 'int64' },
object: {
additionalProperties: this.determineImplicitAdditionalPropertiesValue(),
type: 'object',
},
string: { type: 'string' },
};
return map[dataType];
}
getSwaggerTypeForArrayType(arrayType, title) {
return {
items: this.getSwaggerType(arrayType.elementType, title),
type: 'array',
};
}
determineTypesUsedInEnum(anEnum) {
const typesUsedInEnum = anEnum.reduce((theSet, curr) => {
const typeUsed = curr === null ? 'number' : typeof curr;
theSet.add(typeUsed);
return theSet;
}, new Set());
return typesUsedInEnum;
}
hasUndefined(property) {
return property.type.dataType === 'undefined' || (property.type.dataType === 'union' && property.type.types.some(type => type.dataType === 'undefined'));
}
queriesPropertyToQueryParameter(property) {
return {
parameterName: property.name,
example: [property.example],
description: property.description,
in: 'query',
name: property.name,
required: this.isRequiredWithoutDefault(property),
type: property.type,
default: property.default,
validators: property.validators,
deprecated: property.deprecated,
};
}
isRequiredWithoutDefault(prop) {
return prop.required && prop.default == null;
}
}
exports.SpecGenerator = SpecGenerator;
//# sourceMappingURL=specGenerator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,27 @@
import { Tsoa, Swagger } from '@tsoa/runtime';
import { SpecGenerator } from './specGenerator';
import { ExtendedSpecConfig } from '../cli';
export declare class SpecGenerator2 extends SpecGenerator {
protected readonly metadata: Tsoa.Metadata;
protected readonly config: ExtendedSpecConfig;
constructor(metadata: Tsoa.Metadata, config: ExtendedSpecConfig);
GetSpec(): Swagger.Spec2;
private buildDefinitions;
private buildPaths;
private buildMethod;
protected buildOperation(controllerName: string, method: Tsoa.Method, defaultProduces?: string[]): Swagger.Operation;
private buildBodyPropParameter;
private buildQueriesParameter;
private buildParameter;
protected buildProperties(source: Tsoa.Property[]): {
[propertyName: string]: Swagger.Schema2;
};
protected getSwaggerTypeForUnionType(type: Tsoa.UnionType): Swagger.BaseSchema<unknown>;
protected getSwaggerTypeForIntersectionType(type: Tsoa.IntersectionType): {
type: string;
properties: {};
};
protected getSwaggerTypeForReferenceType(referenceType: Tsoa.ReferenceType): Swagger.BaseSchema;
private decideEnumType;
protected getSwaggerTypeForEnumType(enumType: Tsoa.EnumType): Swagger.Schema2;
}

464
node_modules/@tsoa/cli/dist/swagger/specGenerator2.js generated vendored Normal file
View File

@@ -0,0 +1,464 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpecGenerator2 = void 0;
const merge_anything_1 = require("merge-anything");
const ts_deepmerge_1 = require("ts-deepmerge");
const runtime_1 = require("@tsoa/runtime");
const specGenerator_1 = require("./specGenerator");
const isVoidType_1 = require("../utils/isVoidType");
const pathUtils_1 = require("../utils/pathUtils");
const swaggerUtils_1 = require("../utils/swaggerUtils");
const validatorUtils_1 = require("../utils/validatorUtils");
class SpecGenerator2 extends specGenerator_1.SpecGenerator {
constructor(metadata, config) {
super(metadata, config);
this.metadata = metadata;
this.config = config;
}
GetSpec() {
let spec = {
basePath: (0, pathUtils_1.normalisePath)(this.config.basePath, '/', undefined, false),
consumes: [swaggerUtils_1.DEFAULT_REQUEST_MEDIA_TYPE],
definitions: this.buildDefinitions(),
info: {
title: '',
},
paths: this.buildPaths(),
produces: [swaggerUtils_1.DEFAULT_RESPONSE_MEDIA_TYPE],
swagger: '2.0',
};
const securityDefinitions = this.config.securityDefinitions ? this.config.securityDefinitions : {};
const supportedSchemes = ['basic', 'apiKey', 'oauth2'];
for (const { type } of Object.values(securityDefinitions)) {
if (!supportedSchemes.includes(type)) {
throw new Error(`Swagger 2.0 does not support "${type}" security scheme (allowed values: ${supportedSchemes.join(',')})`);
}
}
spec.securityDefinitions = securityDefinitions;
if (this.config.name) {
spec.info.title = this.config.name;
}
if (this.config.version) {
spec.info.version = this.config.version;
}
if (this.config.host) {
spec.host = this.config.host;
}
if (this.config.description) {
spec.info.description = this.config.description;
}
if (this.config.termsOfService) {
spec.info.termsOfService = this.config.termsOfService;
}
if (this.config.tags) {
spec.tags = this.config.tags;
}
if (this.config.license) {
spec.info.license = { name: this.config.license };
}
if (this.config.contact) {
spec.info.contact = this.config.contact;
}
if (this.config.spec) {
this.config.specMerging = this.config.specMerging || 'immediate';
const mergeFuncs = {
immediate: Object.assign,
recursive: merge_anything_1.merge,
deepmerge: (spec, merge) => (0, ts_deepmerge_1.merge)(spec, merge),
};
spec = mergeFuncs[this.config.specMerging](spec, this.config.spec);
}
if (this.config.schemes) {
spec.schemes = this.config.schemes;
}
return spec;
}
buildDefinitions() {
const definitions = {};
Object.keys(this.metadata.referenceTypeMap).map(typeName => {
const referenceType = this.metadata.referenceTypeMap[typeName];
if (referenceType.dataType === 'refObject') {
const required = referenceType.properties.filter(p => this.isRequiredWithoutDefault(p) && !this.hasUndefined(p)).map(p => p.name);
definitions[referenceType.refName] = {
description: referenceType.description,
properties: this.buildProperties(referenceType.properties),
required: required && required.length > 0 ? Array.from(new Set(required)) : undefined,
type: 'object',
};
if (referenceType.additionalProperties) {
definitions[referenceType.refName].additionalProperties = this.buildAdditionalProperties(referenceType.additionalProperties);
}
else {
// Since additionalProperties was not explicitly set in the TypeScript interface for this model
// ...we need to make a decision
definitions[referenceType.refName].additionalProperties = this.determineImplicitAdditionalPropertiesValue();
}
if (referenceType.example) {
definitions[referenceType.refName].example = referenceType.example;
}
}
else if (referenceType.dataType === 'refEnum') {
definitions[referenceType.refName] = {
description: referenceType.description,
enum: referenceType.enums,
type: this.decideEnumType(referenceType.enums, referenceType.refName),
};
if (this.config.xEnumVarnames && referenceType.enumVarnames !== undefined && referenceType.enums.length === referenceType.enumVarnames.length) {
definitions[referenceType.refName]['x-enum-varnames'] = referenceType.enumVarnames;
}
if (referenceType.example) {
definitions[referenceType.refName].example = referenceType.example;
}
}
else if (referenceType.dataType === 'refAlias') {
const swaggerType = this.getSwaggerType(referenceType.type);
const format = referenceType.format;
const validators = Object.keys(referenceType.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.reduce((acc, key) => {
return {
...acc,
[key]: referenceType.validators[key].value,
};
}, {});
definitions[referenceType.refName] = {
...swaggerType,
default: referenceType.default || swaggerType.default,
example: referenceType.example,
format: format || swaggerType.format,
description: referenceType.description,
...validators,
};
}
else {
(0, runtime_1.assertNever)(referenceType);
}
if (referenceType.deprecated) {
definitions[referenceType.refName]['x-deprecated'] = true;
}
});
return definitions;
}
buildPaths() {
const paths = {};
this.metadata.controllers.forEach(controller => {
const normalisedControllerPath = (0, pathUtils_1.normalisePath)(controller.path, '/');
// construct documentation using all methods except @Hidden
controller.methods
.filter(method => !method.isHidden)
.forEach(method => {
const normalisedMethodPath = (0, pathUtils_1.normalisePath)(method.path, '/');
let path = (0, pathUtils_1.normalisePath)(`${normalisedControllerPath}${normalisedMethodPath}`, '/', '', false);
path = (0, pathUtils_1.convertColonPathParams)(path);
paths[path] = paths[path] || {};
this.buildMethod(controller.name, method, paths[path], controller.produces);
});
});
return paths;
}
buildMethod(controllerName, method, pathObject, defaultProduces) {
const pathMethod = (pathObject[method.method] = this.buildOperation(controllerName, method, defaultProduces));
pathMethod.description = method.description;
pathMethod.summary = method.summary;
pathMethod.tags = method.tags;
// Use operationId tag otherwise fallback to generated. Warning: This doesn't check uniqueness.
pathMethod.operationId = method.operationId || pathMethod.operationId;
if (method.deprecated) {
pathMethod.deprecated = method.deprecated;
}
if (method.security) {
pathMethod.security = method.security;
}
const queriesParams = method.parameters.filter(p => p.in === 'queries');
pathMethod.parameters = method.parameters
.filter(p => {
return ['request', 'body-prop', 'res', 'queries', 'request-prop'].indexOf(p.in) === -1;
})
.map(p => this.buildParameter(p));
if (queriesParams.length > 1) {
throw new Error('Only one queries parameter allowed per controller method.');
}
if (queriesParams.length === 1) {
pathMethod.parameters.push(...this.buildQueriesParameter(queriesParams[0]));
}
const bodyPropParameter = this.buildBodyPropParameter(controllerName, method);
if (bodyPropParameter) {
pathMethod.parameters.push(bodyPropParameter);
}
if (pathMethod.parameters.filter((p) => p.in === 'body').length > 1) {
throw new Error('Only one body parameter allowed per controller method.');
}
method.extensions.forEach(ext => (pathMethod[ext.key] = ext.value));
}
buildOperation(controllerName, method, defaultProduces) {
const swaggerResponses = {};
let produces = [];
method.responses.forEach((res) => {
swaggerResponses[res.name] = {
description: res.description,
};
if (res.schema && !(0, isVoidType_1.isVoidType)(res.schema)) {
if (res.produces) {
produces.push(...res.produces);
}
swaggerResponses[res.name].schema = this.getSwaggerType(res.schema);
}
if (res.examples && res.examples[0]) {
if ((res.exampleLabels?.filter(e => e).length || 0) > 0) {
console.warn('Example labels are not supported in OpenAPI 2');
}
swaggerResponses[res.name].examples = { [swaggerUtils_1.DEFAULT_RESPONSE_MEDIA_TYPE]: res.examples[0] };
}
if (res.headers) {
const headers = {};
if (res.headers.dataType === 'refObject' || res.headers.dataType === 'nestedObjectLiteral') {
res.headers.properties.forEach((each) => {
headers[each.name] = {
...this.getSwaggerType(each.type),
description: each.description,
};
});
}
else {
(0, runtime_1.assertNever)(res.headers);
}
swaggerResponses[res.name].headers = headers;
}
});
produces = Array.from(new Set(produces.filter(p => p !== undefined)));
if (produces.length === 0) {
produces = defaultProduces || [swaggerUtils_1.DEFAULT_RESPONSE_MEDIA_TYPE];
}
const operation = {
operationId: this.getOperationId(controllerName, method),
produces: produces,
responses: swaggerResponses,
};
const hasBody = method.parameters.some(p => p.in === 'body');
const hasFormData = method.parameters.some(p => p.in === 'formData');
if (hasBody || hasFormData) {
operation.consumes = [];
if (hasBody) {
operation.consumes.push(method.consumes || swaggerUtils_1.DEFAULT_REQUEST_MEDIA_TYPE);
}
if (hasFormData) {
operation.consumes.push('multipart/form-data');
}
}
return operation;
}
buildBodyPropParameter(controllerName, method) {
const properties = {};
const required = [];
method.parameters
.filter(p => p.in === 'body-prop')
.forEach(p => {
properties[p.name] = this.getSwaggerType(p.type);
properties[p.name].default = p.default;
properties[p.name].description = p.description;
properties[p.name].example = p.example === undefined ? undefined : p.example[0];
if (this.isRequiredWithoutDefault(p)) {
required.push(p.name);
}
});
if (!Object.keys(properties).length) {
return;
}
const parameter = {
in: 'body',
name: 'body',
schema: {
properties,
title: `${this.getOperationId(controllerName, method)}Body`,
type: 'object',
},
};
if (required.length) {
parameter.schema.required = required;
}
return parameter;
}
buildQueriesParameter(source) {
if (source.type.dataType === 'refObject' || source.type.dataType === 'nestedObjectLiteral') {
const properties = source.type.properties;
return properties.map(property => this.buildParameter(this.queriesPropertyToQueryParameter(property)));
}
throw new Error(`Queries '${source.name}' parameter must be an object.`);
}
buildParameter(source) {
let type = source.type;
if (source.in !== 'body' && source.type.dataType === 'refEnum') {
// swagger does not support referencing enums
// (except for body parameters), so we have to inline it
type = {
dataType: 'enum',
enums: source.type.enums,
};
}
const parameterType = this.getSwaggerType(type);
let parameter = {
default: source.default,
description: source.description,
in: source.in,
name: source.name,
required: this.isRequiredWithoutDefault(source),
...(source.deprecated ? { 'x-deprecated': true } : {}),
...(parameterType.$ref ? { schema: parameterType } : {}),
...(parameterType.format ? { format: this.throwIfNotDataFormat(parameterType.format) } : {}),
};
if (runtime_1.Swagger.isQueryParameter(parameter) && parameterType.type === 'array') {
parameter.collectionFormat = 'multi';
}
if (parameter.schema) {
return parameter;
}
const validatorObjs = {};
Object.keys(source.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.forEach(key => {
validatorObjs[key] = source.validators[key].value;
});
if (source.in === 'body' && source.type.dataType === 'array') {
parameter.schema = {
items: parameterType.items,
type: 'array',
};
}
else {
if (source.type.dataType === 'any') {
if (source.in === 'body') {
parameter.schema = { type: 'object' };
}
else {
parameter.type = 'string';
}
}
else {
if (parameterType.type) {
parameter.type = this.throwIfNotDataType(parameterType.type);
}
parameter.items = parameterType.items;
parameter.enum = parameterType.enum;
}
}
if (parameter.schema) {
parameter.schema = Object.assign({}, parameter.schema, validatorObjs);
}
else {
parameter = Object.assign({}, parameter, validatorObjs);
}
return parameter;
}
buildProperties(source) {
const properties = {};
source.forEach(property => {
let swaggerType = this.getSwaggerType(property.type);
const format = property.format;
swaggerType.description = property.description;
swaggerType.example = property.example;
swaggerType.format = format || swaggerType.format;
if (!swaggerType.$ref) {
swaggerType.default = property.default;
Object.keys(property.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.forEach(key => {
swaggerType = { ...swaggerType, [key]: property.validators[key].value };
});
}
if (property.deprecated) {
swaggerType['x-deprecated'] = true;
}
if (property.extensions) {
property.extensions.forEach(property => {
swaggerType[property.key] = property.value;
});
}
properties[property.name] = swaggerType;
});
return properties;
}
getSwaggerTypeForUnionType(type) {
const typesWithoutUndefined = type.types.filter(x => x.dataType !== 'undefined');
// Backwards compatible representation of a literal enumeration
if (typesWithoutUndefined.every(subType => subType.dataType === 'enum')) {
const mergedEnum = { dataType: 'enum', enums: [] };
typesWithoutUndefined.forEach(t => {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
mergedEnum.enums = [...mergedEnum.enums, ...t.enums];
});
return this.getSwaggerTypeForEnumType(mergedEnum);
}
else if (typesWithoutUndefined.length === 2 && typesWithoutUndefined.find(typeInUnion => typeInUnion.dataType === 'enum' && typeInUnion.enums.includes(null))) {
// Backwards compatible representation of dataType or null, $ref does not allow any sibling attributes, so we have to bail out
const nullEnumIndex = typesWithoutUndefined.findIndex(type => type.dataType === 'enum' && type.enums.includes(null));
const typeIndex = nullEnumIndex === 1 ? 0 : 1;
const swaggerType = this.getSwaggerType(typesWithoutUndefined[typeIndex]);
const isRef = !!swaggerType.$ref;
if (isRef) {
return { type: 'object' };
}
else {
swaggerType['x-nullable'] = true;
return swaggerType;
}
}
else if (process.env.NODE_ENV !== 'tsoa_test') {
// eslint-disable-next-line no-console
console.warn('Swagger 2.0 does not support union types beyond string literals.\n' + 'If you would like to take advantage of this, please change tsoa.json\'s "specVersion" to 3.');
}
return { type: 'object' };
}
getSwaggerTypeForIntersectionType(type) {
const properties = type.types.reduce((acc, type) => {
if (type.dataType === 'refObject') {
let refType = type;
refType = this.metadata.referenceTypeMap[refType.refName];
const props = refType &&
refType.properties &&
refType.properties.reduce((acc, prop) => {
return {
...acc,
[prop.name]: this.getSwaggerType(prop.type),
};
}, {});
return { ...acc, ...props };
}
else {
process.env.NODE_ENV !== 'tsoa_test' &&
// eslint-disable-next-line no-console
console.warn('Swagger 2.0 does not fully support this kind of intersection types. If you would like to take advantage of this, please change tsoa.json\'s "specVersion" to 3.');
return { ...acc };
}
}, {});
return { type: 'object', properties };
}
getSwaggerTypeForReferenceType(referenceType) {
return { $ref: `#/definitions/${encodeURIComponent(referenceType.refName)}` };
}
decideEnumType(anEnum, nameOfEnum) {
const typesUsedInEnum = this.determineTypesUsedInEnum(anEnum);
const badEnumErrorMessage = () => {
const valuesDelimited = Array.from(typesUsedInEnum).join(',');
return `Enums can only have string or number values, but enum ${nameOfEnum} had ${valuesDelimited}`;
};
let enumTypeForSwagger;
if (typesUsedInEnum.has('string') && typesUsedInEnum.size === 1) {
enumTypeForSwagger = 'string';
}
else if (typesUsedInEnum.has('number') && typesUsedInEnum.size === 1) {
enumTypeForSwagger = 'number';
}
else {
throw new Error(badEnumErrorMessage());
}
return enumTypeForSwagger;
}
getSwaggerTypeForEnumType(enumType) {
const types = this.determineTypesUsedInEnum(enumType.enums);
const type = types.size === 1 ? types.values().next().value : 'string';
const nullable = enumType.enums.includes(null) ? true : false;
return { type, enum: enumType.enums.map(member => (0, swaggerUtils_1.getValue)(type, member)), ['x-nullable']: nullable };
}
}
exports.SpecGenerator2 = SpecGenerator2;
//# sourceMappingURL=specGenerator2.js.map

File diff suppressed because one or more lines are too long

158
node_modules/@tsoa/cli/dist/swagger/specGenerator3.d.ts generated vendored Normal file
View File

@@ -0,0 +1,158 @@
import { Swagger, Tsoa } from '@tsoa/runtime';
import { ExtendedSpecConfig } from '../cli';
import { SpecGenerator } from './specGenerator';
/**
* TODO:
* Handle formData parameters
* Handle requestBodies of type other than json
* Handle requestBodies as reusable objects
* Handle headers, examples, responses, etc.
* Cleaner interface between SpecGenerator2 and SpecGenerator3
* Also accept OpenAPI 3.0.0 metadata, like components/securitySchemes instead of securityDefinitions
*/
export declare class SpecGenerator3 extends SpecGenerator {
protected readonly metadata: Tsoa.Metadata;
protected readonly config: ExtendedSpecConfig;
constructor(metadata: Tsoa.Metadata, config: ExtendedSpecConfig);
GetSpec(): Swagger.Spec3;
protected buildInfo(): Swagger.Info;
protected buildComponents(): {
examples: {};
headers: {};
parameters: {};
requestBodies: {};
responses: {};
schemas: {
[name: string]: Swagger.Schema3;
};
securitySchemes: {};
};
protected translateSecurityDefinitions(definitions: {
[name: string]: Swagger.SecuritySchemes;
}): {
[name: string]: Swagger.SecuritySchemes;
};
protected hasOAuthFlow(definition: any): definition is {
flow: string;
};
protected hasOAuthFlows(definition: any): definition is {
flows: Swagger.OAuthFlow;
};
protected buildServers(): Swagger.Server[];
protected buildSchema(): {
[name: string]: Swagger.Schema3;
};
protected buildPaths(): {
[pathName: string]: Swagger.Path3;
};
protected buildMethod(controllerName: string, method: Tsoa.Method, pathObject: any, defaultProduces?: string[]): void;
protected buildOperation(controllerName: string, method: Tsoa.Method, defaultProduces?: string[]): Swagger.Operation3;
protected buildRequestBodyWithFormData(controllerName: string, method: Tsoa.Method, parameters: Tsoa.Parameter[]): Swagger.RequestBody;
protected buildRequestBody(controllerName: string, method: Tsoa.Method, parameter: Tsoa.Parameter): Swagger.RequestBody;
protected buildMediaType(controllerName: string, method: Tsoa.Method, parameter: Tsoa.Parameter): Swagger.MediaType;
protected buildQueriesParameter(source: Tsoa.Parameter): Swagger.Parameter3[];
protected buildParameter(source: Tsoa.Parameter): Swagger.Parameter3;
protected buildExamples(source: Pick<Tsoa.Parameter, 'example' | 'exampleLabels'>): {
example?: unknown;
examples?: {
[name: string]: Swagger.Example3;
};
};
protected buildProperties(source: Tsoa.Property[]): {
[propertyName: string]: Swagger.Schema3;
};
protected getSwaggerTypeForReferenceType(referenceType: Tsoa.ReferenceType): Swagger.BaseSchema;
protected getSwaggerTypeForPrimitiveType(dataType: Tsoa.PrimitiveTypeLiteral): Swagger.BaseSchema;
protected isNull(type: Tsoa.Type): boolean;
protected groupEnums(types: Swagger.BaseSchema[]): Swagger.BaseSchema<unknown>[];
protected removeDuplicateSwaggerTypes(types: Swagger.BaseSchema[]): Swagger.BaseSchema[];
protected getSwaggerTypeForUnionType(type: Tsoa.UnionType, title?: string): {
allOf: Swagger.BaseSchema<unknown>[];
nullable: true;
} | {
nullable: true;
type?: string;
format?: Swagger.DataFormat;
$ref?: string;
title?: string;
description?: string;
default?: string | boolean | number | unknown;
multipleOf?: number;
maximum?: number;
exclusiveMaximum?: number;
minimum?: number;
exclusiveMinimum?: number;
maxLength?: number;
minLength?: number;
pattern?: string;
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
maxProperties?: number;
minProperties?: number;
enum?: Array<boolean | string | number | null>;
'x-enum-varnames'?: string[];
additionalProperties?: boolean | Swagger.BaseSchema;
properties?: {
[propertyName: string]: unknown;
} | undefined;
discriminator?: string;
readOnly?: boolean;
xml?: Swagger.XML;
externalDocs?: Swagger.ExternalDocs;
example?: unknown;
required?: string[];
items?: Swagger.BaseSchema;
allOf?: undefined;
} | {
anyOf: Swagger.BaseSchema<unknown>[];
nullable: true;
title?: string | undefined;
allOf?: undefined;
} | {
type?: string;
format?: Swagger.DataFormat;
$ref?: string;
title?: string;
description?: string;
default?: string | boolean | number | unknown;
multipleOf?: number;
maximum?: number;
exclusiveMaximum?: number;
minimum?: number;
exclusiveMinimum?: number;
maxLength?: number;
minLength?: number;
pattern?: string;
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
maxProperties?: number;
minProperties?: number;
enum?: Array<boolean | string | number | null>;
'x-enum-varnames'?: string[];
additionalProperties?: boolean | Swagger.BaseSchema;
properties?: {
[propertyName: string]: unknown;
} | undefined;
discriminator?: string;
readOnly?: boolean;
xml?: Swagger.XML;
externalDocs?: Swagger.ExternalDocs;
example?: unknown;
required?: string[];
items?: Swagger.BaseSchema;
allOf?: undefined;
nullable?: undefined;
} | {
anyOf: Swagger.BaseSchema<unknown>[];
title?: string | undefined;
allOf?: undefined;
nullable?: undefined;
};
protected getSwaggerTypeForIntersectionType(type: Tsoa.IntersectionType, title?: string): {
title?: string | undefined;
allOf: Swagger.BaseSchema<unknown>[];
};
protected getSwaggerTypeForEnumType(enumType: Tsoa.EnumType, title?: string): Swagger.Schema3;
}

633
node_modules/@tsoa/cli/dist/swagger/specGenerator3.js generated vendored Normal file
View File

@@ -0,0 +1,633 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpecGenerator3 = void 0;
const runtime_1 = require("@tsoa/runtime");
const merge_anything_1 = require("merge-anything");
const ts_deepmerge_1 = require("ts-deepmerge");
const isVoidType_1 = require("../utils/isVoidType");
const validatorUtils_1 = require("../utils/validatorUtils");
const pathUtils_1 = require("./../utils/pathUtils");
const swaggerUtils_1 = require("./../utils/swaggerUtils");
const specGenerator_1 = require("./specGenerator");
/**
* TODO:
* Handle formData parameters
* Handle requestBodies of type other than json
* Handle requestBodies as reusable objects
* Handle headers, examples, responses, etc.
* Cleaner interface between SpecGenerator2 and SpecGenerator3
* Also accept OpenAPI 3.0.0 metadata, like components/securitySchemes instead of securityDefinitions
*/
class SpecGenerator3 extends specGenerator_1.SpecGenerator {
constructor(metadata, config) {
super(metadata, config);
this.metadata = metadata;
this.config = config;
}
GetSpec() {
let spec = {
openapi: '3.0.0',
components: this.buildComponents(),
info: this.buildInfo(),
paths: this.buildPaths(),
servers: this.buildServers(),
tags: this.config.tags,
};
if (this.config.spec) {
this.config.specMerging = this.config.specMerging || 'immediate';
const mergeFuncs = {
immediate: Object.assign,
recursive: merge_anything_1.merge,
deepmerge: (spec, merge) => (0, ts_deepmerge_1.merge)(spec, merge),
};
spec = mergeFuncs[this.config.specMerging](spec, this.config.spec);
}
return spec;
}
buildInfo() {
const info = {
title: this.config.name || '',
};
if (this.config.version) {
info.version = this.config.version;
}
if (this.config.description) {
info.description = this.config.description;
}
if (this.config.termsOfService) {
info.termsOfService = this.config.termsOfService;
}
if (this.config.license) {
info.license = { name: this.config.license };
}
if (this.config.contact) {
info.contact = this.config.contact;
}
return info;
}
buildComponents() {
const components = {
examples: {},
headers: {},
parameters: {},
requestBodies: {},
responses: {},
schemas: this.buildSchema(),
securitySchemes: {},
};
if (this.config.securityDefinitions) {
components.securitySchemes = this.translateSecurityDefinitions(this.config.securityDefinitions);
}
return components;
}
translateSecurityDefinitions(definitions) {
const defs = {};
Object.keys(definitions).forEach(key => {
if (definitions[key].type === 'basic') {
defs[key] = {
scheme: 'basic',
type: 'http',
};
}
else if (definitions[key].type === 'oauth2') {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
const definition = definitions[key];
const oauth = (defs[key] || {
type: 'oauth2',
description: definitions[key].description,
flows: (this.hasOAuthFlows(definition) && definition.flows) || {},
});
if (this.hasOAuthFlow(definition) && definition.flow === 'password') {
oauth.flows.password = { tokenUrl: definition.tokenUrl, scopes: definition.scopes || {} };
}
else if (this.hasOAuthFlow(definition) && definition.flow === 'accessCode') {
oauth.flows.authorizationCode = { tokenUrl: definition.tokenUrl, authorizationUrl: definition.authorizationUrl, scopes: definition.scopes || {} };
}
else if (this.hasOAuthFlow(definition) && definition.flow === 'application') {
oauth.flows.clientCredentials = { tokenUrl: definition.tokenUrl, scopes: definition.scopes || {} };
}
else if (this.hasOAuthFlow(definition) && definition.flow === 'implicit') {
oauth.flows.implicit = { authorizationUrl: definition.authorizationUrl, scopes: definition.scopes || {} };
}
defs[key] = oauth;
}
else {
defs[key] = definitions[key];
}
});
return defs;
}
hasOAuthFlow(definition) {
return !!definition.flow;
}
hasOAuthFlows(definition) {
return !!definition.flows;
}
buildServers() {
const prefix = this.config.disableBasePathPrefixSlash ? undefined : '/';
const basePath = (0, pathUtils_1.normalisePath)(this.config.basePath, prefix, undefined, false);
const scheme = this.config.schemes ? this.config.schemes[0] : 'https';
const hosts = this.config.servers ? this.config.servers : this.config.host ? [this.config.host] : undefined;
const convertHost = (host) => ({ url: `${scheme}://${host}${basePath}` });
return (hosts?.map(convertHost) || [{ url: basePath }]);
}
buildSchema() {
const schema = {};
Object.keys(this.metadata.referenceTypeMap).map(typeName => {
const referenceType = this.metadata.referenceTypeMap[typeName];
if (referenceType.dataType === 'refObject') {
const required = referenceType.properties.filter(p => this.isRequiredWithoutDefault(p) && !this.hasUndefined(p)).map(p => p.name);
schema[referenceType.refName] = {
description: referenceType.description,
properties: this.buildProperties(referenceType.properties),
required: required && required.length > 0 ? Array.from(new Set(required)) : undefined,
type: 'object',
};
if (referenceType.additionalProperties) {
schema[referenceType.refName].additionalProperties = this.buildAdditionalProperties(referenceType.additionalProperties);
}
else {
// Since additionalProperties was not explicitly set in the TypeScript interface for this model
// ...we need to make a decision
schema[referenceType.refName].additionalProperties = this.determineImplicitAdditionalPropertiesValue();
}
if (referenceType.example) {
schema[referenceType.refName].example = referenceType.example;
}
}
else if (referenceType.dataType === 'refEnum') {
const enumTypes = this.determineTypesUsedInEnum(referenceType.enums);
if (enumTypes.size === 1) {
schema[referenceType.refName] = {
description: referenceType.description,
enum: referenceType.enums,
type: enumTypes.has('string') ? 'string' : 'number',
};
if (this.config.xEnumVarnames && referenceType.enumVarnames !== undefined && referenceType.enums.length === referenceType.enumVarnames.length) {
schema[referenceType.refName]['x-enum-varnames'] = referenceType.enumVarnames;
}
}
else {
schema[referenceType.refName] = {
description: referenceType.description,
anyOf: [
{
type: 'number',
enum: referenceType.enums.filter(e => typeof e === 'number'),
},
{
type: 'string',
enum: referenceType.enums.filter(e => typeof e === 'string'),
},
],
};
}
if (referenceType.example) {
schema[referenceType.refName].example = referenceType.example;
}
}
else if (referenceType.dataType === 'refAlias') {
const swaggerType = this.getSwaggerType(referenceType.type);
const format = referenceType.format;
const validators = Object.keys(referenceType.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.reduce((acc, key) => {
return {
...acc,
[key]: referenceType.validators[key].value,
};
}, {});
schema[referenceType.refName] = {
...swaggerType,
default: referenceType.default || swaggerType.default,
example: referenceType.example,
format: format || swaggerType.format,
description: referenceType.description,
...validators,
};
}
else {
(0, runtime_1.assertNever)(referenceType);
}
if (referenceType.deprecated) {
schema[referenceType.refName].deprecated = true;
}
});
return schema;
}
buildPaths() {
const paths = {};
this.metadata.controllers.forEach(controller => {
const normalisedControllerPath = (0, pathUtils_1.normalisePath)(controller.path, '/');
// construct documentation using all methods except @Hidden
controller.methods
.filter(method => !method.isHidden)
.forEach(method => {
const normalisedMethodPath = (0, pathUtils_1.normalisePath)(method.path, '/');
let path = (0, pathUtils_1.normalisePath)(`${normalisedControllerPath}${normalisedMethodPath}`, '/', '', false);
path = (0, pathUtils_1.convertColonPathParams)(path);
paths[path] = paths[path] || {};
this.buildMethod(controller.name, method, paths[path], controller.produces);
});
});
return paths;
}
buildMethod(controllerName, method, pathObject, defaultProduces) {
const pathMethod = (pathObject[method.method] = this.buildOperation(controllerName, method, defaultProduces));
pathMethod.description = method.description;
pathMethod.summary = method.summary;
pathMethod.tags = method.tags;
// Use operationId tag otherwise fallback to generated. Warning: This doesn't check uniqueness.
pathMethod.operationId = method.operationId || pathMethod.operationId;
if (method.deprecated) {
pathMethod.deprecated = method.deprecated;
}
if (method.security) {
pathMethod.security = method.security;
}
const bodyParams = method.parameters.filter(p => p.in === 'body');
const bodyPropParams = method.parameters.filter(p => p.in === 'body-prop');
const formParams = method.parameters.filter(p => p.in === 'formData');
const queriesParams = method.parameters.filter(p => p.in === 'queries');
pathMethod.parameters = method.parameters
.filter(p => {
return ['body', 'formData', 'request', 'body-prop', 'res', 'queries', 'request-prop'].indexOf(p.in) === -1;
})
.map(p => this.buildParameter(p));
if (queriesParams.length > 1) {
throw new Error('Only one queries parameter allowed per controller method.');
}
if (queriesParams.length === 1) {
pathMethod.parameters.push(...this.buildQueriesParameter(queriesParams[0]));
}
if (bodyParams.length > 1) {
throw new Error('Only one body parameter allowed per controller method.');
}
if (bodyParams.length > 0 && formParams.length > 0) {
throw new Error('Either body parameter or form parameters allowed per controller method - not both.');
}
if (bodyPropParams.length > 0) {
if (!bodyParams.length) {
bodyParams.push({
in: 'body',
name: 'body',
parameterName: 'body',
required: true,
type: {
dataType: 'nestedObjectLiteral',
properties: [],
},
validators: {},
deprecated: false,
});
}
const type = bodyParams[0].type;
bodyPropParams.forEach((bodyParam) => {
type.properties.push(bodyParam);
});
}
if (bodyParams.length > 0) {
pathMethod.requestBody = this.buildRequestBody(controllerName, method, bodyParams[0]);
}
else if (formParams.length > 0) {
pathMethod.requestBody = this.buildRequestBodyWithFormData(controllerName, method, formParams);
}
method.extensions.forEach(ext => (pathMethod[ext.key] = ext.value));
}
buildOperation(controllerName, method, defaultProduces) {
const swaggerResponses = {};
method.responses.forEach((res) => {
swaggerResponses[res.name] = {
description: res.description,
};
if (res.schema && !(0, isVoidType_1.isVoidType)(res.schema)) {
swaggerResponses[res.name].content = {};
const produces = res.produces || defaultProduces || [swaggerUtils_1.DEFAULT_RESPONSE_MEDIA_TYPE];
for (const p of produces) {
const { content } = swaggerResponses[res.name];
swaggerResponses[res.name].content = {
...content,
[p]: {
schema: this.getSwaggerType(res.schema, this.config.useTitleTagsForInlineObjects ? this.getOperationId(controllerName, method) + 'Response' : undefined),
},
};
}
if (res.examples) {
let exampleCounter = 1;
const examples = res.examples.reduce((acc, ex, currentIndex) => {
const exampleLabel = res.exampleLabels?.[currentIndex];
return { ...acc, [exampleLabel === undefined ? `Example ${exampleCounter++}` : exampleLabel]: { value: ex } };
}, {});
for (const p of produces) {
/* eslint-disable @typescript-eslint/dot-notation */
(swaggerResponses[res.name].content || {})[p]['examples'] = examples;
}
}
}
if (res.headers) {
const headers = {};
if (res.headers.dataType === 'refObject') {
headers[res.headers.refName] = {
schema: this.getSwaggerTypeForReferenceType(res.headers),
description: res.headers.description,
};
}
else if (res.headers.dataType === 'nestedObjectLiteral') {
res.headers.properties.forEach((each) => {
headers[each.name] = {
schema: this.getSwaggerType(each.type),
description: each.description,
required: this.isRequiredWithoutDefault(each),
};
});
}
else {
(0, runtime_1.assertNever)(res.headers);
}
swaggerResponses[res.name].headers = headers;
}
});
const operation = {
operationId: this.getOperationId(controllerName, method),
responses: swaggerResponses,
};
return operation;
}
buildRequestBodyWithFormData(controllerName, method, parameters) {
const required = [];
const properties = {};
for (const parameter of parameters) {
const mediaType = this.buildMediaType(controllerName, method, parameter);
properties[parameter.name] = mediaType.schema;
if (this.isRequiredWithoutDefault(parameter)) {
required.push(parameter.name);
}
if (parameter.deprecated) {
properties[parameter.name].deprecated = parameter.deprecated;
}
}
const requestBody = {
required: required.length > 0,
content: {
'multipart/form-data': {
schema: {
type: 'object',
properties,
// An empty list required: [] is not valid.
// If all properties are optional, do not specify the required keyword.
...(required && required.length && { required }),
},
},
},
};
return requestBody;
}
buildRequestBody(controllerName, method, parameter) {
const mediaType = this.buildMediaType(controllerName, method, parameter);
const consumes = method.consumes || swaggerUtils_1.DEFAULT_REQUEST_MEDIA_TYPE;
const requestBody = {
description: parameter.description,
required: this.isRequiredWithoutDefault(parameter),
content: {
[consumes]: mediaType,
},
};
return requestBody;
}
buildMediaType(controllerName, method, parameter) {
const validators = Object.keys(parameter.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.reduce((acc, key) => {
return {
...acc,
[key]: parameter.validators[key].value,
};
}, {});
const mediaType = {
schema: {
...this.getSwaggerType(parameter.type, this.config.useTitleTagsForInlineObjects ? this.getOperationId(controllerName, method) + 'RequestBody' : undefined),
...validators,
...(parameter.description && { description: parameter.description }),
},
};
const parameterExamples = parameter.example;
const parameterExampleLabels = parameter.exampleLabels;
if (parameterExamples === undefined) {
mediaType.example = parameterExamples;
}
else if (parameterExamples.length === 1) {
mediaType.example = parameterExamples[0];
}
else {
let exampleCounter = 1;
mediaType.examples = parameterExamples.reduce((acc, ex, currentIndex) => {
const exampleLabel = parameterExampleLabels?.[currentIndex];
return { ...acc, [exampleLabel === undefined ? `Example ${exampleCounter++}` : exampleLabel]: { value: ex } };
}, {});
}
return mediaType;
}
buildQueriesParameter(source) {
if (source.type.dataType === 'refObject' || source.type.dataType === 'nestedObjectLiteral') {
const properties = source.type.properties;
return properties.map(property => this.buildParameter(this.queriesPropertyToQueryParameter(property)));
}
throw new Error(`Queries '${source.name}' parameter must be an object.`);
}
buildParameter(source) {
const parameter = {
description: source.description,
in: source.in,
name: source.name,
required: this.isRequiredWithoutDefault(source),
schema: {
default: source.default,
format: undefined,
},
};
if (source.deprecated) {
parameter.deprecated = true;
}
const parameterType = this.getSwaggerType(source.type);
if (parameterType.format) {
parameter.schema.format = this.throwIfNotDataFormat(parameterType.format);
}
if (parameterType.$ref) {
parameter.schema = parameterType;
return Object.assign(parameter, this.buildExamples(source));
}
const validatorObjs = {};
Object.keys(source.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.forEach(key => {
validatorObjs[key] = source.validators[key].value;
});
if (source.type.dataType === 'any') {
parameter.schema.type = 'string';
}
else {
if (parameterType.type) {
parameter.schema.type = this.throwIfNotDataType(parameterType.type);
}
parameter.schema.items = parameterType.items;
parameter.schema.enum = parameterType.enum;
}
parameter.schema = Object.assign({}, parameter.schema, validatorObjs);
return Object.assign(parameter, this.buildExamples(source));
}
buildExamples(source) {
const { example: parameterExamples, exampleLabels } = source;
if (parameterExamples === undefined) {
return { example: undefined };
}
if (parameterExamples.length === 1) {
return { example: parameterExamples[0] };
}
let exampleCounter = 1;
const examples = parameterExamples.reduce((acc, ex, idx) => {
const label = exampleLabels?.[idx];
const name = label ?? `Example ${exampleCounter++}`;
acc[name] = { value: ex };
return acc;
}, {});
return { examples };
}
buildProperties(source) {
const properties = {};
source.forEach(property => {
let swaggerType = this.getSwaggerType(property.type);
const format = property.format;
swaggerType.description = property.description;
swaggerType.example = property.example;
swaggerType.format = format || swaggerType.format;
if (!swaggerType.$ref) {
swaggerType.default = property.default;
Object.keys(property.validators)
.filter(validatorUtils_1.shouldIncludeValidatorInSchema)
.forEach(key => {
swaggerType = { ...swaggerType, [key]: property.validators[key].value };
});
}
if (property.deprecated) {
swaggerType.deprecated = true;
}
if (property.extensions) {
property.extensions.forEach(property => {
swaggerType[property.key] = property.value;
});
}
properties[property.name] = swaggerType;
});
return properties;
}
getSwaggerTypeForReferenceType(referenceType) {
return { $ref: `#/components/schemas/${encodeURIComponent(referenceType.refName)}` };
}
getSwaggerTypeForPrimitiveType(dataType) {
if (dataType === 'any') {
// Setting additionalProperties causes issues with code generators for OpenAPI 3
// Therefore, we avoid setting it explicitly (since it's the implicit default already)
return {};
}
else if (dataType === 'file') {
return { type: 'string', format: 'binary' };
}
return super.getSwaggerTypeForPrimitiveType(dataType);
}
isNull(type) {
return type.dataType === 'enum' && type.enums.length === 1 && type.enums[0] === null;
}
// Join disparate enums with the same type into one.
//
// grouping enums is helpful because it makes the spec more readable and it
// bypasses a failure in openapi-generator caused by using anyOf with
// duplicate types.
groupEnums(types) {
const returnTypes = [];
const enumValuesByType = {};
for (const type of types) {
if (type.enum && type.type) {
for (const enumValue of type.enum) {
if (!enumValuesByType[type.type]) {
enumValuesByType[type.type] = {};
}
enumValuesByType[type.type][String(enumValue)] = enumValue;
}
}
// preserve non-enum types
else {
returnTypes.push(type);
}
}
Object.keys(enumValuesByType).forEach(dataType => returnTypes.push({
type: dataType,
enum: Object.values(enumValuesByType[dataType]),
}));
return returnTypes;
}
removeDuplicateSwaggerTypes(types) {
if (types.length === 1) {
return types;
}
else {
const typesSet = new Set();
for (const type of types) {
typesSet.add(JSON.stringify(type));
}
return Array.from(typesSet).map(typeString => JSON.parse(typeString));
}
}
getSwaggerTypeForUnionType(type, title) {
// Filter out nulls and undefineds
const actualSwaggerTypes = this.removeDuplicateSwaggerTypes(this.groupEnums(type.types
.filter(x => !this.isNull(x))
.filter(x => x.dataType !== 'undefined')
.map(x => this.getSwaggerType(x))));
const nullable = type.types.some(x => this.isNull(x));
if (nullable) {
if (actualSwaggerTypes.length === 1) {
const [swaggerType] = actualSwaggerTypes;
// for ref union with null, use an allOf with a single
// element since you can't attach nullable directly to a ref.
// https://swagger.io/docs/specification/using-ref/#syntax
if (swaggerType.$ref) {
return { allOf: [swaggerType], nullable };
}
// Note that null must be explicitly included in the list of enum values. Using nullable: true alone is not enough here.
// https://swagger.io/docs/specification/data-models/enums/
if (swaggerType.enum) {
swaggerType.enum.push(null);
}
return { ...(title && { title }), ...swaggerType, nullable };
}
else {
return { ...(title && { title }), anyOf: actualSwaggerTypes, nullable };
}
}
else {
if (actualSwaggerTypes.length === 1) {
return { ...(title && { title }), ...actualSwaggerTypes[0] };
}
else {
return { ...(title && { title }), anyOf: actualSwaggerTypes };
}
}
}
getSwaggerTypeForIntersectionType(type, title) {
return { allOf: type.types.map(x => this.getSwaggerType(x)), ...(title && { title }) };
}
getSwaggerTypeForEnumType(enumType, title) {
const types = this.determineTypesUsedInEnum(enumType.enums);
if (types.size === 1) {
const type = types.values().next().value;
const nullable = enumType.enums.includes(null) ? true : false;
return { ...(title && { title }), type, enum: enumType.enums.map(member => (0, swaggerUtils_1.getValue)(type, member)), nullable };
}
else {
const valuesDelimited = Array.from(types).join(',');
throw new Error(`Enums can only have string or number values, but enum had ${valuesDelimited}`);
}
}
}
exports.SpecGenerator3 = SpecGenerator3;
//# sourceMappingURL=specGenerator3.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,24 @@
import { Swagger, Tsoa } from '@tsoa/runtime';
import { ExtendedSpecConfig } from '../cli';
import { SpecGenerator3 } from './specGenerator3';
/**
* OpenAPI 3.1 Spec Generator
*
* Extends SpecGenerator3 with OpenAPI 3.1 specific features:
* - Tuple support via prefixItems
* - OpenAPI version 3.1.0
*
* Uses inheritance to reuse all building logic from SpecGenerator3,
* only overriding the version and adding tuple type support.
*/
export declare class SpecGenerator31 extends SpecGenerator3 {
protected readonly metadata: Tsoa.Metadata;
protected readonly config: ExtendedSpecConfig;
constructor(metadata: Tsoa.Metadata, config: ExtendedSpecConfig);
GetSpec(): Swagger.Spec31;
/**
* Override to add tuple type support (OpenAPI 3.1 feature via prefixItems)
*/
protected getSwaggerType(type: Tsoa.Type, title?: string): Swagger.BaseSchema;
private isTupleType;
}

73
node_modules/@tsoa/cli/dist/swagger/specGenerator31.js generated vendored Normal file
View File

@@ -0,0 +1,73 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpecGenerator31 = void 0;
const merge_anything_1 = require("merge-anything");
const ts_deepmerge_1 = require("ts-deepmerge");
const specGenerator3_1 = require("./specGenerator3");
/**
* OpenAPI 3.1 Spec Generator
*
* Extends SpecGenerator3 with OpenAPI 3.1 specific features:
* - Tuple support via prefixItems
* - OpenAPI version 3.1.0
*
* Uses inheritance to reuse all building logic from SpecGenerator3,
* only overriding the version and adding tuple type support.
*/
class SpecGenerator31 extends specGenerator3_1.SpecGenerator3 {
constructor(metadata, config) {
super(metadata, config);
this.metadata = metadata;
this.config = config;
}
// Override with OpenAPI 3.1 specific return type
// The base class returns Swagger.Spec30, but this generator produces Swagger.Spec31
GetSpec() {
let spec = {
openapi: '3.1.0',
components: this.buildComponents(),
info: this.buildInfo(),
paths: this.buildPaths(),
servers: this.buildServers(),
tags: this.config.tags,
};
if (this.config.spec) {
this.config.specMerging = this.config.specMerging || 'immediate';
const mergeFuncs = {
immediate: Object.assign,
recursive: merge_anything_1.merge,
deepmerge: (spec, merge) => (0, ts_deepmerge_1.merge)(spec, merge),
};
spec = mergeFuncs[this.config.specMerging](spec, this.config.spec);
}
return spec;
}
/**
* Override to add tuple type support (OpenAPI 3.1 feature via prefixItems)
*/
getSwaggerType(type, title) {
if (this.isTupleType(type)) {
const prefixItems = type.types.map((t) => this.getSwaggerType(t));
const schema = {
type: 'array',
prefixItems,
minItems: prefixItems.length,
...(type.restType
? {
items: this.getSwaggerType(type.restType),
}
: {
maxItems: prefixItems.length,
items: false,
}),
};
return schema;
}
return super.getSwaggerType(type, title);
}
isTupleType(type) {
return type.dataType === 'tuple';
}
}
exports.SpecGenerator31 = SpecGenerator31;
//# sourceMappingURL=specGenerator31.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"specGenerator31.js","sourceRoot":"","sources":["../../src/swagger/specGenerator31.ts"],"names":[],"mappings":";;;AACA,mDAAwD;AACxD,+CAAkD;AAIlD,qDAAkD;AAElD;;;;;;;;;GASG;AACH,MAAa,eAAgB,SAAQ,+BAAc;IACjD,YACqB,QAAuB,EACvB,MAA0B;QAE7C,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAHL,aAAQ,GAAR,QAAQ,CAAe;QACvB,WAAM,GAAN,MAAM,CAAoB;IAG/C,CAAC;IAED,iDAAiD;IACjD,oFAAoF;IACpE,OAAO;QACrB,IAAI,IAAI,GAAmB;YACzB,OAAO,EAAE,OAAO;YAChB,UAAU,EAAE,IAAI,CAAC,eAAe,EAA0B;YAC1D,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE;YACtB,KAAK,EAAE,IAAI,CAAC,UAAU,EAAwC;YAC9D,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE;YAC5B,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;SACvB,CAAC;QAEF,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,WAAW,CAAC;YACjE,MAAM,UAAU,GAAgG;gBAC9G,SAAS,EAAE,MAAM,CAAC,MAAM;gBACxB,SAAS,EAAE,sBAAa;gBACxB,SAAS,EAAE,CAAC,IAAuB,EAAE,KAAwB,EAAqB,EAAE,CAAC,IAAA,oBAAS,EAAC,IAAI,EAAE,KAAK,CAAC;aAC5G,CAAC;YAEF,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAoC,EAAE,IAAI,CAAC,MAAM,CAAC,IAAyB,CAA8B,CAAC;QACvJ,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACO,cAAc,CAAC,IAAe,EAAE,KAAc;QACtD,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,WAAW,GAAuB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAY,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAqB,CAAC,CAAC;YAErH,MAAM,MAAM,GAAqB;gBAC/B,IAAI,EAAE,OAAO;gBACb,WAAW;gBACX,QAAQ,EAAE,WAAW,CAAC,MAAM;gBAC5B,GAAG,CAAC,IAAI,CAAC,QAAQ;oBACf,CAAC,CAAC;wBACE,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAqB;qBAC9D;oBACH,CAAC,CAAC;wBACE,QAAQ,EAAE,WAAW,CAAC,MAAM;wBAC5B,KAAK,EAAE,KAAK;qBACb,CAAC;aACP,CAAC;YAEF,OAAO,MAAuC,CAAC;QACjD,CAAC;QAED,OAAO,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAEO,WAAW,CAAC,IAAe;QACjC,OAAO,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAC;IACnC,CAAC;CACF;AAhED,0CAgEC"}

View File

@@ -0,0 +1,9 @@
import * as ts from 'typescript';
export declare function getDecorators(node: ts.Node, isMatching: (identifier: ts.Identifier) => boolean): ts.Identifier[];
export declare function getNodeFirstDecoratorName(node: ts.Node, isMatching: (identifier: ts.Identifier) => boolean): string | undefined;
export declare function getNodeFirstDecoratorValue(node: ts.Node, typeChecker: ts.TypeChecker, isMatching: (identifier: ts.Identifier) => boolean): any;
export declare function getDecoratorValues(decorator: ts.Identifier, typeChecker: ts.TypeChecker): any[];
export declare function getSecurites(decorator: ts.Identifier, typeChecker: ts.TypeChecker): any;
export declare function isDecorator(node: ts.Node, isMatching: (identifier: ts.Identifier) => boolean): boolean;
export declare function getPath(decorator: ts.Identifier, typeChecker: ts.TypeChecker): string;
export declare function getProduces(node: ts.Node, typeChecker: ts.TypeChecker): string[];

118
node_modules/@tsoa/cli/dist/utils/decoratorUtils.js generated vendored Normal file
View File

@@ -0,0 +1,118 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDecorators = getDecorators;
exports.getNodeFirstDecoratorName = getNodeFirstDecoratorName;
exports.getNodeFirstDecoratorValue = getNodeFirstDecoratorValue;
exports.getDecoratorValues = getDecoratorValues;
exports.getSecurites = getSecurites;
exports.isDecorator = isDecorator;
exports.getPath = getPath;
exports.getProduces = getProduces;
const ts = __importStar(require("typescript"));
const initializer_value_1 = require("../metadataGeneration/initializer-value");
function tsHasDecorators(tsNamespace) {
return typeof tsNamespace.canHaveDecorators === 'function';
}
function getDecorators(node, isMatching) {
// beginning in ts4.8 node.decorator is undefined, use getDecorators instead.
const decorators = tsHasDecorators(ts) && ts.canHaveDecorators(node) ? ts.getDecorators(node) : [];
if (!decorators || !decorators.length) {
return [];
}
return decorators
.map((e) => {
while (e.expression !== undefined) {
e = e.expression;
}
return e;
})
.filter(isMatching);
}
function getNodeFirstDecoratorName(node, isMatching) {
const decorators = getDecorators(node, isMatching);
if (!decorators || !decorators.length) {
return;
}
return decorators[0].text;
}
function getNodeFirstDecoratorValue(node, typeChecker, isMatching) {
const decorators = getDecorators(node, isMatching);
if (!decorators || !decorators.length) {
return;
}
const values = getDecoratorValues(decorators[0], typeChecker);
return values && values[0];
}
function getDecoratorValues(decorator, typeChecker) {
const expression = decorator.parent;
const expArguments = expression.arguments;
if (!expArguments || !expArguments.length) {
return [];
}
return expArguments.map(a => (0, initializer_value_1.getInitializerValue)(a, typeChecker));
}
function getSecurites(decorator, typeChecker) {
const [first, second] = getDecoratorValues(decorator, typeChecker);
if (isObject(first)) {
return first;
}
return { [first]: second || [] };
}
function isDecorator(node, isMatching) {
const decorators = getDecorators(node, isMatching);
if (!decorators || !decorators.length) {
return false;
}
return true;
}
function isObject(v) {
return typeof v === 'object' && v !== null;
}
function getPath(decorator, typeChecker) {
const [path] = getDecoratorValues(decorator, typeChecker);
if (path === undefined) {
return '';
}
return path;
}
function getProduces(node, typeChecker) {
const producesDecorators = getDecorators(node, identifier => identifier.text === 'Produces');
if (!producesDecorators || !producesDecorators.length) {
return [];
}
return producesDecorators.map(decorator => getDecoratorValues(decorator, typeChecker)[0]);
}
//# sourceMappingURL=decoratorUtils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"decoratorUtils.js","sourceRoot":"","sources":["../../src/utils/decoratorUtils.ts"],"names":[],"mappings":"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"}

1
node_modules/@tsoa/cli/dist/utils/flowUtils.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export declare function throwUnless(condition: unknown, error: Error): asserts condition;

8
node_modules/@tsoa/cli/dist/utils/flowUtils.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.throwUnless = throwUnless;
function throwUnless(condition, error) {
if (!condition)
throw error;
}
//# sourceMappingURL=flowUtils.js.map

1
node_modules/@tsoa/cli/dist/utils/flowUtils.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"flowUtils.js","sourceRoot":"","sources":["../../src/utils/flowUtils.ts"],"names":[],"mappings":";;AAAA,kCAEC;AAFD,SAAgB,WAAW,CAAC,SAAkB,EAAE,KAAY;IAC1D,IAAI,CAAC,SAAS;QAAE,MAAM,KAAK,CAAC;AAC9B,CAAC"}

5
node_modules/@tsoa/cli/dist/utils/fs.d.ts generated vendored Normal file
View File

@@ -0,0 +1,5 @@
import * as fs from 'fs';
export declare const fsExists: typeof fs.exists.__promisify__;
export declare const fsMkDir: typeof fs.mkdir.__promisify__;
export declare const fsWriteFile: typeof fs.writeFile.__promisify__;
export declare const fsReadFile: typeof fs.readFile.__promisify__;

43
node_modules/@tsoa/cli/dist/utils/fs.js generated vendored Normal file
View File

@@ -0,0 +1,43 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.fsReadFile = exports.fsWriteFile = exports.fsMkDir = exports.fsExists = void 0;
const fs = __importStar(require("fs"));
const util_1 = require("util");
exports.fsExists = (0, util_1.promisify)(fs.exists);
exports.fsMkDir = (0, util_1.promisify)(fs.mkdir);
exports.fsWriteFile = (0, util_1.promisify)(fs.writeFile);
exports.fsReadFile = (0, util_1.promisify)(fs.readFile);
//# sourceMappingURL=fs.js.map

1
node_modules/@tsoa/cli/dist/utils/fs.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,+BAAiC;AAEpB,QAAA,QAAQ,GAAG,IAAA,gBAAS,EAAC,EAAE,CAAC,MAAM,CAAC,CAAC;AAChC,QAAA,OAAO,GAAG,IAAA,gBAAS,EAAC,EAAE,CAAC,KAAK,CAAC,CAAC;AAC9B,QAAA,WAAW,GAAG,IAAA,gBAAS,EAAC,EAAE,CAAC,SAAS,CAAC,CAAC;AACtC,QAAA,UAAU,GAAG,IAAA,gBAAS,EAAC,EAAE,CAAC,QAAQ,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export declare const isNotNullOrUndefined: <TValue>(value: TValue | null | undefined) => value is TValue;

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isNotNullOrUndefined = void 0;
const isNotNullOrUndefined = (value) => {
return value !== null && value !== undefined;
};
exports.isNotNullOrUndefined = isNotNullOrUndefined;
//# sourceMappingURL=genericTypeGuards.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"genericTypeGuards.js","sourceRoot":"","sources":["../../src/utils/genericTypeGuards.ts"],"names":[],"mappings":";;;AAAO,MAAM,oBAAoB,GAAG,CAAS,KAAgC,EAAmB,EAAE;IAChG,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC;AAC/C,CAAC,CAAC;AAFW,QAAA,oBAAoB,wBAE/B"}

View File

@@ -0,0 +1,5 @@
import { Tsoa } from '@tsoa/runtime';
import { NodeArray, TypeNode } from 'typescript';
import { MetadataGenerator } from '../metadataGeneration/metadataGenerator';
export declare function getHeaderType(typeArgumentNodes: NodeArray<TypeNode> | undefined, index: number, metadataGenerator: MetadataGenerator): Tsoa.HeaderType | undefined;
export declare function isSupportedHeaderDataType(parameterType: Tsoa.Type): parameterType is Tsoa.HeaderType;

27
node_modules/@tsoa/cli/dist/utils/headerTypeHelpers.js generated vendored Normal file
View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getHeaderType = getHeaderType;
exports.isSupportedHeaderDataType = isSupportedHeaderDataType;
const exceptions_1 = require("../metadataGeneration/exceptions");
const typeResolver_1 = require("../metadataGeneration/typeResolver");
function getHeaderType(typeArgumentNodes, index, metadataGenerator) {
if (!typeArgumentNodes || !typeArgumentNodes[index]) {
return undefined;
}
const candidate = new typeResolver_1.TypeResolver(typeArgumentNodes[index], metadataGenerator).resolve();
if (candidate && isSupportedHeaderDataType(candidate)) {
return candidate;
}
else if (candidate) {
throw new exceptions_1.GenerateMetadataError(`Unable to parse Header Type ${typeArgumentNodes[index].getText()}`, typeArgumentNodes[index]);
}
return undefined;
}
function isSupportedHeaderDataType(parameterType) {
const supportedPathDataTypes = ['nestedObjectLiteral', 'refObject'];
if (supportedPathDataTypes.find(t => t === parameterType.dataType)) {
return true;
}
return false;
}
//# sourceMappingURL=headerTypeHelpers.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"headerTypeHelpers.js","sourceRoot":"","sources":["../../src/utils/headerTypeHelpers.ts"],"names":[],"mappings":";;AAMA,sCAcC;AAED,8DAOC;AA3BD,iEAAyE;AAEzE,qEAAkE;AAElE,SAAgB,aAAa,CAAC,iBAAkD,EAAE,KAAa,EAAE,iBAAoC;IACnI,IAAI,CAAC,iBAAiB,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,2BAAY,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC,CAAC,OAAO,EAAE,CAAC;IAE1F,IAAI,SAAS,IAAI,yBAAyB,CAAC,SAAS,CAAC,EAAE,CAAC;QACtD,OAAO,SAAS,CAAC;IACnB,CAAC;SAAM,IAAI,SAAS,EAAE,CAAC;QACrB,MAAM,IAAI,kCAAqB,CAAC,+BAA+B,iBAAiB,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;IACjI,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAgB,yBAAyB,CAAC,aAAwB;IAChE,MAAM,sBAAsB,GAA6B,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC;IAC9F,IAAI,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}

View File

@@ -0,0 +1,4 @@
/**
* Loads all exported classes from the given directory.
*/
export declare function importClassesFromDirectories(directories: string[], formats?: string[]): string[];

View File

@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.importClassesFromDirectories = importClassesFromDirectories;
const path_1 = require("path");
const glob_1 = require("glob");
/**
* Loads all exported classes from the given directory.
*/
function importClassesFromDirectories(directories, formats = ['.ts']) {
const allFiles = directories.reduce((allDirs, dir) => {
// glob docs says: Please only use forward-slashes in glob expressions.
// therefore do not do any normalization of dir path
return allDirs.concat((0, glob_1.sync)(dir));
}, []);
return allFiles.filter(file => {
const dtsExtension = file.substring(file.length - 5, file.length);
return formats.indexOf((0, path_1.extname)(file)) !== -1 && dtsExtension !== '.d.ts';
});
}
//# sourceMappingURL=importClassesFromDirectories.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"importClassesFromDirectories.js","sourceRoot":"","sources":["../../src/utils/importClassesFromDirectories.ts"],"names":[],"mappings":";;AAMA,oEAWC;AAjBD,+BAA+B;AAC/B,+BAA4B;AAE5B;;GAEG;AACH,SAAgB,4BAA4B,CAAC,WAAqB,EAAE,OAAO,GAAG,CAAC,KAAK,CAAC;IACnF,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;QACnD,uEAAuE;QACvE,oDAAoD;QACpD,OAAO,OAAO,CAAC,MAAM,CAAC,IAAA,WAAI,EAAC,GAAG,CAAC,CAAC,CAAC;IACnC,CAAC,EAAE,EAAc,CAAC,CAAC;IAEnB,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;QAC5B,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAClE,OAAO,OAAO,CAAC,OAAO,CAAC,IAAA,cAAO,EAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,YAAY,KAAK,OAAO,CAAC;IAC3E,CAAC,CAAC,CAAC;AACL,CAAC"}

View File

@@ -0,0 +1,5 @@
import { Tsoa } from '@tsoa/runtime';
/**
* This will help us do exhaustive matching against only reference types. For example, once you have narrowed the input, you don't then have to check the case where it's a `integer` because it never will be.
*/
export declare function isRefType(metaType: Tsoa.Type): metaType is Tsoa.ReferenceType;

View File

@@ -0,0 +1,66 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isRefType = isRefType;
// This file is designed to contain functions that narrow the input type to a type within src\metadataGeneration\tsoa.ts
const runtime_1 = require("@tsoa/runtime");
/**
* This will help us do exhaustive matching against only reference types. For example, once you have narrowed the input, you don't then have to check the case where it's a `integer` because it never will be.
*/
function isRefType(metaType) {
switch (metaType.dataType) {
case 'any':
return false;
case 'array':
return false;
case 'binary':
return false;
case 'boolean':
return false;
case 'buffer':
return false;
case 'byte':
return false;
case 'date':
return false;
case 'file':
return false;
case 'datetime':
return false;
case 'double':
return false;
case 'enum':
return false;
case 'float':
return false;
case 'integer':
return false;
case 'intersection':
return false;
case 'long':
return false;
case 'nestedObjectLiteral':
return false;
case 'object':
return false;
case 'refEnum':
return true;
case 'refObject':
return true;
case 'refAlias':
return true;
case 'string':
return false;
case 'tuple':
return false;
case 'union':
return false;
case 'void':
return false;
case 'undefined':
return false;
default: {
return (0, runtime_1.assertNever)(metaType);
}
}
}
//# sourceMappingURL=internalTypeGuards.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"internalTypeGuards.js","sourceRoot":"","sources":["../../src/utils/internalTypeGuards.ts"],"names":[],"mappings":";;AAMA,8BAwDC;AA9DD,wHAAwH;AACxH,2CAAkD;AAElD;;GAEG;AACH,SAAgB,SAAS,CAAC,QAAmB;IAC3C,QAAQ,QAAQ,CAAC,QAAQ,EAAE,CAAC;QAC1B,KAAK,KAAK;YACR,OAAO,KAAK,CAAC;QACf,KAAK,OAAO;YACV,OAAO,KAAK,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,UAAU;YACb,OAAO,KAAK,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,OAAO;YACV,OAAO,KAAK,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC;QACf,KAAK,cAAc;YACjB,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,qBAAqB;YACxB,OAAO,KAAK,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,IAAI,CAAC;QACd,KAAK,WAAW;YACd,OAAO,IAAI,CAAC;QACd,KAAK,UAAU;YACb,OAAO,IAAI,CAAC;QACd,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC;QACf,KAAK,OAAO;YACV,OAAO,KAAK,CAAC;QACf,KAAK,OAAO;YACV,OAAO,KAAK,CAAC;QACf,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,WAAW;YACd,OAAO,KAAK,CAAC;QACf,OAAO,CAAC,CAAC,CAAC;YACR,OAAO,IAAA,qBAAW,EAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;AACH,CAAC"}

2
node_modules/@tsoa/cli/dist/utils/isVoidType.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
import { Tsoa } from '@tsoa/runtime';
export declare const isVoidType: (type: Tsoa.Type) => boolean;

16
node_modules/@tsoa/cli/dist/utils/isVoidType.js generated vendored Normal file
View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isVoidType = void 0;
const isVoidType = (type) => {
if (type.dataType === 'void' || type.dataType === 'undefined') {
return true;
}
else if (type.dataType === 'refAlias') {
return (0, exports.isVoidType)(type.type);
}
else {
return false;
}
};
exports.isVoidType = isVoidType;
//# sourceMappingURL=isVoidType.js.map

Some files were not shown because too many files have changed in this diff Show More