export function createNotificationCoreClient({ baseUrl, token }) { const normalizedBaseUrl = normalizeBaseUrl(baseUrl); const internalToken = typeof token === "string" ? token.trim() : ""; return { isConfigured() { return Boolean(normalizedBaseUrl && internalToken); }, async createEvent(command) { return requestJson({ baseUrl: normalizedBaseUrl, token: internalToken, method: "POST", pathname: "/internal/notifications/events", body: command, }); }, async listNotifications({ subject, surface, limit, unreadOnly }) { const searchParams = new URLSearchParams(); searchParams.set("surface", surface || "hub"); if (limit) searchParams.set("limit", String(limit)); if (unreadOnly) searchParams.set("unreadOnly", "1"); return requestJson({ baseUrl: normalizedBaseUrl, token: internalToken, method: "GET", pathname: `/api/notifications?${searchParams.toString()}`, subject, }); }, async markNotificationRead({ subject, deliveryId }) { return requestJson({ baseUrl: normalizedBaseUrl, token: internalToken, method: "POST", pathname: `/api/notifications/${encodeURIComponent(deliveryId)}/read`, subject, }); }, async markAllNotificationsRead({ subject, surface }) { const searchParams = new URLSearchParams(); searchParams.set("surface", surface || "hub"); return requestJson({ baseUrl: normalizedBaseUrl, token: internalToken, method: "POST", pathname: `/api/notifications/read-all?${searchParams.toString()}`, subject, }); }, async openNotificationStream({ subject, surface, signal }) { if (!normalizedBaseUrl || !internalToken) { throw new Error("notification_core_not_configured"); } const searchParams = new URLSearchParams(); searchParams.set("surface", surface || "hub"); const response = await fetch(`${normalizedBaseUrl}/api/notifications/stream?${searchParams.toString()}`, { method: "GET", headers: buildHeaders(internalToken, subject), signal, }); if (!response.ok || !response.body) { throw new Error(await readError(response)); } return response; }, }; } async function requestJson({ baseUrl, token, method, pathname, body, subject }) { if (!baseUrl || !token) { throw new Error("notification_core_not_configured"); } const response = await fetch(`${baseUrl}${pathname}`, { method, headers: buildHeaders(token, subject, body !== undefined), body: body === undefined ? undefined : JSON.stringify(body), }); let parsed = null; try { parsed = await response.json(); } catch {} if (!response.ok) { const error = new Error(parsed?.error ? String(parsed.error) : `notification_core_http_${response.status}`); error.status = response.status; error.payload = parsed; throw error; } return parsed; } function buildHeaders(token, subject, hasJsonBody = false) { const headers = { Accept: "application/json", Authorization: `Bearer ${token}`, }; if (hasJsonBody) headers["Content-Type"] = "application/json"; if (subject?.userId) headers["X-NODEDC-User-Id"] = subject.userId; if (subject?.email) headers["X-NODEDC-User-Email"] = subject.email; return headers; } async function readError(response) { try { const parsed = await response.json(); if (parsed?.error) return String(parsed.error); } catch {} return `notification_core_http_${response.status}`; } function normalizeBaseUrl(value) { return typeof value === "string" && value.trim() ? value.trim().replace(/\/+$/, "") : ""; }