/** * meta-core API Client for writing metadata * Fails gracefully if meta-core is unavailable (logs warning, returns without throwing) */ export class MetaCoreClient { constructor(private baseUrl: string) {} private async safeFetch(url: string, options: RequestInit): Promise { try { return await fetch(url, { ...options, signal: AbortSignal.timeout(5000) }); } catch (error) { console.warn(`[MetaCoreClient] Warning: meta-core unavailable at ${this.baseUrl}`); return null; } } async setProperty(hashId: string, key: string, value: string): Promise { const response = await this.safeFetch(`${this.baseUrl}/meta/${hashId}/${key}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value }), }); if (response && !response.ok) { console.warn(`[MetaCoreClient] Failed to set property: ${response.status}`); } } async getProperty(hashId: string, key: string): Promise { const response = await this.safeFetch(`${this.baseUrl}/meta/${hashId}/${key}`, { method: 'GET' }); if (!response || response.status === 404) return null; if (!response.ok) return null; const data = await response.json() as { value?: string }; return data.value ?? null; } async mergeMetadata(hashId: string, metadata: Record): Promise { const response = await this.safeFetch(`${this.baseUrl}/meta/${hashId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(metadata), }); if (response && !response.ok) { console.warn(`[MetaCoreClient] Failed to merge metadata: ${response.status}`); } } async deleteProperty(hashId: string, key: string): Promise { const response = await this.safeFetch(`${this.baseUrl}/meta/${hashId}/${key}`, { method: 'DELETE' }); if (response && !response.ok && response.status !== 404) { console.warn(`[MetaCoreClient] Failed to delete property: ${response.status}`); } } async addToSet(hashId: string, key: string, value: string): Promise { const response = await this.safeFetch(`${this.baseUrl}/meta/${hashId}/_add/${key}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value }), }); if (response && !response.ok) { console.warn(`[MetaCoreClient] Failed to add to set: ${response.status}`); } } async getMetadata(hashId: string): Promise> { const response = await this.safeFetch(`${this.baseUrl}/meta/${hashId}`, { method: 'GET' }); if (!response || response.status === 404) return {}; if (!response.ok) return {}; const data = await response.json() as { metadata?: Record }; return data.metadata ?? {}; } }