// 引擎 API 客户端。所有业务 URL 均由后端场景 Schema 给出(commandApi/optionsSource),前端不硬编码业务地址。 // 主体身份经 X-Principal 头传递(演示态;生产应换真实认证)。 const BASE = '/api' async function get(path: string): Promise { const r = await fetch(BASE + path) if (!r.ok) throw new Error(`GET ${path} -> ${r.status}`) return r.json() } async function post(path: string, body: any, principal?: string): Promise { return send('POST', path, body, principal) } async function send(method: string, path: string, body: any, principal?: string): Promise { const headers: Record = { 'Content-Type': 'application/json' } if (principal) headers['X-Principal'] = principal const r = await fetch(BASE + path, { method, headers, body: JSON.stringify(body) }) if (!r.ok) { let msg = `${method} ${path} -> ${r.status}` try { const j = await r.json(); if (j?.message) msg = j.message } catch { /* ignore */ } throw new Error(msg) } return r.json() } export const api = { scenes: () => get('/meta/scenes'), sceneSchema: (sceneId: string) => get(`/meta/scene/${sceneId}`), aggregate: (aggregateId: string) => get(`/meta/aggregate/${aggregateId}`), models: () => get('/meta/models'), model: (code: string) => get(`/meta/model/${code}`), saveModel: (code: string, content: string, principal?: string) => send('PUT', `/meta/model/${code}`, { content }, principal), reload: (principal?: string) => post('/meta/reload', {}, principal), options: (aggregateId: string) => get(`/data/${aggregateId}`), list: (aggregateId: string) => get(`/data/${aggregateId}/list`), events: () => get('/data/events/recent'), precheck: (sceneId: string, payload: any, principal?: string) => post(`/scene/${sceneId}/precheck`, payload, principal), execute: (sceneId: string, payload: any, principal?: string) => post(`/scene/${sceneId}/execute`, payload, principal), }