const FrankieSDK = (() => { const _handlers = {} const _llmResolvers = {} // _handle dispatches an inbound postMessage event to the registered hook. function _handle(type, payload) { const hook = _handlers[type] if (typeof hook === 'function') hook(payload) } // Boot: listen for messages from the Frankie parent frame. window.addEventListener('message', (event) => { const { type, payload, id } = event.data || {} if (typeof type !== 'string' || !type.startsWith('FRANKIE_')) return const hook = type.slice(8).toLowerCase() // FRANKIE_INIT → 'init' // LLM responses are resolved as promises rather than dispatched as hooks. if (hook === 'llm_response') { const resolve = _llmResolvers[id] if (typeof resolve === 'function') { resolve(payload?.content ?? '') delete _llmResolvers[id] } return } _handle(hook, payload) }) return { // on registers a lifecycle hook. Returns the SDK for chaining. on(event, handler) { _handlers[event] = handler return this }, // emit sends a typed event to the Frankie parent frame. emit(event, payload = {}) { window.parent.postMessage( { type: `FRANKIE_${event.toUpperCase()}`, payload }, '*' ) }, // llm sends a chat-completion request to Frankie's LLM and returns a Promise // that resolves with the model's reply as a string. llm(messages) { return new Promise((resolve) => { const id = `llm_${Date.now()}_${Math.random().toString(36).slice(2)}` _llmResolvers[id] = resolve window.parent.postMessage( { type: 'FRANKIE_LLM_REQUEST', id, payload: { messages } }, '*' ) }) } } })() export default FrankieSDK