/** * vapor-chamber — I/O plugins (async/storage/network) * * retry, persist, sync */ import { type Command, type AsyncPlugin, type Plugin } from './command-bus'; export type RetryOptions = { /** Maximum number of attempts (including the first). Default: 3 */ maxAttempts?: number; /** Base delay in ms between retries. Default: 200 */ baseDelay?: number; /** * Backoff strategy: * - 'fixed' — always wait baseDelay ms * - 'linear' — baseDelay * attempt * - 'exponential' — baseDelay * 2^(attempt-1) * Default: 'exponential' */ strategy?: 'fixed' | 'linear' | 'exponential'; /** * Which actions to retry. Glob patterns supported: '*', 'cart*'. * Default: all actions. */ actions?: string[]; /** * Return true if the error is retryable. * * Default: BusErrors (a `.code` starting with 'VC_') are retried only when * the code is transient per RETRYABLE_CODES (throttled, rate-limited, * timeout, circuit-open, ...) — known-permanent codes (validation, sealed * bus, max depth, ...) stop retrying immediately instead of wasting * attempts. All other errors are always retried. (Before v1.3 the default * retried everything; behavior for plain Errors is unchanged.) */ isRetryable?: (error: Error, attempt: number) => boolean; }; /** * retry — async plugin that retries failed dispatches with configurable backoff. * * By default, permanent BusError codes (e.g. validation failures) are not * retried — see RetryOptions.isRetryable to customize. * * @example * const bus = createAsyncCommandBus() * bus.use(retry({ maxAttempts: 3, strategy: 'exponential', baseDelay: 200 })) */ export declare function retry(options?: RetryOptions): AsyncPlugin; export type PersistOptions = { /** * Storage key. Use a unique prefix per feature to avoid collisions. * @example 'vc:cart', 'vc:user-prefs' */ key: string; /** Function that returns the current state to be saved after each command. */ getState: () => T; /** Serializer. Default: JSON.stringify */ serialize?: (state: T) => string; /** * Deserializer. Return null/undefined to skip rehydration. * Default: JSON.parse */ deserialize?: (raw: string) => T | null; /** * Validate deserialized state before returning from load(). * Return true to accept, false to reject (load() returns null). * Use this to reject stale or structurally invalid persisted state * after deploys that change the shape of persisted data. * * @example * persist({ * key: 'vc:cart', * getState: () => cart.value, * validate: (state) => Array.isArray(state.items) && typeof state.total === 'number', * }) */ validate?: (state: T) => boolean; /** Which actions trigger a save. Default: all successful dispatches. */ filter?: (cmd: Command) => boolean; /** * Storage backend. Default: globalThis.localStorage * Pass `sessionStorage` for session-scoped persistence. */ storage?: Pick; /** * When true, collapse back-to-back saves within the same microtask into one. * Trades 1 microtask of latency for one `getState()` + `JSON.stringify()` + * `setItem()` cycle per burst, regardless of how many dispatches triggered it. * * Use when the same state is touched by many rapid commands (form input, * scroll tracking, batched cart updates). Default: false (every successful * dispatch saves immediately, matching pre-v1.2 behavior). * * @example * persist({ key: 'vc:cart', getState: () => cart.value, coalesce: true }) */ coalesce?: boolean; }; /** * persist — auto-save state to localStorage (or custom storage) after each command. * * @example * const cartPersist = persist({ key: 'vc:cart', getState: () => cartState.value }) * bus.use(cartPersist) * const saved = cartPersist.load() */ export declare function persist(options: PersistOptions): Plugin & { load(): T | null; save(): void; clear(): void; }; export type SyncOptions = { /** * BroadcastChannel name. All tabs using the same name receive each other's commands. * @example 'vapor-chamber:app' */ channel: string; /** Which actions to broadcast to other tabs. Default: all successful dispatches. */ filter?: (cmd: Command) => boolean; /** * Called when a command arrives from another tab, before re-dispatching it. * Return false to suppress re-dispatch. */ onReceive?: (cmd: Command) => boolean | void; }; /** * sync — broadcast successful commands to all other open tabs via BroadcastChannel. * * @example * const tabSync = sync({ channel: 'vapor-chamber:app' }) * bus.use(tabSync) * tabSync.close() // on teardown */ export declare function sync(options: SyncOptions, busRef?: { dispatch: (action: string, target: any, payload?: any) => any; }): Plugin & { close(): void; isOpen(): boolean; }; //# sourceMappingURL=plugins-io.d.ts.map