UNPKG

@kellanjs/actioncraft

Version:

Fluent, type-safe builder for Next.js server actions.

104 lines 3.92 kB
import { ActioncraftError } from "./classes/error.js"; import { EXTERNAL_ERROR_TYPES } from "./types/errors.js"; import { err } from "./types/result.js"; export function unwrap(resultOrPromise) { // Handle Promise case if (resultOrPromise instanceof Promise) { return resultOrPromise.then((result) => _unwrapSync(result)); } // Handle direct result case return _unwrapSync(resultOrPromise); } /** * Synchronously unwraps a result, throwing on error with embedded action ID. */ function _unwrapSync(result) { // Extract action ID from result if present const actionId = "__ac_id" in result ? result.__ac_id : undefined; // Handle api-style results ({ success: true/false }) - includes both ApiResult and StatefulApiResult if (typeof result === "object" && result !== null && "success" in result) { const apiResult = result; if (apiResult.success) { return apiResult.data; } throw new ActioncraftError(apiResult.error, actionId); } // Handle functional-style results ({ type: "ok"/"err" }) if (typeof result === "object" && result !== null && "type" in result) { const functionalResult = result; if (functionalResult.type === "ok") { return functionalResult.value; } throw new ActioncraftError(functionalResult.error, actionId); } throw new Error("Invalid result format from Actioncraft action"); } /** * Creates a throwable version of an Actioncraft action. * The returned function throws ActioncraftErrors with automatic action ID verification support. * Errors thrown by this function can be verified with isActioncraftError(error, action). */ export function throwable(action) { // eslint-disable-next-line @typescript-eslint/no-explicit-any return (async (...args) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const result = await action(...args); return unwrap(result); }); } /** * Creates an appropriate initial state for any action based on its configuration. * The initial state uses the action's real ID for consistency with actual results. * * For useActionState actions: returns StatefulApiResult with error and values * For functional format actions: returns Result.err() with error * For regular actions: returns ApiResult with error * * Usage: * - useActionState: const [state, action] = useActionState(myAction, initial(myAction)) * - useState: const [state, setState] = useState(initial(myAction)) */ export function initial(action) { const error = { type: EXTERNAL_ERROR_TYPES.INITIAL_STATE, message: "Action has not been executed yet", }; // Attempt to read the action ID created during craft() const actionId = // eslint-disable-next-line @typescript-eslint/no-explicit-any action?.__ac_id ?? "unknown"; // Attempt to read the Actioncraft config attached during craft() // eslint-disable-next-line @typescript-eslint/no-explicit-any const cfg = action?.__ac_config; // Functional format -> Result<_, _> if (cfg?.resultFormat === "functional") { return err(error, actionId); } // useActionState enabled -> StatefulApiResult if (cfg?.useActionState) { return { success: false, error, values: undefined, __ac_id: actionId, }; } // Default ApiResult shape return { success: false, error, __ac_id: actionId, }; } /** * Utility to extract the action ID from a crafted action. * Useful for debugging and logging purposes. * * @param action - The crafted action * @returns The action ID if available, undefined otherwise */ export function getActionId(action) { // eslint-disable-next-line @typescript-eslint/no-explicit-any return action.__ac_id; } //# sourceMappingURL=utils.js.map