Merge pull request #24 from Start9Labs/feat/new-effects-for-latest

chore: Add in the new effects
This commit is contained in:
J H
2023-02-13 17:06:06 -07:00
committed by GitHub
2 changed files with 102 additions and 165 deletions

View File

@@ -16,14 +16,11 @@ export interface NoRepeat<version extends string, type extends "up" | "down"> {
* @param noFail (optional, default:false) whether or not to fail the migration if fn throws an error * @param noFail (optional, default:false) whether or not to fail the migration if fn throws an error
* @returns a migraion function * @returns a migraion function
*/ */
export function updateConfig< export function updateConfig<version extends string, type extends "up" | "down">(
version extends string, fn: (config: T.Config, effects: T.Effects) => T.Config | Promise<T.Config>,
type extends "up" | "down", configured: boolean,
>( noRepeat?: NoRepeat<version, type>,
fn: (config: T.Config, effects: T.Effects) => T.Config | Promise<T.Config>, noFail = false
configured: boolean,
noRepeat?: NoRepeat<version, type>,
noFail = false,
): M.MigrationFn<version, type> { ): M.MigrationFn<version, type> {
return M.migrationFn(async (effects: T.Effects) => { return M.migrationFn(async (effects: T.Effects) => {
await noRepeatGuard(effects, noRepeat, async () => { await noRepeatGuard(effects, noRepeat, async () => {
@@ -45,20 +42,15 @@ export function updateConfig<
}); });
} }
export async function noRepeatGuard< export async function noRepeatGuard<version extends string, type extends "up" | "down">(
version extends string, effects: T.Effects,
type extends "up" | "down", noRepeat: NoRepeat<version, type> | undefined,
>( fn: () => Promise<void>
effects: T.Effects,
noRepeat: NoRepeat<version, type> | undefined,
fn: () => Promise<void>,
): Promise<void> { ): Promise<void> {
if (!noRepeat) { if (!noRepeat) {
return fn(); return fn();
} }
if ( if (!(await util.exists(effects, { path: "start9/migrations", volumeId: "main" }))) {
!await util.exists(effects, { path: "start9/migrations", volumeId: "main" })
) {
await effects.createDir({ path: "start9/migrations", volumeId: "main" }); await effects.createDir({ path: "start9/migrations", volumeId: "main" });
} }
const migrationPath = { const migrationPath = {
@@ -66,7 +58,7 @@ export async function noRepeatGuard<
volumeId: "main", volumeId: "main",
}; };
if (noRepeat.type === "up") { if (noRepeat.type === "up") {
if (!await util.exists(effects, migrationPath)) { if (!(await util.exists(effects, migrationPath))) {
await fn(); await fn();
await effects.writeFile({ ...migrationPath, toWrite: "" }); await effects.writeFile({ ...migrationPath, toWrite: "" });
} }
@@ -81,11 +73,9 @@ export async function noRepeatGuard<
export async function initNoRepeat<versions extends string>( export async function initNoRepeat<versions extends string>(
effects: T.Effects, effects: T.Effects,
migrations: M.MigrationMapping<versions>, migrations: M.MigrationMapping<versions>,
startingVersion: string, startingVersion: string
) { ) {
if ( if (!(await util.exists(effects, { path: "start9/migrations", volumeId: "main" }))) {
!await util.exists(effects, { path: "start9/migrations", volumeId: "main" })
) {
const starting = EmVer.parse(startingVersion); const starting = EmVer.parse(startingVersion);
await effects.createDir({ path: "start9/migrations", volumeId: "main" }); await effects.createDir({ path: "start9/migrations", volumeId: "main" });
for (const version in migrations) { for (const version in migrations) {
@@ -103,19 +93,11 @@ export async function initNoRepeat<versions extends string>(
export function fromMapping<versions extends string>( export function fromMapping<versions extends string>(
migrations: M.MigrationMapping<versions>, migrations: M.MigrationMapping<versions>,
currentVersion: string, currentVersion: string
): T.ExpectedExports.migration { ): T.ExpectedExports.migration {
const inner = M.fromMapping(migrations, currentVersion); const inner = M.fromMapping(migrations, currentVersion);
return async ( return async (effects: T.Effects, version: string, direction?: unknown) => {
effects: T.Effects, await initNoRepeat(effects, migrations, direction === "from" ? version : currentVersion);
version: string,
direction?: unknown,
) => {
await initNoRepeat(
effects,
migrations,
direction === "from" ? version : currentVersion,
);
return inner(effects, version, direction); return inner(effects, version, direction);
}; };
} }

217
types.ts
View File

@@ -1,10 +1,8 @@
// deno-lint-ignore no-namespace // deno-lint-ignore no-namespace
export namespace ExpectedExports { export namespace ExpectedExports {
version: 2;
/** Set configuration is called after we have modified and saved the configuration in the embassy ui. Use this to make a file for the docker to read from for configuration. */ /** Set configuration is called after we have modified and saved the configuration in the embassy ui. Use this to make a file for the docker to read from for configuration. */
export type setConfig = ( export type setConfig = (effects: Effects, input: Config) => Promise<ResultType<SetResult>>;
effects: Effects,
input: Config,
) => Promise<ResultType<SetResult>>;
/** Get configuration returns a shape that describes the format that the embassy ui will generate, and later send to the set config */ /** Get configuration returns a shape that describes the format that the embassy ui will generate, and later send to the set config */
export type getConfig = (effects: Effects) => Promise<ResultType<ConfigRes>>; export type getConfig = (effects: Effects) => Promise<ResultType<ConfigRes>>;
/** These are how we make sure the our dependency configurations are valid and if not how to fix them. */ /** These are how we make sure the our dependency configurations are valid and if not how to fix them. */
@@ -12,31 +10,17 @@ export namespace ExpectedExports {
/** For backing up service data though the embassyOS UI */ /** For backing up service data though the embassyOS UI */
export type createBackup = (effects: Effects) => Promise<ResultType<unknown>>; export type createBackup = (effects: Effects) => Promise<ResultType<unknown>>;
/** For restoring service data that was previously backed up using the embassyOS UI create backup flow. Backup restores are also triggered via the embassyOS UI, or doing a system restore flow during setup. */ /** For restoring service data that was previously backed up using the embassyOS UI create backup flow. Backup restores are also triggered via the embassyOS UI, or doing a system restore flow during setup. */
export type restoreBackup = ( export type restoreBackup = (effects: Effects) => Promise<ResultType<unknown>>;
effects: Effects,
) => Promise<ResultType<unknown>>;
/** Properties are used to get values from the docker, like a username + password, what ports we are hosting from */ /** Properties are used to get values from the docker, like a username + password, what ports we are hosting from */
export type properties = ( export type properties = (effects: Effects) => Promise<ResultType<Properties>>;
effects: Effects,
) => Promise<ResultType<Properties>>;
export type health = { export type health = {
/** Should be the health check id */ /** Should be the health check id */
[id: string]: ( [id: string]: (effects: Effects, dateMs: number) => Promise<ResultType<unknown>>;
effects: Effects,
dateMs: number,
) => Promise<ResultType<unknown>>;
}; };
export type migration = ( export type migration = (effects: Effects, version: string, ...args: unknown[]) => Promise<ResultType<MigrationRes>>;
effects: Effects,
version: string,
...args: unknown[]
) => Promise<ResultType<MigrationRes>>;
export type action = { export type action = {
[id: string]: ( [id: string]: (effects: Effects, config?: Config) => Promise<ResultType<ActionResult>>;
effects: Effects,
config?: Config,
) => Promise<ResultType<ActionResult>>;
}; };
/** /**
@@ -49,35 +33,32 @@ export namespace ExpectedExports {
/** Used to reach out from the pure js runtime */ /** Used to reach out from the pure js runtime */
export type Effects = { export type Effects = {
/** Usable when not sandboxed */ /** Usable when not sandboxed */
writeFile( writeFile(input: { path: string; volumeId: string; toWrite: string }): Promise<void>;
input: { path: string; volumeId: string; toWrite: string },
): Promise<void>;
readFile(input: { volumeId: string; path: string }): Promise<string>; readFile(input: { volumeId: string; path: string }): Promise<string>;
metadata(input: { volumeId: string; path: string }): Promise<Metadata>; metadata(input: { volumeId: string; path: string }): Promise<Metadata>;
/** Create a directory. Usable when not sandboxed */ /** Create a directory. Usable when not sandboxed */
createDir(input: { volumeId: string; path: string }): Promise<string>; createDir(input: { volumeId: string; path: string }): Promise<string>;
readDir(input: { volumeId: string; path: string }): Promise<string[]>;
/** Remove a directory. Usable when not sandboxed */ /** Remove a directory. Usable when not sandboxed */
removeDir(input: { volumeId: string; path: string }): Promise<string>; removeDir(input: { volumeId: string; path: string }): Promise<string>;
removeFile(input: { volumeId: string; path: string }): Promise<void>; removeFile(input: { volumeId: string; path: string }): Promise<void>;
/** Write a json file into an object. Usable when not sandboxed */ /** Write a json file into an object. Usable when not sandboxed */
writeJsonFile( writeJsonFile(input: { volumeId: string; path: string; toWrite: Record<string, unknown> }): Promise<void>;
input: { volumeId: string; path: string; toWrite: Record<string, unknown> },
): Promise<void>;
/** Read a json file into an object */ /** Read a json file into an object */
readJsonFile( readJsonFile(input: { volumeId: string; path: string }): Promise<Record<string, unknown>>;
input: { volumeId: string; path: string },
): Promise<Record<string, unknown>>;
runCommand( runCommand(input: { command: string; args?: string[]; timeoutMillis?: number }): Promise<ResultType<string>>;
input: { command: string; args?: string[]; timeoutMillis?: number },
): Promise<ResultType<string>>;
runDaemon(input: { command: string; args?: string[] }): { runDaemon(input: { command: string; args?: string[] }): {
wait(): Promise<ResultType<string>>; wait(): Promise<ResultType<string>>;
term(): Promise<void>; term(): Promise<void>;
}; };
chown(input: { volumeId: string; path: string; uid: string }): Promise<null>;
chmod(input: { volumeId: string; path: string; mode: string }): Promise<null>;
sleep(timeMs: number): Promise<null>; sleep(timeMs: number): Promise<null>;
/** Log at the trace level */ /** Log at the trace level */
@@ -95,6 +76,8 @@ export type Effects = {
is_sandboxed(): boolean; is_sandboxed(): boolean;
exists(input: { volumeId: string; path: string }): Promise<boolean>; exists(input: { volumeId: string; path: string }): Promise<boolean>;
bindLocal(options: { internalPort: number; name: string; externalPort: number }): Promise<string>;
bindTor(options: { internalPort: number; name: string; externalPort: number }): Promise<string>;
fetch( fetch(
url: string, url: string,
@@ -102,7 +85,7 @@ export type Effects = {
method?: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "PATCH"; method?: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "PATCH";
headers?: Record<string, string>; headers?: Record<string, string>;
body?: string; body?: string;
}, }
): Promise<{ ): Promise<{
method: string; method: string;
ok: boolean; ok: boolean;
@@ -217,8 +200,8 @@ export type Target<T extends string, V> = V & {
export type UniqueBy = export type UniqueBy =
| { | {
any: UniqueBy[]; any: UniqueBy[];
} }
| string | string
| null; | null;
@@ -228,19 +211,19 @@ export type WithNullable<T> = T & {
export type DefaultString = export type DefaultString =
| string | string
| { | {
/** The chars available for the random generation */ /** The chars available for the random generation */
charset?: string; charset?: string;
/** Length that we generate to */ /** Length that we generate to */
len: number; len: number;
}; };
export type ValueSpecString = // deno-lint-ignore ban-types export type ValueSpecString = // deno-lint-ignore ban-types
( (
| {} | {}
| { | {
pattern: string; pattern: string;
"pattern-description": string; "pattern-description": string;
} }
) & { ) & {
copyable?: boolean; copyable?: boolean;
masked?: boolean; masked?: boolean;
@@ -257,71 +240,63 @@ export type ValueSpecNumber = {
export type ValueSpecBoolean = Record<string, unknown>; export type ValueSpecBoolean = Record<string, unknown>;
export type ValueSpecAny = export type ValueSpecAny =
| Tag<"boolean", WithDescription<WithDefault<ValueSpecBoolean, boolean>>> | Tag<"boolean", WithDescription<WithDefault<ValueSpecBoolean, boolean>>>
| Tag<"string", WithDescription<WithNullableDefault<WithNullable<ValueSpecString>, DefaultString>>>
| Tag<"number", WithDescription<WithNullableDefault<WithNullable<ValueSpecNumber>, number>>>
| Tag< | Tag<
"string", "enum",
WithDescription< WithDescription<
WithNullableDefault<WithNullable<ValueSpecString>, DefaultString> WithDefault<
> {
> values: readonly string[] | string[];
| Tag< "value-names": {
"number", [key: string]: string;
WithDescription<WithNullableDefault<WithNullable<ValueSpecNumber>, number>> };
> },
| Tag< string
"enum", >
WithDescription<
WithDefault<
{
values: readonly string[] | string[];
"value-names": {
[key: string]: string;
};
},
string
> >
> >
>
| Tag<"list", ValueSpecList> | Tag<"list", ValueSpecList>
| Tag<"object", WithDescription<WithNullableDefault<ValueSpecObject, Config>>> | Tag<"object", WithDescription<WithNullableDefault<ValueSpecObject, Config>>>
| Tag<"union", WithOptionalDescription<WithDefault<ValueSpecUnion, string>>> | Tag<"union", WithOptionalDescription<WithDefault<ValueSpecUnion, string>>>
| Tag< | Tag<
"pointer", "pointer",
WithDescription< WithDescription<
| Subtype< | Subtype<
"package", "package",
| Target< | Target<
"tor-key", "tor-key",
{ {
"package-id": string; "package-id": string;
interface: string; interface: string;
} }
> >
| Target< | Target<
"tor-address", "tor-address",
{ {
"package-id": string; "package-id": string;
interface: string; interface: string;
} }
> >
| Target< | Target<
"lan-address", "lan-address",
{ {
"package-id": string; "package-id": string;
interface: string; interface: string;
} }
> >
| Target< | Target<
"config", "config",
{ {
"package-id": string; "package-id": string;
selector: string; selector: string;
multi: boolean; multi: boolean;
} }
> >
>
| Subtype<"system", Record<string, unknown>>
> >
| Subtype<"system", Record<string, unknown>> >;
>
>;
export type ValueSpecUnion = { export type ValueSpecUnion = {
/** What tag for the specification, for tag unions */ /** What tag for the specification, for tag unions */
tag: { tag: {
@@ -345,32 +320,12 @@ export type ValueSpecObject = {
"unique-by"?: UniqueBy; "unique-by"?: UniqueBy;
}; };
export type ValueSpecList = export type ValueSpecList =
| Subtype< | Subtype<"boolean", WithDescription<WithDefault<ListSpec<ValueSpecBoolean>, boolean[]>>>
"boolean", | Subtype<"string", WithDescription<WithDefault<ListSpec<ValueSpecString>, string[]>>>
WithDescription<WithDefault<ListSpec<ValueSpecBoolean>, boolean[]>> | Subtype<"number", WithDescription<WithDefault<ListSpec<ValueSpecNumber>, number[]>>>
> | Subtype<"enum", WithDescription<WithDefault<ListSpec<ValueSpecEnum>, string[]>>>
| Subtype< | Subtype<"object", WithDescription<WithNullableDefault<ListSpec<ValueSpecObject>, Record<string, unknown>[]>>>
"string", | Subtype<"union", WithDescription<WithDefault<ListSpec<ValueSpecUnion>, string[]>>>;
WithDescription<WithDefault<ListSpec<ValueSpecString>, string[]>>
>
| Subtype<
"number",
WithDescription<WithDefault<ListSpec<ValueSpecNumber>, number[]>>
>
| Subtype<
"enum",
WithDescription<WithDefault<ListSpec<ValueSpecEnum>, string[]>>
>
| Subtype<
"object",
WithDescription<
WithNullableDefault<ListSpec<ValueSpecObject>, Record<string, unknown>[]>
>
>
| Subtype<
"union",
WithDescription<WithDefault<ListSpec<ValueSpecUnion>, string[]>>
>;
export type ValueSpecEnum = { export type ValueSpecEnum = {
values: string[]; values: string[];
"value-names": { [key: string]: string }; "value-names": { [key: string]: string };
@@ -422,8 +377,8 @@ export type DependsOn = {
export type KnownError = export type KnownError =
| { error: string } | { error: string }
| { | {
"error-code": [number, string] | readonly [number, string]; "error-code": [number, string] | readonly [number, string];
}; };
export type ResultType<T> = KnownError | { result: T }; export type ResultType<T> = KnownError | { result: T };
export type PackagePropertiesV2 = { export type PackagePropertiesV2 = {