/** * vapor-chamber - Transport plugins * * v0.4.2 — Added: createHttpBridge, createWsBridge, createSseBridge. * v1.9.0 (unreleased) — Added: createBatchingHttpBridge. * * Transports are AsyncPlugin factories that forward commands to a backend. * Use with createAsyncCommandBus() for full async dispatch support. */ import type { Command, CommandResult, AsyncPlugin, BaseBus } from './command-bus'; import { matchesPattern, abortedResult, } from './command-bus'; import { postCommand } from './http'; import type { HttpClient } from './http'; import { signal } from './signal'; import type { Signal } from './signal'; // --------------------------------------------------------------------------- // Shared protocol types // --------------------------------------------------------------------------- /** The JSON shape sent to the backend endpoint */ export type CommandEnvelope = { command: string; target: any; payload?: any; }; /** The JSON shape expected from the backend */ export type BackendResponse = { ok?: boolean; state?: any; error?: string; }; // --------------------------------------------------------------------------- // createHttpBridge // --------------------------------------------------------------------------- export type HttpBridgeOptions = { /** Backend endpoint URL (e.g. '/api/vc') */ endpoint: string; /** * CSRF token strategy: * • `false` (default) — don't attach any CSRF token * • `true` — read from DOM (meta tag, cookie, hidden input) and attach * as the appropriate header. Works with any server-rendered framework * that exposes a token via one of those three sources — Laravel Blade, * Rails, Django, .NET MVC, custom stacks. Auto-refreshes on HTTP 419. * • `'inertia'` — defer token management to Inertia's Axios instance. * The bridge will skip its own CSRF reading and rely on the consumer's * `@inertiajs/inertia` axios setup to inject the token. Use this when * vapor-chamber dispatches share an HTTP layer with Inertia routes. */ csrf?: boolean | 'inertia'; /** * URL to fetch on a CSRF-expiry response (HTTP 419) to obtain a fresh * token. The default targets the Laravel Sanctum SPA convention because * it's the most common 419-issuing backend; override for other frameworks * or set to '' to disable the refresh fetch. * Default: '/sanctum/csrf-cookie'. */ csrfCookieUrl?: string; /** Additional headers merged into every request */ headers?: Record; /** Request timeout in ms. Default: 10_000 */ timeout?: number; /** Max retry attempts on 5xx / 429 / 408. Default: 0 */ retry?: number; /** * Actions that must never be retried regardless of the `retry` setting. * Use for payment and other non-idempotent commands to prevent double execution. * @example noRetry: ['paymentCharge', 'orderPlace'] */ noRetry?: string[]; /** External AbortSignal (e.g. tied to component lifecycle) */ signal?: AbortSignal; /** * Called when a 401 session-expired response is received. * A `session-expired` CustomEvent is also dispatched on `window`. */ onSessionExpired?: (status: number) => void; /** * Called when a backend response indicates a redirect (3xx response with * `Location` header, OR a `{ redirect: '/path' }` field in the JSON body). * Useful for handing 302s to Inertia's router so vapor-chamber dispatches * can trigger page navigations: * * onRedirect: (url) => router.visit(url) * * If not set, redirects are surfaced as `{ ok: false, error: 'Redirect to /path' }`. */ onRedirect?: (url: string) => void; /** * Which actions to forward. Glob patterns supported: '*', 'cart*'. * Default: all actions. */ actions?: string[]; /** * Abort controller whose signal cancels all in-flight requests when the * owning scope/component is disposed. In Vapor components, create an * AbortController in setup and pass it here — call `.abort()` in * onScopeDispose to cancel orphaned requests automatically. * * @example * // In