{"version":3,"file":"ewidget-utils.umd.cjs","names":[],"sources":["../src/AlleoMic.ts","../src/EmbedWidgetMessenger.ts","../src/AlleoEwidgetUtils.ts"],"sourcesContent":["import type { EmbedWidgetMessenger, EmbedWidgetMessageEvent } from './EmbedWidgetMessenger.js'\r\n\r\n/** Maximum allowed audio chunk duration in milliseconds. */\r\nexport const MIC_MAX_TIMESLICE_MS = 200\r\n\r\n/** Default chunk duration if the caller does not provide one. */\r\nexport const MIC_DEFAULT_TIMESLICE_MS = 100\r\n\r\nexport type MicStartOptions = {\r\n    /**\r\n     * Requested audio chunk size in milliseconds.\r\n     * Clamped to the range `[20, 200]`. Defaults to `100`.\r\n     */\r\n    timeslice?: number\r\n    /**\r\n     * Preferred container/codec mime type for the audio chunks\r\n     * (e.g. `'audio/webm;codecs=opus'`, `'audio/mp4'`). The parent widget\r\n     * may fall back to its supported default.\r\n     */\r\n    mimeType?: string\r\n    /** Preferred sample rate in Hz (e.g. `48000`, `16000`). The parent may ignore this. */\r\n    sampleRate?: number\r\n    /** Preferred audio bitrate in bits per second. The parent may ignore this. */\r\n    audioBitsPerSecond?: number\r\n}\r\n\r\nexport type MicStartedPayload = {\r\n    mimeType: string\r\n    sampleRate?: number\r\n    timeslice: number\r\n}\r\n\r\nexport type MicErrorPayload = {\r\n    error: string\r\n}\r\n\r\ntype IncomingChunk = {\r\n    data: ArrayBuffer\r\n    mimeType: string\r\n}\r\n\r\ntype CaptureCapableMediaElement = HTMLMediaElement & {\r\n    captureStream?: () => MediaStream\r\n    mozCaptureStream?: () => MediaStream\r\n}\r\n\r\n/**\r\n * `alleo.mic` — microphone access bridge.\r\n *\r\n * The iframe is sandboxed and cannot call `navigator.mediaDevices.getUserMedia()`\r\n * itself. This class delegates capture to the parent Alleo widget over\r\n * `postMessage`. The parent records the microphone, slices the stream into\r\n * short audio chunks (≤ 200 ms each) and forwards them back to the iframe.\r\n *\r\n * The chunks are reassembled into a live `MediaStream` using a hidden\r\n * `<audio>` element + `MediaSource` + `HTMLMediaElement.captureStream()`.\r\n * Use {@link onTrack} to receive that stream:\r\n *\r\n * ```js\r\n * alleo.mic.onTrack((stream) => {\r\n *     const audio = new Audio()\r\n *     audio.srcObject = stream\r\n *     audio.play()\r\n * })\r\n * alleo.mic.start()\r\n * ```\r\n */\r\nexport class AlleoMic {\r\n    private readonly messenger: EmbedWidgetMessenger\r\n    private readonly unsubscribe: () => void\r\n    private readonly trackHandlers = new Set<(stream: MediaStream) => void>()\r\n    private readonly startedHandlers = new Set<(payload: MicStartedPayload) => void>()\r\n    private readonly stoppedHandlers = new Set<() => void>()\r\n    private readonly errorHandlers = new Set<(payload: MicErrorPayload) => void>()\r\n    private active = false\r\n\r\n    // Pipeline state\r\n    private currentStream: MediaStream | null = null\r\n    private mediaSource: MediaSource | null = null\r\n    private sourceBuffer: SourceBuffer | null = null\r\n    private hiddenAudio: CaptureCapableMediaElement | null = null\r\n    private pendingChunks: ArrayBuffer[] = []\r\n    /**\r\n     * Monotonic id for the currently active capture pipeline. Incremented on\r\n     * every {@link teardownPipeline} so that stale event handlers from the\r\n     * previous pipeline (e.g. a late `updateend` on a SourceBuffer that has\r\n     * since been detached) can detect they no longer belong to the active\r\n     * session and bail out instead of mutating the new pipeline's state.\r\n     */\r\n    private pipelineSession = 0\r\n    private sourceBufferUpdateEnd: (() => void) | null = null\r\n\r\n    public constructor(messenger: EmbedWidgetMessenger) {\r\n        this.messenger = messenger\r\n        this.unsubscribe = messenger.onMessage(this.handleMessage)\r\n    }\r\n\r\n    /** Whether the parent has confirmed that the mic is currently capturing. */\r\n    public get isActive(): boolean {\r\n        return this.active\r\n    }\r\n\r\n    /**\r\n     * Asks the parent to start capturing the microphone.\r\n     *\r\n     * The parent may prompt the user for permission. A live `MediaStream` will\r\n     * be delivered to handlers registered via {@link onTrack}. Errors\r\n     * (including permission denial) are reported via {@link onError}.\r\n     */\r\n    public start(options?: MicStartOptions): void {\r\n        const timeslice = clampTimeslice(options?.timeslice ?? MIC_DEFAULT_TIMESLICE_MS)\r\n        this.messenger.sendMicCommand('micStart', {\r\n            timeslice,\r\n            mimeType: options?.mimeType,\r\n            sampleRate: options?.sampleRate,\r\n            audioBitsPerSecond: options?.audioBitsPerSecond,\r\n        })\r\n    }\r\n\r\n    /** Asks the parent to stop capturing the microphone. */\r\n    public stop(): void {\r\n        this.messenger.sendMicCommand('micStop', {})\r\n    }\r\n\r\n    /**\r\n     * Registers a callback that receives a live `MediaStream` whenever the\r\n     * microphone starts capturing.\r\n     *\r\n     * The callback is invoked once per capture session, as soon as the first\r\n     * audio chunk arrives and the playback pipeline is ready. Assign the\r\n     * returned stream to e.g. `audio.srcObject` to play it back, or pass it\r\n     * to `RTCPeerConnection.addTrack()`, `MediaRecorder`, an `AudioContext`,\r\n     * etc.\r\n     *\r\n     * If a stream is already live when the handler is registered, it is\r\n     * invoked immediately with the existing stream.\r\n     *\r\n     * @returns An unsubscribe function.\r\n     */\r\n    public onTrack(handler: (stream: MediaStream) => void): () => void {\r\n        this.trackHandlers.add(handler)\r\n        if (this.currentStream) {\r\n            try {\r\n                handler(this.currentStream)\r\n            } catch {\r\n                /* ignore */\r\n            }\r\n        }\r\n        return () => this.trackHandlers.delete(handler)\r\n    }\r\n\r\n    /** Registers a callback fired when the parent confirms capture has started. */\r\n    public onStarted(handler: (payload: MicStartedPayload) => void): () => void {\r\n        this.startedHandlers.add(handler)\r\n        return () => this.startedHandlers.delete(handler)\r\n    }\r\n\r\n    /** Registers a callback fired when the parent confirms capture has stopped. */\r\n    public onStopped(handler: () => void): () => void {\r\n        this.stoppedHandlers.add(handler)\r\n        return () => this.stoppedHandlers.delete(handler)\r\n    }\r\n\r\n    /** Registers a callback fired when the parent reports a mic-related error. */\r\n    public onError(handler: (payload: MicErrorPayload) => void): () => void {\r\n        this.errorHandlers.add(handler)\r\n        return () => this.errorHandlers.delete(handler)\r\n    }\r\n\r\n    /** Releases all listeners. The parent is also asked to stop capturing. */\r\n    public destroy(): void {\r\n        try {\r\n            this.stop()\r\n        } catch {\r\n            /* ignore */\r\n        }\r\n        this.teardownPipeline()\r\n        this.unsubscribe()\r\n        this.trackHandlers.clear()\r\n        this.startedHandlers.clear()\r\n        this.stoppedHandlers.clear()\r\n        this.errorHandlers.clear()\r\n        this.active = false\r\n    }\r\n\r\n    private readonly handleMessage = (event: EmbedWidgetMessageEvent): void => {\r\n        switch (event.command) {\r\n            case 'micStarted': {\r\n                const params = event.params as MicStartedPayload\r\n                this.active = true\r\n                for (const h of this.startedHandlers) h(params)\r\n                return\r\n            }\r\n            case 'micStopped': {\r\n                this.active = false\r\n                this.endPipeline()\r\n                for (const h of this.stoppedHandlers) h()\r\n                return\r\n            }\r\n            case 'micChunk': {\r\n                const chunk = normalizeChunk(event.params as Record<string, unknown>)\r\n                if (!chunk) return\r\n                this.handleChunk(chunk)\r\n                return\r\n            }\r\n            case 'micError': {\r\n                const params = event.params as MicErrorPayload\r\n                this.teardownPipeline()\r\n                for (const h of this.errorHandlers) h(params)\r\n                return\r\n            }\r\n            default:\r\n                return\r\n        }\r\n    }\r\n\r\n    private handleChunk(chunk: IncomingChunk): void {\r\n        if (!this.mediaSource) {\r\n            this.setupPipeline(chunk.mimeType)\r\n            if (!this.mediaSource) return // setup failed and reported the error\r\n        }\r\n        this.pendingChunks.push(chunk.data)\r\n        this.flushPendingChunks()\r\n    }\r\n\r\n    private setupPipeline(mimeType: string): void {\r\n        if (typeof window === 'undefined' || typeof MediaSource === 'undefined') {\r\n            this.emitInternalError('MediaSource API is not available in this environment')\r\n            return\r\n        }\r\n\r\n        let resolvedMime = mimeType\r\n        if (!MediaSource.isTypeSupported(resolvedMime)) {\r\n            // Fallback: try common defaults — many browsers accept slightly\r\n            // different mime strings than what MediaRecorder reports.\r\n            const fallbacks = ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4;codecs=mp4a.40.2']\r\n            const supported = fallbacks.find((t) => MediaSource.isTypeSupported(t))\r\n            if (!supported) {\r\n                this.emitInternalError(`No supported MediaSource type for \"${mimeType}\"`)\r\n                return\r\n            }\r\n            resolvedMime = supported\r\n        }\r\n\r\n        const session = ++this.pipelineSession\r\n        const mediaSource = new MediaSource()\r\n        this.mediaSource = mediaSource\r\n\r\n        const audio = document.createElement('audio') as CaptureCapableMediaElement\r\n        audio.muted = true\r\n        audio.autoplay = true\r\n        // Keep it out of layout but still allowed to play.\r\n        audio.style.position = 'fixed'\r\n        audio.style.width = '0'\r\n        audio.style.height = '0'\r\n        audio.style.opacity = '0'\r\n        audio.style.pointerEvents = 'none'\r\n        audio.addEventListener('error', () => {\r\n            if (session !== this.pipelineSession) return\r\n            const err = audio.error\r\n            this.emitInternalError(\r\n                `<audio> element error: code=${err?.code ?? '?'} ${err?.message ?? ''}`.trim(),\r\n            )\r\n        })\r\n        document.body.appendChild(audio)\r\n        this.hiddenAudio = audio\r\n\r\n        const url = URL.createObjectURL(mediaSource)\r\n        audio.src = url\r\n\r\n        mediaSource.addEventListener(\r\n            'sourceopen',\r\n            () => {\r\n                URL.revokeObjectURL(url)\r\n                // Bail if this MediaSource no longer belongs to the active\r\n                // pipeline (i.e. stop() ran before sourceopen fired).\r\n                if (session !== this.pipelineSession) return\r\n                if (this.mediaSource !== mediaSource || mediaSource.readyState !== 'open') return\r\n                let sourceBuffer: SourceBuffer\r\n                try {\r\n                    sourceBuffer = mediaSource.addSourceBuffer(resolvedMime)\r\n                    sourceBuffer.mode = 'sequence'\r\n                } catch (e) {\r\n                    this.emitInternalError(`addSourceBuffer failed: ${describeError(e)}`)\r\n                    return\r\n                }\r\n                const onUpdateEnd = (): void => {\r\n                    if (session !== this.pipelineSession) return\r\n                    this.flushPendingChunks()\r\n                }\r\n                const onSourceBufferError = (): void => {\r\n                    if (session !== this.pipelineSession) return\r\n                    this.emitInternalError('SourceBuffer error while appending audio data')\r\n                }\r\n                sourceBuffer.addEventListener('updateend', onUpdateEnd)\r\n                sourceBuffer.addEventListener('error', onSourceBufferError)\r\n                this.sourceBuffer = sourceBuffer\r\n                this.sourceBufferUpdateEnd = onUpdateEnd\r\n                this.flushPendingChunks()\r\n                this.tryEmitStream()\r\n            },\r\n            { once: true },\r\n        )\r\n\r\n        mediaSource.addEventListener('sourceended', () => {\r\n            if (session !== this.pipelineSession) return\r\n            // The MediaSource ended unexpectedly — surface a concrete error\r\n            // instead of letting the next appendBuffer throw cryptically.\r\n            if (this.mediaSource === mediaSource && mediaSource.readyState !== 'open') {\r\n                // Only treat as an error if we still consider the pipeline\r\n                // active. Normal stop() goes through teardownPipeline() which\r\n                // bumps pipelineSession first.\r\n            }\r\n        })\r\n\r\n        // Some browsers only expose captureStream() after playback starts.\r\n        const playPromise = audio.play()\r\n        if (playPromise && typeof playPromise.catch === 'function') {\r\n            playPromise.catch(() => {\r\n                /* autoplay restrictions — captureStream() may still work */\r\n            })\r\n        }\r\n    }\r\n\r\n    private flushPendingChunks(): void {\r\n        const sb = this.sourceBuffer\r\n        const ms = this.mediaSource\r\n        if (!sb || !ms) return\r\n        if (ms.readyState !== 'open') return\r\n        if (sb.updating) return\r\n        const next = this.pendingChunks.shift()\r\n        if (!next) return\r\n        try {\r\n            sb.appendBuffer(next)\r\n        } catch (e) {\r\n            this.emitInternalError(`appendBuffer failed: ${describeError(e)}`)\r\n        }\r\n    }\r\n\r\n    private tryEmitStream(): void {\r\n        if (this.currentStream || !this.hiddenAudio) return\r\n        const captureFn = this.hiddenAudio.captureStream ?? this.hiddenAudio.mozCaptureStream\r\n        if (!captureFn) {\r\n            this.emitInternalError('HTMLMediaElement.captureStream() is not supported in this browser')\r\n            return\r\n        }\r\n        let stream: MediaStream\r\n        try {\r\n            stream = captureFn.call(this.hiddenAudio)\r\n        } catch (e) {\r\n            this.emitInternalError(`captureStream() failed: ${describeError(e)}`)\r\n            return\r\n        }\r\n        this.currentStream = stream\r\n        for (const h of this.trackHandlers) {\r\n            try {\r\n                h(stream)\r\n            } catch {\r\n                /* ignore */\r\n            }\r\n        }\r\n    }\r\n\r\n    private endPipeline(): void {\r\n        try {\r\n            if (\r\n                this.mediaSource &&\r\n                this.mediaSource.readyState === 'open' &&\r\n                this.sourceBuffer &&\r\n                !this.sourceBuffer.updating\r\n            ) {\r\n                this.mediaSource.endOfStream()\r\n            }\r\n        } catch {\r\n            /* ignore */\r\n        }\r\n        this.teardownPipeline()\r\n    }\r\n\r\n    private teardownPipeline(): void {\r\n        // Invalidate any pending async callbacks from this pipeline so they\r\n        // can detect they belong to a stale session.\r\n        this.pipelineSession++\r\n\r\n        if (this.currentStream) {\r\n            for (const track of this.currentStream.getTracks()) {\r\n                try {\r\n                    track.stop()\r\n                } catch {\r\n                    /* ignore */\r\n                }\r\n            }\r\n        }\r\n        this.currentStream = null\r\n\r\n        const sb = this.sourceBuffer\r\n        const ms = this.mediaSource\r\n        if (sb) {\r\n            if (this.sourceBufferUpdateEnd) {\r\n                try {\r\n                    sb.removeEventListener('updateend', this.sourceBufferUpdateEnd)\r\n                } catch {\r\n                    /* ignore */\r\n                }\r\n            }\r\n            try {\r\n                if (sb.updating) sb.abort()\r\n            } catch {\r\n                /* ignore */\r\n            }\r\n            try {\r\n                if (ms && ms.readyState === 'open') ms.removeSourceBuffer(sb)\r\n            } catch {\r\n                /* ignore */\r\n            }\r\n        }\r\n        this.sourceBufferUpdateEnd = null\r\n        this.sourceBuffer = null\r\n        this.mediaSource = null\r\n        this.pendingChunks = []\r\n        if (this.hiddenAudio) {\r\n            try {\r\n                this.hiddenAudio.pause()\r\n                this.hiddenAudio.removeAttribute('src')\r\n                this.hiddenAudio.load()\r\n                this.hiddenAudio.remove()\r\n            } catch {\r\n                /* ignore */\r\n            }\r\n            this.hiddenAudio = null\r\n        }\r\n    }\r\n\r\n    private emitInternalError(error: string): void {\r\n        for (const h of this.errorHandlers) {\r\n            try {\r\n                h({ error })\r\n            } catch {\r\n                /* ignore */\r\n            }\r\n        }\r\n        this.teardownPipeline()\r\n    }\r\n}\r\n\r\nconst clampTimeslice = (value: number): number => {\r\n    if (!Number.isFinite(value)) return MIC_DEFAULT_TIMESLICE_MS\r\n    return Math.max(20, Math.min(MIC_MAX_TIMESLICE_MS, Math.floor(value)))\r\n}\r\n\r\nconst normalizeChunk = (params: Record<string, unknown>): IncomingChunk | null => {\r\n    const raw = params.data\r\n    let data: ArrayBuffer | null = null\r\n    if (raw instanceof ArrayBuffer) data = raw\r\n    else if (ArrayBuffer.isView(raw)) {\r\n        const view = raw as ArrayBufferView\r\n        const copy = new Uint8Array(view.byteLength)\r\n        copy.set(new Uint8Array(view.buffer as ArrayBufferLike, view.byteOffset, view.byteLength))\r\n        data = copy.buffer\r\n    }\r\n    if (!data || data.byteLength === 0) return null\r\n\r\n    const mimeType = typeof params.mimeType === 'string' ? params.mimeType : 'audio/webm;codecs=opus'\r\n    return { data, mimeType }\r\n}\r\n\r\nconst describeError = (e: unknown): string => (e instanceof Error ? e.message : String(e))\r\n","import { AlleoMic, type MicStartOptions } from './AlleoMic.js'\r\n\r\nexport type BoardObjectContentFormat = 'text' | 'html' | 'markdown'\r\n\r\nexport type ErrorPayload = {\r\n    index: number\r\n    error: string\r\n    requestId?: string\r\n}\r\n\r\nexport type AddContentHtmlParams = {\r\n    type: 'html'\r\n    html: string\r\n}\r\n\r\nexport type AddContentNotepadParams = {\r\n    type: 'notepad'\r\n    text?: string | string[]\r\n    textFormat?: 'text' | 'markdown' | 'html'\r\n}\r\n\r\nexport type AddContentStickyNoteParams = {\r\n    type: 'sticky-note'\r\n    text?: string\r\n    color?: string\r\n    outlineColor?: string\r\n    shape?: string\r\n}\r\n\r\nexport type AddContentImageParams = {\r\n    type: 'image'\r\n    url: string\r\n}\r\n\r\nexport type AddContentVideoParams = {\r\n    type: 'video'\r\n    fileId: string\r\n}\r\n\r\nexport type AddContentParams =\r\n    | AddContentHtmlParams\r\n    | AddContentNotepadParams\r\n    | AddContentStickyNoteParams\r\n    | AddContentImageParams\r\n    | AddContentVideoParams\r\n\r\nexport type EmbedWidgetCommandMap = {\r\n    triggerAction: {\r\n        actionId: string\r\n        data?: Record<string, unknown>\r\n    }\r\n    addContent: AddContentParams\r\n    requestSyncedStatus: Record<string, never>\r\n    setSyncedStatus: {\r\n        status: Record<string, unknown>\r\n    }\r\n    getBoardObjectContent: {\r\n        index: number\r\n        format?: BoardObjectContentFormat\r\n        requestId: string\r\n    }\r\n    replaceBoardObjectContent: {\r\n        index: number\r\n        text: string | string[]\r\n        format?: BoardObjectContentFormat\r\n        requestId: string\r\n    }\r\n    appendBoardObjectContent: {\r\n        index: number\r\n        text: string | string[]\r\n        format?: BoardObjectContentFormat\r\n        requestId: string\r\n    }\r\n    micStart: MicStartOptions & { timeslice: number }\r\n    micStop: Record<string, never>\r\n}\r\n\r\nexport type EmbedWidgetCommand = keyof EmbedWidgetCommandMap\r\n\r\nexport type EmbedWidgetCommandEnvelope<K extends EmbedWidgetCommand = EmbedWidgetCommand> = {\r\n    type: 'EmbedWidgetCommand'\r\n    command: K\r\n    params: EmbedWidgetCommandMap[K]\r\n}\r\n\r\nexport type EmbedWidgetMessageMap = {\r\n    syncedStatusUpdate: {\r\n        syncedStatus: Record<string, unknown>\r\n    }\r\n    incomingAction: {\r\n        actionId: string\r\n        data: Record<string, unknown>\r\n    }\r\n    boardObjectContentResult: {\r\n        index: number\r\n        content: string[]\r\n        requestId: string | undefined\r\n    }\r\n    boardObjectContentError: {\r\n        index: number\r\n        error: string\r\n        requestId: string | undefined\r\n    }\r\n    micStarted: {\r\n        mimeType: string\r\n        sampleRate?: number\r\n        timeslice: number\r\n    }\r\n    micStopped: Record<string, never>\r\n    micChunk: {\r\n        data: ArrayBuffer\r\n        mimeType: string\r\n        timestamp: number\r\n        sequence: number\r\n        sampleRate?: number\r\n    }\r\n    micError: {\r\n        error: string\r\n    }\r\n}\r\n\r\nexport type EmbedWidgetMessageCommand = keyof EmbedWidgetMessageMap\r\n\r\nexport type EmbedWidgetMessageEnvelope<K extends EmbedWidgetMessageCommand = EmbedWidgetMessageCommand> = {\r\n    type: 'EmbedWidgetMessage'\r\n    command: K\r\n    params: EmbedWidgetMessageMap[K]\r\n}\r\n\r\nexport type EmbedWidgetMessageEvent = {\r\n    command: EmbedWidgetMessageCommand\r\n    params: EmbedWidgetMessageMap[EmbedWidgetMessageCommand]\r\n    raw: EmbedWidgetMessageEnvelope\r\n}\r\n\r\nexport type EmbedWidgetMessengerOptions = {\r\n    targetOrigin?: string\r\n    parentWindow?: Window\r\n    autoListen?: boolean\r\n    debug?: boolean\r\n}\r\n\r\nexport class EmbedWidgetMessenger {\r\n    private readonly targetOrigin: string\r\n    private readonly parentWindow: Window | null\r\n    private readonly debugEnabled: boolean\r\n    private listening = false\r\n    private readonly syncedStatusHandlers = new Set<(status: Record<string, unknown>) => void>()\r\n    private readonly incomingActionHandlers = new Set<(payload: EmbedWidgetMessageMap['incomingAction']) => void>()\r\n    private readonly messageHandlers = new Set<(event: EmbedWidgetMessageEvent) => void>()\r\n    private readonly errorHandlers = new Set<(payload: ErrorPayload) => void>()\r\n    private readonly pendingContentRequests = new Map<\r\n        string,\r\n        { resolve: (content: string[]) => void; reject: (error: Error) => void; timer: ReturnType<typeof setTimeout> }\r\n    >()\r\n    private requestIdCounter = 0\r\n\r\n    public constructor(options?: EmbedWidgetMessengerOptions) {\r\n        this.targetOrigin = options?.targetOrigin ?? '*'\r\n        this.debugEnabled = options?.debug ?? false\r\n        this.parentWindow = options?.parentWindow ?? (this.isBrowser ? window.parent : null)\r\n\r\n        const autoListen = options?.autoListen ?? true\r\n        if (autoListen) this.startListening()\r\n    }\r\n\r\n    public get isListening(): boolean {\r\n        return this.listening\r\n    }\r\n\r\n    /**\r\n     * Microphone bridge. The iframe cannot call `getUserMedia()` directly, so\r\n     * this object delegates capture to the parent widget over `postMessage`.\r\n     * Use `mic.start()`, `mic.stop()` and `mic.onTrack(handler)`.\r\n     */\r\n    public get mic(): AlleoMic {\r\n        this.micInstance ??= new AlleoMic(this)\r\n        return this.micInstance\r\n    }\r\n\r\n    private micInstance?: AlleoMic\r\n\r\n    /** @internal — used by {@link AlleoMic} to send `micStart` / `micStop`. */\r\n    public sendMicCommand<K extends 'micStart' | 'micStop'>(command: K, params: EmbedWidgetCommandMap[K]): void {\r\n        this.sendCommand(command, params)\r\n    }\r\n\r\n    public triggerAction(actionId: string, data?: Record<string, unknown>): void {\r\n        if (!actionId) {\r\n            this.log('triggerAction missing actionId')\r\n            return\r\n        }\r\n        this.sendCommand('triggerAction', { actionId, data })\r\n    }\r\n\r\n    public addContent(params: AddContentParams): void {\r\n        this.sendCommand('addContent', params)\r\n    }\r\n\r\n    public requestSyncedStatus(): void {\r\n        this.sendCommand('requestSyncedStatus', {})\r\n    }\r\n\r\n    public setSyncedStatus(status: Record<string, unknown>): void {\r\n        this.sendCommand('setSyncedStatus', { status })\r\n    }\r\n\r\n    public onSyncedStatusUpdate(handler: (status: Record<string, unknown>) => void): () => void {\r\n        this.syncedStatusHandlers.add(handler)\r\n        return () => this.syncedStatusHandlers.delete(handler)\r\n    }\r\n\r\n    public onIncomingAction(handler: (payload: EmbedWidgetMessageMap['incomingAction']) => void): () => void {\r\n        this.incomingActionHandlers.add(handler)\r\n        return () => this.incomingActionHandlers.delete(handler)\r\n    }\r\n\r\n    /**\r\n     * Retrieves the text content of a board object at the given index.\r\n     *\r\n     * The returned promise resolves with an array of content strings when the widget responds,\r\n     * or rejects if the widget reports an error or if the request times out (30 s).\r\n     *\r\n     * @param index - The 1-based index of the board object in the whitelist.\r\n     * @param options - Optional settings.\r\n     * @param options.format - The format to retrieve content in: `'text'`, `'html'`, or `'markdown'`. Defaults to `'text'`.\r\n     * @returns A promise that resolves with an array of content strings.\r\n     */\r\n    public getBoardObjectContent(\r\n        index: number,\r\n        options?: { format?: BoardObjectContentFormat },\r\n    ): Promise<string[]> {\r\n        const requestId = this.generateRequestId()\r\n\r\n        return new Promise<string[]>((resolve, reject) => {\r\n            const timer = setTimeout(() => {\r\n                if (this.pendingContentRequests.has(requestId)) {\r\n                    this.pendingContentRequests.delete(requestId)\r\n                    reject(new Error(`getBoardObjectContent timed out for index ${index} (requestId=${requestId})`))\r\n                }\r\n            }, 30_000)\r\n\r\n            this.pendingContentRequests.set(requestId, { resolve, reject, timer })\r\n\r\n            this.sendCommand('getBoardObjectContent', {\r\n                index,\r\n                format: options?.format,\r\n                requestId,\r\n            })\r\n        })\r\n    }\r\n\r\n    /**\r\n     * Replaces the text content of a board object at the given index.\r\n     *\r\n     * This is a fire-and-forget operation — no success response is returned.\r\n     * Errors are reported asynchronously via {@link onError}.\r\n     *\r\n     * @param index - The 1-based index of the board object in the whitelist.\r\n     * @param text - The replacement text. Can be a single string or an array of strings.\r\n     * @param options - Optional settings.\r\n     * @param options.format - The format of the provided text: `'text'`, `'html'`, or `'markdown'`. Defaults to `'text'`.\r\n     */\r\n    public replaceBoardObjectContent(\r\n        index: number,\r\n        text: string | string[],\r\n        options?: { format?: BoardObjectContentFormat },\r\n    ): void {\r\n        const requestId = this.generateRequestId()\r\n        this.sendCommand('replaceBoardObjectContent', {\r\n            index,\r\n            text,\r\n            format: options?.format,\r\n            requestId,\r\n        })\r\n    }\r\n\r\n    /**\r\n     * Appends text content to a board object at the given index.\r\n     *\r\n     * This is a fire-and-forget operation — no success response is returned.\r\n     * Errors are reported asynchronously via {@link onError}.\r\n     *\r\n     * @param index - The 1-based index of the board object in the whitelist.\r\n     * @param text - The text to append. Can be a single string or an array of strings.\r\n     * @param options - Optional settings.\r\n     * @param options.format - The format of the provided text: `'text'`, `'html'`, or `'markdown'`. Defaults to `'text'`.\r\n     */\r\n    public appendBoardObjectContent(\r\n        index: number,\r\n        text: string | string[],\r\n        options?: { format?: BoardObjectContentFormat },\r\n    ): void {\r\n        const requestId = this.generateRequestId()\r\n        this.sendCommand('appendBoardObjectContent', {\r\n            index,\r\n            text,\r\n            format: options?.format,\r\n            requestId,\r\n        })\r\n    }\r\n\r\n    /**\r\n     * Registers a callback that fires whenever an error occurs.\r\n     *\r\n     * The handler is invoked for errors from any board-object-content command\r\n     * (`getBoardObjectContent`, `replaceBoardObjectContent`, or `appendBoardObjectContent`).\r\n     * For `getBoardObjectContent` errors, the corresponding promise is rejected first,\r\n     * then all registered error handlers are called.\r\n     *\r\n     * @param handler - The callback receiving `{ index, error, requestId }`.\r\n     * @returns An unsubscribe function that removes this handler.\r\n     */\r\n    public onError(\r\n        handler: (payload: ErrorPayload) => void,\r\n    ): () => void {\r\n        this.errorHandlers.add(handler)\r\n        return () => this.errorHandlers.delete(handler)\r\n    }\r\n\r\n    public onMessage(handler: (event: EmbedWidgetMessageEvent) => void): () => void {\r\n        this.messageHandlers.add(handler)\r\n        return () => this.messageHandlers.delete(handler)\r\n    }\r\n\r\n    public startListening(): void {\r\n        if (!this.isBrowser || this.listening) return\r\n        window.addEventListener('message', this.handleMessageEvent)\r\n        this.listening = true\r\n    }\r\n\r\n    public stopListening(): void {\r\n        if (!this.isBrowser || !this.listening) return\r\n        window.removeEventListener('message', this.handleMessageEvent)\r\n        this.listening = false\r\n    }\r\n\r\n    public destroy(): void {\r\n        this.stopListening()\r\n        this.micInstance?.destroy()\r\n        this.micInstance = undefined\r\n        this.syncedStatusHandlers.clear()\r\n        this.incomingActionHandlers.clear()\r\n        this.messageHandlers.clear()\r\n        this.errorHandlers.clear()\r\n        for (const [, entry] of this.pendingContentRequests) {\r\n            clearTimeout(entry.timer)\r\n            entry.reject(new Error('EmbedWidgetMessenger destroyed'))\r\n        }\r\n        this.pendingContentRequests.clear()\r\n    }\r\n\r\n    private get isBrowser(): boolean {\r\n        return typeof window !== 'undefined' && typeof window.addEventListener === 'function'\r\n    }\r\n\r\n    private sendCommand<K extends EmbedWidgetCommand>(command: K, params: EmbedWidgetCommandMap[K]): void {\r\n        if (!this.parentWindow) {\r\n            this.log('postMessage unavailable')\r\n            return\r\n        }\r\n\r\n        const message: EmbedWidgetCommandEnvelope<K> = {\r\n            type: 'EmbedWidgetCommand',\r\n            command,\r\n            params,\r\n        }\r\n\r\n        this.parentWindow.postMessage(message, this.targetOrigin)\r\n    }\r\n\r\n    private readonly handleMessageEvent = (event: MessageEvent): void => {\r\n        if (this.parentWindow && event.source !== this.parentWindow) return\r\n\r\n        const data = event.data\r\n        if (!data || typeof data !== 'object') return\r\n        if ((data as { type?: string }).type !== 'EmbedWidgetMessage') return\r\n\r\n        const envelope = data as EmbedWidgetMessageEnvelope\r\n\r\n        if (this.isSyncedStatusUpdate(envelope)) {\r\n            const status = envelope.params.syncedStatus\r\n            if (status && typeof status === 'object') {\r\n                this.emitSyncedStatusUpdate(status)\r\n            } else {\r\n                this.emitSyncedStatusUpdate({})\r\n            }\r\n\r\n            this.emitMessage({ command: envelope.command, params: envelope.params, raw: envelope })\r\n            return\r\n        }\r\n\r\n        if (this.isIncomingAction(envelope)) {\r\n            if (typeof envelope.params.actionId !== 'string') return\r\n            const actionData =\r\n                envelope.params.data && typeof envelope.params.data === 'object' ? envelope.params.data : {}\r\n            this.emitIncomingAction({ actionId: envelope.params.actionId, data: actionData })\r\n            this.emitMessage({ command: envelope.command, params: envelope.params, raw: envelope })\r\n            return\r\n        }\r\n\r\n        if (this.isBoardObjectContentResult(envelope)) {\r\n            const requestId = envelope.params.requestId\r\n            if (requestId) {\r\n                const entry = this.pendingContentRequests.get(requestId)\r\n                if (entry) {\r\n                    clearTimeout(entry.timer)\r\n                    this.pendingContentRequests.delete(requestId)\r\n                    entry.resolve(envelope.params.content)\r\n                }\r\n            }\r\n            this.emitMessage({ command: envelope.command, params: envelope.params, raw: envelope })\r\n            return\r\n        }\r\n\r\n        if (this.isBoardObjectContentError(envelope)) {\r\n            const requestId = envelope.params.requestId\r\n            // Reject the pending promise first (if any)\r\n            if (requestId) {\r\n                const entry = this.pendingContentRequests.get(requestId)\r\n                if (entry) {\r\n                    clearTimeout(entry.timer)\r\n                    this.pendingContentRequests.delete(requestId)\r\n                    entry.reject(new Error(envelope.params.error))\r\n                }\r\n            }\r\n            // Then notify all registered error handlers\r\n            this.emitError({\r\n                index: envelope.params.index,\r\n                error: envelope.params.error,\r\n                requestId: envelope.params.requestId,\r\n            })\r\n            this.emitMessage({ command: envelope.command, params: envelope.params, raw: envelope })\r\n            return\r\n        }\r\n\r\n        if (this.isMicMessage(envelope)) {\r\n            this.emitMessage({ command: envelope.command, params: envelope.params, raw: envelope })\r\n            return\r\n        }\r\n\r\n        this.log('Unknown message command', envelope.command)\r\n    }\r\n\r\n    private isSyncedStatusUpdate(\r\n        envelope: EmbedWidgetMessageEnvelope,\r\n    ): envelope is EmbedWidgetMessageEnvelope<'syncedStatusUpdate'> {\r\n        return envelope.command === 'syncedStatusUpdate'\r\n    }\r\n\r\n    private isIncomingAction(envelope: EmbedWidgetMessageEnvelope): envelope is EmbedWidgetMessageEnvelope<'incomingAction'> {\r\n        return envelope.command === 'incomingAction'\r\n    }\r\n\r\n    private isBoardObjectContentResult(\r\n        envelope: EmbedWidgetMessageEnvelope,\r\n    ): envelope is EmbedWidgetMessageEnvelope<'boardObjectContentResult'> {\r\n        return envelope.command === 'boardObjectContentResult'\r\n    }\r\n\r\n    private isBoardObjectContentError(\r\n        envelope: EmbedWidgetMessageEnvelope,\r\n    ): envelope is EmbedWidgetMessageEnvelope<'boardObjectContentError'> {\r\n        return envelope.command === 'boardObjectContentError'\r\n    }\r\n\r\n    private isMicMessage(\r\n        envelope: EmbedWidgetMessageEnvelope,\r\n    ): envelope is EmbedWidgetMessageEnvelope<'micStarted' | 'micStopped' | 'micChunk' | 'micError'> {\r\n        return (\r\n            envelope.command === 'micStarted' ||\r\n            envelope.command === 'micStopped' ||\r\n            envelope.command === 'micChunk' ||\r\n            envelope.command === 'micError'\r\n        )\r\n    }\r\n\r\n    private emitSyncedStatusUpdate(status: Record<string, unknown>): void {\r\n        for (const handler of this.syncedStatusHandlers) handler(status)\r\n    }\r\n\r\n    private emitIncomingAction(payload: EmbedWidgetMessageMap['incomingAction']): void {\r\n        for (const handler of this.incomingActionHandlers) handler(payload)\r\n    }\r\n\r\n    private emitMessage(event: EmbedWidgetMessageEvent): void {\r\n        for (const handler of this.messageHandlers) handler(event)\r\n    }\r\n\r\n    private emitError(payload: ErrorPayload): void {\r\n        for (const handler of this.errorHandlers) handler(payload)\r\n    }\r\n\r\n    private generateRequestId(): string {\r\n        return `req-${++this.requestIdCounter}-${Date.now()}`\r\n    }\r\n\r\n    private log(message: string, data?: unknown): void {\r\n        if (!this.debugEnabled) return\r\n        // eslint-disable-next-line no-console\r\n        if (data === undefined) console.debug(`[ewidget-utils] ${message}`)\r\n        // eslint-disable-next-line no-console\r\n        else console.debug(`[ewidget-utils] ${message}`, data)\r\n    }\r\n}\r\n","import { EmbedWidgetMessenger, type EmbedWidgetMessengerOptions } from './EmbedWidgetMessenger.js'\r\n\r\nexport type AlleoEwidgetUtilsOptions = {\r\n    autoDestroy?: boolean\r\n    debug?: boolean\r\n}\r\n\r\ntype GlobalWithAlleo = typeof globalThis & {\r\n    __withAlleoEwidgetUtils__?: {\r\n        instance?: AlleoEwidgetUtils\r\n    }\r\n}\r\n\r\nexport class AlleoEwidgetUtils {\r\n    private static readonly GLOBAL_KEY = '__withAlleoEwidgetUtils__' as const\r\n    private readonly options: Required<AlleoEwidgetUtilsOptions>\r\n    private initialized = false\r\n    private destroyed = false\r\n    private onBeforeUnload?: (event: BeforeUnloadEvent) => void\r\n\r\n    public constructor(options?: AlleoEwidgetUtilsOptions) {\r\n        this.options = {\r\n            autoDestroy: options?.autoDestroy ?? true,\r\n            debug: options?.debug ?? false,\r\n        }\r\n\r\n        this.initialize()\r\n    }\r\n\r\n    public get isInitialized(): boolean {\r\n        return this.initialized && !this.destroyed\r\n    }\r\n\r\n    private get isBrowser(): boolean {\r\n        return typeof window !== 'undefined' && typeof document !== 'undefined'\r\n    }\r\n\r\n    public static getInstance(options?: AlleoEwidgetUtilsOptions): AlleoEwidgetUtils {\r\n        // Prefer a global singleton if running in a browser and the bundle was included multiple times.\r\n        const g = globalThis as GlobalWithAlleo\r\n        if (g && typeof g === 'object') {\r\n            g[AlleoEwidgetUtils.GLOBAL_KEY] ??= {}\r\n            if (g[AlleoEwidgetUtils.GLOBAL_KEY]!.instance) {\r\n                return g[AlleoEwidgetUtils.GLOBAL_KEY]!.instance!\r\n            }\r\n        }\r\n\r\n        const instance = new AlleoEwidgetUtils(options)\r\n        if (g && typeof g === 'object') {\r\n            g[AlleoEwidgetUtils.GLOBAL_KEY]!.instance = instance\r\n        }\r\n        return instance\r\n    }\r\n\r\n    public createEmbedWidgetMessenger(options?: EmbedWidgetMessengerOptions): EmbedWidgetMessenger {\r\n        return new EmbedWidgetMessenger(options)\r\n    }\r\n\r\n    public initialize(): void {\r\n        if (this.isInitialized) return\r\n        this.initialized = true\r\n\r\n        if (!this.isBrowser) return\r\n\r\n        if (this.options.autoDestroy) this.attachListeners()\r\n\r\n        this.log('initialized')\r\n    }\r\n\r\n    public destroy(): void {\r\n        if (this.destroyed) return\r\n        this.destroyed = true\r\n\r\n        if (this.isBrowser) this.detachListeners()\r\n\r\n        // If this instance is the stored global singleton, clear it so callers can recreate.\r\n        const g = globalThis as GlobalWithAlleo\r\n        if (g && typeof g === 'object') {\r\n            const holder = g[AlleoEwidgetUtils.GLOBAL_KEY]\r\n            if (holder?.instance === this) holder.instance = undefined\r\n        }\r\n\r\n        this.log('destroyed')\r\n    }\r\n\r\n    public log(message: string, data?: unknown): void {\r\n        if (!this.options.debug) return\r\n        // eslint-disable-next-line no-console\r\n        if (data === undefined) console.debug(`[ewidget-utils] ${message}`)\r\n        // eslint-disable-next-line no-console\r\n        else console.debug(`[ewidget-utils] ${message}`, data)\r\n    }\r\n\r\n    private attachListeners(): void {\r\n        if (!this.isBrowser) return\r\n\r\n        this.onBeforeUnload ??= () => this.destroy()\r\n        window.addEventListener('beforeunload', this.onBeforeUnload)\r\n    }\r\n\r\n    private detachListeners(): void {\r\n        if (!this.isBrowser) return\r\n        if (!this.onBeforeUnload) return\r\n        window.removeEventListener('beforeunload', this.onBeforeUnload)\r\n    }\r\n}\r\n\r\nexport const getAlleoEwidgetUtils = (options?: AlleoEwidgetUtilsOptions): AlleoEwidgetUtils => AlleoEwidgetUtils.getInstance(options)\r\nexport const getEmbedWidgetMessenger = (options?: EmbedWidgetMessengerOptions) =>\r\n    AlleoEwidgetUtils.getInstance().createEmbedWidgetMessenger(options)\r\n"],"mappings":"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"}