mirror of
https://github.com/Start9Labs/start-sdk.git
synced 2026-03-30 12:21:57 +00:00
chore: Getting in the action builder
This commit is contained in:
43
lib/actions/createAction.ts
Normal file
43
lib/actions/createAction.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import {
|
||||||
|
ActionMetaData,
|
||||||
|
ActionResult,
|
||||||
|
Effects,
|
||||||
|
ExportedAction,
|
||||||
|
} from "../types";
|
||||||
|
import { Utils, utils } from "../util";
|
||||||
|
|
||||||
|
export class CreatedAction<WrapperData, Input> {
|
||||||
|
private constructor(
|
||||||
|
readonly metaData: ActionMetaData,
|
||||||
|
readonly fn: (options: {
|
||||||
|
effects: Effects;
|
||||||
|
utils: Utils<WrapperData>;
|
||||||
|
input: Input;
|
||||||
|
}) => Promise<ActionResult>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
static of<WrapperData, Input>(
|
||||||
|
metaData: ActionMetaData,
|
||||||
|
fn: (options: {
|
||||||
|
effects: Effects;
|
||||||
|
utils: Utils<WrapperData>;
|
||||||
|
input: Input;
|
||||||
|
}) => Promise<ActionResult>,
|
||||||
|
) {
|
||||||
|
return new CreatedAction<WrapperData, Input>(metaData, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
exportedAction: ExportedAction = ({ effects, input }) => {
|
||||||
|
return this.fn({
|
||||||
|
effects,
|
||||||
|
utils: utils<WrapperData>(effects),
|
||||||
|
input: input as Input,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
async exportAction(effects: Effects) {
|
||||||
|
await effects.exportAction(this.metaData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createAction = CreatedAction.of;
|
||||||
3
lib/actions/index.ts
Normal file
3
lib/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export { CreatedAction, createAction } from "./createAction";
|
||||||
|
|
||||||
|
export { setupActions } from "./setupActions";
|
||||||
20
lib/actions/setupActions.ts
Normal file
20
lib/actions/setupActions.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Effects, ExpectedExports, ExportedAction } from "../types";
|
||||||
|
import { ActionMetaData } from "../types";
|
||||||
|
import { CreatedAction } from "./createAction";
|
||||||
|
|
||||||
|
export function setupActions(...createdActions: CreatedAction<any, any>[]) {
|
||||||
|
const actions: Record<string, ExportedAction> = {};
|
||||||
|
for (const action of createdActions) {
|
||||||
|
actions[action.metaData.id] = action.exportedAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
const manifestActions = async (effects: Effects) => {
|
||||||
|
for (const action of createdActions) {
|
||||||
|
action.exportAction(effects);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
actions,
|
||||||
|
manifestActions,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
export * as configBuilder from "./builder";
|
export * as configBuilder from "./builder";
|
||||||
|
|
||||||
export { setupConfigExports } from "./setupConfigExports";
|
export { setupConfig as setupConfigExports } from "./setupConfig";
|
||||||
export { specToBuilder, specToBuilderFile } from "./specToBuilder";
|
export { specToBuilder, specToBuilderFile } from "./specToBuilder";
|
||||||
export * as dependencies from "./dependencies";
|
export * as dependencies from "./dependencies";
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export type DependenciesFn<A> = (options: {
|
|||||||
* @param options
|
* @param options
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function setupConfigExports<A extends InputSpec>(
|
export function setupConfig<A extends InputSpec>(
|
||||||
spec: Config<A>,
|
spec: Config<A>,
|
||||||
write: Write<A>,
|
write: Write<A>,
|
||||||
read: Read<A>,
|
read: Read<A>,
|
||||||
@@ -51,4 +51,4 @@ export function setupConfigExports<A extends InputSpec>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default setupConfigExports;
|
export default setupConfig;
|
||||||
@@ -13,3 +13,4 @@ export * as util from "./util";
|
|||||||
export * as YAML from "yaml";
|
export * as YAML from "yaml";
|
||||||
export * as properties from "./properties";
|
export * as properties from "./properties";
|
||||||
export * as autoconfig from "./autoconfig";
|
export * as autoconfig from "./autoconfig";
|
||||||
|
export * as actions from "./actions";
|
||||||
|
|||||||
37
lib/types.ts
37
lib/types.ts
@@ -2,6 +2,11 @@ export * as configTypes from "./config/configTypes";
|
|||||||
import { InputSpec } from "./config/configTypes";
|
import { InputSpec } from "./config/configTypes";
|
||||||
import { ActionReceipt } from "./init";
|
import { ActionReceipt } from "./init";
|
||||||
|
|
||||||
|
export type ExportedAction = (options: {
|
||||||
|
effects: Effects;
|
||||||
|
input?: Record<string, unknown>;
|
||||||
|
}) => Promise<ActionResult>;
|
||||||
|
|
||||||
export namespace ExpectedExports {
|
export namespace ExpectedExports {
|
||||||
version: 1;
|
version: 1;
|
||||||
/** 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. */
|
||||||
@@ -42,11 +47,8 @@ export namespace ExpectedExports {
|
|||||||
* One old use case is to add a action where we add a file, that will then be run during the
|
* One old use case is to add a action where we add a file, that will then be run during the
|
||||||
* service starting, and that file would indicate that it would rescan all the data.
|
* service starting, and that file would indicate that it would rescan all the data.
|
||||||
*/
|
*/
|
||||||
export type action = {
|
export type actions = {
|
||||||
[id: string]: (options: {
|
[id: string]: ExportedAction;
|
||||||
effects: Effects;
|
|
||||||
input?: Record<string, unknown>;
|
|
||||||
}) => Promise<ActionResult>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,6 +147,18 @@ export type DaemonReturned = {
|
|||||||
term(): Promise<void>;
|
term(): Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ActionMetaData = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
id: string;
|
||||||
|
input: null | InputSpec;
|
||||||
|
runningOnly: boolean;
|
||||||
|
/**
|
||||||
|
* So the ordering of the actions is by alphabetical order of the group, then followed by the alphabetical of the actions
|
||||||
|
*/
|
||||||
|
group?: string;
|
||||||
|
};
|
||||||
|
|
||||||
/** 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 */
|
||||||
@@ -335,17 +349,7 @@ export type Effects = {
|
|||||||
*
|
*
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
exportAction(options: {
|
exportAction(options: ActionMetaData): Promise<void & ActionReceipt>;
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
id: string;
|
|
||||||
input: null | InputSpec;
|
|
||||||
runningOnly: boolean;
|
|
||||||
/**
|
|
||||||
* So the ordering of the actions is by alphabetical order of the group, then followed by the alphabetical of the actions
|
|
||||||
*/
|
|
||||||
group?: string;
|
|
||||||
}): Promise<void & ActionReceipt>;
|
|
||||||
/**
|
/**
|
||||||
* Remove an action that was exported. Used problably during main or during setConfig.
|
* Remove an action that was exported. Used problably during main or during setConfig.
|
||||||
*/
|
*/
|
||||||
@@ -432,7 +436,6 @@ export type MigrationRes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ActionResult = {
|
export type ActionResult = {
|
||||||
version: "0";
|
|
||||||
message: string;
|
message: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
copyable: boolean;
|
copyable: boolean;
|
||||||
|
|||||||
@@ -2,8 +2,12 @@ import { Parser } from "ts-matches";
|
|||||||
import * as T from "../types";
|
import * as T from "../types";
|
||||||
import FileHelper from "./fileHelper";
|
import FileHelper from "./fileHelper";
|
||||||
import nullIfEmpty from "./nullIfEmpty";
|
import nullIfEmpty from "./nullIfEmpty";
|
||||||
import { getWrapperData } from "./getWrapperData";
|
import { WrapperData, getWrapperData } from "./getWrapperData";
|
||||||
import { checkPortListening, checkWebUrl } from "../health/checkFns";
|
import {
|
||||||
|
CheckResult,
|
||||||
|
checkPortListening,
|
||||||
|
checkWebUrl,
|
||||||
|
} from "../health/checkFns";
|
||||||
import { LocalPort, NetworkBuilder, TorHostname } from "../mainFn";
|
import { LocalPort, NetworkBuilder, TorHostname } from "../mainFn";
|
||||||
import { ExtractWrapperData } from "../types";
|
import { ExtractWrapperData } from "../types";
|
||||||
|
|
||||||
@@ -35,7 +39,50 @@ function withAffine<B>() {
|
|||||||
return {} as { [affine]: B };
|
return {} as { [affine]: B };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const utils = <WrapperData = never>(effects: T.Effects) => ({
|
export type WrapperDataOptionals<WrapperData, Path extends string> = {
|
||||||
|
validator?: Parser<unknown, ExtractWrapperData<WrapperData, Path>>;
|
||||||
|
/** Defaults to what ever the package currently in */
|
||||||
|
packageId?: string | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Utils<WD> = {
|
||||||
|
readFile: <A>(fileHelper: FileHelper<A>) => ReturnType<FileHelper<A>["read"]>;
|
||||||
|
writeFile: <A>(
|
||||||
|
fileHelper: FileHelper<A>,
|
||||||
|
data: A,
|
||||||
|
) => ReturnType<FileHelper<A>["write"]>;
|
||||||
|
getWrapperData: <Path extends string>(
|
||||||
|
path: T.EnsureWrapperDataPath<WD, Path>,
|
||||||
|
options?: WrapperDataOptionals<WD, Path>,
|
||||||
|
) => WrapperData<WD, Path>;
|
||||||
|
setWrapperData: <Path extends string | never>(
|
||||||
|
path: T.EnsureWrapperDataPath<WD, Path>,
|
||||||
|
value: ExtractWrapperData<WD, Path>,
|
||||||
|
) => Promise<void>;
|
||||||
|
checkPortListening(
|
||||||
|
port: number,
|
||||||
|
options?: {
|
||||||
|
error?: string;
|
||||||
|
message?: string;
|
||||||
|
},
|
||||||
|
): Promise<CheckResult>;
|
||||||
|
checkWebUrl(
|
||||||
|
url: string,
|
||||||
|
options?: {
|
||||||
|
timeout?: number;
|
||||||
|
successMessage?: string;
|
||||||
|
errorMessage?: string;
|
||||||
|
},
|
||||||
|
): Promise<CheckResult>;
|
||||||
|
localPort: (id: string) => LocalPort;
|
||||||
|
networkBuilder: () => NetworkBuilder;
|
||||||
|
torHostName: (id: string) => TorHostname;
|
||||||
|
exists: (props: { path: string; volumeId: string }) => Promise<boolean>;
|
||||||
|
nullIfEmpty: typeof nullIfEmpty;
|
||||||
|
};
|
||||||
|
export const utils = <WrapperData = never>(
|
||||||
|
effects: T.Effects,
|
||||||
|
): Utils<WrapperData> => ({
|
||||||
readFile: <A>(fileHelper: FileHelper<A>) => fileHelper.read(effects),
|
readFile: <A>(fileHelper: FileHelper<A>) => fileHelper.read(effects),
|
||||||
writeFile: <A>(fileHelper: FileHelper<A>, data: A) =>
|
writeFile: <A>(fileHelper: FileHelper<A>, data: A) =>
|
||||||
fileHelper.write(data, effects),
|
fileHelper.write(data, effects),
|
||||||
@@ -55,9 +102,9 @@ export const utils = <WrapperData = never>(effects: T.Effects) => ({
|
|||||||
) => effects.setWrapperData<WrapperData, Path>({ value, path: path as any }),
|
) => effects.setWrapperData<WrapperData, Path>({ value, path: path as any }),
|
||||||
checkPortListening: checkPortListening.bind(null, effects),
|
checkPortListening: checkPortListening.bind(null, effects),
|
||||||
checkWebUrl: checkWebUrl.bind(null, effects),
|
checkWebUrl: checkWebUrl.bind(null, effects),
|
||||||
localPort: LocalPort.bind(null, effects),
|
localPort: (id: string) => new LocalPort(effects, id),
|
||||||
networkBuilder: NetworkBuilder.of.bind(null, effects),
|
networkBuilder: () => NetworkBuilder.of(effects),
|
||||||
torHostName: TorHostname.of.bind(null, effects),
|
torHostName: (id: string) => TorHostname.of(effects, id),
|
||||||
});
|
});
|
||||||
|
|
||||||
type NeverPossible = { [affine]: string };
|
type NeverPossible = { [affine]: string };
|
||||||
|
|||||||
Reference in New Issue
Block a user