/** * vapor-chamber — IIFE variant: ELEMENTS * * Audience: widget builders. Embeddable chat bubbles, checkout buttons, * support pop-ups, third-party drop-ins. You ship a ` * * */ function defineWidget(tagName: string, options: any, extraOptions?: any): boolean { const El = extraOptions !== undefined ? defineVaporCustomElement(options, extraOptions) : defineVaporCustomElement(options); if (!El) return false; if (typeof customElements === 'undefined') return false; if (customElements.get(tagName)) return true; customElements.define(tagName, El); return true; } /** * Emit a real DOM `CustomEvent` from inside a widget so host pages can * subscribe with `addEventListener`. * * Vue's component `emit(...)` goes through Vue's event system — it does * NOT bubble out as a DOM event. For widgets that need to communicate with * the surrounding page (a `` notifying its container that * a product was added), use this to dispatch an actual `CustomEvent`. * * Pattern adapted from * [vue-custom-element](https://github.com/karol-f/vue-custom-element)'s * `customEmit` helper (Karol-F, MIT) — original predates Vue 3.6's Vapor * but the underlying gap (Vue emit ≠ DOM event) still exists today. * * @example Widget side * VaporChamber.defineWidget('vc-cart', { * setup() { * return () => h('button', { * onClick: (e) => emitDOMEvent(e.target.getRootNode().host, 'cart-added', { sku: 'X' }) * }, 'Add'); * } * }); * * @example Host page * document.querySelector('vc-cart').addEventListener('cart-added', (e) => { * console.log(e.detail.sku); // 'X' * }); * * @param el — the custom element instance (host element) * @param eventName — DOM event name (kebab-case is conventional for custom events) * @param detail — payload attached to event.detail * @param options — bubbles/composed/cancelable. Defaults: bubbles=true, composed=true * (composed=true escapes shadow-DOM boundaries by default — required * for events to reach light-DOM listeners on the host page). * @returns `false` if `event.preventDefault()` was called by a listener, `true` otherwise. */ function emitDOMEvent( el: Element, eventName: string, detail?: any, options: { bubbles?: boolean; composed?: boolean; cancelable?: boolean } = {}, ): boolean { if (typeof CustomEvent !== 'function' || !el) return true; const event = new CustomEvent(eventName, { detail, bubbles: options.bubbles ?? true, composed: options.composed ?? true, cancelable: options.cancelable ?? false, }); return el.dispatchEvent(event); } const VaporChamber = { // Bus createCommandBus, createAsyncCommandBus, // Convenience createApp, connect, // Transport (HTTP only) http: createHttpBridge, // Plugins (lightweight) logger, validator, debounce, throttle, authGuard, retry, // Widget surface (the variant's identity) defineVaporCustomElement, defineWidget, emitDOMEvent, } 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