{"version":3,"file":"main.mjs","names":["createBaseContext"],"sources":["../../../src/adapters/electron/main.ts"],"sourcesContent":["import type { BrowserWindow, IpcMain, IpcMainEvent } from 'electron'\n\nimport type { EventContext } from '../../context'\nimport type { DirectionalEventa, Eventa } from '../../eventa'\n\nimport { createContext as createBaseContext } from '../../context'\nimport { and, defineInboundEventa, defineOutboundEventa, EventaFlowDirection, matchBy } from '../../eventa'\nimport { generatePayload, parsePayload } from './internal'\nimport { errorEvent } from './shared'\n\nfunction withRemoval(ipcMain: IpcMain, type: string, listener: Parameters<IpcMain['on']>[1]) {\n  ipcMain.on(type, listener)\n\n  return {\n    remove: () => {\n      ipcMain.off(type, listener)\n    },\n  }\n}\n\nexport function createContext(ipcMain: IpcMain, window?: BrowserWindow, options?: {\n  onlySameWindow?: boolean\n  messageEventName?: string | false\n  errorEventName?: string | false\n  extraListeners?: Record<string, (ipcMainEvent: IpcMainEvent, event: Event) => void | Promise<void>>\n  throwIfFailedToSend?: boolean\n}) {\n  const ctx = createBaseContext() as EventContext<\n    { invokeRequest?: { raw?: { ipcMainEvent: IpcMainEvent, event: Event | unknown } } },\n    { raw: { ipcMainEvent: IpcMainEvent, event: Event | unknown } }\n  >\n\n  const {\n    messageEventName = 'eventa-message',\n    errorEventName = 'eventa-error',\n    extraListeners = {},\n    onlySameWindow = false,\n  } = options || {}\n\n  const cleanupRemoval: Array<{ remove: () => void }> = []\n\n  ctx.on(and(\n    matchBy('*'),\n    matchBy((e: DirectionalEventa<any>) => e._flowDirection === EventaFlowDirection.Outbound || !e._flowDirection),\n  ), (event, callOptions) => {\n    const eventBody = generatePayload(event.id, { ...defineOutboundEventa(event.type), ...event })\n\n    // The message channel is disabled; do not publish to Electron IPC.\n    if (messageEventName === false) {\n      return\n    }\n\n    try {\n      // Prefer the bound BrowserWindow over inherited raw.sender metadata.\n      if (window != null) {\n        if (window.isDestroyed()) {\n          return\n        }\n\n        // onlySameWindow applies only when this emit inherits an inbound IPC sender.\n        if (onlySameWindow && callOptions?.raw?.ipcMainEvent != null && window.webContents.id !== callOptions.raw.ipcMainEvent.sender.id) {\n          return\n        }\n\n        // Keep one Eventa IPC channel; inherited raw metadata must not choose the wire protocol.\n        window.webContents.send(messageEventName, eventBody)\n      }\n      else {\n        // Without a bound window, the inbound IPC sender is the only known destination.\n        const sender = callOptions?.raw?.ipcMainEvent?.sender\n\n        if (sender == null || sender.isDestroyed()) {\n          return\n        }\n\n        sender.send(messageEventName, eventBody)\n      }\n    }\n    catch (error) {\n      // Electron may close the target between the lifecycle check and send.\n      if (!(error instanceof Error) || error?.message !== 'Object has been destroyed') {\n        throw error\n      }\n    }\n  })\n\n  if (messageEventName) {\n    cleanupRemoval.push(withRemoval(ipcMain, messageEventName, (ipcMainEvent, event: Event | unknown) => {\n      try {\n        const { type, payload } = parsePayload<Eventa<any>>(event)\n        ctx.emit(defineInboundEventa(type), payload.body, { raw: { ipcMainEvent, event } })\n      }\n      catch (error) {\n        console.error('Failed to parse IpcMain message:', error)\n        ctx.emit(errorEvent, { error }, { raw: { ipcMainEvent, event } })\n      }\n    }))\n  }\n\n  if (errorEventName) {\n    cleanupRemoval.push(withRemoval(ipcMain, errorEventName, (ipcMainEvent, error: Event | unknown) => {\n      ctx.emit(errorEvent, { error }, { raw: { ipcMainEvent, event: error } })\n    }))\n  }\n\n  for (const [eventName, listener] of Object.entries(extraListeners)) {\n    cleanupRemoval.push(withRemoval(ipcMain, eventName, listener))\n  }\n\n  return {\n    context: ctx,\n    dispose: (reason?: unknown) => {\n      // Cascade-cancel any in-flight `defineInvoke(...)` so renderer-bound\n      // RPCs don't hang after the main-side adapter is torn down.\n      ctx.abort(reason ?? new Error('eventa: invoke cancelled, electron main ipc disposed'))\n      cleanupRemoval.forEach(removal => removal.remove())\n    },\n  }\n}\n\nexport type * from './shared'\n"],"mappings":";;;AAUA,SAAS,YAAY,SAAkB,MAAc,UAAwC;CAC3F,QAAQ,GAAG,MAAM,QAAQ;CAEzB,OAAO,EACL,cAAc;EACZ,QAAQ,IAAI,MAAM,QAAQ;CAC5B,EACF;AACF;AAEA,SAAgB,cAAc,SAAkB,QAAwB,SAMrE;CACD,MAAM,MAAMA,gBAAkB;CAK9B,MAAM,EACJ,mBAAmB,kBACnB,iBAAiB,gBACjB,iBAAiB,CAAC,GAClB,iBAAiB,UACf,WAAW,CAAC;CAEhB,MAAM,iBAAgD,CAAC;CAEvD,IAAI,GAAG,IACL,QAAQ,GAAG,GACX,SAAS,MAA8B,EAAE,mBAAA,cAAmD,CAAC,EAAE,cAAc,CAC/G,IAAI,OAAO,gBAAgB;EACzB,MAAM,YAAY,gBAAgB,MAAM,IAAI;GAAE,GAAG,qBAAqB,MAAM,IAAI;GAAG,GAAG;EAAM,CAAC;EAG7F,IAAI,qBAAqB,OACvB;EAGF,IAAI;GAEF,IAAI,UAAU,MAAM;IAClB,IAAI,OAAO,YAAY,GACrB;IAIF,IAAI,kBAAkB,aAAa,KAAK,gBAAgB,QAAQ,OAAO,YAAY,OAAO,YAAY,IAAI,aAAa,OAAO,IAC5H;IAIF,OAAO,YAAY,KAAK,kBAAkB,SAAS;GACrD,OACK;IAEH,MAAM,SAAS,aAAa,KAAK,cAAc;IAE/C,IAAI,UAAU,QAAQ,OAAO,YAAY,GACvC;IAGF,OAAO,KAAK,kBAAkB,SAAS;GACzC;EACF,SACO,OAAO;GAEZ,IAAI,EAAE,iBAAiB,UAAU,OAAO,YAAY,6BAClD,MAAM;EAEV;CACF,CAAC;CAED,IAAI,kBACF,eAAe,KAAK,YAAY,SAAS,mBAAmB,cAAc,UAA2B;EACnG,IAAI;GACF,MAAM,EAAE,MAAM,YAAY,aAA0B,KAAK;GACzD,IAAI,KAAK,oBAAoB,IAAI,GAAG,QAAQ,MAAM,EAAE,KAAK;IAAE;IAAc;GAAM,EAAE,CAAC;EACpF,SACO,OAAO;GACZ,QAAQ,MAAM,oCAAoC,KAAK;GACvD,IAAI,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE,KAAK;IAAE;IAAc;GAAM,EAAE,CAAC;EAClE;CACF,CAAC,CAAC;CAGJ,IAAI,gBACF,eAAe,KAAK,YAAY,SAAS,iBAAiB,cAAc,UAA2B;EACjG,IAAI,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE,KAAK;GAAE;GAAc,OAAO;EAAM,EAAE,CAAC;CACzE,CAAC,CAAC;CAGJ,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,cAAc,GAC/D,eAAe,KAAK,YAAY,SAAS,WAAW,QAAQ,CAAC;CAG/D,OAAO;EACL,SAAS;EACT,UAAU,WAAqB;GAG7B,IAAI,MAAM,0BAAU,IAAI,MAAM,sDAAsD,CAAC;GACrF,eAAe,SAAQ,YAAW,QAAQ,OAAO,CAAC;EACpD;CACF;AACF"}