/**
* vapor-chamber — IIFE variant: CORE
*
* Audience: server-rendered apps with sprinkled JS — Blade, Rails, Django,
* .NET MVC, WordPress. The page already returns HTML; you need a command bus
* to dispatch user actions to the backend (cart add, form submit, analytics).
*
* Surface:
* • Command bus (sync + async)
* • HTTP transport only (createHttpBridge)
* • Lightweight plugins: logger, validator, debounce, throttle, retry, authGuard
* • createApp() for one-line setup
* • connect() — even shorter: HTTP + CSRF in a single call
*
* NOT in this variant (use `vapor-chamber.iife.js` if you need them):
* • WebSocket / SSE transports — realtime infra is a different deployment shape
* • persist / sync / history / optimistic — stateful plugins, niche for sprinkled JS
* • mount() — DOM-coupled convenience
* • Vapor custom elements — see `vapor-chamber-elements.iife.js`
*
* Variant contents are not stable across major versions until v2.0; see ROADMAP.md.
*/
import {
createCommandBus,
createAsyncCommandBus,
type CommandBusOptions,
} from './command-bus';
import {
logger,
validator,
debounce,
throttle,
authGuard,
retry,
} from './plugins';
import { createHttpBridge, type HttpBridgeOptions } from './transports';
import type { AsyncPlugin, Plugin } from './command-bus';
export type CreateAppOptions = {
transport?: AsyncPlugin;
plugins?: Plugin[];
onMissing?: CommandBusOptions['onMissing'];
};
function createApp(options: CreateAppOptions = {}) {
const bus = createAsyncCommandBus({ onMissing: options.onMissing ?? 'error' });
if (options.plugins) for (const p of options.plugins) bus.use(p as AsyncPlugin);
if (options.transport) bus.use(options.transport);
return { bus, dispatch: bus.dispatch.bind(bus) };
}
/**
* connect — one-line setup for the sprinkled-JS audience.
*
* Equivalent to:
* createApp({ transport: createHttpBridge({ endpoint, csrf: true, ...rest }) })
*
* @example
*
*/
function connect(options: HttpBridgeOptions & { plugins?: Plugin[]; onMissing?: CommandBusOptions['onMissing'] }) {
const { plugins, onMissing, ...httpOptions } = options;
return createApp({
transport: createHttpBridge({ csrf: true, ...httpOptions }),
plugins,
onMissing,
});
}
const VaporChamber = {
// Bus
createCommandBus,
createAsyncCommandBus,
// Convenience
createApp,
connect,
// Transport (HTTP only in core)
http: createHttpBridge,
// Plugins (lightweight only in core)
logger,
validator,
debounce,
throttle,
authGuard,
retry,
} as const;
if (typeof globalThis !== 'undefined') {
(globalThis as any).VaporChamber = VaporChamber;
}
// Default export only: the IIFE build assigns the DEFAULT export to the
// `VaporChamber` global, so the API object lands directly on window
// (`VaporChamber.connect(...)`). A second named export would force the
// bundler to emit a module-namespace wrapper — `{ VaporChamber, default }`
// — and every documented call site would be undefined in a