diff --git a/lib/actions/createAction.ts b/lib/actions/createAction.ts new file mode 100644 index 0000000..6f96e53 --- /dev/null +++ b/lib/actions/createAction.ts @@ -0,0 +1,43 @@ +import { + ActionMetaData, + ActionResult, + Effects, + ExportedAction, +} from "../types"; +import { Utils, utils } from "../util"; + +export class CreatedAction { + private constructor( + readonly metaData: ActionMetaData, + readonly fn: (options: { + effects: Effects; + utils: Utils; + input: Input; + }) => Promise, + ) {} + + static of( + metaData: ActionMetaData, + fn: (options: { + effects: Effects; + utils: Utils; + input: Input; + }) => Promise, + ) { + return new CreatedAction(metaData, fn); + } + + exportedAction: ExportedAction = ({ effects, input }) => { + return this.fn({ + effects, + utils: utils(effects), + input: input as Input, + }); + }; + + async exportAction(effects: Effects) { + await effects.exportAction(this.metaData); + } +} + +export const createAction = CreatedAction.of; diff --git a/lib/actions/index.ts b/lib/actions/index.ts new file mode 100644 index 0000000..f2ce18d --- /dev/null +++ b/lib/actions/index.ts @@ -0,0 +1,3 @@ +export { CreatedAction, createAction } from "./createAction"; + +export { setupActions } from "./setupActions"; diff --git a/lib/actions/setupActions.ts b/lib/actions/setupActions.ts new file mode 100644 index 0000000..3ee7eb1 --- /dev/null +++ b/lib/actions/setupActions.ts @@ -0,0 +1,20 @@ +import { Effects, ExpectedExports, ExportedAction } from "../types"; +import { ActionMetaData } from "../types"; +import { CreatedAction } from "./createAction"; + +export function setupActions(...createdActions: CreatedAction[]) { + const actions: Record = {}; + 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, + }; +} diff --git a/lib/config/index.ts b/lib/config/index.ts index 5aa1a25..2d7184e 100644 --- a/lib/config/index.ts +++ b/lib/config/index.ts @@ -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"; diff --git a/lib/config/setupConfigExports.ts b/lib/config/setupConfig.ts similarity index 94% rename from lib/config/setupConfigExports.ts rename to lib/config/setupConfig.ts index 5039aed..74c466e 100644 --- a/lib/config/setupConfigExports.ts +++ b/lib/config/setupConfig.ts @@ -22,7 +22,7 @@ export type DependenciesFn = (options: { * @param options * @returns */ -export function setupConfigExports( +export function setupConfig( spec: Config, write: Write, read: Read, @@ -51,4 +51,4 @@ export function setupConfigExports( }; } -export default setupConfigExports; +export default setupConfig; diff --git a/lib/index.ts b/lib/index.ts index 175083b..a330113 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -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"; diff --git a/lib/types.ts b/lib/types.ts index f856e16..34fbe7e 100644 --- a/lib/types.ts +++ b/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; +}) => Promise; + 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; - }) => Promise; + export type actions = { + [id: string]: ExportedAction; }; /** @@ -145,6 +147,18 @@ export type DaemonReturned = { term(): Promise; }; +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; + exportAction(options: ActionMetaData): Promise; /** * 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; diff --git a/lib/util/index.ts b/lib/util/index.ts index b4279dc..2754e2e 100644 --- a/lib/util/index.ts +++ b/lib/util/index.ts @@ -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() { return {} as { [affine]: B }; } -export const utils = (effects: T.Effects) => ({ +export type WrapperDataOptionals = { + validator?: Parser>; + /** Defaults to what ever the package currently in */ + packageId?: string | undefined; +}; + +export type Utils = { + readFile: (fileHelper: FileHelper) => ReturnType["read"]>; + writeFile: ( + fileHelper: FileHelper, + data: A, + ) => ReturnType["write"]>; + getWrapperData: ( + path: T.EnsureWrapperDataPath, + options?: WrapperDataOptionals, + ) => WrapperData; + setWrapperData: ( + path: T.EnsureWrapperDataPath, + value: ExtractWrapperData, + ) => Promise; + checkPortListening( + port: number, + options?: { + error?: string; + message?: string; + }, + ): Promise; + checkWebUrl( + url: string, + options?: { + timeout?: number; + successMessage?: string; + errorMessage?: string; + }, + ): Promise; + localPort: (id: string) => LocalPort; + networkBuilder: () => NetworkBuilder; + torHostName: (id: string) => TorHostname; + exists: (props: { path: string; volumeId: string }) => Promise; + nullIfEmpty: typeof nullIfEmpty; +}; +export const utils = ( + effects: T.Effects, +): Utils => ({ readFile: (fileHelper: FileHelper) => fileHelper.read(effects), writeFile: (fileHelper: FileHelper, data: A) => fileHelper.write(data, effects), @@ -55,9 +102,9 @@ export const utils = (effects: T.Effects) => ({ ) => effects.setWrapperData({ 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 };