import ts from 'typescript';
import { VirtualTypeScriptEnvironment } from '@typescript/vfs';

declare class WrappedSymbol {
    typeChecker: ts.TypeChecker;
    symbol: ts.Symbol;
    constructor(typeChecker: ts.TypeChecker, symbol: ts.Symbol);
    get isFunctionDeclaration(): boolean;
    get isModuleDeclaration(): boolean;
    get exports(): WrappedSymbol[];
    get aliasedSymbolIfNecessary(): WrappedSymbol;
    get comment(): string;
    get name(): string;
    get parameters(): WrappedSymbol[];
    get examples(): string[];
    get type(): ts.TypeNode;
    get hasFunctionTag(): boolean;
    get isExportAlias(): boolean;
    get jsDocTags(): ts.JSDocTag[];
}

/**
 * Object for holding references to compiler components to make working with
 * the TypeScript compiler easier.
 *
 * - `system`
 *   The virtual filesystem allowing adding files in memory.
 * - `env`
 *   The TypeScript environment, where the files provided by the `system` are
 *   the source of truth.
 */
declare class Project {
    fsMap: Map<string, string>;
    env: VirtualTypeScriptEnvironment;
    system: ts.System;
    transformers: Array<(program: ts.Program, options: {}) => ts.TransformerFactory<ts.SourceFile>>;
    transformOptions: {};
    ts: typeof ts;
    constructor(fsMap?: Map<string, string>);
    /**
     * Add the adaptors type definition to the compilers filesystem map
     * @param moduleName the name of the module that will be imported,
     * i.e. the adaptors name `@openfn/language-http`.
     * @param dts the .d.ts file as a string
     */
    addTypeDefinition(moduleName: string, dts: string, fileName?: string): void;
    get program(): ts.Program;
    get sourceFile(): ts.SourceFile;
    get typeChecker(): ts.TypeChecker;
    getSourceFile(filename: string): ts.SourceFile | undefined;
    /**
     * Add a file to the environment.
     *
     * Note that files added via the constructor are not considered part of the
     * 'project', rather files available for reading during diagnostics and
     * compilation. To add packages or type definitions use `addFile`.
     */
    createFile(expression: string, filename?: string): ts.SourceFile;
    /**
     * Add a file to the system.
     *
     * Differs from `createFile` in that these files are not considered part of
     * the project - but part of the file system.
     */
    addToFS(mapOrContent: string | Map<string, string>, path?: string): void;
    /**
     * Return a list of symbols that have been marked for export.
     * In the case of aliases (`export {a, b}`), these aliases are followed
     * to their original symbol.
     */
    getSymbol(sourceFile: ts.SourceFile): WrappedSymbol;
    getDiagnostics(sourceFile?: ts.SourceFile): readonly ts.Diagnostic[];
    formatDiagnostics(sourceFile?: ts.SourceFile): string[];
    compile(sourceFile?: ts.SourceFile): ts.EmitResult;
    static compilerOpts: ts.CompilerOptions;
}

type Options = {
    path?: string;
    repo?: string;
    project?: any;
    cache?: any;
};
type PackageDescription = {
    name: string;
    version: string;
    functions: FunctionDescription[];
    namespaces: NamespaceDescription[];
};
type FunctionDescription = {
    name: string;
    type: 'function';
    magic: boolean;
    isOperation: boolean;
    parameters: ParameterDescription[];
    description: string;
    examples: ExampleDescription[];
    parent?: string;
    package?: string;
};
type NamespaceDescription = {
    name: string;
    type: 'namespace';
};
type ExampleDescription = {
    code: string;
    caption?: string;
};
type ParameterDescription = {
    name: string;
    optional: boolean;
    type: string;
};
declare const describePackage: (specifier: string, _options?: Options) => Promise<PackageDescription>;
declare const loadDTS: (_specifier: string, _options: Options) => Promise<string[]>;

type DescribeOptions = {
    includePrivate?: boolean;
};
declare const describeProject: (project: Project, typesEntry?: string, options?: DescribeOptions) => (FunctionDescription | NamespaceDescription)[];

/**
 * # Pack
 *
 * Object used to handle loading and querying module packages.
 *
 * Internally the file listing is kept without a directory prefix, and
 * all paths returned by `.types` and `.getFiles()` are prefixed by
 * `packageBase`. This allows a packages to be loaded from different sources
 * such as the local filesystem or from a CDN, and the resulting paths to mimic
 * a local filesystem.
 *
 * At a minimum, a Pack requires a `path`. A path is intended to represent
 * where to find the package - at present it is an npm style specifier
 * (e.g. `@myorg/mypackage@1.0.0`, or `mypackage`).
 *
 * While you can build a new Pack instance via the constructor, the easiest way
 * is to use the public methods.
 *
 * ## Loading from Unpkg
 *
 * ```js
 * const pack = await Pack.fetch("@myorg/mypackage")
 * ```
 *
 * This will create a new Pack instance and download it's `package.json` and
 * get a file listing.
 * Next in order to download all the files in a package you can use
 * `pack.getFiles()`, or pass in a list of files you want to be retrieved.
 *
 * ```js
 * const packageOrDts = /(?:package.json)|(?:\.d\.ts$)/i;
 * const files = await pack.getFiles(
 *   pack.fileListing.filter((path) => packageOrDts.test(path))
 * );
 * ```
 */
interface PackParameters {
    path: string;
    packageJson?: PackageJson;
    fileListing?: string[];
    fsMap?: Map<string, string>;
    packageBase?: string;
}
interface PackageJson {
    name: string;
    version: string;
    types?: string;
}
declare class Pack {
    path: string;
    _packageJson: PackageJson | undefined;
    _fileListing: string[] | undefined;
    fsMap: Map<string, string>;
    _packageBase: string | undefined;
    constructor(params: PackParameters);
    get packageJson(): PackageJson;
    /**
     * The file contents of the package according to the source.
     *
     * _Different to `fsMap`, which includes the `root`._
     */
    get fileListing(): string[];
    /**
     * The absolute name and version of the package.
     *
     * i.e. `@<org>/<package>@<version>`
     *
     * This may differ from the `path` passed in during creation.
     * For example, if the package path was `mypackage@latest` and the latest
     * version is `1.2.3` as stated by the `package.json` the resulting
     * specifier will be `mypackage@1.2.3`.
     */
    get specifier(): string;
    /**
     * The version of the package specified in the `package.json`
     */
    get version(): string;
    get packageBase(): string;
    /**
     * Get the "types" property from `package.json`, and ensure it's path is
     * relative to `/`.
     */
    get types(): string | null;
    getFiles(files?: string[]): Promise<Map<string, string>>;
    getFile(file: string): Promise<void>;
    static fetch(specifier: string): Promise<Pack>;
}

declare class NotFound extends Error {
    constructor(message: string);
}
declare function fetchFileListing(packageName: string): Promise<string[]>;
/**
 * Retrieves a list of .d.ts files for a given package from unpkg.com.
 * @example
 * for await (const f of fetchDTS("@typescript/vfs")) {
 *   console.log(f);
 * }
 * @param packageName string
 */
declare function fetchDTSListing(packageName: string): AsyncGenerator<string, void, unknown>;
/**
 * Retrieves a file for a given package/path from unpkg.com.
 * @param path string
 */
declare function fetchFile(path: string): Promise<string>;

export { type FunctionDescription, NotFound, Pack, type PackageDescription, type ParameterDescription, Project, describeProject as describeDts, describePackage, fetchDTSListing, fetchFile, fetchFileListing, loadDTS };
