import type { PaginatedResponse, Credential, Environment, ScenarioCredential, Session, Scenario, ScenarioRun, ScenarioRunDetail, ScenarioRunStep, ScenarioStep, Snippet, } from './types'; // In dev, Vite proxies /environments /sessions /scenarios to localhost:3000. // In production (or when VITE_API_URL is set) we hit the configured origin directly. const BASE_URL: string = (import.meta.env.VITE_API_URL as string | undefined) ?? ''; async function request(path: string, init?: RequestInit): Promise { const res = await fetch(`${BASE_URL}${path}`, { headers: { 'Content-Type': 'application/json', ...init?.headers }, ...init, }); if (!res.ok) { const text = await res.text().catch(() => res.statusText); throw new Error(`${res.status} ${text}`); } const contentLength = res.headers.get('content-length'); if (res.status === 204 || contentLength === '0') return undefined as T; const contentType = res.headers.get('content-type')?.toLowerCase() ?? ''; const text = await res.text(); if (!text) return undefined as T; if (contentType.includes('application/json')) { return JSON.parse(text) as T; } return text as T; } // ── Credentials ─────────────────────────────────────────────────────────────── export const credentials = { list(page = 1, limit = 50): Promise> { return request(`/credentials?page=${page}&limit=${limit}`); }, get(id: string): Promise { return request(`/credentials/${id}`); }, create(name: string, data?: string): Promise { return request('/credentials', { method: 'POST', body: JSON.stringify({ name, data }), }); }, update(id: string, patch: Partial>): Promise { return request(`/credentials/${id}`, { method: 'PATCH', body: JSON.stringify(patch), }); }, remove(id: string): Promise { return request(`/credentials/${id}`, { method: 'DELETE' }); }, exportCredential(id: string): Promise { return request(`/credentials/${id}/export`); }, importCredential(payload: unknown): Promise { return request('/credentials/import', { method: 'POST', body: JSON.stringify(payload), }); }, }; // ── Snippets ────────────────────────────────────────────────────────────────── export const snippets = { list(page = 1, limit = 50): Promise> { return request(`/snippets?page=${page}&limit=${limit}`); }, get(id: string): Promise { return request(`/snippets/${id}`); }, create(payload: Pick & { description?: string }): Promise { return request('/snippets', { method: 'POST', body: JSON.stringify(payload), }); }, update( id: string, patch: Partial>, ): Promise { return request(`/snippets/${id}`, { method: 'PATCH', body: JSON.stringify(patch), }); }, remove(id: string): Promise { return request(`/snippets/${id}`, { method: 'DELETE' }); }, exportSnippet(id: string): Promise { return request(`/snippets/${id}/export`); }, importSnippet(payload: unknown): Promise { return request('/snippets/import', { method: 'POST', body: JSON.stringify(payload), }); }, }; // ── Environments ────────────────────────────────────────────────────────────── export const environments = { list(page = 1, limit = 50): Promise> { return request(`/environments?page=${page}&limit=${limit}`); }, get(id: string): Promise { return request(`/environments/${id}`); }, create(name: string, urls: Environment['urls']): Promise { return request('/environments', { method: 'POST', body: JSON.stringify({ name, urls }), }); }, update(id: string, patch: Partial>): Promise { return request(`/environments/${id}`, { method: 'PATCH', body: JSON.stringify(patch), }); }, remove(id: string): Promise { return request(`/environments/${id}`, { method: 'DELETE' }); }, exportEnvironment(id: string): Promise { return request(`/environments/${id}/export`); }, importEnvironment(payload: unknown): Promise { return request('/environments/import', { method: 'POST', body: JSON.stringify(payload), }); }, }; // ── Sessions ────────────────────────────────────────────────────────────────── export const sessions = { list(page = 1, limit = 50): Promise> { return request(`/sessions?page=${page}&limit=${limit}`); }, get(id: string): Promise { return request(`/sessions/${id}`); }, remove(id: string): Promise { return request(`/sessions/${id}`, { method: 'DELETE' }); }, }; // ── Scenarios ───────────────────────────────────────────────────────────────── export const scenarios = { list(page = 1, limit = 50): Promise> { return request(`/scenarios?page=${page}&limit=${limit}&orderBy=updatedAt&orderDir=DESC`); }, get(id: string): Promise { return request(`/scenarios/${id}`); }, create(name: string): Promise { return request('/scenarios', { method: 'POST', body: JSON.stringify({ name }), }); }, update(id: string, patch: Partial>): Promise { return request(`/scenarios/${id}`, { method: 'PATCH', body: JSON.stringify(patch), }); }, remove(id: string): Promise { return request(`/scenarios/${id}`, { method: 'DELETE' }); }, run(id: string): Promise { return request(`/scenarios/${id}/run`, { method: 'POST' }); }, getRun(scenarioId: string, runId: string): Promise { return request(`/scenarios/${scenarioId}/run/${runId}`); }, waitForRun(scenarioId: string, runId: string): Promise { return request(`/scenarios/${scenarioId}/run/${runId}/wait`, { method: 'POST' }); }, listRuns( scenarioId: string, page = 1, limit = 20, ): Promise> { return request(`/scenarios/${scenarioId}/runs?page=${page}&limit=${limit}`); }, exportScenario(id: string): Promise { return request(`/scenarios/${id}/export`); }, importScenario(payload: unknown): Promise { return request('/scenarios/import', { method: 'POST', body: JSON.stringify(payload), }); }, }; // ── Runs ───────────────────────────────────────────────────────────────────── export const runs = { listAll( page = 1, limit = 20, status?: string, ): Promise< PaginatedResponse< ScenarioRun & { stepRuns: ScenarioRunStep[]; scenario: { id: string; name: string } } > > { const q = new URLSearchParams({ page: String(page), limit: String(limit) }); if (status) q.set('status', status); return request(`/scenarios/runs?${q}`); }, list( scenarioId: string, page = 1, limit = 20, ): Promise> { return request(`/scenarios/${scenarioId}/runs?page=${page}&limit=${limit}`); }, get(scenarioId: string, runId: string, q?: string): Promise { const qs = q?.trim() ? `?q=${encodeURIComponent(q.trim())}` : ''; return request(`/scenarios/${scenarioId}/run/${runId}${qs}`); }, }; // ── Scenario Credentials ────────────────────────────────────────────────────── export const scenarioCredentials = { list(scenarioId: string): Promise { return request(`/scenarios/${scenarioId}/credentials`); }, add(scenarioId: string, credentialId: string, alias: string): Promise { return request(`/scenarios/${scenarioId}/credentials`, { method: 'POST', body: JSON.stringify({ credentialId, alias }), }); }, remove(scenarioId: string, scCredId: string): Promise { return request(`/scenarios/${scenarioId}/credentials/${scCredId}`, { method: 'DELETE', }); }, }; // ── Scenario Steps ──────────────────────────────────────────────────────────── export interface CreateStepPayload { title?: string; execCode?: string; validateCode?: string; } export interface UpdateStepPayload { title?: string; order?: number; execCode?: string; validateCode?: string; } export const steps = { get(scenarioId: string, stepId: string): Promise { return request(`/scenarios/${scenarioId}/steps/${stepId}`); }, create(scenarioId: string, payload: CreateStepPayload): Promise { return request(`/scenarios/${scenarioId}/steps`, { method: 'POST', body: JSON.stringify(payload), }); }, update(scenarioId: string, stepId: string, payload: UpdateStepPayload): Promise { return request(`/scenarios/${scenarioId}/steps/${stepId}`, { method: 'PATCH', body: JSON.stringify(payload), }); }, remove(scenarioId: string, stepId: string): Promise { return request(`/scenarios/${scenarioId}/steps/${stepId}`, { method: 'DELETE' }); }, };