UNPKG

openpkg-sdk

Version:

TypeScript package specification SDK

961 lines (953 loc) 32.4 kB
// src/extractor.ts import * as ts4 from "typescript"; import * as path from "path"; import * as fs from "fs"; // src/utils/type-utils.ts import * as ts from "typescript"; function collectReferencedTypes(type, typeChecker, referencedTypes2, visitedTypes = new Set) { if (visitedTypes.has(type)) return; visitedTypes.add(type); const symbol = type.getSymbol(); if (symbol) { const symbolName = symbol.getName(); if (!symbolName.startsWith("__") && !isBuiltInType(symbolName)) { referencedTypes2.add(symbolName); } } if (type.isIntersection()) { for (const intersectionType of type.types) { collectReferencedTypes(intersectionType, typeChecker, referencedTypes2, visitedTypes); } } if (type.isUnion()) { for (const unionType of type.types) { collectReferencedTypes(unionType, typeChecker, referencedTypes2, visitedTypes); } } if (type.flags & ts.TypeFlags.Object) { const objectType = type; if (objectType.objectFlags & ts.ObjectFlags.Reference) { const typeRef = objectType; if (typeRef.typeArguments) { for (const typeArg of typeRef.typeArguments) { collectReferencedTypes(typeArg, typeChecker, referencedTypes2, visitedTypes); } } } } } function isBuiltInType(name) { const builtIns = [ "string", "number", "boolean", "bigint", "symbol", "undefined", "null", "any", "unknown", "never", "void", "object", "Array", "Promise", "Map", "Set", "WeakMap", "WeakSet", "Date", "RegExp", "Error", "Function", "Object", "String", "Number", "Boolean", "BigInt", "Symbol", "Uint8Array", "Int8Array", "Uint16Array", "Int16Array", "Uint32Array", "Int32Array", "Float32Array", "Float64Array", "BigInt64Array", "BigUint64Array", "Uint8ClampedArray", "ArrayBuffer", "ArrayBufferLike", "DataView", "Uint8ArrayConstructor", "ArrayBufferConstructor", "JSON", "Math", "Reflect", "Proxy", "Intl", "globalThis", "__type" ]; return builtIns.includes(name); } // src/utils/tsdoc-utils.ts import * as ts2 from "typescript"; function parseJSDocComment(symbol, typeChecker, sourceFileOverride) { const node = symbol.valueDeclaration || symbol.declarations?.[0]; if (!node) return null; const sourceFile = sourceFileOverride || node.getSourceFile(); const commentRanges = ts2.getLeadingCommentRanges(sourceFile.text, node.pos); if (!commentRanges || commentRanges.length === 0) { return null; } const lastComment = commentRanges[commentRanges.length - 1]; const commentText = sourceFile.text.substring(lastComment.pos, lastComment.end); return parseJSDocText(commentText); } function parseJSDocText(commentText) { const result = { description: "", params: [], examples: [] }; const cleanedText = commentText.replace(/^\/\*\*\s*/, "").replace(/\s*\*\/$/, "").replace(/^\s*\* ?/gm, ""); const lines = cleanedText.split(` `); let currentTag = ""; let currentContent = []; for (const line of lines) { const tagMatch = line.match(/^@(\w+)(?:\s+(.*))?$/); if (tagMatch) { if (currentTag) { processTag(result, currentTag, currentContent.join(` `)); } currentTag = tagMatch[1]; currentContent = tagMatch[2] ? [tagMatch[2]] : []; } else if (currentTag) { currentContent.push(line); } else { if (line.trim()) { result.description += (result.description ? ` ` : "") + line; } } } if (currentTag) { processTag(result, currentTag, currentContent.join(` `)); } return result; } function processTag(result, tag, content) { switch (tag) { case "param": case "parameter": { const paramMatch = content.match(/^(?:\{([^}]+)\}\s+)?(\S+)(?:\s+-\s+)?(.*)$/); if (paramMatch) { const [, type, name, description] = paramMatch; result.params.push({ name: name || "", description: description || "", type }); } break; } case "returns": case "return": { result.returns = content; break; } case "example": { result.examples?.push(content); break; } } } function extractDestructuredParams(parsedDoc, paramName) { const destructuredParams = new Map; const paramPrefix = paramName + "."; for (const param of parsedDoc.params) { if (param.name.startsWith(paramPrefix)) { const propertyName = param.name.substring(paramPrefix.length); destructuredParams.set(propertyName, param.description); } else if (param.name.includes(".") && paramName === "__0") { const [prefix, propertyName] = param.name.split(".", 2); if (propertyName) { destructuredParams.set(propertyName, param.description); } } } return destructuredParams; } function getParameterDocumentation(param, paramDecl, typeChecker) { const result = { description: "" }; const funcNode = paramDecl.parent; if (ts2.isFunctionDeclaration(funcNode) || ts2.isFunctionExpression(funcNode)) { const funcSymbol = typeChecker.getSymbolAtLocation(funcNode.name || funcNode); if (funcSymbol) { const parsedDoc = parseJSDocComment(funcSymbol, typeChecker); if (parsedDoc) { const paramName = param.getName(); const paramDoc = parsedDoc.params.find((p) => p.name === paramName || p.name.split(".")[0] === paramName); if (paramDoc) { result.description = paramDoc.description; } const destructuredProps = extractDestructuredParams(parsedDoc, paramName); if (destructuredProps.size > 0) { result.destructuredProperties = Array.from(destructuredProps.entries()).map(([name, description]) => ({ name, description })); } } } } return result; } // src/utils/parameter-utils.ts import * as ts3 from "typescript"; function propertiesToSchema(properties, description) { const schema = { type: "object", properties: {} }; const required = []; for (const prop of properties) { const propType = prop.type; let propSchema; if (typeof propType === "string") { if (["string", "number", "boolean", "bigint", "null"].includes(propType)) { propSchema = { type: propType === "bigint" ? "string" : propType }; } else { propSchema = { type: propType }; } } else if (propType && typeof propType === "object") { propSchema = propType; } else { propSchema = { type: "any" }; } if (prop.description) { propSchema.description = prop.description; } schema.properties[prop.name] = propSchema; if (!prop.optional) { required.push(prop.name); } } if (required.length > 0) { schema.required = required; } if (description) { schema.description = description; } return schema; } function formatTypeReference(type, typeChecker, typeRefs, referencedTypes2) { const typeString = typeChecker.typeToString(type); const primitives = ["string", "number", "boolean", "bigint", "symbol", "any", "unknown", "void", "undefined", "null", "never"]; if (primitives.includes(typeString)) { if (typeString === "bigint") { return { type: "string", format: "bigint" }; } else if (typeString === "undefined" || typeString === "null") { return { type: "null" }; } else if (typeString === "void" || typeString === "never") { return { type: "null" }; } else { return { type: typeString }; } } if (type.isUnion()) { const unionType = type; const parts = unionType.types.map((t) => formatTypeReference(t, typeChecker, typeRefs, referencedTypes2)); return { anyOf: parts }; } const symbol = type.getSymbol(); if (symbol) { const symbolName = symbol.getName(); if (symbolName.startsWith("__")) { if (type.getFlags() & ts3.TypeFlags.Object) { const properties = type.getProperties(); if (properties.length > 0) { const objSchema = { type: "object", properties: {} }; const required = []; for (const prop of properties) { const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration); const propName = prop.getName(); objSchema.properties[propName] = formatTypeReference(propType, typeChecker, typeRefs, referencedTypes2); if (!(prop.flags & ts3.SymbolFlags.Optional)) { required.push(propName); } } if (required.length > 0) { objSchema.required = required; } return objSchema; } } return { type: "object" }; } else { if (typeRefs.has(symbolName)) { return { $ref: `#/types/${symbolName}` }; } if (referencedTypes2 && !isBuiltInType(symbolName)) { referencedTypes2.add(symbolName); return { $ref: `#/types/${symbolName}` }; } if (symbolName === "Array") { return { type: "array" }; } else if (symbolName === "Promise" || symbolName === "Uint8Array") { return { type: symbolName }; } return { $ref: `#/types/${symbolName}` }; } } if (type.isLiteral()) { if (typeString.startsWith('"') && typeString.endsWith('"')) { const literalValue = typeString.slice(1, -1); return { enum: [literalValue] }; } return { enum: [Number(typeString)] }; } const typePattern = /^(\w+)(\s*\|\s*undefined)?$/; const match = typeString.match(typePattern); if (match) { const [, typeName, hasUndefined] = match; if (typeRefs.has(typeName) || !isBuiltInType(typeName)) { if (hasUndefined) { return { anyOf: [ { $ref: `#/types/${typeName}` }, { type: "null" } ] }; } return { $ref: `#/types/${typeName}` }; } } return { type: typeString }; } function structureParameter(param, paramDecl, paramType, typeChecker, typeRefs, functionDoc, paramDoc, referencedTypes2) { const paramName = param.getName(); if (paramType.isIntersection()) { const properties = []; const intersectionType = paramType; for (const subType of intersectionType.types) { const symbol2 = subType.getSymbol(); const typeString = typeChecker.typeToString(subType); if (!symbol2 || symbol2.getName().startsWith("__")) { for (const prop of subType.getProperties()) { const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration); let description = ""; if (functionDoc) { let docParam = functionDoc.params.find((p) => p.name === `${paramName}.${prop.getName()}`); if (!docParam && paramName === "__0") { docParam = functionDoc.params.find((p) => p.name.endsWith(`.${prop.getName()}`)); } if (docParam) { description = docParam.description; } } properties.push({ name: prop.getName(), type: formatTypeReference(propType, typeChecker, typeRefs, referencedTypes2), description, optional: !!(prop.flags & ts3.SymbolFlags.Optional) }); } } else { const symbolName = symbol2.getName(); for (const prop of subType.getProperties()) { const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration); properties.push({ name: prop.getName(), type: formatTypeReference(propType, typeChecker, typeRefs, referencedTypes2), description: "", optional: !!(prop.flags & ts3.SymbolFlags.Optional) }); } } } const actualName = paramName === "__0" && functionDoc ? getActualParamName(functionDoc) : paramName; return { name: actualName, required: !typeChecker.isOptionalParameter(paramDecl), description: paramDoc?.description || "", schema: propertiesToSchema(properties) }; } if (paramType.isUnion()) { const unionType = paramType; const objectOptions = []; let hasNonObjectTypes = false; for (const subType of unionType.types) { const symbol2 = subType.getSymbol(); if (!symbol2 || symbol2.getName().startsWith("__")) { const properties = []; for (const prop of subType.getProperties()) { const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration); properties.push({ name: prop.getName(), type: formatTypeReference(propType, typeChecker, typeRefs, referencedTypes2), description: "", optional: !!(prop.flags & ts3.SymbolFlags.Optional) }); } if (properties.length > 0) { objectOptions.push({ properties }); } } else { hasNonObjectTypes = true; } } if (objectOptions.length > 0 && !hasNonObjectTypes) { return { name: paramName, required: !typeChecker.isOptionalParameter(paramDecl), description: paramDoc?.description || "", schema: { oneOf: objectOptions.map((opt) => propertiesToSchema(opt.properties)) } }; } } const symbol = paramType.getSymbol(); if (symbol && symbol.getName().startsWith("__") && paramType.getProperties().length > 0) { const properties = []; for (const prop of paramType.getProperties()) { const propType = typeChecker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration); properties.push({ name: prop.getName(), type: formatTypeReference(propType, typeChecker, typeRefs, referencedTypes2), description: "", optional: !!(prop.flags & ts3.SymbolFlags.Optional) }); } return { name: paramName, required: !typeChecker.isOptionalParameter(paramDecl), description: paramDoc?.description || "", schema: propertiesToSchema(properties) }; } const typeRef = formatTypeReference(paramType, typeChecker, typeRefs, referencedTypes2); let schema; if (typeof typeRef === "string") { if (["string", "number", "boolean", "null", "undefined", "any", "unknown", "never", "void"].includes(typeRef)) { schema = { type: typeRef }; } else { schema = { type: typeRef }; } } else { schema = typeRef; } return { name: paramName, required: !typeChecker.isOptionalParameter(paramDecl), description: paramDoc?.description || "", schema }; } function getActualParamName(functionDoc) { const docParam = functionDoc.params.find((p) => p.name.includes(".")); if (docParam) { return docParam.name.split(".")[0]; } return "__0"; } // src/extractor.ts async function extractPackageSpec(entryFile, packageDir, content, options) { const baseDir = packageDir || path.dirname(entryFile); const nodeModulesPath = path.join(baseDir, "node_modules"); const hasNodeModules = fs.existsSync(nodeModulesPath); const resolveExternalTypes = options?.resolveExternalTypes ?? hasNodeModules; if (hasNodeModules && resolveExternalTypes) { console.log("node_modules detected, resolving external types"); } const configPath = ts4.findConfigFile(baseDir, ts4.sys.fileExists, "tsconfig.json"); let compilerOptions = { target: ts4.ScriptTarget.Latest, module: ts4.ModuleKind.CommonJS, lib: ["lib.es2021.d.ts"], allowJs: true, declaration: true, moduleResolution: ts4.ModuleResolutionKind.NodeJs }; if (configPath) { const configFile = ts4.readConfigFile(configPath, ts4.sys.readFile); const parsedConfig = ts4.parseJsonConfigFileContent(configFile.config, ts4.sys, path.dirname(configPath)); compilerOptions = { ...compilerOptions, ...parsedConfig.options }; } let program; if (content !== undefined) { const sourceFile2 = ts4.createSourceFile(entryFile, content, ts4.ScriptTarget.Latest, true); const compilerHost = ts4.createCompilerHost(compilerOptions); const originalGetSourceFile = compilerHost.getSourceFile; compilerHost.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => { if (fileName === entryFile) { return sourceFile2; } return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile); }; program = ts4.createProgram([entryFile], compilerOptions, compilerHost); } else { program = ts4.createProgram([entryFile], compilerOptions); } const typeChecker = program.getTypeChecker(); const sourceFile = program.getSourceFile(entryFile); if (!sourceFile) { throw new Error(`Could not load ${entryFile}`); } const packageJsonPath = path.join(baseDir, "package.json"); const packageJson = fs.existsSync(packageJsonPath) ? JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) : {}; const spec = { $schema: "https://raw.githubusercontent.com/ryanwaits/openpkg/main/schemas/v0.1.0/openpkg.schema.json", openpkg: "0.1.0", meta: { name: packageJson.name || "unknown", version: packageJson.version || "1.0.0", description: packageJson.description || "", license: packageJson.license || "", repository: packageJson.repository?.url || packageJson.repository || "", ecosystem: "js/ts" }, exports: [], types: [] }; const typeRefs = new Map; const typeDefinitions = new Map; const referencedTypes2 = new Set; let typeCounter = 0; const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile); if (moduleSymbol) { const exports = typeChecker.getExportsOfModule(moduleSymbol); for (const symbol of exports) { const declaration = symbol.valueDeclaration || symbol.declarations?.[0]; if (!declaration) continue; const name = symbol.getName(); if (ts4.isClassDeclaration(declaration) || ts4.isInterfaceDeclaration(declaration) || ts4.isTypeAliasDeclaration(declaration) || ts4.isEnumDeclaration(declaration)) { typeRefs.set(name, name); } } for (const symbol of exports) { const declaration = symbol.valueDeclaration || symbol.declarations?.[0]; if (!declaration) continue; const name = symbol.getName(); const id = name; if (ts4.isFunctionDeclaration(declaration)) { spec.exports.push({ id, name, kind: "function", signatures: getFunctionSignatures(declaration, typeChecker, typeRefs, referencedTypes2), description: getJSDocComment(symbol, typeChecker), source: getSourceLocation(declaration) }); } else if (ts4.isClassDeclaration(declaration)) { spec.exports.push({ id, name, kind: "class", description: getJSDocComment(symbol, typeChecker), source: getSourceLocation(declaration) }); if (!typeDefinitions.has(name)) { const typeDef = { id, name, kind: "class", description: getJSDocComment(symbol, typeChecker), source: getSourceLocation(declaration) }; typeDefinitions.set(name, typeDef); spec.types?.push(typeDef); typeRefs.set(name, id); } } else if (ts4.isInterfaceDeclaration(declaration)) { spec.exports.push({ id, name, kind: "interface", description: getJSDocComment(symbol, typeChecker), source: getSourceLocation(declaration) }); if (!typeDefinitions.has(name)) { const typeDef = { id, name, kind: "interface", schema: interfaceToSchema(declaration, typeChecker, typeRefs, referencedTypes2), description: getJSDocComment(symbol, typeChecker), source: getSourceLocation(declaration) }; typeDefinitions.set(name, typeDef); spec.types?.push(typeDef); typeRefs.set(name, id); } } else if (ts4.isTypeAliasDeclaration(declaration)) { spec.exports.push({ id, name, kind: "type", type: typeToRef(declaration.type, typeChecker, typeRefs), description: getJSDocComment(symbol, typeChecker), source: getSourceLocation(declaration) }); if (!typeDefinitions.has(name)) { const typeDef = { id, name, kind: "type", type: typeChecker.typeToString(typeChecker.getTypeAtLocation(declaration)), description: getJSDocComment(symbol, typeChecker), source: getSourceLocation(declaration) }; typeDefinitions.set(name, typeDef); spec.types?.push(typeDef); typeRefs.set(name, id); } } else if (ts4.isEnumDeclaration(declaration)) { spec.exports.push({ id, name, kind: "enum", description: getJSDocComment(symbol, typeChecker), source: getSourceLocation(declaration) }); if (!typeDefinitions.has(name)) { const typeDef = { id, name, kind: "enum", members: getEnumMembers(declaration), description: getJSDocComment(symbol, typeChecker), source: getSourceLocation(declaration) }; typeDefinitions.set(name, typeDef); spec.types?.push(typeDef); typeRefs.set(name, id); } } else if (ts4.isVariableDeclaration(declaration)) { const type = typeChecker.getTypeAtLocation(declaration); spec.exports.push({ id, name, kind: "variable", type: typeToRef(declaration, typeChecker, typeRefs), description: getJSDocComment(symbol, typeChecker), source: getSourceLocation(declaration) }); } } for (const typeName of referencedTypes2) { if (!typeRefs.has(typeName) && !typeDefinitions.has(typeName)) { const allSourceFiles = program.getSourceFiles(); for (const file of allSourceFiles) { if (!resolveExternalTypes && (file.fileName.includes("node_modules") || file.fileName.endsWith(".d.ts") && !file.fileName.startsWith(baseDir))) { continue; } const fileSymbol = typeChecker.getSymbolAtLocation(file); if (fileSymbol) { const exports2 = typeChecker.getExportsOfModule(fileSymbol); for (const exportSymbol of exports2) { if (exportSymbol.getName() === typeName && !typeDefinitions.has(typeName)) { const declaration = exportSymbol.valueDeclaration || exportSymbol.declarations?.[0]; if (!declaration) continue; if (ts4.isInterfaceDeclaration(declaration)) { const typeDef = { id: typeName, name: typeName, kind: "interface", schema: interfaceToSchema(declaration, typeChecker, typeRefs, referencedTypes2), description: getJSDocComment(exportSymbol, typeChecker), source: getSourceLocation(declaration) }; typeDefinitions.set(typeName, typeDef); spec.types?.push(typeDef); typeRefs.set(typeName, typeName); } else if (ts4.isTypeAliasDeclaration(declaration)) { const typeDef = { id: typeName, name: typeName, kind: "type", type: typeChecker.typeToString(typeChecker.getTypeAtLocation(declaration)), description: getJSDocComment(exportSymbol, typeChecker), source: getSourceLocation(declaration) }; typeDefinitions.set(typeName, typeDef); spec.types?.push(typeDef); typeRefs.set(typeName, typeName); } } } } } } } } return spec; } function getJSDocComment(symbol, typeChecker) { const comments = symbol.getDocumentationComment(typeChecker); return ts4.displayPartsToString(comments); } function getSourceLocation(node) { const sourceFile = node.getSourceFile(); const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart()); return { file: sourceFile.fileName, line: line + 1 }; } function typeToRef(node, typeChecker, typeRefs, referencedTypes2) { const type = typeChecker.getTypeAtLocation(node); if (referencedTypes2) { collectReferencedTypes(type, typeChecker, referencedTypes2); } return formatTypeReference(type, typeChecker, typeRefs, referencedTypes2); } function getFunctionSignatures(func, typeChecker, typeRefs, referencedTypes2) { const signature = typeChecker.getSignatureFromDeclaration(func); if (!signature) return []; const funcSymbol = typeChecker.getSymbolAtLocation(func.name || func); const functionDoc = funcSymbol ? parseJSDocComment(funcSymbol, typeChecker) : null; return [{ parameters: signature.getParameters().map((param) => { const paramDecl = param.valueDeclaration; const paramType = typeChecker.getTypeAtLocation(paramDecl); if (referencedTypes2) { collectReferencedTypes(paramType, typeChecker, referencedTypes2); } const paramDoc = getParameterDocumentation(param, paramDecl, typeChecker); return structureParameter(param, paramDecl, paramType, typeChecker, typeRefs, functionDoc, paramDoc, referencedTypes2); }), returns: { schema: signature.getReturnType() ? formatTypeReference(signature.getReturnType(), typeChecker, typeRefs, referencedTypes2) : { type: "void" }, description: functionDoc?.returns || "" } }]; } function interfaceToSchema(iface, typeChecker, typeRefs, referencedTypes2) { const schema = { type: "object", properties: {} }; const required = []; iface.members.filter(ts4.isPropertySignature).forEach((prop) => { const propName = prop.name?.getText() || ""; if (prop.type && referencedTypes2) { const propType = typeChecker.getTypeAtLocation(prop.type); collectReferencedTypes(propType, typeChecker, referencedTypes2); } schema.properties[propName] = prop.type ? formatTypeReference(typeChecker.getTypeAtLocation(prop.type), typeChecker, typeRefs, referencedTypes2) : { type: "any" }; if (!prop.questionToken) { required.push(propName); } }); if (required.length > 0) { schema.required = required; } return schema; } function getEnumMembers(enumDecl) { return enumDecl.members.map((member) => ({ name: member.name?.getText() || "", value: member.initializer ? member.initializer.getText() : undefined, description: "" })); } // src/index.ts import * as fs2 from "fs/promises"; import * as path2 from "path"; // src/types/openpkg.ts import { z } from "zod"; var schemaSchema = z.lazy(() => z.union([ z.object({ type: z.enum(["string", "number", "boolean", "integer", "null", "array", "object"]) }), z.object({ $ref: z.string() }), z.object({ type: z.literal("array"), items: schemaSchema.optional(), description: z.string().optional() }), z.object({ type: z.literal("object"), properties: z.record(z.string(), schemaSchema).optional(), required: z.array(z.string()).optional(), description: z.string().optional(), additionalProperties: z.union([z.boolean(), schemaSchema]).optional() }), z.object({ oneOf: z.array(schemaSchema), description: z.string().optional() }), z.object({ anyOf: z.array(schemaSchema), description: z.string().optional() }), z.object({ allOf: z.array(schemaSchema), description: z.string().optional() }), z.object({ enum: z.array(z.union([z.string(), z.number(), z.null()])), description: z.string().optional() }) ])); var parameterSchema = z.object({ name: z.string(), in: z.literal("query").optional(), required: z.boolean().optional(), description: z.string().optional(), schema: schemaSchema }); var returnTypeSchema = z.object({ schema: schemaSchema, description: z.string().optional() }); var classMemberSchema = z.object({ id: z.string(), name: z.string(), kind: z.enum(["method", "property", "constructor", "accessor"]), visibility: z.enum(["public", "private", "protected"]).optional(), signatures: z.array(z.object({ parameters: z.array(parameterSchema).optional(), returns: returnTypeSchema.optional(), description: z.string().optional() })).optional(), schema: schemaSchema.optional(), description: z.string().optional(), examples: z.array(z.string()).optional(), flags: z.record(z.string(), z.boolean()).optional() }); var enumMemberSchema = z.object({ id: z.string(), name: z.string(), value: z.union([z.string(), z.number()]).optional(), description: z.string().optional() }); var memberSchema = z.union([classMemberSchema, enumMemberSchema]); var openPkgSchema = z.object({ $schema: z.string().optional(), openpkg: z.literal("0.1.0"), meta: z.object({ name: z.string(), version: z.string(), description: z.string().optional(), license: z.string().optional(), repository: z.string().optional(), ecosystem: z.string().default("js/ts") }), exports: z.array(z.object({ id: z.string(), name: z.string(), kind: z.enum(["function", "class", "variable", "interface", "type", "enum", "module", "namespace", "reference"]), signatures: z.array(z.object({ parameters: z.array(parameterSchema).optional(), returns: returnTypeSchema.optional(), description: z.string().optional() })).optional(), members: z.array(memberSchema).optional(), schema: schemaSchema.optional(), description: z.string().optional(), examples: z.array(z.string()).optional(), source: z.object({ file: z.string().optional(), line: z.number().optional(), url: z.string().optional() }).optional(), flags: z.record(z.string(), z.unknown()).optional(), tags: z.array(z.object({ name: z.string(), text: z.string() })).optional() })), types: z.array(z.object({ id: z.string(), name: z.string(), kind: z.enum(["class", "interface", "type", "enum"]), description: z.string().optional(), schema: schemaSchema.optional(), members: z.array(z.object({ name: z.string(), value: z.union([z.string(), z.number()]).optional(), description: z.string().optional() })).optional(), source: z.object({ file: z.string().optional(), line: z.number().optional(), url: z.string().optional() }).optional(), tags: z.array(z.object({ name: z.string(), text: z.string() })).optional(), rawComments: z.string().optional() })).optional(), examples: z.array(z.object({})).optional(), extensions: z.record(z.string(), z.unknown()).optional() }); // src/index.ts class OpenPkg { options; constructor(options = {}) { this.options = { includePrivate: false, followImports: true, ...options }; } async analyze(code, fileName = "temp.ts") { const tempDir = path2.dirname(fileName); const result = await extractPackageSpec(fileName, tempDir, code, this.options); return result; } async analyzeFile(filePath) { const content = await fs2.readFile(filePath, "utf-8"); const dir = path2.dirname(filePath); const result = await extractPackageSpec(filePath, dir, content, this.options); return result; } async analyzeProject(entryPath) { return this.analyzeFile(entryPath); } async analyzeWithDiagnostics(code, fileName) { const spec = await this.analyze(code, fileName); return { spec, diagnostics: [] }; } } async function analyze(code) { return new OpenPkg().analyze(code); } async function analyzeFile(filePath) { return new OpenPkg().analyzeFile(filePath); } export { openPkgSchema, extractPackageSpec, analyzeFile, analyze, OpenPkg };