feat: add autoConfig/ better types for wrapperData

This commit is contained in:
BluJ
2023-04-19 17:23:16 -06:00
parent 7c4f94ba8f
commit e279711f8e
39 changed files with 431 additions and 291 deletions

View File

@@ -1,70 +1,42 @@
import deepmerge from "deepmerge";
import { AutoConfigure, Effects } from "../types";
import { Message, MaybePromise, ReadonlyDeep } from ".";
import { AutoConfigure, Effects, ExpectedExports } from "../types";
import { deepEqual, deepMerge } from "../util";
class AutoConfigBuilt<Config> implements AutoConfigure<Config> {
constructor(private autoConfig: AutoConfig<Config>) {}
async check(effects: Effects, config: Config): Promise<void> {
for (const [message, configure] of this.autoConfig.getConfigures()) {
const value = await configure({ effects, config });
if (value !== null) {
throw new Error(message);
}
}
}
/** This is called after we know that the dependency package needs a new configuration, this would be a transform for defaults */
async autoConfigure(effects: Effects, config: Config): Promise<Config> {
const input = { effects, config };
const newOverwrites = (
await Promise.all(this.autoConfig.getConfigures().map((x) => x[1](input)))
).filter((x): x is NonNullable<typeof x> => x !== null);
return deepmerge.all([config, ...newOverwrites]);
}
}
export class AutoConfig<Config> {
private constructor(
private configures: Array<
[
Message,
(
options: Readonly<{ config: Config; effects: Effects }>
) => MaybePromise<null | Partial<Config>>
]
>
export type AutoConfigFrom = {
[key: string]: (options: {
effects: Effects;
localConfig: unknown;
remoteConfig: unknown;
}) => Promise<void | Record<string, unknown>>;
};
export class AutoConfig {
constructor(
readonly configs: AutoConfigFrom,
readonly path: keyof AutoConfigFrom,
) {}
getConfigures(): ReadonlyDeep<
Array<
[
Message,
(
options: Readonly<{ config: Config; effects: Effects }>
) => MaybePromise<null | Partial<Config>>
]
>
> {
return this.configures;
}
static autoConfig<Config>(
message: Message,
configure: (
options: Readonly<{ config: Config; effects: Effects }>
) => MaybePromise<null | Partial<Config>>
): AutoConfig<Config> {
return new AutoConfig([[message, configure]]);
async check(
options: Parameters<AutoConfigure["check"]>[0],
): ReturnType<AutoConfigure["check"]> {
const origConfig = JSON.parse(JSON.stringify(options.localConfig));
if (
!deepEqual(
origConfig,
deepMerge(
{},
options.localConfig,
await this.configs[this.path](options),
),
)
)
throw new Error(`Check failed for ${this.path}`);
}
autoConfig(
message: Message,
configure: (
options: Readonly<{ config: Config; effects: Effects }>
) => MaybePromise<null | Partial<Config>>
): AutoConfig<Config> {
this.configures.push([message, configure]);
return this;
}
build() {
return new AutoConfigBuilt(this);
async autoConfigure(
options: Parameters<AutoConfigure["autoConfigure"]>[0],
): ReturnType<AutoConfigure["autoConfigure"]> {
return deepMerge(
{},
options.localConfig,
await this.configs[this.path](options),
);
}
}