@grlt-hub/app-compose
Version:
Compose modules into apps
197 lines (183 loc) • 7.08 kB
TypeScript
import * as effector from 'effector';
import { StoreWritable } from 'effector';
type NonEmptyTuple<T = unknown> = [T, ...T[]];
type StageId = string;
type Stage = {
id: StageId;
containersToBoot: AnyContainer[];
};
type AnyObject = Record<string, unknown>;
type ValueOf<T> = T[keyof T];
declare const CONTAINER_STATUS: {
readonly idle: "idle";
readonly pending: "pending";
readonly done: "done";
readonly fail: "fail";
readonly off: "off";
};
type ContainerStatus = ValueOf<typeof CONTAINER_STATUS>;
type StartResult<T> = Promise<{
api: T;
}> | {
api: T;
};
type EnableResult = Promise<boolean> | boolean;
type AnyAPI = AnyObject | null;
type AnyStartFn = (...x: any) => StartResult<AnyAPI>;
type AnyContainer = {
id: string;
domain: string;
$status: StoreWritable<ContainerStatus>;
start: AnyStartFn;
dependencies?: AnyContainer[];
optionalDependencies?: AnyContainer[];
enable?: (..._: any) => EnableResult;
};
type AnyDeps = NonEmptyTuple<AnyContainer> | void;
type ContainerId = AnyContainer['id'];
type ContainerDomain = AnyContainer['domain'];
type ValidateParams = Pick<AnyContainer, 'id' | 'domain' | 'dependencies' | 'optionalDependencies'>;
declare const ERROR: {
readonly CONTAINER_ID_EMPTY_STRING: "Container ID cannot be an empty string.";
readonly CONTAINER_DOMAIN_NAME_EMPTY_STRING: "Container Domain cannot be an empty string.";
readonly depsIntersection: (intersection: string[], containerId: ValidateParams["id"]) => string;
};
type ContainerIdEmptyStringError = ValidateParams & {
id: never;
error: typeof ERROR.CONTAINER_ID_EMPTY_STRING;
};
type ContainerDomainNameEmptyStringError = ValidateParams & {
domain: never;
error: typeof ERROR.CONTAINER_DOMAIN_NAME_EMPTY_STRING;
};
type ExtractDeps<D extends AnyContainer[]> = {
[K in D[number] as Awaited<ReturnType<K['start']>>['api'] extends Record<string, never> ? never : K['id']]: Awaited<ReturnType<K['start']>>['api'];
};
type ExtractEnabled<D extends AnyContainer[]> = {
[K in D[number] as K['id']]: boolean;
};
type DependenciesConfig<Deps extends AnyDeps, OptionalDeps extends AnyDeps> = [
Deps
] extends [void] ? [
OptionalDeps
] extends [void] ? {} : {
optionalDependencies: Exclude<OptionalDeps, void>;
} : [OptionalDeps] extends [void] ? {
dependencies: Exclude<Deps, void>;
} : {
dependencies: Exclude<Deps, void>;
optionalDependencies: Exclude<OptionalDeps, void>;
};
type Params$4<Id extends string, Domain extends string, API extends AnyAPI, Deps extends AnyDeps = void, OptionalDeps extends AnyDeps = void> = '' extends Id ? ContainerIdEmptyStringError : '' extends Domain ? ContainerDomainNameEmptyStringError : DependenciesConfig<Deps, OptionalDeps> & {
id: Id;
domain: Domain;
start: (api: ExtractDeps<Exclude<Deps, void>> & Partial<ExtractDeps<Exclude<OptionalDeps, void>>>, enabled: ExtractEnabled<Exclude<Deps, void>> & ExtractEnabled<Exclude<OptionalDeps, void>>) => StartResult<API>;
enable?: (api: ExtractDeps<Exclude<Deps, void>> & Partial<ExtractDeps<Exclude<OptionalDeps, void>>>, enabled: ExtractEnabled<Exclude<Deps, void>> & ExtractEnabled<Exclude<OptionalDeps, void>>) => EnableResult;
};
declare const createContainer: <Id extends string, Domain extends string, API extends AnyAPI, Deps extends AnyDeps = void, OptionalDeps extends AnyDeps = void>(__params: Params$4<Id, Domain, API, Deps, OptionalDeps>) => Exclude<Params$4<Id, Domain, API, Deps, OptionalDeps>, ContainerIdEmptyStringError | ContainerDomainNameEmptyStringError> & {
$status: effector.StoreWritable<ContainerStatus>;
};
type View = 'domains' | 'containers';
type TransitiveDependency<Id extends ContainerId | ContainerDomain = ContainerId> = {
id: Id;
path: string;
};
type ContainersGraph = Record<ContainerId, {
domain: ContainerDomain;
dependencies: ContainerId[];
optionalDependencies: ContainerId[];
transitive: {
dependencies: TransitiveDependency<ContainerId>[];
optionalDependencies: TransitiveDependency<ContainerId>[];
};
}>;
type DomainsGraph = Record<ContainerDomain, {
containers: ContainerId[];
strict: ContainerDomain[];
optional: ContainerDomain[];
transitive: {
strict: TransitiveDependency<ContainerDomain>[];
optional: TransitiveDependency<ContainerDomain>[];
};
}>;
type StageTuples = [Stage['id'], NonEmptyTuple<AnyContainer>][];
type Params$3 = {
stages: Stage[];
};
type Result$1<T extends View = 'containers'> = T extends 'domains' ? {
graph: DomainsGraph;
dependsOn: (_: ContainerDomain[]) => DomainsGraph;
requiredBy: (_: ContainerDomain[]) => DomainsGraph;
} : {
graph: ContainersGraph;
dependsOn: (_: AnyContainer[]) => ContainersGraph;
requiredBy: (_: AnyContainer[]) => ContainersGraph;
};
declare const graph: <T extends View = "containers">(params: Params$3, config: {
view: T;
}) => Result$1<T>;
type Config$1 = {
debug?: boolean;
onContainerFail?: (_: {
container: {
id: ContainerId;
domain: ContainerDomain;
};
stageId: Stage['id'];
error: Error;
}) => unknown;
};
type APIs = Record<string, Awaited<ReturnType<AnyContainer['start']>>['api']>;
type UpResult = {
allDone: boolean;
containerStatuses: Record<ContainerId, ContainerStatus>;
};
declare const createStageUpFn: (__config?: Config$1) => (stage: Stage, apis: APIs) => Promise<UpResult>;
type Params$2 = {
required: (AnyContainer | NonEmptyTuple<AnyContainer>)[] | 'all' | undefined;
} & Pick<Awaited<ReturnType<ReturnType<typeof createStageUpFn>>>, 'containerStatuses'>;
type Result = {
ok: true;
} | {
ok: false;
id: ContainerId[];
};
declare const validateStageUp: (params: Params$2) => Result;
type Params$1 = {
stages: Stage[];
required?: Parameters<typeof validateStageUp>[0]['required'];
};
type Config = Parameters<typeof createStageUpFn>[0];
declare const up: (params: Params$1, config: Config) => Promise<{
allDone: boolean;
stages: Record<string, {
allDone: boolean;
containerStatuses: Record<ContainerId, ContainerStatus>;
}>;
}>;
type UpFn = typeof up;
type GraphFn = typeof graph;
type Params = {
stages: StageTuples;
required?: Parameters<UpFn>[0]['required'];
};
declare const compose: (params: Params) => Promise<{
up: (config?: Parameters<UpFn>[1]) => Promise<{
allDone: boolean;
stages: Record<string, {
allDone: boolean;
containerStatuses: Record<ContainerId, ContainerStatus>;
}>;
}>;
diff: () => Promise<void>;
graph: (config?: Parameters<GraphFn>[1]) => Promise<{
graph: DomainsGraph;
dependsOn: (_: ContainerDomain[]) => DomainsGraph;
requiredBy: (_: ContainerDomain[]) => DomainsGraph;
} | {
graph: ContainersGraph;
dependsOn: (_: AnyContainer[]) => ContainersGraph;
requiredBy: (_: AnyContainer[]) => ContainersGraph;
}>;
}>;
export { compose, createContainer };