import { type X2jOptions, type XmlBuilderOptions } from 'fast-xml-parser';
import * as INI from 'ini';
import { z } from 'zod';
import * as T from '../../../base/lib/types';
import { PathBase } from './Volume';
/**
 * Bidirectional transformers for converting between the raw file format and
 * the application-level data type. Used with FileHelper factory methods.
 *
 * @typeParam Raw - The native type the file format parses to (e.g. `Record<string, unknown>` for JSON)
 * @typeParam Transformed - The application-level type after transformation
 */
export type Transformers<Raw = unknown, Transformed = unknown, Validated extends Transformed = Transformed> = {
    /** Transform raw parsed data into the application type */
    onRead: (value: Raw) => Transformed;
    /** Transform application data back into the raw format for writing */
    onWrite: (value: Validated) => Raw;
};
type ToPath = string | {
    base: PathBase;
    subpath: string;
};
type Validator<_T, U> = z.ZodType<U>;
type ReadType<A> = {
    once: () => Promise<A | null>;
    const: (effects: T.Effects) => Promise<A | null>;
    watch: (effects: T.Effects, abort?: AbortSignal) => AsyncGenerator<A | null, never, unknown>;
    onChange: (effects: T.Effects, callback: (value: A | null, error?: Error) => {
        cancel: boolean;
    } | Promise<{
        cancel: boolean;
    }>) => void;
    waitFor: (effects: T.Effects, pred: (value: A | null) => boolean) => Promise<A | null>;
};
/**
 * @description Use this class to read/write an underlying configuration file belonging to the upstream service.
 *
 *   These type definitions should reflect the underlying file as closely as possible. For example, if the service does not require a particular value, it should be marked as optional(), even if your package requires it.
 *
 *   It is recommended to use onMismatch() whenever possible. This provides an escape hatch in case the user edits the file manually and accidentally sets a value to an unsupported type.
 *
 *   Officially supported file types are json, yaml, and toml. Other files types can use "raw"
 *
 *   Choose between officially supported file formats (), or a custom format (raw).
 *
 * @example
 * Below are a few examples
 *
 * ```
 * import { matches, FileHelper } from '@start9labs/start-sdk'
 * const { arrayOf, boolean, literal, literals, object, natural, string } = matches
 *
 * export const jsonFile = FileHelper.json('./inputSpec.json', object({
 *   passwords: arrayOf(string).onMismatch([])
 *   type: literals('private', 'public').optional().onMismatch(undefined)
 * }))
 *
 * export const tomlFile = FileHelper.toml('./inputSpec.toml', object({
 *   url: literal('https://start9.com').onMismatch('https://start9.com')
 *   public: boolean.onMismatch(true)
 * }))
 *
 * export const yamlFile = FileHelper.yaml('./inputSpec.yml', object({
 *   name: string.optional().onMismatch(undefined)
 *   age: natural.optional().onMismatch(undefined)
 * }))
 *
 * export const bitcoinConfFile = FileHelper.raw(
 *   './service.conf',
 *   (obj: CustomType) => customConvertObjToFormattedString(obj),
 *   (str) => customParseStringToTypedObj(str),
 * )
 * ```
 */
