mirror of
https://github.com/Start9Labs/start-sdk.git
synced 2026-03-26 02:11:56 +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 { setupConfigExports } from "./setupConfigExports";
|
||||
export { setupConfig as setupConfigExports } from "./setupConfig";
|
||||
export { specToBuilder, specToBuilderFile } from "./specToBuilder";
|
||||
export * as dependencies from "./dependencies";
|
||||
|
||||
@@ -22,7 +22,7 @@ export type DependenciesFn<A> = (options: {
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
export function setupConfigExports<A extends InputSpec>(
|
||||
export function setupConfig<A extends InputSpec>(
|
||||
spec: Config<A>,
|
||||
write: Write<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 properties from "./properties";
|
||||
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 { ActionReceipt } from "./init";
|
||||
|
||||
export type ExportedAction = (options: {
|
||||
effects: Effects;
|
||||
input?: Record<string, unknown>;
|
||||
}) => Promise<ActionResult>;
|
||||
|
||||
export namespace ExpectedExports {
|
||||
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. */
|
||||
@@ -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
|
||||
* service starting, and that file would indicate that it would rescan all the data.
|
||||
*/
|
||||
export type action = {
|
||||
[id: string]: (options: {
|
||||
effects: Effects;
|
||||
input?: Record<string, unknown>;
|
||||
}) => Promise<ActionResult>;
|
||||
export type actions = {
|
||||
[id: string]: ExportedAction;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -145,6 +147,18 @@ export type DaemonReturned = {
|
||||
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 */
|
||||
export type Effects = {
|
||||
/** Usable when not sandboxed */
|
||||
@@ -335,17 +349,7 @@ export type Effects = {
|
||||
*
|
||||
* @param options
|
||||
*/
|
||||
exportAction(options: {
|
||||
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>;
|
||||
exportAction(options: ActionMetaData): Promise<void & ActionReceipt>;
|
||||
/**
|
||||
* Remove an action that was exported. Used problably during main or during setConfig.
|
||||
*/
|
||||
@@ -432,7 +436,6 @@ export type MigrationRes = {
|
||||
};
|
||||
|
||||
export type ActionResult = {
|
||||
version: "0";
|
||||
message: string;
|
||||
value?: string;
|
||||
copyable: boolean;
|
||||
|
||||
@@ -2,8 +2,12 @@ import { Parser } from "ts-matches";
|
||||
import * as T from "../types";
|
||||
import FileHelper from "./fileHelper";
|
||||
import nullIfEmpty from "./nullIfEmpty";
|
||||
import { getWrapperData } from "./getWrapperData";
|
||||
import { checkPortListening, checkWebUrl } from "../health/checkFns";
|
||||
import { WrapperData, getWrapperData } from "./getWrapperData";
|
||||
import {
|
||||
CheckResult,
|
||||
checkPortListening,
|
||||
checkWebUrl,
|
||||
} from "../health/checkFns";
|
||||
import { LocalPort, NetworkBuilder, TorHostname } from "../mainFn";
|
||||
import { ExtractWrapperData } from "../types";
|
||||
|
||||
@@ -35,7 +39,50 @@ function withAffine<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),
|
||||
writeFile: <A>(fileHelper: FileHelper<A>, data: A) =>
|
||||
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 }),
|
||||
checkPortListening: checkPortListening.bind(null, effects),
|
||||
checkWebUrl: checkWebUrl.bind(null, effects),
|
||||
localPort: LocalPort.bind(null, effects),
|
||||
networkBuilder: NetworkBuilder.of.bind(null, effects),
|
||||
torHostName: TorHostname.of.bind(null, effects),
|
||||
localPort: (id: string) => new LocalPort(effects, id),
|
||||
networkBuilder: () => NetworkBuilder.of(effects),
|
||||
torHostName: (id: string) => TorHostname.of(effects, id),
|
||||
});
|
||||
|
||||
type NeverPossible = { [affine]: string };
|
||||
|
||||
Reference in New Issue
Block a user