/** * SSE stream for /api/generate-sequences — usable from wizard or dashboard. */ export function buildGenerateSequencesUrl(fileId, { reset = false, preview = false, contactLimit = null } = {}) { const params = new URLSearchParams({ file_id: fileId, reset: reset ? '1' : '0', }); if (preview) params.set('preview', '1'); if (contactLimit != null && contactLimit > 0) { params.set('contact_limit', String(contactLimit)); } return `/api/generate-sequences?${params.toString()}`; } export function openCampaignGenerationStream(fileId, options, handlers = {}) { const url = buildGenerateSequencesUrl(fileId, options); const es = new EventSource(url); es.onmessage = (event) => { try { const data = JSON.parse(event.data); if (data.type === 'sequence' && handlers.onSequence) { handlers.onSequence(data.sequence); } else if (data.type === 'progress' && typeof data.progress === 'number' && handlers.onProgress) { handlers.onProgress(data.progress); } else if (data.type === 'phase' && handlers.onPhase) { handlers.onPhase(data.phase); } else if (data.type === 'preview_complete') { if (handlers.onPreviewComplete) handlers.onPreviewComplete(); es.close(); } else if (data.type === 'complete') { if (handlers.onComplete) handlers.onComplete(); es.close(); } else if (data.type === 'error') { if (handlers.onError) handlers.onError(data.error || 'Generation failed'); es.close(); } } catch (e) { if (handlers.onError) handlers.onError(String(e)); } }; es.onerror = () => { if (handlers.onError) handlers.onError('Connection lost'); es.close(); }; return es; }