UNPKG

@arifwidianto/rpc-agent

Version:

RPC Agent for both client and server, extends more methods easily

221 lines (220 loc) 12 kB
"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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SchemaGenerator = void 0; const path = __importStar(require("node:path")); const fs = __importStar(require("node:fs")); const ts = __importStar(require("typescript")); class SchemaGenerator { sourceFile; program; initialize(entryPointPath) { const configPath = ts.findConfigFile(path.dirname(entryPointPath), ts.sys.fileExists, "tsconfig.json"); if (!configPath) { throw new Error("Could not find a valid tsconfig.json."); } const { config } = ts.readConfigFile(configPath, ts.sys.readFile); const { options } = ts.parseJsonConfigFileContent(config, ts.sys, path.dirname(configPath)); this.program = ts.createProgram([entryPointPath], options); this.sourceFile = this.program.getSourceFile(entryPointPath); if (!this.sourceFile) { throw new Error(`Could not load source file: ${entryPointPath}`); } } generateSchema(entryPointPath) { this.initialize(entryPointPath); if (!this.sourceFile || !this.program) { throw new Error("Source file or program not initialized"); } const schema = {}; const checker = this.program.getTypeChecker(); ts.forEachChild(this.sourceFile, (node) => { if (ts.isClassDeclaration(node) && node.name) { const symbol = checker.getSymbolAtLocation(node.name); if (symbol) { this.processClassMethods(node, schema); } } }); const schemaPath = path.join(path.dirname(entryPointPath), "schema.ts"); const schemaContent = this.generateSchemaFileContent(schema); console.log(`Writing schema to ${schemaPath}`); fs.writeFileSync(schemaPath, schemaContent); } processClassMethods(classNode, schema) { console.log("Processing class:", classNode.name?.text); classNode.members.forEach((member) => { if (ts.isPropertyDeclaration(member) && member.name.getText() === "methods") { console.log("Found methods property"); if (member.initializer && ts.isObjectLiteralExpression(member.initializer)) { console.log("Methods is an object literal with", member.initializer.properties.length, "properties"); member.initializer.properties.forEach((prop) => { console.log("Property kind:", ts.SyntaxKind[prop.kind]); if (ts.isPropertyAssignment(prop)) { console.log("Property name:", prop.name.getText()); console.log("Property initializer kind:", ts.SyntaxKind[prop.initializer.kind]); const methodName = prop.name.getText(); console.log("Found method:", methodName); const typeChecker = this.program.getTypeChecker(); const type = typeChecker.getTypeAtLocation(prop.initializer); console.log("Method type:", typeChecker.typeToString(type)); if (ts.isArrowFunction(prop.initializer) || ts.isFunctionExpression(prop.initializer) || ts.isMethodDeclaration(prop.initializer)) { const signature = typeChecker.getSignatureFromDeclaration(prop.initializer); console.log("Signature:", signature ? "Found" : "Not found"); if (signature) { console.log("Parameters:", signature.getParameters().map((p) => p.name)); console.log("Return type:", typeChecker.typeToString(typeChecker.getReturnTypeOfSignature(signature))); const methodSchema = { input: this.extractMethodInputSchema(signature, prop.initializer), output: this.extractMethodOutputSchema(signature, prop.initializer), example: { shell: `echo '{"jsonrpc":"2.0","method":"${methodName}","params":{},"id":1}' | nc --tcp localhost 9101`, node: `node -e "const net = require('net'); const client = new net.Socket(); client.connect(9101, 'localhost', () => { client.write(JSON.stringify({jsonrpc: '2.0', method: '${methodName}', params: {}, id: Date.now()}) + '\\n'); }); client.on('data', (data) => { console.log(data.toString()); client.destroy(); });"`, }, }; console.log("Method schema:", Object.keys(methodSchema).join(", ")); schema[methodName] = methodSchema; } else { console.log("No signature found for method:", methodName); } } } else if (ts.isMethodDeclaration(prop)) { const methodName = prop.name.getText(); console.log("Found method:", methodName); const typeChecker = this.program.getTypeChecker(); const type = typeChecker.getTypeAtLocation(prop); console.log("Method type:", typeChecker.typeToString(type)); const signature = typeChecker.getSignatureFromDeclaration(prop); console.log("Signature:", signature ? "Found" : "Not found"); if (signature) { console.log("Parameters:", signature.getParameters().map((p) => p.name)); console.log("Return type:", typeChecker.typeToString(typeChecker.getReturnTypeOfSignature(signature))); const methodSchema = { input: this.extractMethodInputSchema(signature, prop), output: this.extractMethodOutputSchema(signature, prop), example: { shell: `echo '{"jsonrpc":"2.0","method":"${methodName}","params":{},"id":1}' | nc --tcp localhost 9101`, node: `node -e "const net = require('net'); const client = new net.Socket(); client.connect(9101, 'localhost', () => { client.write(JSON.stringify({jsonrpc: '2.0', method: '${methodName}', params: {}, id: Date.now()}) + '\\n'); }); client.on('data', (data) => { console.log(data.toString()); client.destroy(); });"`, }, }; console.log("Method schema:", Object.keys(methodSchema).join(", ")); schema[methodName] = methodSchema; } else { console.log("No signature found for method:", methodName); } } }); } } }); } extractMethodInputSchema(signature, methodNode) { const typeChecker = this.program.getTypeChecker(); const parameters = signature.getParameters(); if (parameters.length === 0) { return {}; } const firstParam = parameters[0]; const paramType = typeChecker.getTypeOfSymbolAtLocation(firstParam, methodNode); const properties = paramType.getProperties(); const inputSchema = {}; properties.forEach((prop) => { const propType = typeChecker.getTypeOfSymbolAtLocation(prop, methodNode); const typeString = typeChecker.typeToString(propType); inputSchema[prop.name] = { type: this.mapTypeScriptTypeToSchemaType(typeString), description: `Parameter ${prop.name}`, required: true, }; }); return inputSchema; } extractMethodOutputSchema(signature, methodNode) { const typeChecker = this.program.getTypeChecker(); const returnType = typeChecker.getReturnTypeOfSignature(signature); let resolvedType = returnType; if (returnType.symbol?.name === "Promise") { const typeArguments = returnType.typeArguments; if (typeArguments && typeArguments.length > 0) { resolvedType = typeArguments[0]; } } const properties = resolvedType.getProperties(); const outputSchema = {}; properties.forEach((prop) => { const propType = typeChecker.getTypeOfSymbolAtLocation(prop, methodNode); const typeString = typeChecker.typeToString(propType); outputSchema[prop.name] = { type: this.mapTypeScriptTypeToSchemaType(typeString), description: `Output ${prop.name}`, }; }); return outputSchema; } mapTypeScriptTypeToSchemaType(tsType) { const cleanType = tsType.toLowerCase().trim(); if (cleanType.includes("number")) return "number"; if (cleanType.includes("string")) return "string"; if (cleanType.includes("boolean")) return "boolean"; if (cleanType.includes("date")) return "string"; if (cleanType.includes("array")) return "array"; if (cleanType === "any" || cleanType === "unknown") return "any"; return "object"; } generateSchemaFileContent(schema) { const formatValue = (value, indent = 2) => { if (typeof value === "string") { const escaped = value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n"); return `'${escaped}'`; } else if (typeof value === "object" && value !== null) { const spaces = " ".repeat(indent); const entries = Object.entries(value); if (entries.length === 0) return "{}"; return `{ ${entries.map(([key, val]) => `${spaces}${key}: ${formatValue(val, indent + 2)}`).join(",\n")} ${" ".repeat(indent - 2)}}`; } return String(value); }; return `import { ExtensionSchema } from "@arifwidianto/rpc-agent" export const schema: ExtensionSchema = ${formatValue(schema)} `; } } exports.SchemaGenerator = SchemaGenerator; //# sourceMappingURL=schema-generator.js.map