This document lists exactly what the @c-a-f/core package exports. These are the domain-agnostic primitives and interfaces that form the CAF architecture.
| Export | Kind | Description |
|---|---|---|
UseCase |
Interface | Contract for application use cases |
Ploc |
Class | Presentation logic container with reactive state |
Pulse |
Class | Observable value container |
pulse |
Function | Factory for creating a Pulse instance |
ApiRequest |
Class | Wraps an async request with loading/data/error state |
RouteManager |
Class | Coordinates routing (depends on RouteRepository); optional auth guard via RouteManagerAuthOptions |
RouteManagerAuthOptions |
Interface | Optional auth config for login redirect (no browser APIs in core) |
RouteRepository |
Interface | Abstraction for the routing system |
RequestResult |
Type | Shape of loading/data/error for use cases |
IRequest |
Type | Promise-like type for async requests |
IApiClient |
Interface | Base API client interface for HTTP implementations |
ApiRequestConfig |
Interface | Configuration for API requests |
ApiResponse |
Interface | Standard API response wrapper |
ApiError |
Interface | Standard API error response |
HttpMethod |
Type | HTTP method types |
extractApiData |
Function | Helper to extract data from wrapped API responses |
normalizeApiError |
Function | Helper to normalize errors into ApiError format |
IRequestHandler |
Interface | Interface for request handler implementations (allows swapping real API, mocks, cached) |
PromiseRequestHandler |
Class | Adapter to convert Promise to IRequestHandler |
toRequestHandler |
Function | Helper to normalize IRequest or IRequestHandler to IRequestHandler |
Interface. Represents a single application use case (command or query).
interface UseCase<A extends any[], T> {
execute: (...args: A) => Promise<RequestResult<T>>;
}- Type parameters:
A= tuple of arguments,T= result data type. - Returns:
Promise<RequestResult<T>>(seeRequestResultbelow). - Implement this interface for each application operation (e.g.
LoginUser,GetUsers).
Abstract class. Holds presentation state and notifies subscribers when state changes. Built on Pulse (one reactive primitive). Use Ploc for a stateful bloc with structured state and logic (e.g. a screen or feature); use Pulse for a single reactive value.
abstract class Ploc<S> {
constructor(internalState: S);
get state(): S;
changeState(state: S): void;
subscribe(listener: (state: S) => void): void;
unsubscribe(listener: (state: S) => void): void;
}- Type parameter:
S= state type. - Usage: Extend
Ploc<S>in your UI layer; callchangeStatewhen use cases complete; subscribe from the view to re-render.
Class and factory. Single reactive value: holds a value and notifies listeners when it changes. Use Pulse for one reactive cell (e.g. loading flag, current user); use Ploc for a stateful bloc with structured state and logic.
class Pulse<T> {
// Proxied so that .value is readable/writable
value: T; // (via proxy)
subscribe(listener: (value: T) => void): void;
unsubscribe(listener: (value: T) => void): void;
}
function pulse<T>(initialValue: T): Pulse<T> & { value: T };- Type parameter:
T= value type. - Usage: Create with
pulse(initialValue). Read/write.value; subscribe to be notified on change. Used insideRequestResultandApiRequest.
Type. Describes the reactive state of an async operation (e.g. a use case).
type RequestResult<T> = {
loading: Pulse<boolean> & { value: boolean };
data: Pulse<T> & { value: T };
error: Pulse<Error> & { value: Error };
};- Type parameter:
T= success payload type. - Usage: Use cases return
Promise<RequestResult<T>>; the UI subscribes toloading,data, anderrorto show loading state, result, or error.
Type. Represents an async request (e.g. a repository call).
type IRequest<T> = Promise<T>;- Type parameter:
T= resolved value type. - Usage: Implemented by infrastructure (e.g. HTTP calls). Passed into
ApiRequestor used to buildRequestResultin use cases.
Class. Wraps an IRequest<T> or IRequestHandler<T> and exposes reactive loading, data, and error (same shape as RequestResult).
class ApiRequest<T> {
readonly loading: Pulse<boolean> & { value: boolean };
readonly data: Pulse<T> & { value: T };
readonly error: Pulse<Error> & { value: Error };
constructor(service: IRequest<T> | IRequestHandler<T>);
mutate(options?: { onSuccess: (data: T) => void }): Promise<{
loading: ...;
data: ...;
error: ...;
}>;
onSuccess(onSuccessFn: (data: T) => void): void;
}- Type parameter:
T= response data type. - Usage: Instantiate with a promise-returning service or
IRequestHandler; callmutate()to run the request and updateloading/data/error. Subscribe to these for reactive UI. Accepts bothIRequest<T>(Promise) andIRequestHandler<T>for flexibility, allowing swapping implementations (real API, mocks, cached) without changing core.
Interface. Abstraction for the routing system (framework-agnostic).
interface RouteRepository {
currentRoute: string;
change(route: string): void;
}- Usage: Implement in infrastructure (e.g. React Router, Vue Router). Injected into
RouteManager.
Interface. Optional auth configuration for route guards. Core does not use localStorage or any browser API; the caller (e.g. your app or infrastructure) provides isLoggedIn.
interface RouteManagerAuthOptions {
loginPath: string;
isLoggedIn: () => boolean;
}- Usage: Pass as the second argument to
RouteManagerwhen you wantcheckForLoginRoute()to redirect unauthenticated users. ImplementisLoggedInin your application code (e.g.() => !!localStorage.getItem('token')). See example apps for reference implementations.
Class. Coordinates navigation using a RouteRepository. Optionally accepts auth options for a login redirect guard.
class RouteManager {
constructor(
routingSystem: RouteRepository,
authOptions?: RouteManagerAuthOptions
);
checkForLoginRoute(): void; // no-op if authOptions not set; else redirects to loginPath when not logged in
isUserLoggedIn(): boolean; // returns authOptions?.isLoggedIn() ?? false
changeRoute(route: string): void;
}- Usage: Inject a
RouteRepositoryimplementation from framework-specific infrastructure packages (@c-a-f/infrastructure-react,@c-a-f/infrastructure-vue,@c-a-f/infrastructure-angular). Optionally passRouteManagerAuthOptionswith your application's login path and authentication check. Core remains free of browser/API specifics.
| Use Pulse when … | Use Ploc when … |
|---|---|
| You need a single reactive value (e.g. loading flag, current user, one piece of data). | You have a stateful bloc with structured state and logic (e.g. a screen or feature with multiple states like 'loading' | 'loaded' | 'error'). |
You are building RequestResult / ApiRequest (loading, data, error) or similar. |
You extend a class, add methods, and drive UI from state and changeState. |
Ploc is implemented on top of Pulse, so there is one reactive engine; the choice is API and intent.
- Core does not depend on any app domain, UI framework, or HTTP client.
- UseCase and RequestResult / IRequest are used by the application layer.
- Ploc and Pulse / pulse are used by the presentation layer.
- RouteRepository is implemented by infrastructure; RouteManager consumes it.
- ApiRequest bridges async services (IRequest) and reactive state (Pulse).
Consumers should import from the package root:
import {
UseCase,
Ploc,
Pulse,
pulse,
ApiRequest,
RouteManager,
RouteManagerAuthOptions,
RouteRepository,
RequestResult,
IRequest,
IApiClient,
ApiRequestConfig,
ApiResponse,
ApiError,
HttpMethod,
extractApiData,
normalizeApiError,
IRequestHandler,
PromiseRequestHandler,
toRequestHandler,
} from '@c-a-f/core';Core has no browser or API specifics; auth behavior is injected via RouteManagerAuthOptions.
Interfaces, types, and helpers. Provides framework-agnostic conventions for API requests and responses. Infrastructure implementations (e.g., Axios, Fetch) can use these types and helpers to standardize their API client implementations.
Interface. Base interface for API client implementations.
interface IApiClient {
request<T>(config: ApiRequestConfig): Promise<T>;
}- Type parameter:
T= response data type. - Usage: Infrastructure packages should implement this interface to provide a consistent API client abstraction.
Interface. Configuration for API requests.
interface ApiRequestConfig {
method: HttpMethod;
url: string;
data?: unknown;
headers?: Record<string, string>;
params?: Record<string, string | number | boolean>;
}- Usage: Pass to
IApiClient.request()to configure HTTP method, URL, body, headers, and query parameters.
Interface. Standard API response wrapper for APIs that wrap data in a response object.
interface ApiResponse<T> {
data: T;
message?: string;
status?: number;
success?: boolean;
}- Type parameter:
T= actual data type. - Usage: Use when your API wraps responses in a standard format. Use
extractApiData()helper to unwrap.
Interface. Standard API error response format.
interface ApiError {
message: string;
code?: string | number;
errors?: Record<string, string[]>;
status?: number;
}- Usage: Standard format for API errors. Use
normalizeApiError()helper to convert various error formats.
Type. HTTP method types.
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';- Usage: Specify HTTP method in
ApiRequestConfig.
Function. Helper to extract data from wrapped API responses.
function extractApiData<T>(response: T | ApiResponse<T>): T;- Type parameter:
T= data type. - Usage: Handles both direct data and
ApiResponse<T>formats. Returns the unwrapped data.
Function. Helper to normalize errors into ApiError format.
function normalizeApiError(error: unknown): ApiError;- Usage: Converts various error formats (Error objects, API error responses, strings) into a standardized
ApiErrorformat.
import { IApiClient, ApiRequestConfig, extractApiData, normalizeApiError } from '@c-a-f/core';
// Infrastructure implementation
class AxiosApiClient implements IApiClient {
constructor(private axios: AxiosInstance) {}
async request<T>(config: ApiRequestConfig): Promise<T> {
try {
const response = await this.axios.request({
method: config.method,
url: config.url,
data: config.data,
headers: config.headers,
params: config.params,
});
// Extract data if wrapped in ApiResponse format
return extractApiData<T>(response.data);
} catch (error) {
// Normalize error to ApiError format
throw normalizeApiError(error);
}
}
}Core has no browser or API specifics; auth behavior is injected via RouteManagerAuthOptions.
Interfaces and utility class. Provides framework-agnostic interfaces for managing workflows and state machines. Built on top of Ploc for reactive state management.
Interface. Base interface for workflow implementations.
interface IWorkflow {
getState(): WorkflowStateSnapshot;
dispatch(event: WorkflowEventId, payload?: unknown): Promise<boolean> | boolean;
canTransition(event: WorkflowEventId): boolean | Promise<boolean>;
reset(): void | Promise<void>;
updateContext(context: Partial<WorkflowContext>): void;
getDefinition(): WorkflowDefinition;
}- Usage: Implement to create workflow instances. Can be built on top of Ploc for reactive state management.
Interface. Definition of a workflow with states and transitions.
interface WorkflowDefinition {
id: string;
initialState: WorkflowStateId;
states: Record<WorkflowStateId, WorkflowState>;
}- Usage: Define your workflow structure with states, transitions, guards, and actions.
Interface. Definition of a workflow state.
interface WorkflowState {
id: WorkflowStateId;
label?: string;
transitions: Record<WorkflowEventId, WorkflowTransition>;
onEnter?: WorkflowAction;
onExit?: WorkflowAction;
}- Usage: Define states with transitions, enter/exit actions, and optional labels.
Interface. Definition of a workflow transition.
interface WorkflowTransition {
target: WorkflowStateId;
guard?: WorkflowGuard;
action?: WorkflowAction;
}- Usage: Define transitions with target state, optional guard (condition check), and optional action.
Interface. Current workflow state snapshot.
interface WorkflowStateSnapshot {
currentState: WorkflowStateId;
context: WorkflowContext;
isFinal: boolean;
}- Usage: Represents the current state of the workflow, including context data and whether it's in a final state.
Class. Workflow manager built on Ploc for reactive state management.
class WorkflowManager extends Ploc<WorkflowStateSnapshot> implements IWorkflow {
constructor(definition: WorkflowDefinition, initialContext?: WorkflowContext);
getState(): WorkflowStateSnapshot;
dispatch(event: WorkflowEventId, payload?: unknown): Promise<boolean>;
canTransition(event: WorkflowEventId): boolean;
reset(): Promise<void>;
updateContext(context: Partial<WorkflowContext>): void;
getDefinition(): WorkflowDefinition;
getCurrentStateDefinition(): WorkflowState | undefined;
getAvailableTransitions(): Record<WorkflowEventId, WorkflowTransition>;
}- Usage: Extends Ploc for reactive state management. Subscribers are notified when workflow state changes. Provides methods to dispatch events, check transitions, and manage workflow context.
import { WorkflowManager, WorkflowDefinition } from '@c-a-f/core';
// Define workflow
const orderWorkflow: WorkflowDefinition = {
id: 'order',
initialState: 'pending',
states: {
pending: {
id: 'pending',
label: 'Pending',
transitions: {
approve: {
target: 'approved',
guard: (context) => context.userRole === 'admin',
},
cancel: {
target: 'cancelled',
},
},
onEnter: async (context) => {
console.log('Order pending');
},
},
approved: {
id: 'approved',
label: 'Approved',
transitions: {
ship: {
target: 'shipped',
},
},
},
shipped: {
id: 'shipped',
label: 'Shipped',
transitions: {},
},
cancelled: {
id: 'cancelled',
label: 'Cancelled',
transitions: {},
},
},
};
// Create workflow manager
const workflow = new WorkflowManager(orderWorkflow, { userRole: 'admin' });
// Subscribe to state changes
workflow.subscribe((snapshot) => {
console.log('Current state:', snapshot.currentState);
console.log('Is final:', snapshot.isFinal);
});
// Dispatch events
await workflow.dispatch('approve');
await workflow.dispatch('ship');
// Check if transition is available
if (workflow.canTransition('approve')) {
await workflow.dispatch('approve');
}
// Update context
workflow.updateContext({ orderId: '12345' });
// Reset workflow
await workflow.reset();Core has no browser or API specifics; auth behavior is injected via RouteManagerAuthOptions.