UNPKG

@nestia/sdk

Version:

Nestia SDK and Swagger generator

166 lines 8.16 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SdkGenerator = void 0; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const CloneGenerator_1 = require("./CloneGenerator"); const SdkDistributionComposer_1 = require("./internal/SdkDistributionComposer"); const SdkFileProgrammer_1 = require("./internal/SdkFileProgrammer"); const SdkHttpParameterProgrammer_1 = require("./internal/SdkHttpParameterProgrammer"); var SdkGenerator; (function (SdkGenerator) { SdkGenerator.generate = (app) => __awaiter(this, void 0, void 0, function* () { if (app.project.config.output === undefined) throw new Error("Output directory is not defined."); // PREPARE NEW DIRECTORIES console.log("Generating SDK Library"); yield fs_1.default.promises.mkdir(app.project.config.output, { recursive: true }); // BUNDLING yield SdkGenerator.bundle(app.project.config.output); // STRUCTURES if (app.project.config.clone === true) yield CloneGenerator_1.CloneGenerator.write(app); // FUNCTIONAL yield SdkFileProgrammer_1.SdkFileProgrammer.generate(app); // DISTRIBUTION if (app.project.config.distribute !== undefined) yield SdkDistributionComposer_1.SdkDistributionComposer.compose({ config: app.project.config, mcp: app.routes.some((r) => r.protocol === "mcp"), websocket: app.routes.some((r) => r.protocol === "websocket"), }); }); SdkGenerator.validate = (app) => { const errors = []; validateMcpDuplicates(errors)(app.routes); validateMcpAccessors(errors)(app.routes); if (app.project.config.clone === true) return errors; for (const route of app.routes) if (route.protocol === "http") validateImplicit({ config: app.project.config, errors, route, }); return errors; }; const validateMcpDuplicates = (errors) => (routes) => { var _a; const dict = new Map(); for (const route of routes) if (route.protocol === "mcp") { const array = (_a = dict.get(route.toolName)) !== null && _a !== void 0 ? _a : []; array.push(route); dict.set(route.toolName, array); } for (const [toolName, list] of dict) if (list.length > 1) for (const route of list) errors.push({ file: route.controller.file, class: route.controller.class.name, function: route.function.name || route.name, from: `@McpRoute(${JSON.stringify(toolName)})`, contents: [ `Duplicate MCP tool name ${JSON.stringify(toolName)} is not allowed.`, ], }); }; const validateMcpAccessors = (errors) => (routes) => { var _a; const dict = new Map(); for (const route of routes) if (route.protocol === "mcp") { const accessor = route.accessor.join("."); const array = (_a = dict.get(accessor)) !== null && _a !== void 0 ? _a : []; array.push(route); dict.set(accessor, array); } for (const [accessor, list] of dict) if (list.length > 1) for (const route of list) errors.push({ file: route.controller.file, class: route.controller.class.name, function: route.function.name || route.name, from: `@McpRoute(${JSON.stringify(route.toolName)})`, contents: [ `MCP tool name ${JSON.stringify(route.toolName)} conflicts on generated SDK accessor "api.functional.${accessor}".`, ], }); }; const validateImplicit = (props) => { for (const p of SdkHttpParameterProgrammer_1.SdkHttpParameterProgrammer.getAll(props.route)) { if (isImplicitType(p.type)) props.errors.push({ file: props.route.controller.file, class: props.route.controller.class.name, function: props.route.name, from: `parameter ${JSON.stringify(p.name)}`, contents: [`implicit (unnamed) parameter type.`], }); } if (props.config.propagate === true) for (const [key, value] of Object.entries(props.route.exceptions)) if (isImplicitType(value.type)) props.errors.push({ file: props.route.controller.file, class: props.route.controller.class.name, function: props.route.name, from: `exception ${JSON.stringify(key)}`, contents: [`implicit (unnamed) exception type.`], }); if (props.route.success.binary === false && isImplicitType(props.route.success.type)) props.errors.push({ file: props.route.controller.file, class: props.route.controller.class.name, function: props.route.name, from: "success", contents: [`implicit (unnamed) return type.`], }); }; const isImplicitType = (type) => { var _a; return type.name === "__type" || type.name === "__object" || type.name.startsWith("__type.") || type.name.startsWith("__object.") || type.name.includes("readonly [") || (!!((_a = type.typeArguments) === null || _a === void 0 ? void 0 : _a.length) && type.typeArguments.some(isImplicitType)); }; /** * Emplace the static bundle files (`index.ts`, `module.ts`, ...) into the SDK * output directory. * * Files the user already has are never touched, so that hand-written * customizations (e.g. extra re-exports in `module.ts`) survive regeneration. * Only missing files are filled in from the bundle. */ SdkGenerator.bundle = (output) => __awaiter(this, void 0, void 0, function* () { const files = yield fs_1.default.promises.readdir(SdkGenerator.BUNDLE_PATH); for (const file of files) { const current = `${SdkGenerator.BUNDLE_PATH}/${file}`; const target = `${output}/${file}`; const stats = yield fs_1.default.promises.stat(current); if (stats.isFile() === false) continue; if (fs_1.default.existsSync(target) === true) continue; const content = yield fs_1.default.promises.readFile(current, "utf8"); yield fs_1.default.promises.writeFile(target, content, "utf8"); } }); SdkGenerator.BUNDLE_PATH = path_1.default.join(__dirname, "..", "..", "assets", "bundle", "api"); })(SdkGenerator || (exports.SdkGenerator = SdkGenerator = {})); //# sourceMappingURL=SdkGenerator.js.map