* Install merge-related window shims. * * Both Electron and web-serve use the same HTTP merge routes. The shim * maintains a filePath→handle Map so that existing frontend code * (session.ts, BatchManageTab.vue) can continue calling with filePaths * while the HTTP layer operates with UUID handle
(platform: 'electron' | 'web-serve')
| 156 | * while the HTTP layer operates with UUID handles. |
| 157 | */ |
| 158 | function installMergeShims(platform: 'electron' | 'web-serve'): void { |
| 159 | const pathToHandle = new Map<string, string>() |
| 160 | |
| 161 | const isHandle = (v: string) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v) |
| 162 | |
| 163 | const resolveHandle = (filePathOrHandle: string) => pathToHandle.get(filePathOrHandle) ?? filePathOrHandle |
| 164 | |
| 165 | async function ensureOk(resp: Response, context: string): Promise<void> { |
| 166 | if (!resp.ok) { |
| 167 | const body = await resp.text().catch(() => '') |
| 168 | throw new Error(`[mergeApi] ${context} failed (${resp.status}): ${body}`) |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | ;(window as any).mergeApi = { |
| 173 | exportSessionsToTempFiles: async (sessionIds: string[]) => { |
| 174 | try { |
| 175 | const resp = await fetchWithAuth('/_web/sessions/export-for-merge', { |
| 176 | method: 'POST', |
| 177 | headers: { 'Content-Type': 'application/json' }, |
| 178 | body: JSON.stringify({ sessionIds }), |
| 179 | }) |
| 180 | await ensureOk(resp, 'exportSessionsToTempFiles') |
| 181 | const result = await resp.json() |
| 182 | if (!result.success) return { success: false, tempFiles: [], error: result.error } |
| 183 | const tempFiles = result.handles.map((h: { handle: string }) => h.handle) |
| 184 | return { success: true, tempFiles } |
| 185 | } catch (error) { |
| 186 | return { success: false, tempFiles: [], error: error instanceof Error ? error.message : String(error) } |
| 187 | } |
| 188 | }, |
| 189 | |
| 190 | cleanupTempExportFiles: async (filePaths: string[]) => { |
| 191 | try { |
| 192 | for (const fp of filePaths) { |
| 193 | const handle = resolveHandle(fp) |
| 194 | await fetchWithAuth('/_web/merge/clear', { |
| 195 | method: 'POST', |
| 196 | headers: { 'Content-Type': 'application/json' }, |
| 197 | body: JSON.stringify({ handle }), |
| 198 | }) |
| 199 | pathToHandle.delete(fp) |
| 200 | } |
| 201 | return { success: true } |
| 202 | } catch (error) { |
| 203 | return { success: false, error: error instanceof Error ? error.message : String(error) } |
| 204 | } |
| 205 | }, |
| 206 | |
| 207 | parseFileInfo: async (filePath: string) => { |
| 208 | if (isHandle(filePath) || pathToHandle.has(filePath)) { |
| 209 | return { name: '', format: '', platform: '', messageCount: 0, memberCount: 0, fileSize: 0 } |
| 210 | } |
| 211 | |
| 212 | if (platform === 'electron') { |
| 213 | const resp = await fetchWithAuth('/_web/merge/parse', { |
| 214 | method: 'POST', |
| 215 | headers: { 'Content-Type': 'application/json' }, |
no test coverage detected