mirror of
https://github.com/Start9Labs/start-sdk.git
synced 2026-03-26 10:21:55 +00:00
Merge branch 'master' of github.com:Start9Labs/start-sdk
This commit is contained in:
@@ -8,13 +8,9 @@ export function setupActions(...createdActions: CreatedAction<any, any>[]) {
|
||||
actions[action.metaData.id] = action.exportedAction;
|
||||
}
|
||||
|
||||
const initializeActions = async (effects: Effects) => {
|
||||
for (const action of createdActions) {
|
||||
action.exportAction(effects);
|
||||
}
|
||||
};
|
||||
const actionsMetadata = createdActions.map((x) => x.metaData);
|
||||
return {
|
||||
actions,
|
||||
initializeActions,
|
||||
actionsMetadata,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { AutoConfigure, Effects, ExpectedExports } from "../types";
|
||||
import { AutoConfigure, DeepPartial, Effects, ExpectedExports } from "../types";
|
||||
import { Utils, deepEqual, deepMerge, utils } from "../util";
|
||||
|
||||
export type AutoConfigFrom<WD, Input> = {
|
||||
[key: string]: (options: {
|
||||
export type AutoConfigFrom<WD, Input, NestedConfigs> = {
|
||||
[key in keyof NestedConfigs & string]: (options: {
|
||||
effects: Effects;
|
||||
localConfig: Input;
|
||||
remoteConfig: unknown;
|
||||
remoteConfig: NestedConfigs[key];
|
||||
utils: Utils<WD>;
|
||||
}) => Promise<void | Record<string, unknown>>;
|
||||
}) => Promise<void | DeepPartial<NestedConfigs[key]>>;
|
||||
};
|
||||
export class AutoConfig<WD, Input> {
|
||||
export class AutoConfig<WD, Input, NestedConfigs> {
|
||||
constructor(
|
||||
readonly configs: AutoConfigFrom<WD, Input>,
|
||||
readonly path: keyof AutoConfigFrom<WD, Input>,
|
||||
readonly configs: AutoConfigFrom<WD, Input, NestedConfigs>,
|
||||
readonly path: keyof AutoConfigFrom<WD, Input, NestedConfigs>,
|
||||
) {}
|
||||
|
||||
async check(
|
||||
@@ -23,6 +23,7 @@ export class AutoConfig<WD, Input> {
|
||||
...options,
|
||||
utils: utils<WD>(options.effects),
|
||||
localConfig: options.localConfig as Input,
|
||||
remoteConfig: options.remoteConfig as any,
|
||||
};
|
||||
if (
|
||||
!deepEqual(
|
||||
@@ -43,6 +44,7 @@ export class AutoConfig<WD, Input> {
|
||||
...options,
|
||||
utils: utils<WD>(options.effects),
|
||||
localConfig: options.localConfig as Input,
|
||||
remoteConfig: options.remoteConfig as any,
|
||||
};
|
||||
return deepMerge(
|
||||
{},
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
import { GenericManifest } from "../manifest/ManifestTypes";
|
||||
import { AutoConfig, AutoConfigFrom } from "./AutoConfig";
|
||||
|
||||
export function setupAutoConfig<WD, Input>(configs: AutoConfigFrom<WD, Input>) {
|
||||
export function setupAutoConfig<
|
||||
WD,
|
||||
Input,
|
||||
Manifest extends GenericManifest,
|
||||
NestedConfigs extends {
|
||||
[key in keyof Manifest["dependencies"]]: unknown;
|
||||
},
|
||||
>(configs: AutoConfigFrom<WD, Input, NestedConfigs>) {
|
||||
type C = typeof configs;
|
||||
const answer = { ...configs } as unknown as {
|
||||
[k in keyof C]: AutoConfig<WD, Input>;
|
||||
[k in keyof C]: AutoConfig<WD, Input, NestedConfigs>;
|
||||
};
|
||||
for (const key in configs) {
|
||||
answer[key] = new AutoConfig<WD, Input>(configs, key);
|
||||
answer[key as keyof typeof configs] = new AutoConfig<
|
||||
WD,
|
||||
Input,
|
||||
NestedConfigs
|
||||
>(configs, key as keyof typeof configs);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export type Save<WD, A> = (options: {
|
||||
export type Read<WD, A> = (options: {
|
||||
effects: Effects;
|
||||
utils: Utils<WD>;
|
||||
}) => Promise<null | DeepPartial<A>>;
|
||||
}) => Promise<void | DeepPartial<A>>;
|
||||
/**
|
||||
* We want to setup a config export with a get and set, this
|
||||
* is going to be the default helper to setup config, because it will help
|
||||
@@ -46,7 +46,9 @@ export function setupConfig<WD, A extends Config<InputSpec>>(
|
||||
getConfig: (async ({ effects, config }) => {
|
||||
return {
|
||||
spec: spec.build(),
|
||||
config: nullIfEmpty(await read({ effects, utils: utils<WD>(effects) })),
|
||||
config: nullIfEmpty(
|
||||
(await read({ effects, utils: utils<WD>(effects) })) || null,
|
||||
),
|
||||
};
|
||||
}) as ExpectedExports.getConfig,
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as matches from "ts-matches";
|
||||
|
||||
const starSub = /((\d+\.)*\d+)\.\*/;
|
||||
// prettier-ignore
|
||||
export type ValidEmVer = `${'>' | '<' | '>=' | '<=' | '=' | ''}${number | '*'}${`.${number | '*'}` | ""}${`.${number | '*'}` | ""}${`.${number | '*'}` | ""}${`-${string}` | ""}`;
|
||||
export type ValidEmVer = `${number | '*'}${`.${number | '*'}` | ""}${`.${number | '*'}` | ""}${`-${string}` | ""}`;
|
||||
|
||||
function incrementLastNumber(list: number[]) {
|
||||
const newList = [...list];
|
||||
|
||||
@@ -1,55 +1,43 @@
|
||||
// import { InterfaceReceipt } from "../mainFn/interfaceReceipt";
|
||||
// import { Daemon, Effects } from "../types";
|
||||
// import { CheckResult } from "./checkFns/CheckResult";
|
||||
// import { ReadyReceipt } from "./ReadyProof";
|
||||
// import { HealthReceipt } from "./HealthReceipt";
|
||||
// import { Trigger } from "./trigger";
|
||||
// import { TriggerInput } from "./trigger/TriggerInput";
|
||||
// import { defaultTrigger } from "./trigger/defaultTrigger";
|
||||
import { InterfaceReceipt } from "../mainFn/interfaceReceipt";
|
||||
import { Daemon, Effects } from "../types";
|
||||
import { CheckResult } from "./checkFns/CheckResult";
|
||||
import { HealthReceipt } from "./HealthReceipt";
|
||||
import { Trigger } from "./trigger";
|
||||
import { TriggerInput } from "./trigger/TriggerInput";
|
||||
import { defaultTrigger } from "./trigger/defaultTrigger";
|
||||
|
||||
// function readReciptOf<A extends { daemon: Daemon }>(a: A) {
|
||||
// return a as A & ReadyReceipt;
|
||||
// }
|
||||
// export function readyCheck(o: {
|
||||
// effects: Effects;
|
||||
// started(onTerm: () => void): null;
|
||||
// interfaceReceipt: InterfaceReceipt;
|
||||
// healthReceipts: Iterable<HealthReceipt>;
|
||||
// daemonReceipt: Daemon;
|
||||
// name: string;
|
||||
// trigger?: Trigger;
|
||||
// fn(): Promise<CheckResult> | CheckResult;
|
||||
// onFirstSuccess?: () => () => Promise<unknown> | unknown;
|
||||
// }) {
|
||||
// new Promise(async () => {
|
||||
// const trigger = (o.trigger ?? defaultTrigger)();
|
||||
// let currentValue: TriggerInput = {
|
||||
// lastResult: null,
|
||||
// hadSuccess: false,
|
||||
// };
|
||||
// for (
|
||||
// let res = await trigger.next(currentValue);
|
||||
// !res.done;
|
||||
// res = await trigger.next(currentValue)
|
||||
// ) {
|
||||
// try {
|
||||
// const { status, message } = await o.fn();
|
||||
// if (!currentValue.hadSuccess) {
|
||||
// await o.started(o?.onFirstSuccess ?? (() => o.daemonReceipt.term()));
|
||||
// }
|
||||
// await o.effects.setHealth({
|
||||
// name: o.name,
|
||||
// status,
|
||||
// message,
|
||||
// });
|
||||
// currentValue.hadSuccess = true;
|
||||
// currentValue.lastResult = "success";
|
||||
// } catch (_) {
|
||||
// currentValue.lastResult = "failure";
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// return readReciptOf({
|
||||
// daemon: o.daemonReceipt,
|
||||
// });
|
||||
// }
|
||||
export function healthCheck(o: {
|
||||
effects: Effects;
|
||||
name: string;
|
||||
trigger?: Trigger;
|
||||
fn(): Promise<CheckResult> | CheckResult;
|
||||
onFirstSuccess?: () => () => Promise<unknown> | unknown;
|
||||
}) {
|
||||
new Promise(async () => {
|
||||
let currentValue: TriggerInput = {
|
||||
lastResult: null,
|
||||
hadSuccess: false,
|
||||
};
|
||||
const getCurrentValue = () => currentValue;
|
||||
const trigger = (o.trigger ?? defaultTrigger)(getCurrentValue);
|
||||
for (
|
||||
let res = await trigger.next();
|
||||
!res.done;
|
||||
res = await trigger.next()
|
||||
) {
|
||||
try {
|
||||
const { status, message } = await o.fn();
|
||||
await o.effects.setHealth({
|
||||
name: o.name,
|
||||
status,
|
||||
message,
|
||||
});
|
||||
currentValue.hadSuccess = true;
|
||||
currentValue.lastResult = "success";
|
||||
} catch (_) {
|
||||
currentValue.lastResult = "failure";
|
||||
}
|
||||
}
|
||||
});
|
||||
return {} as HealthReceipt;
|
||||
}
|
||||
|
||||
@@ -4,25 +4,28 @@ import { Trigger } from "./index";
|
||||
export function changeOnFirstSuccess(o: {
|
||||
beforeFirstSuccess: Trigger;
|
||||
afterFirstSuccess: Trigger;
|
||||
}) {
|
||||
return async function* () {
|
||||
const beforeFirstSuccess = o.beforeFirstSuccess();
|
||||
let currentValue: TriggerInput = yield;
|
||||
beforeFirstSuccess.next(currentValue);
|
||||
}): Trigger {
|
||||
return async function* (getInput) {
|
||||
const beforeFirstSuccess = o.beforeFirstSuccess(getInput);
|
||||
yield;
|
||||
let currentValue = getInput();
|
||||
beforeFirstSuccess.next();
|
||||
for (
|
||||
let res = await beforeFirstSuccess.next(currentValue);
|
||||
let res = await beforeFirstSuccess.next();
|
||||
currentValue?.lastResult !== "success" && !res.done;
|
||||
res = await beforeFirstSuccess.next(currentValue)
|
||||
res = await beforeFirstSuccess.next()
|
||||
) {
|
||||
currentValue = yield;
|
||||
yield;
|
||||
currentValue = getInput();
|
||||
}
|
||||
const afterFirstSuccess = o.afterFirstSuccess();
|
||||
const afterFirstSuccess = o.afterFirstSuccess(getInput);
|
||||
for (
|
||||
let res = await afterFirstSuccess.next(currentValue);
|
||||
let res = await afterFirstSuccess.next();
|
||||
!res.done;
|
||||
res = await afterFirstSuccess.next(currentValue)
|
||||
res = await afterFirstSuccess.next()
|
||||
) {
|
||||
currentValue = yield;
|
||||
yield;
|
||||
currentValue = getInput();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,4 +2,6 @@ import { TriggerInput } from "./TriggerInput";
|
||||
export { changeOnFirstSuccess } from "./changeOnFirstSuccess";
|
||||
export { cooldownTrigger } from "./cooldownTrigger";
|
||||
|
||||
export type Trigger = () => AsyncIterator<unknown, unknown, TriggerInput>;
|
||||
export type Trigger = (
|
||||
getInput: () => TriggerInput,
|
||||
) => AsyncIterator<unknown, unknown, never>;
|
||||
|
||||
@@ -15,3 +15,4 @@ export * as properties from "./properties";
|
||||
export * as autoconfig from "./autoconfig";
|
||||
export * as actions from "./actions";
|
||||
export * as manifest from "./manifest";
|
||||
export * as inits from "./inits";
|
||||
|
||||
3
lib/inits/index.ts
Normal file
3
lib/inits/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { setupInit } from "./setupInit";
|
||||
export { setupUninstall } from "./setupUninstall";
|
||||
export { setupInstall } from "./setupInstall";
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ManifestVersion } from "../manifest/ManifestTypes";
|
||||
import { Effects } from "../types";
|
||||
import { Utils } from "../util";
|
||||
import { ManifestVersion } from "../../manifest/ManifestTypes";
|
||||
import { Effects } from "../../types";
|
||||
import { Utils } from "../../util";
|
||||
|
||||
export class Migration<Version extends ManifestVersion> {
|
||||
constructor(
|
||||
69
lib/inits/migrations/setupMigrations.ts
Normal file
69
lib/inits/migrations/setupMigrations.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { setupActions } from "../../actions/setupActions";
|
||||
import { EmVer } from "../../emverLite/mod";
|
||||
import { GenericManifest } from "../../manifest/ManifestTypes";
|
||||
import { ExpectedExports } from "../../types";
|
||||
import { once } from "../../util/once";
|
||||
import { Migration } from "./Migration";
|
||||
|
||||
export class Migrations {
|
||||
private constructor(
|
||||
readonly manifest: GenericManifest,
|
||||
readonly migrations: Array<Migration<any>>,
|
||||
) {}
|
||||
private sortedMigrations = once(() => {
|
||||
const migrationsAsVersions = (this.migrations as Array<Migration<any>>).map(
|
||||
(x) => [EmVer.parse(x.options.version), x] as const,
|
||||
);
|
||||
migrationsAsVersions.sort((a, b) => a[0].compareForSort(b[0]));
|
||||
return migrationsAsVersions;
|
||||
});
|
||||
private currentVersion = once(() => EmVer.parse(this.manifest.version));
|
||||
static of<Migrations extends Array<Migration<any>>>(
|
||||
manifest: GenericManifest,
|
||||
...migrations: EnsureUniqueId<Migrations>
|
||||
) {
|
||||
return new Migrations(manifest, migrations as Array<Migration<any>>);
|
||||
}
|
||||
async init({
|
||||
effects,
|
||||
previousVersion,
|
||||
}: Parameters<ExpectedExports.init>[0]) {
|
||||
if (!!previousVersion) {
|
||||
const previousVersionEmVer = EmVer.parse(previousVersion);
|
||||
for (const [_, migration] of this.sortedMigrations()
|
||||
.filter((x) => x[0].greaterThan(previousVersionEmVer))
|
||||
.filter((x) => x[0].lessThanOrEqual(this.currentVersion()))) {
|
||||
await migration.up({ effects });
|
||||
}
|
||||
}
|
||||
}
|
||||
async uninit({
|
||||
effects,
|
||||
nextVersion,
|
||||
}: Parameters<ExpectedExports.uninit>[0]) {
|
||||
if (!!nextVersion) {
|
||||
const nextVersionEmVer = EmVer.parse(nextVersion);
|
||||
const reversed = [...this.sortedMigrations()].reverse();
|
||||
for (const [_, migration] of reversed
|
||||
.filter((x) => x[0].greaterThan(nextVersionEmVer))
|
||||
.filter((x) => x[0].lessThanOrEqual(this.currentVersion()))) {
|
||||
await migration.down({ effects });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setupMigrations<Migrations extends Array<Migration<any>>>(
|
||||
manifest: GenericManifest,
|
||||
...migrations: EnsureUniqueId<Migrations>
|
||||
) {
|
||||
return Migrations.of(manifest, ...migrations);
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
export type EnsureUniqueId<A, B = A, ids = never> =
|
||||
B extends [] ? A :
|
||||
B extends [Migration<infer id>, ...infer Rest] ? (
|
||||
id extends ids ? "One of the ids are not unique"[] :
|
||||
EnsureUniqueId<A, Rest, id | ids>
|
||||
) : "There exists a migration that is not a Migration"[]
|
||||
24
lib/inits/setupInit.ts
Normal file
24
lib/inits/setupInit.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ExpectedExports } from "../types";
|
||||
import { Migrations } from "./migrations/setupMigrations";
|
||||
import { Install } from "./setupInstall";
|
||||
import { Uninstall } from "./setupUninstall";
|
||||
|
||||
export function setupInit<WrapperData>(
|
||||
migrations: Migrations,
|
||||
install: Install<WrapperData>,
|
||||
uninstall: Uninstall<WrapperData>,
|
||||
): {
|
||||
init: ExpectedExports.init;
|
||||
uninit: ExpectedExports.uninit;
|
||||
} {
|
||||
return {
|
||||
init: async (opts) => {
|
||||
await migrations.init(opts);
|
||||
await install.init(opts);
|
||||
},
|
||||
uninit: async (opts) => {
|
||||
await migrations.uninit(opts);
|
||||
await uninstall.uninit(opts);
|
||||
},
|
||||
};
|
||||
}
|
||||
24
lib/inits/setupInstall.ts
Normal file
24
lib/inits/setupInstall.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Effects, ExpectedExports } from "../types";
|
||||
import { Utils, utils } from "../util";
|
||||
|
||||
export type InstallFn<WrapperData> = (opts: {
|
||||
effects: Effects;
|
||||
utils: Utils<WrapperData>;
|
||||
}) => Promise<void>;
|
||||
export class Install<WrapperData> {
|
||||
private constructor(readonly fn: InstallFn<WrapperData>) {}
|
||||
static of<WrapperData>(fn: InstallFn<WrapperData>) {
|
||||
return new Install(fn);
|
||||
}
|
||||
|
||||
async init({
|
||||
effects,
|
||||
previousVersion,
|
||||
}: Parameters<ExpectedExports.init>[0]) {
|
||||
if (!previousVersion) await this.fn({ effects, utils: utils(effects) });
|
||||
}
|
||||
}
|
||||
|
||||
export function setupInstall<WrapperData>(fn: InstallFn<WrapperData>) {
|
||||
return Install.of(fn);
|
||||
}
|
||||
24
lib/inits/setupUninstall.ts
Normal file
24
lib/inits/setupUninstall.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Effects, ExpectedExports } from "../types";
|
||||
import { Utils, utils } from "../util";
|
||||
|
||||
export type UninstallFn<WrapperData> = (opts: {
|
||||
effects: Effects;
|
||||
utils: Utils<WrapperData>;
|
||||
}) => Promise<void>;
|
||||
export class Uninstall<WrapperData> {
|
||||
private constructor(readonly fn: UninstallFn<WrapperData>) {}
|
||||
static of<WrapperData>(fn: UninstallFn<WrapperData>) {
|
||||
return new Uninstall(fn);
|
||||
}
|
||||
|
||||
async uninit({
|
||||
effects,
|
||||
nextVersion,
|
||||
}: Parameters<ExpectedExports.uninit>[0]) {
|
||||
if (!nextVersion) await this.fn({ effects, utils: utils(effects) });
|
||||
}
|
||||
}
|
||||
|
||||
export function setupUninstall<WrapperData>(fn: UninstallFn<WrapperData>) {
|
||||
return Uninstall.of(fn);
|
||||
}
|
||||
@@ -82,11 +82,15 @@ export class Daemons<Ids extends string | never> {
|
||||
const { command } = daemon;
|
||||
|
||||
const child = effects.runDaemon(command);
|
||||
const trigger = (daemon.ready.trigger ?? defaultTrigger)();
|
||||
let currentInput = {};
|
||||
const getCurrentInput = () => currentInput;
|
||||
const trigger = (daemon.ready.trigger ?? defaultTrigger)(
|
||||
getCurrentInput,
|
||||
);
|
||||
for (
|
||||
let res = await trigger.next({});
|
||||
let res = await trigger.next();
|
||||
!res.done;
|
||||
res = await trigger.next({})
|
||||
res = await trigger.next()
|
||||
) {
|
||||
const response = await daemon.ready.fn();
|
||||
if (response.status === "passing") {
|
||||
|
||||
@@ -57,11 +57,11 @@ export interface GenericManifest {
|
||||
actions: Array<ActionMetaData>;
|
||||
alerts: {
|
||||
install: string | null;
|
||||
update: string | null;
|
||||
uninstall: string | null;
|
||||
restore: string | null;
|
||||
start: string | null;
|
||||
stop: string | null;
|
||||
update: string | null;
|
||||
};
|
||||
dependencies: Record<string, Dependency>;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { GenericManifest, ManifestVersion } from "./ManifestTypes";
|
||||
|
||||
export function setupManifest<
|
||||
M extends GenericManifest & { id: Id; version: Version },
|
||||
Id extends string,
|
||||
Version extends ManifestVersion,
|
||||
>(manifest: M): M {
|
||||
Dependencies extends Record<string, unknown>,
|
||||
>(
|
||||
manifest: GenericManifest & {
|
||||
dependencies: Dependencies;
|
||||
id: Id;
|
||||
version: Version;
|
||||
},
|
||||
): GenericManifest & { dependencies: Dependencies; id: Id; version: Version } {
|
||||
return manifest;
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import { setupActions } from "../actions/setupActions";
|
||||
import { EmVer } from "../emverLite/mod";
|
||||
import { GenericManifest } from "../manifest/ManifestTypes";
|
||||
import { ExpectedExports } from "../types";
|
||||
import { once } from "../util/once";
|
||||
import { Migration } from "./Migration";
|
||||
|
||||
export function setupMigrations<Migrations extends Array<Migration<any>>>(
|
||||
manifest: GenericManifest,
|
||||
initializeActions: ReturnType<typeof setupActions>["initializeActions"],
|
||||
...migrations: EnsureUniqueId<Migrations>
|
||||
) {
|
||||
const sortedMigrations = once(() => {
|
||||
const migrationsAsVersions = (migrations as Array<Migration<any>>).map(
|
||||
(x) => [EmVer.parse(x.options.version), x] as const,
|
||||
);
|
||||
migrationsAsVersions.sort((a, b) => a[0].compareForSort(b[0]));
|
||||
return migrationsAsVersions;
|
||||
});
|
||||
const currentVersion = once(() => EmVer.parse(manifest.version));
|
||||
const init: ExpectedExports.init = async ({ effects, previousVersion }) => {
|
||||
await initializeActions(effects);
|
||||
if (!!previousVersion) {
|
||||
const previousVersionEmVer = EmVer.parse(previousVersion);
|
||||
for (const [_, migration] of sortedMigrations()
|
||||
.filter((x) => x[0].greaterThan(previousVersionEmVer))
|
||||
.filter((x) => x[0].lessThanOrEqual(currentVersion()))) {
|
||||
await migration.up({ effects });
|
||||
}
|
||||
}
|
||||
};
|
||||
const uninit: ExpectedExports.uninit = async ({ effects, nextVersion }) => {
|
||||
if (!!nextVersion) {
|
||||
const nextVersionEmVer = EmVer.parse(nextVersion);
|
||||
const reversed = [...sortedMigrations()].reverse();
|
||||
for (const [_, migration] of reversed
|
||||
.filter((x) => x[0].greaterThan(nextVersionEmVer))
|
||||
.filter((x) => x[0].lessThanOrEqual(currentVersion()))) {
|
||||
await migration.down({ effects });
|
||||
}
|
||||
}
|
||||
};
|
||||
return { init, uninit };
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
export type EnsureUniqueId<A, B = A, ids = never> =
|
||||
B extends [] ? A :
|
||||
B extends [Migration<infer id>, ...infer Rest] ? (
|
||||
id extends ids ? "One of the ids are not unique"[] :
|
||||
EnsureUniqueId<A, Rest, id | ids>
|
||||
) : "There exists a migration that is not a Migration"[]
|
||||
@@ -1,9 +1,17 @@
|
||||
import { EmVer, notRange, rangeAnd, rangeOf, rangeOr } from "./mod";
|
||||
import { EmVer, notRange, rangeAnd, rangeOf, rangeOr } from "../emverLite/mod";
|
||||
describe("EmVer", () => {
|
||||
{
|
||||
{
|
||||
const checker = rangeOf("*");
|
||||
test("rangeOf('*')", () => {
|
||||
checker.check("1");
|
||||
checker.check("1.2");
|
||||
checker.check("1.2.3");
|
||||
checker.check("1.2.3.4");
|
||||
// @ts-expect-error
|
||||
checker.check("1.2.3.4.5");
|
||||
// @ts-expect-error
|
||||
checker.check("1.2.3.4.5.6");
|
||||
expect(checker.check("1")).toEqual(true);
|
||||
expect(checker.check("1.2")).toEqual(true);
|
||||
expect(checker.check("1.2.3.4")).toEqual(true);
|
||||
@@ -23,6 +31,7 @@ describe("EmVer", () => {
|
||||
expect(checker.check("2-beta123")).toEqual(true);
|
||||
expect(checker.check("2")).toEqual(true);
|
||||
expect(checker.check("1.2.3.5")).toEqual(true);
|
||||
// @ts-expect-error
|
||||
expect(checker.check("1.2.3.4.1")).toEqual(true);
|
||||
});
|
||||
|
||||
@@ -49,6 +58,7 @@ describe("EmVer", () => {
|
||||
test(`rangeOf(">=1.2.3.4") valid`, () => {
|
||||
expect(checker.check("2")).toEqual(true);
|
||||
expect(checker.check("1.2.3.5")).toEqual(true);
|
||||
// @ts-expect-error
|
||||
expect(checker.check("1.2.3.4.1")).toEqual(true);
|
||||
expect(checker.check("1.2.3.4")).toEqual(true);
|
||||
});
|
||||
@@ -63,6 +73,7 @@ describe("EmVer", () => {
|
||||
test(`rangeOf("<1.2.3.4") invalid`, () => {
|
||||
expect(checker.check("2")).toEqual(false);
|
||||
expect(checker.check("1.2.3.5")).toEqual(false);
|
||||
// @ts-expect-error
|
||||
expect(checker.check("1.2.3.4.1")).toEqual(false);
|
||||
expect(checker.check("1.2.3.4")).toEqual(false);
|
||||
});
|
||||
@@ -77,6 +88,7 @@ describe("EmVer", () => {
|
||||
test(`rangeOf("<=1.2.3.4") invalid`, () => {
|
||||
expect(checker.check("2")).toEqual(false);
|
||||
expect(checker.check("1.2.3.5")).toEqual(false);
|
||||
// @ts-expect-error
|
||||
expect(checker.check("1.2.3.4.1")).toEqual(false);
|
||||
});
|
||||
|
||||
@@ -184,6 +196,7 @@ describe("EmVer", () => {
|
||||
test(`rangeOf("!>1.2.3.4") invalid`, () => {
|
||||
expect(checker.check("2")).toEqual(false);
|
||||
expect(checker.check("1.2.3.5")).toEqual(false);
|
||||
// @ts-expect-error
|
||||
expect(checker.check("1.2.3.4.1")).toEqual(false);
|
||||
});
|
||||
|
||||
@@ -437,7 +437,7 @@ export type MigrationRes = {
|
||||
|
||||
export type ActionResult = {
|
||||
message: string;
|
||||
value?: string;
|
||||
value: null | string;
|
||||
copyable: boolean;
|
||||
qr: boolean;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user