// SPDX-License-Identifier: EUPL-1.2 /** * ScmApi provides a typed fetch wrapper for the /api/v1/scm/* endpoints. */ export class ScmApi { constructor(private baseUrl: string = '') {} private get base(): string { return `${this.baseUrl}/api/v1/scm`; } private async request(path: string, opts?: RequestInit): Promise { const res = await fetch(`${this.base}${path}`, opts); const json = await res.json().catch(() => null); if (!res.ok) { throw new Error(json?.error?.message ?? `Request failed (${res.status})`); } if (!json?.success) { throw new Error(json?.error?.message ?? 'Request failed'); } return json.data as T; } marketplace(query?: string, category?: string) { const params = new URLSearchParams(); if (query) params.set('q', query); if (category) params.set('category', category); const qs = params.toString(); return this.request(`/marketplace${qs ? `?${qs}` : ''}`); } marketplaceItem(code: string) { return this.request(`/marketplace/${encodeURIComponent(code)}`); } install(code: string) { return this.request(`/marketplace/${encodeURIComponent(code)}/install`, { method: 'POST' }); } remove(code: string) { return this.request(`/marketplace/${encodeURIComponent(code)}`, { method: 'DELETE' }); } installed() { return this.request('/installed'); } updateInstalled(code: string) { return this.request(`/installed/${encodeURIComponent(code)}/update`, { method: 'POST' }); } manifest() { return this.request('/manifest'); } verify(publicKey: string) { return this.request('/manifest/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ public_key: publicKey }), }); } sign(privateKey: string) { return this.request('/manifest/sign', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ private_key: privateKey }), }); } permissions() { return this.request('/manifest/permissions'); } registry() { return this.request('/registry'); } }