export declare class FileHelper<A> {
    readonly path: string;
    readonly writeData: (dataIn: A) => string;
    readonly readData: (stringValue: string) => unknown;
    readonly validate: (value: unknown) => A;
    private consts;
    protected constructor(path: string, writeData: (dataIn: A) => string, readData: (stringValue: string) => unknown, validate: (value: unknown) => A);
    private writeFileRaw;
    /**
     * Accepts structured data and overwrites the existing file on disk.
     */
    private writeFile;
    private readFileRaw;
    private readFile;
    /**
     * Reads the file from disk and converts it to structured data.
     */
    private readOnce;
    private createFileWatchable;
    /**
     * Create a reactive reader for this file.
     *
     * Returns an object with multiple read strategies:
     * - `once()` - Read the file once and return the parsed value
     * - `const(effects)` - Read once but re-read when the file changes (for use with constRetry)
     * - `watch(effects)` - Async generator yielding new values on each file change
     * - `onChange(effects, callback)` - Fire a callback on each file change
     * - `waitFor(effects, predicate)` - Block until the file value satisfies a predicate
     *
     * @param map - Optional transform function applied after validation
     * @param eq - Optional equality function to deduplicate watch emissions
     */
    read(): ReadType<A>;
    read<B>(map: (value: A) => B, eq?: (left: B | null, right: B | null) => boolean): ReadType<B>;
    /**
     * Accepts full structured data and overwrites the existing file on disk if it exists.
     */
    write(effects: T.Effects, data: T.AllowReadonly<A> | A, options?: {
        allowWriteAfterConst?: boolean;
    }): Promise<null>;
    /**
     * Accepts partial structured data and performs a merge with the existing file on disk.
     */
    merge(effects: T.Effects, data: T.AllowReadonly<T.DeepPartial<A>>, options?: {
        allowWriteAfterConst?: boolean;
    }): Promise<null>;
    /**
     * We wanted to be able to have a fileHelper, and just modify the path later in time.
     * Like one behavior of another dependency or something similar.
     */
    withPath(path: ToPath): FileHelper<A>;
    /**
     * Create a File Helper for an arbitrary file type.
     *
     * Provide custom functions for translating data to/from the file format.
     */
    static raw<A>(path: ToPath, toFile: (dataIn: A) => string, fromFile: (rawData: string) => unknown, validate: (data: unknown) => A): FileHelper<A>;
    private static rawTransformed;
    /**
     * Create a File Helper for a text file
     */
    static string(path: ToPath): FileHelper<string>;
    static string<A extends string>(path: ToPath, shape: Validator<string, A>): FileHelper<A>;
    static string<A extends Transformed, Transformed = string>(path: ToPath, shape: Validator<Transformed, A>, transformers: Transformers<string, Transformed, A>): FileHelper<A>;
    /**
     * Create a File Helper for a .json file.
     */
    static json<A>(path: ToPath, shape: Validator<unknown, A>): FileHelper<A>;
    static json<A extends Transformed, Transformed = unknown>(path: ToPath, shape: Validator<unknown, A>, transformers: Transformers<unknown, Transformed, A>): FileHelper<A>;
    /**
     * Create a File Helper for a .yaml file
     */
    static yaml<A extends Record<string, unknown>>(path: ToPath, shape: Validator<Record<string, unknown>, A>): FileHelper<A>;
    static yaml<A extends Transformed, Transformed = Record<string, unknown>>(path: ToPath, shape: Validator<Transformed, A>, transformers: Transformers<Record<string, unknown>, Transformed, A>): FileHelper<A>;
    /**
     * Create a File Helper for a .toml file
     */
    static toml<A extends Record<string, unknown>>(path: ToPath, shape: Validator<Record<string, unknown>, A>): FileHelper<A>;
    static toml<A extends Transformed, Transformed = Record<string, unknown>>(path: ToPath, shape: Validator<Transformed, A>, transformers: Transformers<Record<string, unknown>, Transformed, A>): FileHelper<A>;
    /**
     * Create a File Helper for a .ini file.
     *
     * Supports optional encode/decode options and custom transformers.
     */
    static ini<A extends Record<string, unknown>>(path: ToPath, shape: Validator<Record<string, unknown>, A>, options?: INI.EncodeOptions & INI.DecodeOptions): FileHelper<A>;
    static ini<A extends Transformed, Transformed = Record<string, unknown>>(path: ToPath, shape: Validator<Transformed, A>, options: INI.EncodeOptions & INI.DecodeOptions, transformers: Transformers<Record<string, unknown>, Transformed, A>): FileHelper<A>;
    /**
     * Create a File Helper for a .env file (KEY=VALUE format, one per line).
     *
     * Lines starting with `#` are treated as comments and ignored on read.
     */
    static env<A extends Record<string, string>>(path: ToPath, shape: Validator<Record<string, string>, A>): FileHelper<A>;
    static env<A extends Transformed, Transformed = Record<string, string>>(path: ToPath, shape: Validator<Transformed, A>, transformers: Transformers<Record<string, string>, Transformed, A>): FileHelper<A>;
    /**
     * Create a File Helper for an .xml file.
     *
     * Supports optional parser/builder options from `fast-xml-parser`.
     */
    static xml<A extends Record<string, unknown>>(path: ToPath, shape: Validator<Record<string, unknown>, A>, options?: {
        parser?: X2jOptions;
        builder?: XmlBuilderOptions;
    }): FileHelper<A>;
    static xml<A extends Transformed, Transformed = Record<string, unknown>>(path: ToPath, shape: Validator<Transformed, A>, options: {
        parser?: X2jOptions;
        builder?: XmlBuilderOptions;
    }, transformers: Transformers<Record<string, unknown>, Transformed, A>): FileHelper<A>;
}
export default FileHelper;
