/** * Generic flow-config factory. * * Provides a reusable, type-safe `createActionConfig` helper for any entity * flow configuration. Eliminates boilerplate type plumbing (ActionStatusMap, * StateStatusForAction, createTransition) that would otherwise be duplicated * in every flow config file. * * --- * **Usage — basic** * ```typescript * import { createFlowConfigFactory } from "./base-flow-config.factory"; * * const ACTION_MAP = { * [MyActionEnum.APPROVE]: { statuses: [MyStatusEnum.APPROVED] as const, permissions: ["MY_APPROVE"] }, * [MyActionEnum.REJECT]: { statuses: [MyStatusEnum.REJECTED] as const, permissions: ["MY_REJECT"] }, * } satisfies ActionMap; * * const { createActionConfig } = createFlowConfigFactory()(ACTION_MAP); * * const transition = createActionConfig(MyStatusEnum.APPROVED, MyActionEnum.APPROVE); * ``` * * --- * **Usage — overriding a transition permission** * ```typescript * createActionConfig(MyStatusEnum.APPROVED, MyActionEnum.APPROVE, ["CUSTOM_APPROVE_PERM"]); * ``` * * --- * **Usage — context-driven (multi-outcome) transition** * ```typescript * createActionConfig( * (ctx: IMyContextData) => ctx.fast ? MyStatusEnum.APPROVED : MyStatusEnum.PENDING, * MyActionEnum.APPROVE, * ); * ``` * * --- * **Usage — subset narrowing** * Restrict the compiler to a subset of the action's possible target statuses: * ```typescript * createActionConfig( * MyStatusEnum.APPROVED, * MyActionEnum.APPROVE, * ); * ``` * * --- * **Extending base enums** * TypeScript enums cannot be inherited, but entity-specific enums can * re-declare base values and add new ones: * ```typescript * import { BaseFlowActionEnum, BaseFlowStatusEnum } from "./base-flow-config.enums"; * * export enum VendorActionEnum { * VIEW = BaseFlowActionEnum.VIEW, * CREATE = BaseFlowActionEnum.CREATE, * UPDATE = BaseFlowActionEnum.UPDATE, * DELETE = BaseFlowActionEnum.DELETE, * APPROVE = BaseFlowActionEnum.APPROVE, * REJECT = BaseFlowActionEnum.REJECT, * RECALL = BaseFlowActionEnum.RECALL, * DEACTIVATE = BaseFlowActionEnum.DEACTIVATE, * SUBMIT = "submit", // ← extension * } * * export enum VendorStatusEnum { * ACTIVE = BaseFlowStatusEnum.ACTIVE, * // ... all base statuses ... * SUBMITTED = "SUBMITTED", // ← extension * } * ``` */ /** * Shape of a single entry in the action map. * `statuses` must be a `const` tuple so TypeScript preserves the exact union * of reachable statuses for subset-narrowing in `createActionConfig`. */ export type ActionMapEntry = { readonly reachableStatues: readonly S[]; readonly permissions: string[]; }; /** * Full action map: one entry per action value. * Use `satisfies ActionMap` on your map literal * so that the compiler catches missing/misspelled keys while still preserving * the precise `as const` tuple types for subset-narrowing. * * @example * const ACTION_MAP = { * [MyActionEnum.APPROVE]: { statuses: [MyStatusEnum.APPROVED] as const, permissions: ["MY_APPROVE"] }, * } satisfies ActionMap; */ export type ActionMap = { readonly [key in A]: ActionMapEntry; }; /** * Curried factory — call with the context-data type first, then pass the * action map. This two-step pattern lets TypeScript infer the precise action * map type (preserving `as const` tuple narrowing) while still allowing the * caller to set the context-data type explicitly. * * @template CTX - Shape of the context object passed to function-based * transition resolvers. Use `never` for flows where `next()` * needs no runtime data (equivalent to `() => status`). * * @returns A function that accepts an `ActionMap` and returns `createActionConfig`. */ /** * Optional per-transition extras. Both are spread onto the action config only when supplied, so a * call site that does not pass them produces exactly the object it produced before this argument * existed — the sixteen already-converted flow configs are byte-identical. * * @property isApplicable runtime guard evaluated against the same context as `next()` * @property description human-readable copy for this transition. Already part of * `IFlowConfigActionConfig` and rendered by the entity-flow * component, so a converted config must be able to emit it. */ export type ActionConfigOptions = { isApplicable?: (context: CTX) => boolean; description?: string; }; export declare function createFlowConfigFactory(): >(actionMap: AM) => { createActionConfig: (action: Act, nextStatus: Subset | ((context: CTX) => Subset), overridePermissions?: string[], options?: ActionConfigOptions) => { description?: string | undefined; isApplicable?: ((context: CTX) => boolean) | undefined; permissions: string[]; next: (context: CTX) => Subset; }; createTransition: (transition: Subset_1 | ((context: CTX) => Subset_1)) => (context: CTX) => Subset_1; };