Fix StartOS 0.4 TypeScript packaging to match SDK API
This commit is contained in:
+182
@@ -0,0 +1,182 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user