@brandfine/client 0.14.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @brandfine/client
2
2
 
3
+ ## 0.14.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 90e060c: Live chat: `ChatMessageSender` gains `'BOT'` — AI-assistant replies that
8
+ arrive once a workspace enables the live-chat AI agent. Render `BOT` (and
9
+ any future unknown sender value) on the business/agent side rather than
10
+ switching exhaustively; the floating widget already does. Type-level
11
+ change only — no runtime behavior changes until the AI agent ships
12
+ server-side.
13
+
3
14
  ## 0.14.0
4
15
 
5
16
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/live-chat-session.ts","../src/client.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Headless Live Chat session — the conversation WITHOUT the widget.\n *\n * `bf.liveChat.createSession()` gives a consumer everything needed to\n * render chat inline in their own design system: transcript state, a\n * send method, and a subscribe API shaped for\n * `useSyncExternalStore` / Svelte stores / Vue refs. Brandfine keeps\n * owning transport, identity, threading and storage; the consumer\n * owns pixels.\n *\n * Storage keys are IDENTICAL to the floating widget's, so a visitor\n * who talks through the widget on one page and an inline panel on\n * another continues the same thread.\n */\n\nexport type ChatMessageSender = 'VISITOR' | 'AGENT' | 'SYSTEM'\n\nexport type ChatMessage = {\n id: string\n body: string\n sender: ChatMessageSender\n createdAt: string\n /** Monotonic per-conversation sequence — the realtime resume\n * cursor. Optional: older APIs don't send it. */\n eventId?: number\n}\n\nexport type ConversationStatus = 'OPEN' | 'CLOSED'\n\nexport type LiveChatSessionState = 'CONNECTING' | 'OPEN' | 'CLOSED' | 'ERROR'\n\n/** The `/external/live-chat/config` shape — the RUNTIME config the\n * widget fetches per page load. Distinct from the server-side\n * `LiveChatBootstrap` (which additionally carries the publishable\n * key + script path). */\nexport type LiveChatRuntimeConfig =\n | { enabled: false }\n | {\n enabled: true\n greeting: string | null\n offlineMessage: string | null\n theme: Record<string, string> | null\n online?: boolean\n /** Realtime endpoint (wss://…). Absent = poll (older API, or\n * the transport is off). The session upgrades automatically\n * when present; consumers never touch it. */\n realtimeUrl?: string\n }\n\nexport type LiveChatSessionSnapshot = {\n messages: ChatMessage[]\n status: LiveChatSessionState\n /** Agents available right now (business-hours based today; agent\n * presence once the realtime transport lands). */\n online: boolean\n /** Localized via the session's `locale`. */\n greeting: string | null\n offlineMessage: string | null\n /** An outbound `send()` is in flight. */\n sending: boolean\n error: Error | null\n}\n\nexport type StartConversationResult =\n | { enabled: false }\n | {\n enabled: true\n conversationId: string\n conversationToken: string\n greeting: string | null\n resumed: boolean\n online?: boolean\n }\n\n/** Wire access the session needs — provided by the SDK client so the\n * session never hardcodes endpoints or headers. */\nexport type LiveChatWire = {\n runtimeConfig(locale?: string): Promise<LiveChatRuntimeConfig>\n startConversation(input: {\n visitorSessionId: string\n conversationToken?: string\n pageUrl?: string\n referrer?: string\n locale?: string\n visitor?: Record<string, unknown>\n }): Promise<StartConversationResult>\n sendMessage(\n conversationToken: string,\n input: { body: string; clientId?: string },\n ): Promise<ChatMessage>\n history(\n conversationToken: string,\n opts?: { after?: string; afterEvent?: number },\n ): Promise<{ messages: ChatMessage[]; status: ConversationStatus }>\n}\n\nexport type LiveChatCreateSessionOptions = {\n /** The server-half bootstrap (same object `install()` takes) —\n * supplies the publishable key (and, when the API advertises it,\n * the realtime endpoint — passed through wholesale). */\n config: { enabled: boolean; publishableKey?: string; realtimeUrl?: string }\n /** Signed identity — SAME shape and rules as `install()`. A bad\n * signature downgrades to anonymous server-side; it is never a\n * second identity path. */\n visitor?: Record<string, unknown>\n /** BCP-47 tag for greeting/away localization. Defaults to\n * `<html lang>` then browser language, like the widget. */\n locale?: string\n /** Defaults to `location.href`. */\n pageUrl?: string\n referrer?: string\n /** Transcript refresh cadence. Polling is the transport today; a\n * realtime upgrade will keep this as the fallback. */\n pollIntervalMs?: number\n}\n\nexport type LiveChatSession = {\n /** Current transcript (same array identity as the latest snapshot). */\n readonly messages: readonly ChatMessage[]\n readonly state: LiveChatSessionState\n send(body: string): Promise<ChatMessage>\n /** Listener fires on every snapshot change. Returns unsubscribe. */\n subscribe(listener: (s: LiveChatSessionSnapshot) => void): () => void\n getSnapshot(): LiveChatSessionSnapshot\n /** Stop polling, abort in-flight work, drop listeners. Idempotent. */\n close(): void\n /** Drop the stored conversation token and start a fresh thread —\n * identity switch on a shared browser. */\n reset(): Promise<void>\n}\n\n// ── Storage (MUST stay in lockstep with packages/widget-live-chat) ──\n\nconst SESSION_STORAGE_KEY = 'bf-live-chat-session'\n\nfunction tokenStorageKey(publishableKey: string) {\n return `bf-live-chat-token:${publishableKey.slice(-12)}`\n}\n\nfunction getVisitorSessionId(): string {\n try {\n const existing = localStorage.getItem(SESSION_STORAGE_KEY)\n if (existing) return existing\n const generated =\n typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID().replace(/-/g, '')\n : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n localStorage.setItem(SESSION_STORAGE_KEY, generated)\n return generated\n } catch {\n return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n }\n}\n\nfunction readStoredToken(publishableKey: string): string | undefined {\n try {\n return localStorage.getItem(tokenStorageKey(publishableKey)) ?? undefined\n } catch {\n return undefined\n }\n}\n\nfunction writeStoredToken(publishableKey: string, token: string | null) {\n try {\n if (token === null) localStorage.removeItem(tokenStorageKey(publishableKey))\n else localStorage.setItem(tokenStorageKey(publishableKey), token)\n } catch {\n /* storage blocked — resume just won't survive reloads */\n }\n}\n\nfunction resolvePageLocale(explicit?: string): string | undefined {\n if (explicit) return explicit\n if (typeof document === 'undefined') return undefined\n const htmlLang = document.documentElement.lang?.trim()\n if (htmlLang) return htmlLang\n return typeof navigator !== 'undefined'\n ? navigator.language || undefined\n : undefined\n}\n\n// ── The session ─────────────────────────────────────────────────────\n\nexport async function createLiveChatSession(\n wire: LiveChatWire,\n opts: LiveChatCreateSessionOptions,\n): Promise<LiveChatSession> {\n if (typeof window === 'undefined') {\n throw new Error(\n 'liveChat.createSession() is browser-only — build the transcript UI ' +\n 'client-side; fetch the bootstrap with getConfig() on the server.',\n )\n }\n const publishableKey = opts.config.enabled\n ? opts.config.publishableKey\n : undefined\n\n const locale = resolvePageLocale(opts.locale)\n const pollMs = Math.max(1000, opts.pollIntervalMs ?? 5000)\n\n let snapshot: LiveChatSessionSnapshot = {\n messages: [],\n status: 'CONNECTING',\n online: false,\n greeting: null,\n offlineMessage: null,\n sending: false,\n error: null,\n }\n const listeners = new Set<(s: LiveChatSessionSnapshot) => void>()\n const seen = new Set<string>()\n let cursor: string | undefined\n let lastEventId = 0\n let token: string | undefined\n let timer: ReturnType<typeof setInterval> | undefined\n let closed = false\n let ticking = false\n const aborter = new AbortController()\n\n // ── Realtime transport state (see startRealtime below) ──\n let realtimeUrl: string | undefined = opts.config.realtimeUrl\n let ws: WebSocket | undefined\n let wsLive = false\n let wsFailures = 0\n let wsGivenUp = false\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined\n let pingTimer: ReturnType<typeof setInterval> | undefined\n\n function emit(patch: Partial<LiveChatSessionSnapshot>) {\n snapshot = { ...snapshot, ...patch }\n for (const listener of listeners) listener(snapshot)\n }\n\n function appendMessages(incoming: ChatMessage[]) {\n const fresh = incoming.filter((m) => !seen.has(m.id))\n if (fresh.length === 0) {\n // Even a full dupe batch may advance the event cursor (e.g. the\n // socket redelivers what a poll already fetched).\n for (const m of incoming) {\n if (typeof m.eventId === 'number' && m.eventId > lastEventId) {\n lastEventId = m.eventId\n }\n }\n return\n }\n for (const m of fresh) {\n seen.add(m.id)\n if (typeof m.eventId === 'number' && m.eventId > lastEventId) {\n lastEventId = m.eventId\n }\n }\n const messages = [...snapshot.messages, ...fresh]\n cursor = messages[messages.length - 1]!.createdAt\n emit({ messages })\n }\n\n function markClosed() {\n if (snapshot.status !== 'CLOSED') emit({ status: 'CLOSED' })\n stopPolling()\n teardownRealtime()\n wsGivenUp = true // a closed thread never reconnects; reset() re-arms\n }\n\n async function tick() {\n // Re-entrancy guard: a slow response must not overlap the next\n // interval firing (double-fetch → possible reorder).\n if (ticking || closed || !token) return\n ticking = true\n try {\n const result = await wire.history(\n token,\n // The eventId cursor is exact (no equal-millisecond drop\n // window); the timestamp cursor remains for old APIs that\n // never sent eventIds.\n lastEventId > 0 ? { afterEvent: lastEventId } : { after: cursor },\n )\n if (closed) return\n appendMessages(result.messages)\n if (result.status === 'CLOSED') markClosed()\n } catch {\n /* transient poll failure — next tick retries */\n } finally {\n ticking = false\n }\n }\n\n function startPolling() {\n stopPolling()\n timer = setInterval(() => void tick(), pollMs)\n }\n function stopPolling() {\n if (timer !== undefined) clearInterval(timer)\n timer = undefined\n }\n\n // ── Realtime transport (delivery-only WebSocket) ────────────────\n //\n // Invisible above the session API by contract: same subscribe,\n // same snapshots. While the socket is live, polling stops; the\n // moment it isn't (drop, backoff wait, permanent failure, no\n // WebSocket in the runtime, no realtimeUrl advertised), polling\n // covers the gap exactly as before. Sends stay on REST.\n\n function teardownRealtime() {\n if (reconnectTimer !== undefined) clearTimeout(reconnectTimer)\n reconnectTimer = undefined\n if (pingTimer !== undefined) clearInterval(pingTimer)\n pingTimer = undefined\n wsLive = false\n if (ws) {\n const socket = ws\n ws = undefined\n try {\n socket.onopen = socket.onmessage = socket.onclose = socket.onerror =\n null\n socket.close()\n } catch {\n /* already closed */\n }\n }\n }\n\n function scheduleReconnect() {\n if (closed || wsGivenUp || reconnectTimer !== undefined) return\n // 1s → 30s exponential with jitter; after 5 consecutive\n // failures, stay on polling for the session's lifetime — a\n // socket a corporate proxy kills will never open, and the user\n // still gets their messages.\n if (wsFailures >= 5) {\n wsGivenUp = true\n return\n }\n const base = Math.min(30_000, 1_000 * 2 ** wsFailures)\n const delay = base / 2 + Math.random() * (base / 2)\n reconnectTimer = setTimeout(() => {\n reconnectTimer = undefined\n startRealtime()\n }, delay)\n }\n\n function handleSocketDown() {\n teardownRealtime()\n if (closed || wsGivenUp || snapshot.status === 'CLOSED') return\n // Polling covers the gap from the FIRST millisecond of the\n // outage; a later successful socket auth stops it again.\n startPolling()\n wsFailures += 1\n scheduleReconnect()\n }\n\n function startRealtime() {\n if (\n closed ||\n wsGivenUp ||\n wsLive ||\n ws !== undefined ||\n !realtimeUrl ||\n !token ||\n !publishableKey ||\n typeof WebSocket === 'undefined' ||\n snapshot.status === 'CLOSED'\n ) {\n return\n }\n let socket: WebSocket\n try {\n socket = new WebSocket(realtimeUrl)\n } catch {\n wsFailures += 1\n scheduleReconnect()\n return\n }\n ws = socket\n socket.onopen = () => {\n // Auth in the FIRST FRAME, never the URL — tokens must not\n // land in proxy logs or browser history.\n try {\n socket.send(\n JSON.stringify({\n type: 'auth',\n publishableKey,\n conversationToken: token,\n lastEventId,\n }),\n )\n } catch {\n handleSocketDown()\n }\n }\n socket.onmessage = (event) => {\n let frame: { type?: string } & Record<string, unknown>\n try {\n frame = JSON.parse(String(event.data)) as typeof frame\n } catch {\n return // unknown/garbage frames are ignored by contract\n }\n switch (frame.type) {\n case 'ready': {\n wsLive = true\n wsFailures = 0\n stopPolling() // the socket is now the transport\n if (pingTimer === undefined) {\n pingTimer = setInterval(() => {\n try {\n socket.send(JSON.stringify({ type: 'ping' }))\n } catch {\n /* close race — the onclose handler recovers */\n }\n }, 30_000)\n }\n break\n }\n case 'message': {\n const m = frame.message as ChatMessage | undefined\n if (m && typeof m.id === 'string') {\n appendMessages([\n {\n ...m,\n eventId:\n typeof frame.eventId === 'number' ? frame.eventId : m.eventId,\n },\n ])\n }\n break\n }\n case 'status': {\n if (frame.status === 'CLOSED') markClosed()\n break\n }\n case 'error': {\n // The server refused us (bad auth, socket caps…). Fall\n // back to polling permanently — retrying a refusal is\n // just noise, and delivery still works.\n wsGivenUp = true\n teardownRealtime()\n if (!closed && snapshot.status !== 'CLOSED') startPolling()\n break\n }\n default:\n break // pong + future frame types\n }\n }\n socket.onclose = () => handleSocketDown()\n socket.onerror = () => {\n /* onclose always follows; avoid double-handling */\n }\n }\n\n async function connect(resumeToken: string | undefined) {\n if (!publishableKey) {\n emit({ status: 'CLOSED' })\n return\n }\n // Config first — greeting/away/online, localized.\n try {\n const cfg = await wire.runtimeConfig(locale)\n if (closed) return\n if (!cfg.enabled) {\n emit({ status: 'CLOSED' })\n return\n }\n emit({\n greeting: cfg.greeting,\n offlineMessage: cfg.offlineMessage,\n online: cfg.online !== false,\n })\n // Runtime config wins over the build-time bootstrap — it's\n // fetched live, so switching realtime off takes effect on the\n // next page load without a redeploy.\n if (cfg.realtimeUrl !== undefined) realtimeUrl = cfg.realtimeUrl\n } catch (error) {\n if (closed) return\n emit({ status: 'ERROR', error: error as Error })\n return\n }\n\n try {\n const started = await wire.startConversation({\n visitorSessionId: getVisitorSessionId(),\n conversationToken: resumeToken,\n pageUrl:\n opts.pageUrl ??\n (typeof location !== 'undefined'\n ? location.href.slice(0, 2048)\n : undefined),\n referrer: opts.referrer,\n locale,\n visitor: opts.visitor,\n })\n if (closed) return\n if (!started.enabled) {\n emit({ status: 'CLOSED' })\n return\n }\n token = started.conversationToken\n writeStoredToken(publishableKey, token)\n if (started.online !== undefined) emit({ online: started.online })\n emit({ status: 'OPEN' })\n await tick() // initial history before the first interval\n if (!closed) {\n startPolling()\n startRealtime() // upgrades in place; polling stops on `ready`\n }\n } catch (error) {\n if (closed) return\n emit({ status: 'ERROR', error: error as Error })\n }\n }\n\n const session: LiveChatSession = {\n get messages() {\n return snapshot.messages\n },\n get state() {\n return snapshot.status\n },\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n async send(body) {\n const trimmed = body.trim()\n if (!trimmed) throw new Error('send(): message body is empty')\n if (!token) throw new Error('send(): session is not connected')\n if (snapshot.status === 'CLOSED') {\n throw new Error('send(): conversation is closed — call reset()')\n }\n emit({ sending: true })\n try {\n // Idempotency key: if the response is lost mid-flight, a\n // consumer retry with the same body can't duplicate — the\n // server replays the original row for this clientId.\n const clientId =\n typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID()\n : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n const message = await wire.sendMessage(token, {\n body: trimmed,\n clientId,\n })\n appendMessages([message])\n return message\n } finally {\n emit({ sending: false })\n }\n },\n close() {\n if (closed) return\n closed = true\n stopPolling()\n teardownRealtime()\n aborter.abort()\n listeners.clear()\n },\n async reset() {\n // Socket FIRST — a socket left attached to the old conversation\n // would defeat the shared-browser guarantee reset() exists for.\n teardownRealtime()\n wsFailures = 0\n wsGivenUp = false\n if (publishableKey) writeStoredToken(publishableKey, null)\n token = undefined\n cursor = undefined\n lastEventId = 0\n seen.clear()\n stopPolling()\n emit({ messages: [], status: 'CONNECTING', error: null })\n await connect(undefined)\n },\n }\n\n await connect(publishableKey ? readStoredToken(publishableKey) : undefined)\n return session\n}\n","/**\n * `createBrandfineClient` — the SDK's entry point.\n *\n * Returns a stateless, multi-instance-safe handle scoped to a\n * single `(baseUrl, apiKey)` pair. Pattern follows the Stripe /\n * Algolia / OpenAI SDKs — explicit construction with config,\n * namespaced methods (`bf.posts.list(...)`, `bf.workspace.get()`),\n * no module-level singletons.\n *\n * Why factory not module-level state: multi-tenant consumers\n * sometimes need two clients in the same process (e.g. main site\n * + admin preview). Module-level env reading makes that impossible\n * without monkey-patching.\n */\n\nimport type {\n BrandfineCategory,\n BrandfineNavigation,\n BrandfinePost,\n BrandfinePostListResponse,\n BrandfineWorkspace,\n ListCategoriesOptions,\n ListPostsOptions,\n} from './types'\n\nimport {\n createLiveChatSession,\n type ChatMessage,\n type ConversationStatus,\n type LiveChatCreateSessionOptions,\n type LiveChatRuntimeConfig,\n type LiveChatSession,\n type LiveChatWire,\n type StartConversationResult,\n} from './live-chat-session'\n\nexport type {\n ChatMessage,\n ChatMessageSender,\n ConversationStatus,\n LiveChatCreateSessionOptions,\n LiveChatRuntimeConfig,\n LiveChatSession,\n LiveChatSessionSnapshot,\n LiveChatSessionState,\n StartConversationResult,\n} from './live-chat-session'\n\nexport type BrandfineClientConfig = {\n /** Base URL of the Brandfine API. OPTIONAL — defaults to the\n * production API (`https://api.brandfine.co`); on the server the\n * `BRANDFINE_API_URL` env var overrides the default (set it for\n * local dev / staging). Explicit option wins over both. No\n * trailing slash — the client trims one if you pass it anyway. */\n baseUrl?: string\n /** Workspace-scoped API key. Generated from the cms's Workspace\n * settings; identifies which workspace the client talks to. */\n apiKey: string\n /** Optional fetch override. Useful for tests (inject a stub),\n * for runtimes that need a custom implementation (edge workers\n * with non-standard fetch), or to add cross-cutting concerns\n * like tracing / retries. Defaults to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch\n /** Optional User-Agent header. Falls back to a generic SDK tag. */\n userAgent?: string\n}\n\n/**\n * Structured error thrown by every request helper on non-2xx\n * responses. Carries the raw body so consumers can log it for\n * debugging without re-fetching.\n */\nexport class BrandfineApiError extends Error {\n override readonly name = 'BrandfineApiError'\n readonly status: number\n readonly statusText: string\n readonly body: string\n readonly url: string\n\n constructor(args: {\n status: number\n statusText: string\n body: string\n url: string\n }) {\n super(\n `[brandfine] ${args.status} ${args.statusText} on ${args.url} — ${args.body.slice(0, 200)}`,\n )\n this.status = args.status\n this.statusText = args.statusText\n this.body = args.body\n this.url = args.url\n }\n}\n\ntype RequestOptions = {\n /** When true and the response is 404, return `null` instead of\n * throwing. Used by endpoints where 404 is a meaningful empty\n * state (navigation by key, single post by slug). */\n nullable404?: boolean\n signal?: AbortSignal\n}\n\nexport type BrandfineClient = {\n /** Low-level GET. Reserved for endpoints we don't have a typed\n * helper for yet. Adds the X-Api-Key header automatically. */\n get: <T>(path: string, opts?: RequestOptions) => Promise<T>\n posts: PostsApi\n categories: CategoriesApi\n workspace: WorkspaceApi\n navigations: NavigationsApi\n analytics: AnalyticsApi\n submissions: SubmissionsApi\n appointments: AppointmentsApi\n liveChat: LiveChatApi\n}\n\ntype PostsApi = {\n /** Paginated list of published posts. Handles the cms's\n * pagination transparently — caller gets a flat array. */\n list: <TConfig = unknown>(\n opts?: ListPostsOptions,\n ) => Promise<BrandfinePost<TConfig>[]>\n /** Single post by per-locale URL slug, scoped to the active\n * locale on the workspace's content. Returns `null` for 404 so\n * callers can render their own \"not found\" page without try/catch. */\n getBySlug: <TConfig = unknown>(\n slug: string,\n ) => Promise<BrandfinePost<TConfig> | null>\n}\n\ntype CategoriesApi = {\n list: (opts?: ListCategoriesOptions) => Promise<BrandfineCategory[]>\n}\n\ntype WorkspaceApi = {\n get: <\n TCustomConfig = Record<string, unknown>,\n TSchemaOrg = Record<string, unknown>,\n >() => Promise<BrandfineWorkspace<TCustomConfig, TSchemaOrg>>\n}\n\ntype NavigationsApi = {\n /** Navigation by its workspace-scoped `key` (e.g. `'header'`).\n * Returns `null` for 404 so consumers can fall back to a\n * hardcoded default without try/catch. `TConfig` narrows each\n * item's `customConfig` (default `unknown`). */\n get: <TConfig = unknown>(\n key: string,\n ) => Promise<BrandfineNavigation<TConfig> | null>\n}\n\nexport type CreateSubmissionInput = {\n /** Required. Display name of the submitter. */\n name: string\n /** Required. Validated server-side. */\n email: string\n /** Optional. Free-text up to 40 chars. */\n phone?: string\n /** Optional. Free-text up to 200 chars. */\n subject?: string\n /** Required. The message body — up to 10,000 chars. */\n message: string\n /** Optional. Where the submission came from — e.g. a route path\n * like `/contact`, or a marketing campaign label. Up to 500 chars. */\n source?: string\n /** Optional. Free-form JSON metadata the consumer attaches; the\n * cms surfaces it verbatim in the submissions admin view. */\n metadata?: Record<string, unknown>\n}\n\nexport type Submission = {\n id: string\n createdAt: string\n}\n\ntype SubmissionsApi = {\n /**\n * Posts a contact-form submission to `POST /external/submissions`\n * for this workspace. The cms surfaces the submission in the\n * Submissions inbox.\n *\n * Throws `BrandfineApiError` on validation failures (400) or\n * any other non-2xx — caller decides whether to surface that as\n * a user-visible error or a silent retry.\n */\n create: (input: CreateSubmissionInput) => Promise<Submission>\n}\n\n// ----------------------------------------------------------------\n// Appointments plugin SDK — pairs with the Appointments embed\n// widget. Consumers who want full control over the booking UI use\n// these methods directly; consumers who want the drop-in widget\n// use the `<script>` embed (which itself uses these methods under\n// the hood). The same `BrandfineClient` instance powers both.\n// ----------------------------------------------------------------\n\nexport type AppointmentSlot = {\n /** UTC ISO 8601 timestamp of the slot start. */\n start: string\n /** UTC ISO 8601 timestamp of the slot end. */\n end: string\n}\n\nexport type AppointmentAvailability = {\n /** False = plugin not activated, or activation row's `enabled`\n * flag is off. Widgets should render a \"not accepting bookings\"\n * state, not throw. */\n enabled: boolean\n /** Source-of-truth IANA timezone for the workspace's business\n * hours. Visitors see slots in their local TZ — use this for\n * the \"(workspace local: HH:MM)\" subtext. */\n timezone: string\n slotDurationMinutes: number\n leadTimeHours: number\n bookingWindowDays: number\n policyText: string | null\n slots: AppointmentSlot[]\n /** UTC ISO 8601. Useful for the widget's date range label. */\n windowStart: string\n windowEnd: string\n}\n\nexport type CreateAppointmentRequestInput = {\n visitorName: string\n visitorEmail: string\n visitorPhone?: string\n visitorMessage?: string\n /** UTC ISO 8601 of the requested slot start. Server re-validates\n * against business hours + busy ranges before accepting. */\n requestedAt: string\n /** Optional cookie-derived session id from the consumer site. */\n visitorSessionId?: string\n /** Signed host-app identity — the SAME payload shape and signature\n * Live Chat accepts, so one `liveChat.identityToken(externalId)`\n * call signs for both plugins. When present and valid, the booking\n * is attributed to the person (verified name/email take precedence\n * over the free-text fields above, and the booking links to their\n * chat threads via the shared externalId). Invalid or absent →\n * the booking proceeds anonymously — it is never rejected. */\n visitor?: VerifiedVisitor\n}\n\nexport type CreatedAppointmentRequest = {\n id: string\n createdAt: string\n requestedAt: string\n durationMinutes: number\n status: 'PENDING'\n /** True iff `visitor.identityToken` HMAC-verified — your signal\n * that the booking landed attributed rather than anonymous. */\n identityVerified: boolean\n /** Visitor's self-cancel token. Embed it in confirmation\n * emails / on-page UI so the visitor can cancel without an\n * account. One-time use; revoked once any party acts. */\n cancellationToken: string | null\n}\n\nexport type AppointmentStatus =\n | 'PENDING'\n | 'CONFIRMED'\n | 'REJECTED'\n | 'CANCELLED'\n\n/** One appointment as the identity-scoped read-back returns it —\n * the visitor-safe projection (\"where does my request stand?\"). */\nexport type Appointment = {\n id: string\n status: AppointmentStatus\n /** UTC ISO 8601 of the requested slot start (reflects the current\n * slot after a reschedule). */\n requestedAt: string\n /** The confirmed slot — non-null only once CONFIRMED. */\n scheduledAt: string | null\n durationMinutes: number\n /** Workspace's IANA timezone for local rendering. */\n timezone: string\n /** Customer's note — populated only on REJECTED. */\n declineReason: string | null\n rescheduleCount: number\n respondedAt: string | null\n createdAt: string\n}\n\n/** Proof of identity for read-back calls: the same externalId +\n * `liveChat.identityToken(externalId)` pair used when booking. */\nexport type AppointmentIdentity = {\n externalId: string\n identityToken: string\n}\n\ntype AppointmentsApi = {\n /**\n * Available slots for the workspace's booking window.\n * `from` / `to` are optional clamps inside the workspace's\n * configured window — the server ignores ranges outside.\n */\n getAvailability: (opts?: {\n from?: Date | string\n to?: Date | string\n }) => Promise<AppointmentAvailability>\n /**\n * Submit a visitor's appointment request. Server-side validates\n * the slot is still bookable; if it isn't, throws\n * `BrandfineApiError` with status 404 / 409.\n *\n * Pass `visitor` (signed with `liveChat.identityToken()`) to book\n * as a known person — that unlocks `list`/`get` read-back below.\n * Anonymous bookings remain fully supported; their status flow\n * stays email-driven via the cancellation-token link.\n */\n createRequest: (\n input: CreateAppointmentRequestInput,\n ) => Promise<CreatedAppointmentRequest>\n /**\n * Every appointment belonging to the verified person, newest\n * first. Requires a valid identity signature — throws\n * `BrandfineApiError` 401 on a bad one (reads need proof; there\n * is no anonymous downgrade for reading history). Server-side\n * only, like `identityToken()` itself.\n */\n list: (identity: AppointmentIdentity) => Promise<Appointment[]>\n /**\n * One appointment by id, identity-scoped. 404s when the id does\n * not exist OR belongs to someone else — indistinguishable by\n * design.\n */\n get: (id: string, identity: AppointmentIdentity) => Promise<Appointment>\n /**\n * Spend the one-time `cancellationToken` from `createRequest` to\n * cancel a still-PENDING request. The token IS the credential —\n * no id or API key needed. Throws `BrandfineApiError` 400 when\n * the request is no longer PENDING, 404 when the token is\n * unknown/already spent.\n */\n cancel: (\n cancellationToken: string,\n ) => Promise<{ id: string; status: 'CANCELLED' }>\n}\n\nexport type AnalyticsConfig =\n | {\n enabled: false\n /** GA4 Measurement ID — present when the workspace's Google\n * Analytics property was provisioned through Brandfine AND\n * the customer opted into tag injection. `install()` loads\n * gtag for it. Note gtag sets cookies: consent banners are\n * your site's responsibility. */\n gaMeasurementId?: string\n }\n | {\n enabled: true\n websiteId: string\n scriptUrl: string\n gaMeasurementId?: string\n }\n\nexport type AnalyticsOverviewRange = '24h' | '7d' | '30d' | '90d'\n\n/**\n * Composed traffic report for the workspace — summary KPIs +\n * bucketed chart data + top pages in one payload. Mirrors\n * `GET /external/analytics/overview` (see the API's\n * `ExternalAnalyticsOverview` type); additive changes only.\n *\n * Three shapes to handle:\n * - `{ enabled: false }` — analytics never enabled for the\n * workspace. Show an enable CTA.\n * - `{ enabled: true, verified: false }` — tracker provisioned\n * but no pageview recorded yet. Show \"waiting for first visit\".\n * - full payload — render the dashboard.\n */\nexport type AnalyticsOverview =\n | { enabled: false }\n | { enabled: true; verified: false }\n | {\n enabled: true\n verified: true\n range: AnalyticsOverviewRange\n summary: {\n visitors: number\n /** Fractional change vs the prior window (0.12 = +12%). */\n visitorsChange: number\n pageviews: number\n pageviewsChange: number\n visits: number\n visitsChange: number\n /** 0..1 fraction. */\n bounceRate: number\n bounceRateChange: number\n avgVisitSeconds: number\n avgVisitSecondsChange: number\n /** Visitors active in the last ~5 minutes. */\n activeNow: number\n }\n /** Bucketed chart data, oldest → newest. Hourly buckets for\n * `24h`, daily otherwise. `t` is an ISO-8601 bucket start. */\n timeseries: Array<{ t: string; visitors: number; pageviews: number }>\n /** Top 10 paths by views in the window. */\n topPages: Array<{ path: string; views: number; visitors: number }>\n /** Top 10 referrer sources by visitors. Empty-string source\n * means direct traffic. */\n sources: Array<{ source: string; visitors: number }>\n /** Top 10 visitor countries (ISO 3166-1 alpha-2 codes —\n * map to display names on your side, e.g. via\n * `Intl.DisplayNames`). */\n countries: Array<{ country: string; visitors: number }>\n /** Visitors by device class (`desktop` / `mobile` /\n * `tablet` / …). */\n devices: Array<{ device: string; visitors: number }>\n }\n\nexport type AnalyticsInstallResult =\n | { installed: false; reason: 'disabled' | 'ssr' | 'already-installed' }\n | { installed: true; websiteId: string }\n\nexport type InstallOptions = {\n /**\n * Pre-known config. When provided, `install()` skips the round-\n * trip to `/external/analytics-config` and injects the script\n * immediately. Use this when you've baked the values into your\n * build (env vars, CMS-side config dump, etc.) — typical for\n * static sites where the analytics state is decided at deploy\n * time, not per page load.\n *\n * Trade-off vs the default fetch path: if you disable analytics\n * in Brandfine, the tracker keeps loading until your next\n * deploy. That's usually the right trade for static sites\n * (which redeploy on every content change anyway) and the wrong\n * trade for dynamic sites where the api round-trip is cheap\n * relative to the rest of the page.\n *\n * Pass `{ enabled: false }` to force a no-op without touching\n * the api (e.g. to disable analytics for one environment without\n * changing Brandfine's state).\n */\n config?: AnalyticsConfig\n}\n\ntype AnalyticsApi = {\n /**\n * Injects the Brandfine analytics tracker into `document.head`\n * once. Safe to call on every page load — idempotent via a\n * marker attribute on the injected script tag.\n *\n * Two paths:\n * - `install()` — fetches the config from Brandfine, then\n * injects. Reflects enable/disable state on next page load.\n * - `install({ config })` — uses caller-provided config, skips\n * the fetch. Faster, no round-trip; ignores Brandfine state\n * changes until the consumer's next deploy.\n *\n * Returns details about what happened:\n * - `{ installed: true, websiteId }` — script was just injected.\n * - `{ installed: false, reason: 'disabled' }` — config says\n * analytics is off; no-op.\n * - `{ installed: false, reason: 'ssr' }` — no `document` in\n * scope (server-side). Call again on the client.\n * - `{ installed: false, reason: 'already-installed' }` — a\n * prior call (or another tab in the same SPA) already injected.\n *\n * Throws `BrandfineApiError` on non-2xx responses other than the\n * disabled case (which is a valid `{ enabled: false }` body).\n */\n install: (opts?: InstallOptions) => Promise<AnalyticsInstallResult>\n\n /** Lower-level helper — fetches the raw config without touching\n * the DOM. Useful when you want to inject the script yourself\n * (e.g. via a framework's <Script> component for nonce/csp). */\n getConfig: () => Promise<AnalyticsConfig>\n\n /**\n * Traffic report for the workspace — summary KPIs, bucketed\n * timeseries for charting, and top pages, in one round-trip.\n * This is a server-to-server read (it returns your site's\n * traffic data); call it from your backend or build step, not\n * from visitor-facing browser code.\n *\n * @param opts.range Window preset. Defaults to `'7d'`.\n */\n overview: (opts?: {\n range?: AnalyticsOverviewRange\n }) => Promise<AnalyticsOverview>\n}\n\nexport type LiveChatBootstrap =\n | { enabled: false }\n | {\n enabled: true\n /** The workspace's SCOPED publishable key — safe to bake into\n * public HTML; it can only start chat conversations. */\n publishableKey: string\n greeting: string | null\n offlineMessage: string | null\n theme: Record<string, string> | null\n /** Widget bundle path relative to the API base URL. */\n scriptPath: string\n /** Realtime endpoint (wss://…). OPTIONAL by design — this is\n * what makes the upgrade non-breaking: an older SDK ignores\n * it, a newer SDK against an older API sees it absent and\n * polls. Consumers pass `config` through wholesale, so it\n * reaches the browser with no change on their side. */\n realtimeUrl?: string\n }\n\nexport type LiveChatInstallResult =\n | { installed: false; reason: 'disabled' | 'ssr' | 'already-installed' }\n | { installed: true }\n\n/**\n * A signed visitor identity for Live Chat. Build it SERVER-SIDE:\n * compute `identityToken` with `liveChat.identityToken()` (or your\n * own HMAC_SHA256(identitySecret, externalId), hex) and pass the\n * whole object to `install()` — the widget forwards it verbatim and\n * the Brandfine API verifies the signature. An invalid or missing\n * token silently downgrades the conversation to anonymous.\n */\n/**\n * A signed host-app identity payload. Named per-plugin below for\n * discoverability, but it is ONE shape signed ONE way: a token from\n * `liveChat.identityToken(externalId)` is accepted by Live Chat AND\n * Appointments (`createRequest.visitor`, `list`/`get`).\n */\nexport type VerifiedVisitor = LiveChatVisitor\n\nexport type LiveChatVisitor = {\n /** Your app's stable id for this person (user id, lead reference…).\n * Conversations sharing an externalId are the same person across\n * devices and sessions. Max 128 chars. */\n externalId: string\n /** Display name shown in the Brandfine inbox. Max 120 chars. */\n name?: string\n /** Max 200 chars. */\n email?: string\n /** Small display-only key→string map (≤10 keys, values ≤100 chars). */\n attributes?: Record<string, string>\n /** hex HMAC_SHA256(identitySecret, externalId) — REQUIRED, computed\n * on your server. Never derive this in a browser. */\n identityToken: string\n}\n\nexport type LiveChatInstallOptions = {\n /**\n * Pre-known bootstrap. Same build-time/runtime trade-off as the\n * analytics `install()`: pass the value your server half fetched\n * (static-export sites bake it at build time), or omit — NOT\n * recommended in browsers, because fetching here would require\n * the broad key client-side. In practice: always pass `config`\n * from your server half.\n */\n config: LiveChatBootstrap\n /**\n * Already-signed visitor identity (see LiveChatVisitor). Optional —\n * omit for anonymous chat. The object must arrive from your server\n * with `identityToken` precomputed; `install()` only serializes it\n * onto the widget host, it performs no crypto.\n */\n visitor?: LiveChatVisitor\n /**\n * Locale for the widget's visitor-facing strings (greeting, away\n * message), e.g. your i18n router's active locale. Optional — the\n * widget falls back to the page's `<html lang>` and then the\n * browser language. Resolved server-side against the workspace's\n * configured translations; unknown locales get the default text.\n */\n locale?: string\n}\n\ntype LiveChatApi = {\n /**\n * Fetches the Live Chat bootstrap (publishable key + display\n * config) with the broad workspace key. SERVER-SIDE ONLY — call\n * from your backend, RSC, or build step, never from browser code.\n */\n getConfig: () => Promise<LiveChatBootstrap>\n\n /**\n * Injects the chat widget into the page: appends a host\n * `<div data-bf-live-chat>` carrying the publishable key and the\n * widget `<script>` tag. Idempotent via a marker attribute.\n * Browser-side half of the pair — receives the bootstrap your\n * server half fetched with `getConfig()`.\n *\n * Returns what happened, mirroring `analytics.install()`:\n * - `{ installed: true }` — widget just injected.\n * - `{ installed: false, reason: 'disabled' }` — chat is off.\n * - `{ installed: false, reason: 'ssr' }` — no `document`.\n * - `{ installed: false, reason: 'already-installed' }`.\n */\n install: (opts: LiveChatInstallOptions) => Promise<LiveChatInstallResult>\n\n /**\n * Computes the visitor identity token:\n * hex(HMAC_SHA256(signingSecret, externalId)). SERVER-ONLY — it\n * throws in a browser context rather than ever computing next to\n * the DOM.\n *\n * Signing secret, in order: `{ secret }` option →\n * `BRANDFINE_LIVE_CHAT_IDENTITY_SECRET` env → **derived from the\n * client's API key** (HMAC with a fixed domain-separation\n * constant; the Brandfine API derives the same value from its\n * stored copy). The derived path needs ZERO extra configuration —\n * an explicit secret is only for signers that shouldn't hold the\n * broad key, or legacy workspaces without a revealable key\n * (generate one in the CMS: Plugins → Live Chat → Integrate).\n */\n identityToken: (\n externalId: string,\n opts?: { secret?: string },\n ) => Promise<string>\n\n /**\n * Headless session — the conversation without the widget, for\n * building your own inline chat UI. Browser-only. Takes the same\n * `config` and signed `visitor` as `install()` (identical identity\n * rules and thread continuity — it IS the same conversation the\n * widget would join, sharing its storage keys). Transport today is\n * polling (`pollIntervalMs`, default 5000); a realtime upgrade\n * will keep this API and the poll as fallback.\n */\n createSession: (\n opts: LiveChatCreateSessionOptions,\n ) => Promise<LiveChatSession>\n\n /**\n * Raw, stateless wire methods for full control. NOTE on auth: the\n * config + start endpoints authenticate with this client's\n * `apiKey` header — in a browser, construct the client with the\n * PUBLISHABLE key (`createBrandfineClient({ apiKey: config.publishableKey })`),\n * never the broad key. Message endpoints authenticate with the\n * conversation token alone — treat it as a bearer credential:\n * don't log it, don't put it in URLs you share.\n */\n runtimeConfig: (locale?: string) => Promise<LiveChatRuntimeConfig>\n startConversation: (input: {\n visitorSessionId: string\n conversationToken?: string\n pageUrl?: string\n referrer?: string\n locale?: string\n visitor?: Record<string, unknown>\n }) => Promise<StartConversationResult>\n sendMessage: (\n conversationToken: string,\n input: { body: string; clientId?: string },\n ) => Promise<ChatMessage>\n history: (\n conversationToken: string,\n opts?: { after?: string; afterEvent?: number },\n ) => Promise<{ messages: ChatMessage[]; status: ConversationStatus }>\n}\n\n/** Attribute we stamp on the injected <script> so `install()` is\n * idempotent across re-renders and SPA route changes. */\nconst INSTALLED_MARKER = 'data-brandfine-analytics'\n\n/** Same idempotency contract for the live-chat widget injection. */\nconst LIVE_CHAT_MARKER = 'data-brandfine-live-chat'\n\n/**\n * Domain-separation constant for deriving the live-chat identity\n * signing secret from the workspace API key. MUST match the API's\n * constant (`live-chat-external.service.ts`) byte for byte — bump\n * the version suffix only in lockstep with a server release.\n */\nconst LIVE_CHAT_IDENTITY_CONTEXT = 'brandfine:live-chat:identity:v1'\n\n/** hex(HMAC_SHA256(key, message)) via WebCrypto — Node ≥20 global\n * AND browser-safe, so bundlers never see a node:crypto import. */\nasync function hmacHex(key: string, message: string): Promise<string> {\n const enc = new TextEncoder()\n const cryptoKey = await globalThis.crypto.subtle.importKey(\n 'raw',\n enc.encode(key),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n )\n const sig = await globalThis.crypto.subtle.sign(\n 'HMAC',\n cryptoKey,\n enc.encode(message),\n )\n return Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/** Marker for the injected Google tag — same idempotency contract. */\nconst GTAG_MARKER = 'data-brandfine-gtag'\n\n/**\n * Inject the Google tag (gtag.js) for an auto-provisioned GA4\n * property. No-ops when ANY gtag script is already on the page —\n * a site that hand-installed Google Analytics must not get a\n * second config (double-counted sessions are worse than a missing\n * tag). Safe to call repeatedly; the marker makes it idempotent.\n */\nfunction injectGoogleTag(measurementId: string): void {\n if (typeof document === 'undefined') return\n const existing = document.querySelector(\n `script[src*=\"googletagmanager.com/gtag/js\"], script[${GTAG_MARKER}]`,\n )\n if (existing) return\n\n const loader = document.createElement('script')\n loader.async = true\n loader.src = `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(measurementId)}`\n loader.setAttribute(GTAG_MARKER, measurementId)\n document.head.appendChild(loader)\n\n const w = window as unknown as { dataLayer?: unknown[] }\n w.dataLayer = w.dataLayer ?? []\n // gtag() must push `arguments` (an Arguments object), not a\n // plain array — GA's snippet relies on it.\n function gtag(..._args: unknown[]) {\n // eslint-disable-next-line prefer-rest-params\n w.dataLayer!.push(arguments)\n }\n gtag('js', new Date())\n gtag('config', measurementId)\n}\n\nconst DEFAULT_USER_AGENT = '@brandfine/client'\n\n/** Production API origin — the default when no `baseUrl` is given.\n * Consumers only configure a URL for local dev or staging. */\nconst DEFAULT_BASE_URL = 'https://api.brandfine.co'\n\n/**\n * Resolve the API origin: explicit option → `BRANDFINE_API_URL`\n * env (server-side only — browsers have no `process`, and an env\n * indirection in a browser bundle would be inlined at build time\n * anyway) → production default.\n */\nfunction resolveBaseUrl(explicit: string | undefined): string {\n if (explicit) return explicit\n if (typeof process !== 'undefined' && process.env?.BRANDFINE_API_URL) {\n return process.env.BRANDFINE_API_URL\n }\n return DEFAULT_BASE_URL\n}\n\nexport function createBrandfineClient(\n config: BrandfineClientConfig,\n): BrandfineClient {\n if (!config.apiKey)\n throw new Error('createBrandfineClient: `apiKey` is required')\n\n const baseUrl = resolveBaseUrl(config.baseUrl).replace(/\\/$/, '')\n const apiKey = config.apiKey\n // Resolve fetch lazily so consumers in environments without a\n // global fetch can polyfill before constructing the client.\n const fetchImpl: typeof fetch = config.fetch ?? globalThis.fetch\n const userAgent = config.userAgent ?? DEFAULT_USER_AGENT\n\n async function get<T>(path: string, opts: RequestOptions = {}): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method: 'GET',\n headers: {\n 'X-Api-Key': apiKey,\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n signal: opts.signal,\n })\n if (res.status === 404 && opts.nullable404) {\n // Drain the body so the underlying socket can be reused —\n // fetch implementations that don't auto-drain (older Node)\n // can leak otherwise.\n await res.text().catch(() => '')\n return null as T\n }\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body,\n url,\n })\n }\n return (await res.json()) as T\n }\n\n const posts: PostsApi = {\n async list<TConfig = unknown>(opts: ListPostsOptions = {}) {\n const out: BrandfinePost<TConfig>[] = []\n let page = 1\n const typeQuery = opts.type ? `&type=${encodeURIComponent(opts.type)}` : ''\n const localeQuery = opts.locale\n ? `&locale=${encodeURIComponent(opts.locale)}`\n : ''\n // Default pagination at the cms's 50-per-page cap. `forceLimit`\n // opts past it for content types that would otherwise need\n // many round-trips.\n const sizeQuery = opts.forceLimit\n ? `&force_limit=${opts.forceLimit}`\n : '&limit=50'\n // Pathological safety brake — 200 pages × 50 = 10k posts. If\n // a workspace ever needs more, callers should hit the API\n // directly with their own pagination logic.\n const MAX_PAGES = 200\n while (page <= MAX_PAGES) {\n const data = await get<BrandfinePostListResponse<TConfig>>(\n `/external/posts?include=content${sizeQuery}&page=${page}${typeQuery}${localeQuery}`,\n )\n out.push(...data.items)\n if (!data.pageInfo.hasNext) break\n page += 1\n }\n return out\n },\n async getBySlug<TConfig = unknown>(slug: string) {\n return get<BrandfinePost<TConfig> | null>(\n `/external/posts/${encodeURIComponent(slug)}`,\n { nullable404: true },\n )\n },\n }\n\n const categories: CategoriesApi = {\n async list(opts: ListCategoriesOptions = {}) {\n const qs = opts.locale ? `?locale=${encodeURIComponent(opts.locale)}` : ''\n const data = await get<{ items: BrandfineCategory[] }>(\n `/external/categories${qs}`,\n )\n return data.items\n },\n }\n\n const workspace: WorkspaceApi = {\n get<\n TCustomConfig = Record<string, unknown>,\n TSchemaOrg = Record<string, unknown>,\n >() {\n return get<BrandfineWorkspace<TCustomConfig, TSchemaOrg>>(\n '/external/workspace',\n )\n },\n }\n\n const navigations: NavigationsApi = {\n get<TConfig = unknown>(key: string) {\n return get<BrandfineNavigation<TConfig> | null>(\n `/external/navigations/${encodeURIComponent(key)}`,\n { nullable404: true },\n )\n },\n }\n\n const analytics: AnalyticsApi = {\n getConfig() {\n return get<AnalyticsConfig>('/external/analytics-config')\n },\n overview(opts = {}) {\n const range = opts.range ?? '7d'\n return get<AnalyticsOverview>(\n `/external/analytics/overview?range=${encodeURIComponent(range)}`,\n )\n },\n async install(opts: InstallOptions = {}) {\n // SSR safety: nothing to inject without a DOM. Consumers\n // call this from useEffect / onMount, but defensive anyway\n // (some frameworks still execute the file body on the server).\n if (typeof document === 'undefined') {\n return { installed: false, reason: 'ssr' as const }\n }\n\n // Use caller-provided config if present (build-time path),\n // otherwise fetch (runtime path).\n const cfg = opts.config ?? (await analytics.getConfig())\n\n // Google tag rides alongside the built-in tracker — injected\n // even when Brandfine analytics itself is off, because the\n // opt-in lives on the GA integration, not on the tracker.\n if (cfg.gaMeasurementId) {\n injectGoogleTag(cfg.gaMeasurementId)\n }\n\n if (!cfg.enabled) {\n return { installed: false, reason: 'disabled' as const }\n }\n\n // Idempotency: a prior call (StrictMode double-invoke, SPA\n // re-mount, second instance with the same workspace) may\n // have already injected. The marker attribute is the source\n // of truth — checking by script src would also miss the case\n // where two workspaces share the same scriptUrl.\n const existing = document.querySelector<HTMLScriptElement>(\n `script[${INSTALLED_MARKER}=\"${cfg.websiteId}\"]`,\n )\n if (existing) {\n return { installed: false, reason: 'already-installed' as const }\n }\n\n const script = document.createElement('script')\n script.defer = true\n script.src = cfg.scriptUrl\n script.setAttribute('data-website-id', cfg.websiteId)\n // The marker doubles as a sentinel + a debug aid (you can\n // grep the DOM for `data-brandfine-analytics` to confirm\n // an install).\n script.setAttribute(INSTALLED_MARKER, cfg.websiteId)\n document.head.appendChild(script)\n return { installed: true, websiteId: cfg.websiteId }\n },\n }\n\n /** Shared fetch for the live-chat public wire: optional key header\n * (publishable for config/start, none for token-authed message\n * endpoints), JSON in/out, BrandfineApiError on non-2xx. */\n async function liveChatWireRequest<T>(\n key: string | undefined,\n method: 'GET' | 'POST',\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method,\n headers: {\n Accept: 'application/json',\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...(key ? { 'X-Api-Key': key } : {}),\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body: text,\n url,\n })\n }\n return (await res.json()) as T\n }\n\n const liveChat: LiveChatApi = {\n getConfig() {\n return get<LiveChatBootstrap>('/external/live-chat/bootstrap')\n },\n async install(opts: LiveChatInstallOptions) {\n if (typeof document === 'undefined') {\n return { installed: false, reason: 'ssr' as const }\n }\n\n const cfg = opts.config\n if (!cfg.enabled) {\n return { installed: false, reason: 'disabled' as const }\n }\n\n const existing = document.querySelector(`[${LIVE_CHAT_MARKER}]`)\n if (existing) {\n return { installed: false, reason: 'already-installed' as const }\n }\n\n // Host div the widget script mounts into. Theme vars ride as\n // inline custom properties — they inherit through the widget's\n // shadow boundary.\n const host = document.createElement('div')\n host.setAttribute('data-bf-live-chat', '')\n host.setAttribute('data-publishable-key', cfg.publishableKey)\n host.setAttribute('data-base-url', baseUrl)\n host.setAttribute(LIVE_CHAT_MARKER, '')\n // Signed visitor identity → data-visitor. Serialization only;\n // the signature was computed server-side (identityToken()).\n if (opts.visitor?.externalId && opts.visitor.identityToken) {\n host.setAttribute('data-visitor', JSON.stringify(opts.visitor))\n }\n if (opts.locale) {\n host.setAttribute('data-locale', opts.locale)\n }\n if (cfg.theme) {\n for (const [key, value] of Object.entries(cfg.theme)) {\n if (key.startsWith('--bf-chat-')) {\n host.style.setProperty(key, value)\n }\n }\n }\n document.body.appendChild(host)\n\n const script = document.createElement('script')\n script.defer = true\n script.src = `${baseUrl}${cfg.scriptPath}`\n script.setAttribute(LIVE_CHAT_MARKER, 'script')\n document.head.appendChild(script)\n\n return { installed: true }\n },\n\n createSession(opts: LiveChatCreateSessionOptions) {\n // The session's key-authed calls use the PUBLISHABLE key from\n // the passed config (the browser client is often constructed\n // with a placeholder apiKey for install()-style usage).\n const pk = opts.config.enabled ? opts.config.publishableKey : undefined\n const wire: LiveChatWire = {\n runtimeConfig: (locale) => liveChatWireRequest(pk, 'GET',\n `/external/live-chat/config${locale ? `?locale=${encodeURIComponent(locale)}` : ''}`),\n startConversation: (input) =>\n liveChatWireRequest(pk, 'POST', '/external/live-chat/conversations', input),\n sendMessage: (conversationToken, input) =>\n liveChatWireRequest(undefined, 'POST',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages`, input),\n history: (conversationToken, o = {}) =>\n liveChatWireRequest(undefined, 'GET',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages${\n o.afterEvent !== undefined\n ? `?afterEvent=${encodeURIComponent(String(o.afterEvent))}`\n : o.after\n ? `?after=${encodeURIComponent(o.after)}`\n : ''\n }`),\n }\n return createLiveChatSession(wire, opts)\n },\n\n runtimeConfig(locale) {\n return liveChatWireRequest(apiKey, 'GET',\n `/external/live-chat/config${locale ? `?locale=${encodeURIComponent(locale)}` : ''}`)\n },\n startConversation(input) {\n return liveChatWireRequest(apiKey, 'POST', '/external/live-chat/conversations', input)\n },\n sendMessage(conversationToken, input) {\n return liveChatWireRequest(undefined, 'POST',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages`, input)\n },\n history(conversationToken, o = {}) {\n // afterEvent (eventId cursor, exact) wins over the legacy\n // timestamp cursor when the session knows one.\n const qs =\n o.afterEvent !== undefined\n ? `?afterEvent=${encodeURIComponent(String(o.afterEvent))}`\n : o.after\n ? `?after=${encodeURIComponent(o.after)}`\n : ''\n return liveChatWireRequest(undefined, 'GET',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages${qs}`)\n },\n\n async identityToken(externalId, opts = {}) {\n // Refuse to run where the secret could be exfiltrated. A page\n // that needs a token must get it from ITS server, not compute\n // one next to the DOM.\n if (typeof document !== 'undefined' || typeof window !== 'undefined') {\n throw new Error(\n 'liveChat.identityToken() is server-only — never compute identity ' +\n 'tokens in a browser. Sign the visitor on your server and pass ' +\n 'the result to install({ visitor }).',\n )\n }\n if (!externalId) {\n throw new Error('liveChat.identityToken(): externalId is required.')\n }\n // Signing-secret resolution:\n // 1. Explicit { secret } / BRANDFINE_LIVE_CHAT_IDENTITY_SECRET —\n // the decoupled option (a signer that shouldn't hold the\n // broad key, or a legacy workspace).\n // 2. DERIVED from the API key this client already holds:\n // HMAC(apiKey, domain-separation constant). Zero extra\n // config — the server derives the same value from its\n // stored copy of the key. Rotating the API key rotates\n // signing with it, which is the lifecycle you want anyway.\n const explicit =\n opts.secret ??\n (typeof process !== 'undefined'\n ? process.env.BRANDFINE_LIVE_CHAT_IDENTITY_SECRET\n : undefined)\n const secret =\n explicit ?? (await hmacHex(apiKey, LIVE_CHAT_IDENTITY_CONTEXT))\n return hmacHex(secret, externalId)\n },\n }\n\n const submissions: SubmissionsApi = {\n async create(input: CreateSubmissionInput) {\n const url = `${baseUrl}/external/submissions`\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n 'X-Api-Key': apiKey,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n body: JSON.stringify(input),\n })\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body,\n url,\n })\n }\n return (await res.json()) as Submission\n },\n }\n\n /**\n * Shared POST helper for the appointments namespace. The main\n * `get()` helper handles GETs; submissions has its own inline\n * POST because it predates this refactor. New plugin namespaces\n * (appointments first, others to follow) share this one so the\n * error-handling shape stays consistent.\n */\n async function post<T>(\n path: string,\n body: unknown,\n opts: RequestOptions = {},\n ): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n 'X-Api-Key': apiKey,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n body: JSON.stringify(body),\n signal: opts.signal,\n })\n if (!res.ok) {\n const errBody = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body: errBody,\n url,\n })\n }\n if (res.status === 204) return undefined as T\n return (await res.json()) as T\n }\n\n const appointments: AppointmentsApi = {\n getAvailability(opts = {}) {\n const qs: string[] = []\n if (opts.from) qs.push(`from=${encodeURIComponent(toIso(opts.from))}`)\n if (opts.to) qs.push(`to=${encodeURIComponent(toIso(opts.to))}`)\n const suffix = qs.length ? `?${qs.join('&')}` : ''\n return get<AppointmentAvailability>(\n `/external/appointments/availability${suffix}`,\n )\n },\n createRequest(input) {\n return post<CreatedAppointmentRequest>(\n '/external/appointments/requests',\n input,\n )\n },\n async list(identity) {\n // POST (not GET) so the identityToken never lands in URLs,\n // proxy logs, or browser history.\n const res = await post<{ appointments: Appointment[] }>(\n '/external/appointments/requests/lookup',\n identity,\n )\n return res.appointments\n },\n async get(id, identity) {\n // Same lookup endpoint narrowed by id — the server 404s when\n // the id is missing or belongs to another person, so a\n // successful response always carries exactly one row. The\n // guard below is defensive symmetry, not a real path.\n const res = await post<{ appointments: Appointment[] }>(\n '/external/appointments/requests/lookup',\n { ...identity, id },\n )\n const row = res.appointments[0]\n if (!row) {\n throw new BrandfineApiError({\n status: 404,\n statusText: 'Not Found',\n body: 'Appointment not found.',\n url: '/external/appointments/requests/lookup',\n })\n }\n return row\n },\n cancel(cancellationToken) {\n return post<{ id: string; status: 'CANCELLED' }>(\n `/external/appointments/requests/${encodeURIComponent(cancellationToken)}/cancel`,\n {},\n )\n },\n }\n\n return {\n get,\n posts,\n categories,\n workspace,\n navigations,\n analytics,\n submissions,\n appointments,\n liveChat,\n }\n}\n\n/** Accepts a Date or an already-ISO string and returns ISO. Saves\n * every caller from `.toISOString()`-ing manually. */\nfunction toIso(d: Date | string): string {\n return typeof d === 'string' ? d : d.toISOString()\n}\n","/**\n * @brandfine/client — root entry.\n *\n * The full SDK surface is exposed here for \"import everything from\n * one place\" usage. Tree-shaking + `sideEffects: false` mean\n * consumers don't pay a bundle cost for what they don't import.\n *\n * Heavier or framework-coupled pieces still live under subpath\n * exports (`@brandfine/client/cache`, `/resolvers`, `/webhook`) so\n * consumers with poor tree-shaking — or who only need one slice —\n * can scope their imports.\n */\n\nexport const SDK_VERSION = '0.0.0' as const\n\nexport {\n BrandfineApiError,\n createBrandfineClient,\n type AnalyticsConfig,\n type AnalyticsInstallResult,\n type AnalyticsOverview,\n type AnalyticsOverviewRange,\n type Appointment,\n type AppointmentAvailability,\n type AppointmentIdentity,\n type AppointmentSlot,\n type AppointmentStatus,\n type BrandfineClient,\n type BrandfineClientConfig,\n type ChatMessage,\n type CreateAppointmentRequestInput,\n type CreatedAppointmentRequest,\n type ChatMessageSender,\n type ConversationStatus,\n type CreateSubmissionInput,\n type InstallOptions,\n type LiveChatBootstrap,\n type LiveChatCreateSessionOptions,\n type LiveChatInstallOptions,\n type LiveChatInstallResult,\n type LiveChatRuntimeConfig,\n type LiveChatSession,\n type LiveChatSessionSnapshot,\n type LiveChatSessionState,\n type LiveChatVisitor,\n type StartConversationResult,\n type Submission,\n type VerifiedVisitor,\n} from './client'\n\nexport {\n createCache,\n createKeyedCache,\n type Cache,\n type CacheOptions,\n type KeyedCache,\n type KeyedCacheOptions,\n} from './cache/index'\n\nexport {\n isLocale,\n localizePath,\n pickLocale,\n resolveNavigation,\n stripLocalePrefix,\n type HydratedNav,\n type HydratedNavItem,\n type LocaleOptions,\n type ResolveNavigationOptions,\n} from './resolvers/index'\n\nexport {\n createBrandfineWebhookHandler,\n parseWebhookPayload,\n verifyWebhookSecret,\n type BrandfineWebhookEvent,\n type BrandfineWebhookHandlerOptions,\n type BrandfineWebhookPayload,\n} from './webhook/index'\n\nexport type {\n BrandfineCategory,\n BrandfineNavItem,\n BrandfineNavItemType,\n BrandfineNavPost,\n BrandfineNavigation,\n BrandfinePost,\n BrandfinePostListResponse,\n BrandfinePostTranslation,\n BrandfineWorkspace,\n ListCategoriesOptions,\n ListPostsOptions,\n} from './types'\n"]}
1
+ {"version":3,"sources":["../src/live-chat-session.ts","../src/client.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Headless Live Chat session — the conversation WITHOUT the widget.\n *\n * `bf.liveChat.createSession()` gives a consumer everything needed to\n * render chat inline in their own design system: transcript state, a\n * send method, and a subscribe API shaped for\n * `useSyncExternalStore` / Svelte stores / Vue refs. Brandfine keeps\n * owning transport, identity, threading and storage; the consumer\n * owns pixels.\n *\n * Storage keys are IDENTICAL to the floating widget's, so a visitor\n * who talks through the widget on one page and an inline panel on\n * another continues the same thread.\n */\n\n/**\n * `BOT` = AI-assistant replies (arriving once a workspace enables the\n * live-chat AI agent). Render `BOT` on the business/agent side, ideally\n * with an \"AI\" label — and treat any FUTURE unknown sender value the\n * same way (agent-side, generic label) rather than switching\n * exhaustively: the server may add sender kinds before your copy of\n * this SDK learns their names.\n */\nexport type ChatMessageSender = 'VISITOR' | 'AGENT' | 'SYSTEM' | 'BOT'\n\nexport type ChatMessage = {\n id: string\n body: string\n sender: ChatMessageSender\n createdAt: string\n /** Monotonic per-conversation sequence — the realtime resume\n * cursor. Optional: older APIs don't send it. */\n eventId?: number\n}\n\nexport type ConversationStatus = 'OPEN' | 'CLOSED'\n\nexport type LiveChatSessionState = 'CONNECTING' | 'OPEN' | 'CLOSED' | 'ERROR'\n\n/** The `/external/live-chat/config` shape — the RUNTIME config the\n * widget fetches per page load. Distinct from the server-side\n * `LiveChatBootstrap` (which additionally carries the publishable\n * key + script path). */\nexport type LiveChatRuntimeConfig =\n | { enabled: false }\n | {\n enabled: true\n greeting: string | null\n offlineMessage: string | null\n theme: Record<string, string> | null\n online?: boolean\n /** Realtime endpoint (wss://…). Absent = poll (older API, or\n * the transport is off). The session upgrades automatically\n * when present; consumers never touch it. */\n realtimeUrl?: string\n }\n\nexport type LiveChatSessionSnapshot = {\n messages: ChatMessage[]\n status: LiveChatSessionState\n /** Agents available right now (business-hours based today; agent\n * presence once the realtime transport lands). */\n online: boolean\n /** Localized via the session's `locale`. */\n greeting: string | null\n offlineMessage: string | null\n /** An outbound `send()` is in flight. */\n sending: boolean\n error: Error | null\n}\n\nexport type StartConversationResult =\n | { enabled: false }\n | {\n enabled: true\n conversationId: string\n conversationToken: string\n greeting: string | null\n resumed: boolean\n online?: boolean\n }\n\n/** Wire access the session needs — provided by the SDK client so the\n * session never hardcodes endpoints or headers. */\nexport type LiveChatWire = {\n runtimeConfig(locale?: string): Promise<LiveChatRuntimeConfig>\n startConversation(input: {\n visitorSessionId: string\n conversationToken?: string\n pageUrl?: string\n referrer?: string\n locale?: string\n visitor?: Record<string, unknown>\n }): Promise<StartConversationResult>\n sendMessage(\n conversationToken: string,\n input: { body: string; clientId?: string },\n ): Promise<ChatMessage>\n history(\n conversationToken: string,\n opts?: { after?: string; afterEvent?: number },\n ): Promise<{ messages: ChatMessage[]; status: ConversationStatus }>\n}\n\nexport type LiveChatCreateSessionOptions = {\n /** The server-half bootstrap (same object `install()` takes) —\n * supplies the publishable key (and, when the API advertises it,\n * the realtime endpoint — passed through wholesale). */\n config: { enabled: boolean; publishableKey?: string; realtimeUrl?: string }\n /** Signed identity — SAME shape and rules as `install()`. A bad\n * signature downgrades to anonymous server-side; it is never a\n * second identity path. */\n visitor?: Record<string, unknown>\n /** BCP-47 tag for greeting/away localization. Defaults to\n * `<html lang>` then browser language, like the widget. */\n locale?: string\n /** Defaults to `location.href`. */\n pageUrl?: string\n referrer?: string\n /** Transcript refresh cadence. Polling is the transport today; a\n * realtime upgrade will keep this as the fallback. */\n pollIntervalMs?: number\n}\n\nexport type LiveChatSession = {\n /** Current transcript (same array identity as the latest snapshot). */\n readonly messages: readonly ChatMessage[]\n readonly state: LiveChatSessionState\n send(body: string): Promise<ChatMessage>\n /** Listener fires on every snapshot change. Returns unsubscribe. */\n subscribe(listener: (s: LiveChatSessionSnapshot) => void): () => void\n getSnapshot(): LiveChatSessionSnapshot\n /** Stop polling, abort in-flight work, drop listeners. Idempotent. */\n close(): void\n /** Drop the stored conversation token and start a fresh thread —\n * identity switch on a shared browser. */\n reset(): Promise<void>\n}\n\n// ── Storage (MUST stay in lockstep with packages/widget-live-chat) ──\n\nconst SESSION_STORAGE_KEY = 'bf-live-chat-session'\n\nfunction tokenStorageKey(publishableKey: string) {\n return `bf-live-chat-token:${publishableKey.slice(-12)}`\n}\n\nfunction getVisitorSessionId(): string {\n try {\n const existing = localStorage.getItem(SESSION_STORAGE_KEY)\n if (existing) return existing\n const generated =\n typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID().replace(/-/g, '')\n : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n localStorage.setItem(SESSION_STORAGE_KEY, generated)\n return generated\n } catch {\n return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n }\n}\n\nfunction readStoredToken(publishableKey: string): string | undefined {\n try {\n return localStorage.getItem(tokenStorageKey(publishableKey)) ?? undefined\n } catch {\n return undefined\n }\n}\n\nfunction writeStoredToken(publishableKey: string, token: string | null) {\n try {\n if (token === null) localStorage.removeItem(tokenStorageKey(publishableKey))\n else localStorage.setItem(tokenStorageKey(publishableKey), token)\n } catch {\n /* storage blocked — resume just won't survive reloads */\n }\n}\n\nfunction resolvePageLocale(explicit?: string): string | undefined {\n if (explicit) return explicit\n if (typeof document === 'undefined') return undefined\n const htmlLang = document.documentElement.lang?.trim()\n if (htmlLang) return htmlLang\n return typeof navigator !== 'undefined'\n ? navigator.language || undefined\n : undefined\n}\n\n// ── The session ─────────────────────────────────────────────────────\n\nexport async function createLiveChatSession(\n wire: LiveChatWire,\n opts: LiveChatCreateSessionOptions,\n): Promise<LiveChatSession> {\n if (typeof window === 'undefined') {\n throw new Error(\n 'liveChat.createSession() is browser-only — build the transcript UI ' +\n 'client-side; fetch the bootstrap with getConfig() on the server.',\n )\n }\n const publishableKey = opts.config.enabled\n ? opts.config.publishableKey\n : undefined\n\n const locale = resolvePageLocale(opts.locale)\n const pollMs = Math.max(1000, opts.pollIntervalMs ?? 5000)\n\n let snapshot: LiveChatSessionSnapshot = {\n messages: [],\n status: 'CONNECTING',\n online: false,\n greeting: null,\n offlineMessage: null,\n sending: false,\n error: null,\n }\n const listeners = new Set<(s: LiveChatSessionSnapshot) => void>()\n const seen = new Set<string>()\n let cursor: string | undefined\n let lastEventId = 0\n let token: string | undefined\n let timer: ReturnType<typeof setInterval> | undefined\n let closed = false\n let ticking = false\n const aborter = new AbortController()\n\n // ── Realtime transport state (see startRealtime below) ──\n let realtimeUrl: string | undefined = opts.config.realtimeUrl\n let ws: WebSocket | undefined\n let wsLive = false\n let wsFailures = 0\n let wsGivenUp = false\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined\n let pingTimer: ReturnType<typeof setInterval> | undefined\n\n function emit(patch: Partial<LiveChatSessionSnapshot>) {\n snapshot = { ...snapshot, ...patch }\n for (const listener of listeners) listener(snapshot)\n }\n\n function appendMessages(incoming: ChatMessage[]) {\n const fresh = incoming.filter((m) => !seen.has(m.id))\n if (fresh.length === 0) {\n // Even a full dupe batch may advance the event cursor (e.g. the\n // socket redelivers what a poll already fetched).\n for (const m of incoming) {\n if (typeof m.eventId === 'number' && m.eventId > lastEventId) {\n lastEventId = m.eventId\n }\n }\n return\n }\n for (const m of fresh) {\n seen.add(m.id)\n if (typeof m.eventId === 'number' && m.eventId > lastEventId) {\n lastEventId = m.eventId\n }\n }\n const messages = [...snapshot.messages, ...fresh]\n cursor = messages[messages.length - 1]!.createdAt\n emit({ messages })\n }\n\n function markClosed() {\n if (snapshot.status !== 'CLOSED') emit({ status: 'CLOSED' })\n stopPolling()\n teardownRealtime()\n wsGivenUp = true // a closed thread never reconnects; reset() re-arms\n }\n\n async function tick() {\n // Re-entrancy guard: a slow response must not overlap the next\n // interval firing (double-fetch → possible reorder).\n if (ticking || closed || !token) return\n ticking = true\n try {\n const result = await wire.history(\n token,\n // The eventId cursor is exact (no equal-millisecond drop\n // window); the timestamp cursor remains for old APIs that\n // never sent eventIds.\n lastEventId > 0 ? { afterEvent: lastEventId } : { after: cursor },\n )\n if (closed) return\n appendMessages(result.messages)\n if (result.status === 'CLOSED') markClosed()\n } catch {\n /* transient poll failure — next tick retries */\n } finally {\n ticking = false\n }\n }\n\n function startPolling() {\n stopPolling()\n timer = setInterval(() => void tick(), pollMs)\n }\n function stopPolling() {\n if (timer !== undefined) clearInterval(timer)\n timer = undefined\n }\n\n // ── Realtime transport (delivery-only WebSocket) ────────────────\n //\n // Invisible above the session API by contract: same subscribe,\n // same snapshots. While the socket is live, polling stops; the\n // moment it isn't (drop, backoff wait, permanent failure, no\n // WebSocket in the runtime, no realtimeUrl advertised), polling\n // covers the gap exactly as before. Sends stay on REST.\n\n function teardownRealtime() {\n if (reconnectTimer !== undefined) clearTimeout(reconnectTimer)\n reconnectTimer = undefined\n if (pingTimer !== undefined) clearInterval(pingTimer)\n pingTimer = undefined\n wsLive = false\n if (ws) {\n const socket = ws\n ws = undefined\n try {\n socket.onopen = socket.onmessage = socket.onclose = socket.onerror =\n null\n socket.close()\n } catch {\n /* already closed */\n }\n }\n }\n\n function scheduleReconnect() {\n if (closed || wsGivenUp || reconnectTimer !== undefined) return\n // 1s → 30s exponential with jitter; after 5 consecutive\n // failures, stay on polling for the session's lifetime — a\n // socket a corporate proxy kills will never open, and the user\n // still gets their messages.\n if (wsFailures >= 5) {\n wsGivenUp = true\n return\n }\n const base = Math.min(30_000, 1_000 * 2 ** wsFailures)\n const delay = base / 2 + Math.random() * (base / 2)\n reconnectTimer = setTimeout(() => {\n reconnectTimer = undefined\n startRealtime()\n }, delay)\n }\n\n function handleSocketDown() {\n teardownRealtime()\n if (closed || wsGivenUp || snapshot.status === 'CLOSED') return\n // Polling covers the gap from the FIRST millisecond of the\n // outage; a later successful socket auth stops it again.\n startPolling()\n wsFailures += 1\n scheduleReconnect()\n }\n\n function startRealtime() {\n if (\n closed ||\n wsGivenUp ||\n wsLive ||\n ws !== undefined ||\n !realtimeUrl ||\n !token ||\n !publishableKey ||\n typeof WebSocket === 'undefined' ||\n snapshot.status === 'CLOSED'\n ) {\n return\n }\n let socket: WebSocket\n try {\n socket = new WebSocket(realtimeUrl)\n } catch {\n wsFailures += 1\n scheduleReconnect()\n return\n }\n ws = socket\n socket.onopen = () => {\n // Auth in the FIRST FRAME, never the URL — tokens must not\n // land in proxy logs or browser history.\n try {\n socket.send(\n JSON.stringify({\n type: 'auth',\n publishableKey,\n conversationToken: token,\n lastEventId,\n }),\n )\n } catch {\n handleSocketDown()\n }\n }\n socket.onmessage = (event) => {\n let frame: { type?: string } & Record<string, unknown>\n try {\n frame = JSON.parse(String(event.data)) as typeof frame\n } catch {\n return // unknown/garbage frames are ignored by contract\n }\n switch (frame.type) {\n case 'ready': {\n wsLive = true\n wsFailures = 0\n stopPolling() // the socket is now the transport\n if (pingTimer === undefined) {\n pingTimer = setInterval(() => {\n try {\n socket.send(JSON.stringify({ type: 'ping' }))\n } catch {\n /* close race — the onclose handler recovers */\n }\n }, 30_000)\n }\n break\n }\n case 'message': {\n const m = frame.message as ChatMessage | undefined\n if (m && typeof m.id === 'string') {\n appendMessages([\n {\n ...m,\n eventId:\n typeof frame.eventId === 'number' ? frame.eventId : m.eventId,\n },\n ])\n }\n break\n }\n case 'status': {\n if (frame.status === 'CLOSED') markClosed()\n break\n }\n case 'error': {\n // The server refused us (bad auth, socket caps…). Fall\n // back to polling permanently — retrying a refusal is\n // just noise, and delivery still works.\n wsGivenUp = true\n teardownRealtime()\n if (!closed && snapshot.status !== 'CLOSED') startPolling()\n break\n }\n default:\n break // pong + future frame types\n }\n }\n socket.onclose = () => handleSocketDown()\n socket.onerror = () => {\n /* onclose always follows; avoid double-handling */\n }\n }\n\n async function connect(resumeToken: string | undefined) {\n if (!publishableKey) {\n emit({ status: 'CLOSED' })\n return\n }\n // Config first — greeting/away/online, localized.\n try {\n const cfg = await wire.runtimeConfig(locale)\n if (closed) return\n if (!cfg.enabled) {\n emit({ status: 'CLOSED' })\n return\n }\n emit({\n greeting: cfg.greeting,\n offlineMessage: cfg.offlineMessage,\n online: cfg.online !== false,\n })\n // Runtime config wins over the build-time bootstrap — it's\n // fetched live, so switching realtime off takes effect on the\n // next page load without a redeploy.\n if (cfg.realtimeUrl !== undefined) realtimeUrl = cfg.realtimeUrl\n } catch (error) {\n if (closed) return\n emit({ status: 'ERROR', error: error as Error })\n return\n }\n\n try {\n const started = await wire.startConversation({\n visitorSessionId: getVisitorSessionId(),\n conversationToken: resumeToken,\n pageUrl:\n opts.pageUrl ??\n (typeof location !== 'undefined'\n ? location.href.slice(0, 2048)\n : undefined),\n referrer: opts.referrer,\n locale,\n visitor: opts.visitor,\n })\n if (closed) return\n if (!started.enabled) {\n emit({ status: 'CLOSED' })\n return\n }\n token = started.conversationToken\n writeStoredToken(publishableKey, token)\n if (started.online !== undefined) emit({ online: started.online })\n emit({ status: 'OPEN' })\n await tick() // initial history before the first interval\n if (!closed) {\n startPolling()\n startRealtime() // upgrades in place; polling stops on `ready`\n }\n } catch (error) {\n if (closed) return\n emit({ status: 'ERROR', error: error as Error })\n }\n }\n\n const session: LiveChatSession = {\n get messages() {\n return snapshot.messages\n },\n get state() {\n return snapshot.status\n },\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n async send(body) {\n const trimmed = body.trim()\n if (!trimmed) throw new Error('send(): message body is empty')\n if (!token) throw new Error('send(): session is not connected')\n if (snapshot.status === 'CLOSED') {\n throw new Error('send(): conversation is closed — call reset()')\n }\n emit({ sending: true })\n try {\n // Idempotency key: if the response is lost mid-flight, a\n // consumer retry with the same body can't duplicate — the\n // server replays the original row for this clientId.\n const clientId =\n typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID()\n : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n const message = await wire.sendMessage(token, {\n body: trimmed,\n clientId,\n })\n appendMessages([message])\n return message\n } finally {\n emit({ sending: false })\n }\n },\n close() {\n if (closed) return\n closed = true\n stopPolling()\n teardownRealtime()\n aborter.abort()\n listeners.clear()\n },\n async reset() {\n // Socket FIRST — a socket left attached to the old conversation\n // would defeat the shared-browser guarantee reset() exists for.\n teardownRealtime()\n wsFailures = 0\n wsGivenUp = false\n if (publishableKey) writeStoredToken(publishableKey, null)\n token = undefined\n cursor = undefined\n lastEventId = 0\n seen.clear()\n stopPolling()\n emit({ messages: [], status: 'CONNECTING', error: null })\n await connect(undefined)\n },\n }\n\n await connect(publishableKey ? readStoredToken(publishableKey) : undefined)\n return session\n}\n","/**\n * `createBrandfineClient` — the SDK's entry point.\n *\n * Returns a stateless, multi-instance-safe handle scoped to a\n * single `(baseUrl, apiKey)` pair. Pattern follows the Stripe /\n * Algolia / OpenAI SDKs — explicit construction with config,\n * namespaced methods (`bf.posts.list(...)`, `bf.workspace.get()`),\n * no module-level singletons.\n *\n * Why factory not module-level state: multi-tenant consumers\n * sometimes need two clients in the same process (e.g. main site\n * + admin preview). Module-level env reading makes that impossible\n * without monkey-patching.\n */\n\nimport type {\n BrandfineCategory,\n BrandfineNavigation,\n BrandfinePost,\n BrandfinePostListResponse,\n BrandfineWorkspace,\n ListCategoriesOptions,\n ListPostsOptions,\n} from './types'\n\nimport {\n createLiveChatSession,\n type ChatMessage,\n type ConversationStatus,\n type LiveChatCreateSessionOptions,\n type LiveChatRuntimeConfig,\n type LiveChatSession,\n type LiveChatWire,\n type StartConversationResult,\n} from './live-chat-session'\n\nexport type {\n ChatMessage,\n ChatMessageSender,\n ConversationStatus,\n LiveChatCreateSessionOptions,\n LiveChatRuntimeConfig,\n LiveChatSession,\n LiveChatSessionSnapshot,\n LiveChatSessionState,\n StartConversationResult,\n} from './live-chat-session'\n\nexport type BrandfineClientConfig = {\n /** Base URL of the Brandfine API. OPTIONAL — defaults to the\n * production API (`https://api.brandfine.co`); on the server the\n * `BRANDFINE_API_URL` env var overrides the default (set it for\n * local dev / staging). Explicit option wins over both. No\n * trailing slash — the client trims one if you pass it anyway. */\n baseUrl?: string\n /** Workspace-scoped API key. Generated from the cms's Workspace\n * settings; identifies which workspace the client talks to. */\n apiKey: string\n /** Optional fetch override. Useful for tests (inject a stub),\n * for runtimes that need a custom implementation (edge workers\n * with non-standard fetch), or to add cross-cutting concerns\n * like tracing / retries. Defaults to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch\n /** Optional User-Agent header. Falls back to a generic SDK tag. */\n userAgent?: string\n}\n\n/**\n * Structured error thrown by every request helper on non-2xx\n * responses. Carries the raw body so consumers can log it for\n * debugging without re-fetching.\n */\nexport class BrandfineApiError extends Error {\n override readonly name = 'BrandfineApiError'\n readonly status: number\n readonly statusText: string\n readonly body: string\n readonly url: string\n\n constructor(args: {\n status: number\n statusText: string\n body: string\n url: string\n }) {\n super(\n `[brandfine] ${args.status} ${args.statusText} on ${args.url} — ${args.body.slice(0, 200)}`,\n )\n this.status = args.status\n this.statusText = args.statusText\n this.body = args.body\n this.url = args.url\n }\n}\n\ntype RequestOptions = {\n /** When true and the response is 404, return `null` instead of\n * throwing. Used by endpoints where 404 is a meaningful empty\n * state (navigation by key, single post by slug). */\n nullable404?: boolean\n signal?: AbortSignal\n}\n\nexport type BrandfineClient = {\n /** Low-level GET. Reserved for endpoints we don't have a typed\n * helper for yet. Adds the X-Api-Key header automatically. */\n get: <T>(path: string, opts?: RequestOptions) => Promise<T>\n posts: PostsApi\n categories: CategoriesApi\n workspace: WorkspaceApi\n navigations: NavigationsApi\n analytics: AnalyticsApi\n submissions: SubmissionsApi\n appointments: AppointmentsApi\n liveChat: LiveChatApi\n}\n\ntype PostsApi = {\n /** Paginated list of published posts. Handles the cms's\n * pagination transparently — caller gets a flat array. */\n list: <TConfig = unknown>(\n opts?: ListPostsOptions,\n ) => Promise<BrandfinePost<TConfig>[]>\n /** Single post by per-locale URL slug, scoped to the active\n * locale on the workspace's content. Returns `null` for 404 so\n * callers can render their own \"not found\" page without try/catch. */\n getBySlug: <TConfig = unknown>(\n slug: string,\n ) => Promise<BrandfinePost<TConfig> | null>\n}\n\ntype CategoriesApi = {\n list: (opts?: ListCategoriesOptions) => Promise<BrandfineCategory[]>\n}\n\ntype WorkspaceApi = {\n get: <\n TCustomConfig = Record<string, unknown>,\n TSchemaOrg = Record<string, unknown>,\n >() => Promise<BrandfineWorkspace<TCustomConfig, TSchemaOrg>>\n}\n\ntype NavigationsApi = {\n /** Navigation by its workspace-scoped `key` (e.g. `'header'`).\n * Returns `null` for 404 so consumers can fall back to a\n * hardcoded default without try/catch. `TConfig` narrows each\n * item's `customConfig` (default `unknown`). */\n get: <TConfig = unknown>(\n key: string,\n ) => Promise<BrandfineNavigation<TConfig> | null>\n}\n\nexport type CreateSubmissionInput = {\n /** Required. Display name of the submitter. */\n name: string\n /** Required. Validated server-side. */\n email: string\n /** Optional. Free-text up to 40 chars. */\n phone?: string\n /** Optional. Free-text up to 200 chars. */\n subject?: string\n /** Required. The message body — up to 10,000 chars. */\n message: string\n /** Optional. Where the submission came from — e.g. a route path\n * like `/contact`, or a marketing campaign label. Up to 500 chars. */\n source?: string\n /** Optional. Free-form JSON metadata the consumer attaches; the\n * cms surfaces it verbatim in the submissions admin view. */\n metadata?: Record<string, unknown>\n}\n\nexport type Submission = {\n id: string\n createdAt: string\n}\n\ntype SubmissionsApi = {\n /**\n * Posts a contact-form submission to `POST /external/submissions`\n * for this workspace. The cms surfaces the submission in the\n * Submissions inbox.\n *\n * Throws `BrandfineApiError` on validation failures (400) or\n * any other non-2xx — caller decides whether to surface that as\n * a user-visible error or a silent retry.\n */\n create: (input: CreateSubmissionInput) => Promise<Submission>\n}\n\n// ----------------------------------------------------------------\n// Appointments plugin SDK — pairs with the Appointments embed\n// widget. Consumers who want full control over the booking UI use\n// these methods directly; consumers who want the drop-in widget\n// use the `<script>` embed (which itself uses these methods under\n// the hood). The same `BrandfineClient` instance powers both.\n// ----------------------------------------------------------------\n\nexport type AppointmentSlot = {\n /** UTC ISO 8601 timestamp of the slot start. */\n start: string\n /** UTC ISO 8601 timestamp of the slot end. */\n end: string\n}\n\nexport type AppointmentAvailability = {\n /** False = plugin not activated, or activation row's `enabled`\n * flag is off. Widgets should render a \"not accepting bookings\"\n * state, not throw. */\n enabled: boolean\n /** Source-of-truth IANA timezone for the workspace's business\n * hours. Visitors see slots in their local TZ — use this for\n * the \"(workspace local: HH:MM)\" subtext. */\n timezone: string\n slotDurationMinutes: number\n leadTimeHours: number\n bookingWindowDays: number\n policyText: string | null\n slots: AppointmentSlot[]\n /** UTC ISO 8601. Useful for the widget's date range label. */\n windowStart: string\n windowEnd: string\n}\n\nexport type CreateAppointmentRequestInput = {\n visitorName: string\n visitorEmail: string\n visitorPhone?: string\n visitorMessage?: string\n /** UTC ISO 8601 of the requested slot start. Server re-validates\n * against business hours + busy ranges before accepting. */\n requestedAt: string\n /** Optional cookie-derived session id from the consumer site. */\n visitorSessionId?: string\n /** Signed host-app identity — the SAME payload shape and signature\n * Live Chat accepts, so one `liveChat.identityToken(externalId)`\n * call signs for both plugins. When present and valid, the booking\n * is attributed to the person (verified name/email take precedence\n * over the free-text fields above, and the booking links to their\n * chat threads via the shared externalId). Invalid or absent →\n * the booking proceeds anonymously — it is never rejected. */\n visitor?: VerifiedVisitor\n}\n\nexport type CreatedAppointmentRequest = {\n id: string\n createdAt: string\n requestedAt: string\n durationMinutes: number\n status: 'PENDING'\n /** True iff `visitor.identityToken` HMAC-verified — your signal\n * that the booking landed attributed rather than anonymous. */\n identityVerified: boolean\n /** Visitor's self-cancel token. Embed it in confirmation\n * emails / on-page UI so the visitor can cancel without an\n * account. One-time use; revoked once any party acts. */\n cancellationToken: string | null\n}\n\nexport type AppointmentStatus =\n | 'PENDING'\n | 'CONFIRMED'\n | 'REJECTED'\n | 'CANCELLED'\n\n/** One appointment as the identity-scoped read-back returns it —\n * the visitor-safe projection (\"where does my request stand?\"). */\nexport type Appointment = {\n id: string\n status: AppointmentStatus\n /** UTC ISO 8601 of the requested slot start (reflects the current\n * slot after a reschedule). */\n requestedAt: string\n /** The confirmed slot — non-null only once CONFIRMED. */\n scheduledAt: string | null\n durationMinutes: number\n /** Workspace's IANA timezone for local rendering. */\n timezone: string\n /** Customer's note — populated only on REJECTED. */\n declineReason: string | null\n rescheduleCount: number\n respondedAt: string | null\n createdAt: string\n}\n\n/** Proof of identity for read-back calls: the same externalId +\n * `liveChat.identityToken(externalId)` pair used when booking. */\nexport type AppointmentIdentity = {\n externalId: string\n identityToken: string\n}\n\ntype AppointmentsApi = {\n /**\n * Available slots for the workspace's booking window.\n * `from` / `to` are optional clamps inside the workspace's\n * configured window — the server ignores ranges outside.\n */\n getAvailability: (opts?: {\n from?: Date | string\n to?: Date | string\n }) => Promise<AppointmentAvailability>\n /**\n * Submit a visitor's appointment request. Server-side validates\n * the slot is still bookable; if it isn't, throws\n * `BrandfineApiError` with status 404 / 409.\n *\n * Pass `visitor` (signed with `liveChat.identityToken()`) to book\n * as a known person — that unlocks `list`/`get` read-back below.\n * Anonymous bookings remain fully supported; their status flow\n * stays email-driven via the cancellation-token link.\n */\n createRequest: (\n input: CreateAppointmentRequestInput,\n ) => Promise<CreatedAppointmentRequest>\n /**\n * Every appointment belonging to the verified person, newest\n * first. Requires a valid identity signature — throws\n * `BrandfineApiError` 401 on a bad one (reads need proof; there\n * is no anonymous downgrade for reading history). Server-side\n * only, like `identityToken()` itself.\n */\n list: (identity: AppointmentIdentity) => Promise<Appointment[]>\n /**\n * One appointment by id, identity-scoped. 404s when the id does\n * not exist OR belongs to someone else — indistinguishable by\n * design.\n */\n get: (id: string, identity: AppointmentIdentity) => Promise<Appointment>\n /**\n * Spend the one-time `cancellationToken` from `createRequest` to\n * cancel a still-PENDING request. The token IS the credential —\n * no id or API key needed. Throws `BrandfineApiError` 400 when\n * the request is no longer PENDING, 404 when the token is\n * unknown/already spent.\n */\n cancel: (\n cancellationToken: string,\n ) => Promise<{ id: string; status: 'CANCELLED' }>\n}\n\nexport type AnalyticsConfig =\n | {\n enabled: false\n /** GA4 Measurement ID — present when the workspace's Google\n * Analytics property was provisioned through Brandfine AND\n * the customer opted into tag injection. `install()` loads\n * gtag for it. Note gtag sets cookies: consent banners are\n * your site's responsibility. */\n gaMeasurementId?: string\n }\n | {\n enabled: true\n websiteId: string\n scriptUrl: string\n gaMeasurementId?: string\n }\n\nexport type AnalyticsOverviewRange = '24h' | '7d' | '30d' | '90d'\n\n/**\n * Composed traffic report for the workspace — summary KPIs +\n * bucketed chart data + top pages in one payload. Mirrors\n * `GET /external/analytics/overview` (see the API's\n * `ExternalAnalyticsOverview` type); additive changes only.\n *\n * Three shapes to handle:\n * - `{ enabled: false }` — analytics never enabled for the\n * workspace. Show an enable CTA.\n * - `{ enabled: true, verified: false }` — tracker provisioned\n * but no pageview recorded yet. Show \"waiting for first visit\".\n * - full payload — render the dashboard.\n */\nexport type AnalyticsOverview =\n | { enabled: false }\n | { enabled: true; verified: false }\n | {\n enabled: true\n verified: true\n range: AnalyticsOverviewRange\n summary: {\n visitors: number\n /** Fractional change vs the prior window (0.12 = +12%). */\n visitorsChange: number\n pageviews: number\n pageviewsChange: number\n visits: number\n visitsChange: number\n /** 0..1 fraction. */\n bounceRate: number\n bounceRateChange: number\n avgVisitSeconds: number\n avgVisitSecondsChange: number\n /** Visitors active in the last ~5 minutes. */\n activeNow: number\n }\n /** Bucketed chart data, oldest → newest. Hourly buckets for\n * `24h`, daily otherwise. `t` is an ISO-8601 bucket start. */\n timeseries: Array<{ t: string; visitors: number; pageviews: number }>\n /** Top 10 paths by views in the window. */\n topPages: Array<{ path: string; views: number; visitors: number }>\n /** Top 10 referrer sources by visitors. Empty-string source\n * means direct traffic. */\n sources: Array<{ source: string; visitors: number }>\n /** Top 10 visitor countries (ISO 3166-1 alpha-2 codes —\n * map to display names on your side, e.g. via\n * `Intl.DisplayNames`). */\n countries: Array<{ country: string; visitors: number }>\n /** Visitors by device class (`desktop` / `mobile` /\n * `tablet` / …). */\n devices: Array<{ device: string; visitors: number }>\n }\n\nexport type AnalyticsInstallResult =\n | { installed: false; reason: 'disabled' | 'ssr' | 'already-installed' }\n | { installed: true; websiteId: string }\n\nexport type InstallOptions = {\n /**\n * Pre-known config. When provided, `install()` skips the round-\n * trip to `/external/analytics-config` and injects the script\n * immediately. Use this when you've baked the values into your\n * build (env vars, CMS-side config dump, etc.) — typical for\n * static sites where the analytics state is decided at deploy\n * time, not per page load.\n *\n * Trade-off vs the default fetch path: if you disable analytics\n * in Brandfine, the tracker keeps loading until your next\n * deploy. That's usually the right trade for static sites\n * (which redeploy on every content change anyway) and the wrong\n * trade for dynamic sites where the api round-trip is cheap\n * relative to the rest of the page.\n *\n * Pass `{ enabled: false }` to force a no-op without touching\n * the api (e.g. to disable analytics for one environment without\n * changing Brandfine's state).\n */\n config?: AnalyticsConfig\n}\n\ntype AnalyticsApi = {\n /**\n * Injects the Brandfine analytics tracker into `document.head`\n * once. Safe to call on every page load — idempotent via a\n * marker attribute on the injected script tag.\n *\n * Two paths:\n * - `install()` — fetches the config from Brandfine, then\n * injects. Reflects enable/disable state on next page load.\n * - `install({ config })` — uses caller-provided config, skips\n * the fetch. Faster, no round-trip; ignores Brandfine state\n * changes until the consumer's next deploy.\n *\n * Returns details about what happened:\n * - `{ installed: true, websiteId }` — script was just injected.\n * - `{ installed: false, reason: 'disabled' }` — config says\n * analytics is off; no-op.\n * - `{ installed: false, reason: 'ssr' }` — no `document` in\n * scope (server-side). Call again on the client.\n * - `{ installed: false, reason: 'already-installed' }` — a\n * prior call (or another tab in the same SPA) already injected.\n *\n * Throws `BrandfineApiError` on non-2xx responses other than the\n * disabled case (which is a valid `{ enabled: false }` body).\n */\n install: (opts?: InstallOptions) => Promise<AnalyticsInstallResult>\n\n /** Lower-level helper — fetches the raw config without touching\n * the DOM. Useful when you want to inject the script yourself\n * (e.g. via a framework's <Script> component for nonce/csp). */\n getConfig: () => Promise<AnalyticsConfig>\n\n /**\n * Traffic report for the workspace — summary KPIs, bucketed\n * timeseries for charting, and top pages, in one round-trip.\n * This is a server-to-server read (it returns your site's\n * traffic data); call it from your backend or build step, not\n * from visitor-facing browser code.\n *\n * @param opts.range Window preset. Defaults to `'7d'`.\n */\n overview: (opts?: {\n range?: AnalyticsOverviewRange\n }) => Promise<AnalyticsOverview>\n}\n\nexport type LiveChatBootstrap =\n | { enabled: false }\n | {\n enabled: true\n /** The workspace's SCOPED publishable key — safe to bake into\n * public HTML; it can only start chat conversations. */\n publishableKey: string\n greeting: string | null\n offlineMessage: string | null\n theme: Record<string, string> | null\n /** Widget bundle path relative to the API base URL. */\n scriptPath: string\n /** Realtime endpoint (wss://…). OPTIONAL by design — this is\n * what makes the upgrade non-breaking: an older SDK ignores\n * it, a newer SDK against an older API sees it absent and\n * polls. Consumers pass `config` through wholesale, so it\n * reaches the browser with no change on their side. */\n realtimeUrl?: string\n }\n\nexport type LiveChatInstallResult =\n | { installed: false; reason: 'disabled' | 'ssr' | 'already-installed' }\n | { installed: true }\n\n/**\n * A signed visitor identity for Live Chat. Build it SERVER-SIDE:\n * compute `identityToken` with `liveChat.identityToken()` (or your\n * own HMAC_SHA256(identitySecret, externalId), hex) and pass the\n * whole object to `install()` — the widget forwards it verbatim and\n * the Brandfine API verifies the signature. An invalid or missing\n * token silently downgrades the conversation to anonymous.\n */\n/**\n * A signed host-app identity payload. Named per-plugin below for\n * discoverability, but it is ONE shape signed ONE way: a token from\n * `liveChat.identityToken(externalId)` is accepted by Live Chat AND\n * Appointments (`createRequest.visitor`, `list`/`get`).\n */\nexport type VerifiedVisitor = LiveChatVisitor\n\nexport type LiveChatVisitor = {\n /** Your app's stable id for this person (user id, lead reference…).\n * Conversations sharing an externalId are the same person across\n * devices and sessions. Max 128 chars. */\n externalId: string\n /** Display name shown in the Brandfine inbox. Max 120 chars. */\n name?: string\n /** Max 200 chars. */\n email?: string\n /** Small display-only key→string map (≤10 keys, values ≤100 chars). */\n attributes?: Record<string, string>\n /** hex HMAC_SHA256(identitySecret, externalId) — REQUIRED, computed\n * on your server. Never derive this in a browser. */\n identityToken: string\n}\n\nexport type LiveChatInstallOptions = {\n /**\n * Pre-known bootstrap. Same build-time/runtime trade-off as the\n * analytics `install()`: pass the value your server half fetched\n * (static-export sites bake it at build time), or omit — NOT\n * recommended in browsers, because fetching here would require\n * the broad key client-side. In practice: always pass `config`\n * from your server half.\n */\n config: LiveChatBootstrap\n /**\n * Already-signed visitor identity (see LiveChatVisitor). Optional —\n * omit for anonymous chat. The object must arrive from your server\n * with `identityToken` precomputed; `install()` only serializes it\n * onto the widget host, it performs no crypto.\n */\n visitor?: LiveChatVisitor\n /**\n * Locale for the widget's visitor-facing strings (greeting, away\n * message), e.g. your i18n router's active locale. Optional — the\n * widget falls back to the page's `<html lang>` and then the\n * browser language. Resolved server-side against the workspace's\n * configured translations; unknown locales get the default text.\n */\n locale?: string\n}\n\ntype LiveChatApi = {\n /**\n * Fetches the Live Chat bootstrap (publishable key + display\n * config) with the broad workspace key. SERVER-SIDE ONLY — call\n * from your backend, RSC, or build step, never from browser code.\n */\n getConfig: () => Promise<LiveChatBootstrap>\n\n /**\n * Injects the chat widget into the page: appends a host\n * `<div data-bf-live-chat>` carrying the publishable key and the\n * widget `<script>` tag. Idempotent via a marker attribute.\n * Browser-side half of the pair — receives the bootstrap your\n * server half fetched with `getConfig()`.\n *\n * Returns what happened, mirroring `analytics.install()`:\n * - `{ installed: true }` — widget just injected.\n * - `{ installed: false, reason: 'disabled' }` — chat is off.\n * - `{ installed: false, reason: 'ssr' }` — no `document`.\n * - `{ installed: false, reason: 'already-installed' }`.\n */\n install: (opts: LiveChatInstallOptions) => Promise<LiveChatInstallResult>\n\n /**\n * Computes the visitor identity token:\n * hex(HMAC_SHA256(signingSecret, externalId)). SERVER-ONLY — it\n * throws in a browser context rather than ever computing next to\n * the DOM.\n *\n * Signing secret, in order: `{ secret }` option →\n * `BRANDFINE_LIVE_CHAT_IDENTITY_SECRET` env → **derived from the\n * client's API key** (HMAC with a fixed domain-separation\n * constant; the Brandfine API derives the same value from its\n * stored copy). The derived path needs ZERO extra configuration —\n * an explicit secret is only for signers that shouldn't hold the\n * broad key, or legacy workspaces without a revealable key\n * (generate one in the CMS: Plugins → Live Chat → Integrate).\n */\n identityToken: (\n externalId: string,\n opts?: { secret?: string },\n ) => Promise<string>\n\n /**\n * Headless session — the conversation without the widget, for\n * building your own inline chat UI. Browser-only. Takes the same\n * `config` and signed `visitor` as `install()` (identical identity\n * rules and thread continuity — it IS the same conversation the\n * widget would join, sharing its storage keys). Transport today is\n * polling (`pollIntervalMs`, default 5000); a realtime upgrade\n * will keep this API and the poll as fallback.\n */\n createSession: (\n opts: LiveChatCreateSessionOptions,\n ) => Promise<LiveChatSession>\n\n /**\n * Raw, stateless wire methods for full control. NOTE on auth: the\n * config + start endpoints authenticate with this client's\n * `apiKey` header — in a browser, construct the client with the\n * PUBLISHABLE key (`createBrandfineClient({ apiKey: config.publishableKey })`),\n * never the broad key. Message endpoints authenticate with the\n * conversation token alone — treat it as a bearer credential:\n * don't log it, don't put it in URLs you share.\n */\n runtimeConfig: (locale?: string) => Promise<LiveChatRuntimeConfig>\n startConversation: (input: {\n visitorSessionId: string\n conversationToken?: string\n pageUrl?: string\n referrer?: string\n locale?: string\n visitor?: Record<string, unknown>\n }) => Promise<StartConversationResult>\n sendMessage: (\n conversationToken: string,\n input: { body: string; clientId?: string },\n ) => Promise<ChatMessage>\n history: (\n conversationToken: string,\n opts?: { after?: string; afterEvent?: number },\n ) => Promise<{ messages: ChatMessage[]; status: ConversationStatus }>\n}\n\n/** Attribute we stamp on the injected <script> so `install()` is\n * idempotent across re-renders and SPA route changes. */\nconst INSTALLED_MARKER = 'data-brandfine-analytics'\n\n/** Same idempotency contract for the live-chat widget injection. */\nconst LIVE_CHAT_MARKER = 'data-brandfine-live-chat'\n\n/**\n * Domain-separation constant for deriving the live-chat identity\n * signing secret from the workspace API key. MUST match the API's\n * constant (`live-chat-external.service.ts`) byte for byte — bump\n * the version suffix only in lockstep with a server release.\n */\nconst LIVE_CHAT_IDENTITY_CONTEXT = 'brandfine:live-chat:identity:v1'\n\n/** hex(HMAC_SHA256(key, message)) via WebCrypto — Node ≥20 global\n * AND browser-safe, so bundlers never see a node:crypto import. */\nasync function hmacHex(key: string, message: string): Promise<string> {\n const enc = new TextEncoder()\n const cryptoKey = await globalThis.crypto.subtle.importKey(\n 'raw',\n enc.encode(key),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n )\n const sig = await globalThis.crypto.subtle.sign(\n 'HMAC',\n cryptoKey,\n enc.encode(message),\n )\n return Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/** Marker for the injected Google tag — same idempotency contract. */\nconst GTAG_MARKER = 'data-brandfine-gtag'\n\n/**\n * Inject the Google tag (gtag.js) for an auto-provisioned GA4\n * property. No-ops when ANY gtag script is already on the page —\n * a site that hand-installed Google Analytics must not get a\n * second config (double-counted sessions are worse than a missing\n * tag). Safe to call repeatedly; the marker makes it idempotent.\n */\nfunction injectGoogleTag(measurementId: string): void {\n if (typeof document === 'undefined') return\n const existing = document.querySelector(\n `script[src*=\"googletagmanager.com/gtag/js\"], script[${GTAG_MARKER}]`,\n )\n if (existing) return\n\n const loader = document.createElement('script')\n loader.async = true\n loader.src = `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(measurementId)}`\n loader.setAttribute(GTAG_MARKER, measurementId)\n document.head.appendChild(loader)\n\n const w = window as unknown as { dataLayer?: unknown[] }\n w.dataLayer = w.dataLayer ?? []\n // gtag() must push `arguments` (an Arguments object), not a\n // plain array — GA's snippet relies on it.\n function gtag(..._args: unknown[]) {\n // eslint-disable-next-line prefer-rest-params\n w.dataLayer!.push(arguments)\n }\n gtag('js', new Date())\n gtag('config', measurementId)\n}\n\nconst DEFAULT_USER_AGENT = '@brandfine/client'\n\n/** Production API origin — the default when no `baseUrl` is given.\n * Consumers only configure a URL for local dev or staging. */\nconst DEFAULT_BASE_URL = 'https://api.brandfine.co'\n\n/**\n * Resolve the API origin: explicit option → `BRANDFINE_API_URL`\n * env (server-side only — browsers have no `process`, and an env\n * indirection in a browser bundle would be inlined at build time\n * anyway) → production default.\n */\nfunction resolveBaseUrl(explicit: string | undefined): string {\n if (explicit) return explicit\n if (typeof process !== 'undefined' && process.env?.BRANDFINE_API_URL) {\n return process.env.BRANDFINE_API_URL\n }\n return DEFAULT_BASE_URL\n}\n\nexport function createBrandfineClient(\n config: BrandfineClientConfig,\n): BrandfineClient {\n if (!config.apiKey)\n throw new Error('createBrandfineClient: `apiKey` is required')\n\n const baseUrl = resolveBaseUrl(config.baseUrl).replace(/\\/$/, '')\n const apiKey = config.apiKey\n // Resolve fetch lazily so consumers in environments without a\n // global fetch can polyfill before constructing the client.\n const fetchImpl: typeof fetch = config.fetch ?? globalThis.fetch\n const userAgent = config.userAgent ?? DEFAULT_USER_AGENT\n\n async function get<T>(path: string, opts: RequestOptions = {}): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method: 'GET',\n headers: {\n 'X-Api-Key': apiKey,\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n signal: opts.signal,\n })\n if (res.status === 404 && opts.nullable404) {\n // Drain the body so the underlying socket can be reused —\n // fetch implementations that don't auto-drain (older Node)\n // can leak otherwise.\n await res.text().catch(() => '')\n return null as T\n }\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body,\n url,\n })\n }\n return (await res.json()) as T\n }\n\n const posts: PostsApi = {\n async list<TConfig = unknown>(opts: ListPostsOptions = {}) {\n const out: BrandfinePost<TConfig>[] = []\n let page = 1\n const typeQuery = opts.type ? `&type=${encodeURIComponent(opts.type)}` : ''\n const localeQuery = opts.locale\n ? `&locale=${encodeURIComponent(opts.locale)}`\n : ''\n // Default pagination at the cms's 50-per-page cap. `forceLimit`\n // opts past it for content types that would otherwise need\n // many round-trips.\n const sizeQuery = opts.forceLimit\n ? `&force_limit=${opts.forceLimit}`\n : '&limit=50'\n // Pathological safety brake — 200 pages × 50 = 10k posts. If\n // a workspace ever needs more, callers should hit the API\n // directly with their own pagination logic.\n const MAX_PAGES = 200\n while (page <= MAX_PAGES) {\n const data = await get<BrandfinePostListResponse<TConfig>>(\n `/external/posts?include=content${sizeQuery}&page=${page}${typeQuery}${localeQuery}`,\n )\n out.push(...data.items)\n if (!data.pageInfo.hasNext) break\n page += 1\n }\n return out\n },\n async getBySlug<TConfig = unknown>(slug: string) {\n return get<BrandfinePost<TConfig> | null>(\n `/external/posts/${encodeURIComponent(slug)}`,\n { nullable404: true },\n )\n },\n }\n\n const categories: CategoriesApi = {\n async list(opts: ListCategoriesOptions = {}) {\n const qs = opts.locale ? `?locale=${encodeURIComponent(opts.locale)}` : ''\n const data = await get<{ items: BrandfineCategory[] }>(\n `/external/categories${qs}`,\n )\n return data.items\n },\n }\n\n const workspace: WorkspaceApi = {\n get<\n TCustomConfig = Record<string, unknown>,\n TSchemaOrg = Record<string, unknown>,\n >() {\n return get<BrandfineWorkspace<TCustomConfig, TSchemaOrg>>(\n '/external/workspace',\n )\n },\n }\n\n const navigations: NavigationsApi = {\n get<TConfig = unknown>(key: string) {\n return get<BrandfineNavigation<TConfig> | null>(\n `/external/navigations/${encodeURIComponent(key)}`,\n { nullable404: true },\n )\n },\n }\n\n const analytics: AnalyticsApi = {\n getConfig() {\n return get<AnalyticsConfig>('/external/analytics-config')\n },\n overview(opts = {}) {\n const range = opts.range ?? '7d'\n return get<AnalyticsOverview>(\n `/external/analytics/overview?range=${encodeURIComponent(range)}`,\n )\n },\n async install(opts: InstallOptions = {}) {\n // SSR safety: nothing to inject without a DOM. Consumers\n // call this from useEffect / onMount, but defensive anyway\n // (some frameworks still execute the file body on the server).\n if (typeof document === 'undefined') {\n return { installed: false, reason: 'ssr' as const }\n }\n\n // Use caller-provided config if present (build-time path),\n // otherwise fetch (runtime path).\n const cfg = opts.config ?? (await analytics.getConfig())\n\n // Google tag rides alongside the built-in tracker — injected\n // even when Brandfine analytics itself is off, because the\n // opt-in lives on the GA integration, not on the tracker.\n if (cfg.gaMeasurementId) {\n injectGoogleTag(cfg.gaMeasurementId)\n }\n\n if (!cfg.enabled) {\n return { installed: false, reason: 'disabled' as const }\n }\n\n // Idempotency: a prior call (StrictMode double-invoke, SPA\n // re-mount, second instance with the same workspace) may\n // have already injected. The marker attribute is the source\n // of truth — checking by script src would also miss the case\n // where two workspaces share the same scriptUrl.\n const existing = document.querySelector<HTMLScriptElement>(\n `script[${INSTALLED_MARKER}=\"${cfg.websiteId}\"]`,\n )\n if (existing) {\n return { installed: false, reason: 'already-installed' as const }\n }\n\n const script = document.createElement('script')\n script.defer = true\n script.src = cfg.scriptUrl\n script.setAttribute('data-website-id', cfg.websiteId)\n // The marker doubles as a sentinel + a debug aid (you can\n // grep the DOM for `data-brandfine-analytics` to confirm\n // an install).\n script.setAttribute(INSTALLED_MARKER, cfg.websiteId)\n document.head.appendChild(script)\n return { installed: true, websiteId: cfg.websiteId }\n },\n }\n\n /** Shared fetch for the live-chat public wire: optional key header\n * (publishable for config/start, none for token-authed message\n * endpoints), JSON in/out, BrandfineApiError on non-2xx. */\n async function liveChatWireRequest<T>(\n key: string | undefined,\n method: 'GET' | 'POST',\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method,\n headers: {\n Accept: 'application/json',\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...(key ? { 'X-Api-Key': key } : {}),\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body: text,\n url,\n })\n }\n return (await res.json()) as T\n }\n\n const liveChat: LiveChatApi = {\n getConfig() {\n return get<LiveChatBootstrap>('/external/live-chat/bootstrap')\n },\n async install(opts: LiveChatInstallOptions) {\n if (typeof document === 'undefined') {\n return { installed: false, reason: 'ssr' as const }\n }\n\n const cfg = opts.config\n if (!cfg.enabled) {\n return { installed: false, reason: 'disabled' as const }\n }\n\n const existing = document.querySelector(`[${LIVE_CHAT_MARKER}]`)\n if (existing) {\n return { installed: false, reason: 'already-installed' as const }\n }\n\n // Host div the widget script mounts into. Theme vars ride as\n // inline custom properties — they inherit through the widget's\n // shadow boundary.\n const host = document.createElement('div')\n host.setAttribute('data-bf-live-chat', '')\n host.setAttribute('data-publishable-key', cfg.publishableKey)\n host.setAttribute('data-base-url', baseUrl)\n host.setAttribute(LIVE_CHAT_MARKER, '')\n // Signed visitor identity → data-visitor. Serialization only;\n // the signature was computed server-side (identityToken()).\n if (opts.visitor?.externalId && opts.visitor.identityToken) {\n host.setAttribute('data-visitor', JSON.stringify(opts.visitor))\n }\n if (opts.locale) {\n host.setAttribute('data-locale', opts.locale)\n }\n if (cfg.theme) {\n for (const [key, value] of Object.entries(cfg.theme)) {\n if (key.startsWith('--bf-chat-')) {\n host.style.setProperty(key, value)\n }\n }\n }\n document.body.appendChild(host)\n\n const script = document.createElement('script')\n script.defer = true\n script.src = `${baseUrl}${cfg.scriptPath}`\n script.setAttribute(LIVE_CHAT_MARKER, 'script')\n document.head.appendChild(script)\n\n return { installed: true }\n },\n\n createSession(opts: LiveChatCreateSessionOptions) {\n // The session's key-authed calls use the PUBLISHABLE key from\n // the passed config (the browser client is often constructed\n // with a placeholder apiKey for install()-style usage).\n const pk = opts.config.enabled ? opts.config.publishableKey : undefined\n const wire: LiveChatWire = {\n runtimeConfig: (locale) => liveChatWireRequest(pk, 'GET',\n `/external/live-chat/config${locale ? `?locale=${encodeURIComponent(locale)}` : ''}`),\n startConversation: (input) =>\n liveChatWireRequest(pk, 'POST', '/external/live-chat/conversations', input),\n sendMessage: (conversationToken, input) =>\n liveChatWireRequest(undefined, 'POST',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages`, input),\n history: (conversationToken, o = {}) =>\n liveChatWireRequest(undefined, 'GET',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages${\n o.afterEvent !== undefined\n ? `?afterEvent=${encodeURIComponent(String(o.afterEvent))}`\n : o.after\n ? `?after=${encodeURIComponent(o.after)}`\n : ''\n }`),\n }\n return createLiveChatSession(wire, opts)\n },\n\n runtimeConfig(locale) {\n return liveChatWireRequest(apiKey, 'GET',\n `/external/live-chat/config${locale ? `?locale=${encodeURIComponent(locale)}` : ''}`)\n },\n startConversation(input) {\n return liveChatWireRequest(apiKey, 'POST', '/external/live-chat/conversations', input)\n },\n sendMessage(conversationToken, input) {\n return liveChatWireRequest(undefined, 'POST',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages`, input)\n },\n history(conversationToken, o = {}) {\n // afterEvent (eventId cursor, exact) wins over the legacy\n // timestamp cursor when the session knows one.\n const qs =\n o.afterEvent !== undefined\n ? `?afterEvent=${encodeURIComponent(String(o.afterEvent))}`\n : o.after\n ? `?after=${encodeURIComponent(o.after)}`\n : ''\n return liveChatWireRequest(undefined, 'GET',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages${qs}`)\n },\n\n async identityToken(externalId, opts = {}) {\n // Refuse to run where the secret could be exfiltrated. A page\n // that needs a token must get it from ITS server, not compute\n // one next to the DOM.\n if (typeof document !== 'undefined' || typeof window !== 'undefined') {\n throw new Error(\n 'liveChat.identityToken() is server-only — never compute identity ' +\n 'tokens in a browser. Sign the visitor on your server and pass ' +\n 'the result to install({ visitor }).',\n )\n }\n if (!externalId) {\n throw new Error('liveChat.identityToken(): externalId is required.')\n }\n // Signing-secret resolution:\n // 1. Explicit { secret } / BRANDFINE_LIVE_CHAT_IDENTITY_SECRET —\n // the decoupled option (a signer that shouldn't hold the\n // broad key, or a legacy workspace).\n // 2. DERIVED from the API key this client already holds:\n // HMAC(apiKey, domain-separation constant). Zero extra\n // config — the server derives the same value from its\n // stored copy of the key. Rotating the API key rotates\n // signing with it, which is the lifecycle you want anyway.\n const explicit =\n opts.secret ??\n (typeof process !== 'undefined'\n ? process.env.BRANDFINE_LIVE_CHAT_IDENTITY_SECRET\n : undefined)\n const secret =\n explicit ?? (await hmacHex(apiKey, LIVE_CHAT_IDENTITY_CONTEXT))\n return hmacHex(secret, externalId)\n },\n }\n\n const submissions: SubmissionsApi = {\n async create(input: CreateSubmissionInput) {\n const url = `${baseUrl}/external/submissions`\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n 'X-Api-Key': apiKey,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n body: JSON.stringify(input),\n })\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body,\n url,\n })\n }\n return (await res.json()) as Submission\n },\n }\n\n /**\n * Shared POST helper for the appointments namespace. The main\n * `get()` helper handles GETs; submissions has its own inline\n * POST because it predates this refactor. New plugin namespaces\n * (appointments first, others to follow) share this one so the\n * error-handling shape stays consistent.\n */\n async function post<T>(\n path: string,\n body: unknown,\n opts: RequestOptions = {},\n ): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n 'X-Api-Key': apiKey,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n body: JSON.stringify(body),\n signal: opts.signal,\n })\n if (!res.ok) {\n const errBody = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body: errBody,\n url,\n })\n }\n if (res.status === 204) return undefined as T\n return (await res.json()) as T\n }\n\n const appointments: AppointmentsApi = {\n getAvailability(opts = {}) {\n const qs: string[] = []\n if (opts.from) qs.push(`from=${encodeURIComponent(toIso(opts.from))}`)\n if (opts.to) qs.push(`to=${encodeURIComponent(toIso(opts.to))}`)\n const suffix = qs.length ? `?${qs.join('&')}` : ''\n return get<AppointmentAvailability>(\n `/external/appointments/availability${suffix}`,\n )\n },\n createRequest(input) {\n return post<CreatedAppointmentRequest>(\n '/external/appointments/requests',\n input,\n )\n },\n async list(identity) {\n // POST (not GET) so the identityToken never lands in URLs,\n // proxy logs, or browser history.\n const res = await post<{ appointments: Appointment[] }>(\n '/external/appointments/requests/lookup',\n identity,\n )\n return res.appointments\n },\n async get(id, identity) {\n // Same lookup endpoint narrowed by id — the server 404s when\n // the id is missing or belongs to another person, so a\n // successful response always carries exactly one row. The\n // guard below is defensive symmetry, not a real path.\n const res = await post<{ appointments: Appointment[] }>(\n '/external/appointments/requests/lookup',\n { ...identity, id },\n )\n const row = res.appointments[0]\n if (!row) {\n throw new BrandfineApiError({\n status: 404,\n statusText: 'Not Found',\n body: 'Appointment not found.',\n url: '/external/appointments/requests/lookup',\n })\n }\n return row\n },\n cancel(cancellationToken) {\n return post<{ id: string; status: 'CANCELLED' }>(\n `/external/appointments/requests/${encodeURIComponent(cancellationToken)}/cancel`,\n {},\n )\n },\n }\n\n return {\n get,\n posts,\n categories,\n workspace,\n navigations,\n analytics,\n submissions,\n appointments,\n liveChat,\n }\n}\n\n/** Accepts a Date or an already-ISO string and returns ISO. Saves\n * every caller from `.toISOString()`-ing manually. */\nfunction toIso(d: Date | string): string {\n return typeof d === 'string' ? d : d.toISOString()\n}\n","/**\n * @brandfine/client — root entry.\n *\n * The full SDK surface is exposed here for \"import everything from\n * one place\" usage. Tree-shaking + `sideEffects: false` mean\n * consumers don't pay a bundle cost for what they don't import.\n *\n * Heavier or framework-coupled pieces still live under subpath\n * exports (`@brandfine/client/cache`, `/resolvers`, `/webhook`) so\n * consumers with poor tree-shaking — or who only need one slice —\n * can scope their imports.\n */\n\nexport const SDK_VERSION = '0.0.0' as const\n\nexport {\n BrandfineApiError,\n createBrandfineClient,\n type AnalyticsConfig,\n type AnalyticsInstallResult,\n type AnalyticsOverview,\n type AnalyticsOverviewRange,\n type Appointment,\n type AppointmentAvailability,\n type AppointmentIdentity,\n type AppointmentSlot,\n type AppointmentStatus,\n type BrandfineClient,\n type BrandfineClientConfig,\n type ChatMessage,\n type CreateAppointmentRequestInput,\n type CreatedAppointmentRequest,\n type ChatMessageSender,\n type ConversationStatus,\n type CreateSubmissionInput,\n type InstallOptions,\n type LiveChatBootstrap,\n type LiveChatCreateSessionOptions,\n type LiveChatInstallOptions,\n type LiveChatInstallResult,\n type LiveChatRuntimeConfig,\n type LiveChatSession,\n type LiveChatSessionSnapshot,\n type LiveChatSessionState,\n type LiveChatVisitor,\n type StartConversationResult,\n type Submission,\n type VerifiedVisitor,\n} from './client'\n\nexport {\n createCache,\n createKeyedCache,\n type Cache,\n type CacheOptions,\n type KeyedCache,\n type KeyedCacheOptions,\n} from './cache/index'\n\nexport {\n isLocale,\n localizePath,\n pickLocale,\n resolveNavigation,\n stripLocalePrefix,\n type HydratedNav,\n type HydratedNavItem,\n type LocaleOptions,\n type ResolveNavigationOptions,\n} from './resolvers/index'\n\nexport {\n createBrandfineWebhookHandler,\n parseWebhookPayload,\n verifyWebhookSecret,\n type BrandfineWebhookEvent,\n type BrandfineWebhookHandlerOptions,\n type BrandfineWebhookPayload,\n} from './webhook/index'\n\nexport type {\n BrandfineCategory,\n BrandfineNavItem,\n BrandfineNavItemType,\n BrandfineNavPost,\n BrandfineNavigation,\n BrandfinePost,\n BrandfinePostListResponse,\n BrandfinePostTranslation,\n BrandfineWorkspace,\n ListCategoriesOptions,\n ListPostsOptions,\n} from './types'\n"]}
package/dist/index.d.cts CHANGED
@@ -17,7 +17,15 @@ export { BrandfineWebhookEvent, BrandfineWebhookHandlerOptions, BrandfineWebhook
17
17
  * who talks through the widget on one page and an inline panel on
18
18
  * another continues the same thread.
19
19
  */
20
- type ChatMessageSender = 'VISITOR' | 'AGENT' | 'SYSTEM';
20
+ /**
21
+ * `BOT` = AI-assistant replies (arriving once a workspace enables the
22
+ * live-chat AI agent). Render `BOT` on the business/agent side, ideally
23
+ * with an "AI" label — and treat any FUTURE unknown sender value the
24
+ * same way (agent-side, generic label) rather than switching
25
+ * exhaustively: the server may add sender kinds before your copy of
26
+ * this SDK learns their names.
27
+ */
28
+ type ChatMessageSender = 'VISITOR' | 'AGENT' | 'SYSTEM' | 'BOT';
21
29
  type ChatMessage = {
22
30
  id: string;
23
31
  body: string;
package/dist/index.d.ts CHANGED
@@ -17,7 +17,15 @@ export { BrandfineWebhookEvent, BrandfineWebhookHandlerOptions, BrandfineWebhook
17
17
  * who talks through the widget on one page and an inline panel on
18
18
  * another continues the same thread.
19
19
  */
20
- type ChatMessageSender = 'VISITOR' | 'AGENT' | 'SYSTEM';
20
+ /**
21
+ * `BOT` = AI-assistant replies (arriving once a workspace enables the
22
+ * live-chat AI agent). Render `BOT` on the business/agent side, ideally
23
+ * with an "AI" label — and treat any FUTURE unknown sender value the
24
+ * same way (agent-side, generic label) rather than switching
25
+ * exhaustively: the server may add sender kinds before your copy of
26
+ * this SDK learns their names.
27
+ */
28
+ type ChatMessageSender = 'VISITOR' | 'AGENT' | 'SYSTEM' | 'BOT';
21
29
  type ChatMessage = {
22
30
  id: string;
23
31
  body: string;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/live-chat-session.ts","../src/client.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Headless Live Chat session — the conversation WITHOUT the widget.\n *\n * `bf.liveChat.createSession()` gives a consumer everything needed to\n * render chat inline in their own design system: transcript state, a\n * send method, and a subscribe API shaped for\n * `useSyncExternalStore` / Svelte stores / Vue refs. Brandfine keeps\n * owning transport, identity, threading and storage; the consumer\n * owns pixels.\n *\n * Storage keys are IDENTICAL to the floating widget's, so a visitor\n * who talks through the widget on one page and an inline panel on\n * another continues the same thread.\n */\n\nexport type ChatMessageSender = 'VISITOR' | 'AGENT' | 'SYSTEM'\n\nexport type ChatMessage = {\n id: string\n body: string\n sender: ChatMessageSender\n createdAt: string\n /** Monotonic per-conversation sequence — the realtime resume\n * cursor. Optional: older APIs don't send it. */\n eventId?: number\n}\n\nexport type ConversationStatus = 'OPEN' | 'CLOSED'\n\nexport type LiveChatSessionState = 'CONNECTING' | 'OPEN' | 'CLOSED' | 'ERROR'\n\n/** The `/external/live-chat/config` shape — the RUNTIME config the\n * widget fetches per page load. Distinct from the server-side\n * `LiveChatBootstrap` (which additionally carries the publishable\n * key + script path). */\nexport type LiveChatRuntimeConfig =\n | { enabled: false }\n | {\n enabled: true\n greeting: string | null\n offlineMessage: string | null\n theme: Record<string, string> | null\n online?: boolean\n /** Realtime endpoint (wss://…). Absent = poll (older API, or\n * the transport is off). The session upgrades automatically\n * when present; consumers never touch it. */\n realtimeUrl?: string\n }\n\nexport type LiveChatSessionSnapshot = {\n messages: ChatMessage[]\n status: LiveChatSessionState\n /** Agents available right now (business-hours based today; agent\n * presence once the realtime transport lands). */\n online: boolean\n /** Localized via the session's `locale`. */\n greeting: string | null\n offlineMessage: string | null\n /** An outbound `send()` is in flight. */\n sending: boolean\n error: Error | null\n}\n\nexport type StartConversationResult =\n | { enabled: false }\n | {\n enabled: true\n conversationId: string\n conversationToken: string\n greeting: string | null\n resumed: boolean\n online?: boolean\n }\n\n/** Wire access the session needs — provided by the SDK client so the\n * session never hardcodes endpoints or headers. */\nexport type LiveChatWire = {\n runtimeConfig(locale?: string): Promise<LiveChatRuntimeConfig>\n startConversation(input: {\n visitorSessionId: string\n conversationToken?: string\n pageUrl?: string\n referrer?: string\n locale?: string\n visitor?: Record<string, unknown>\n }): Promise<StartConversationResult>\n sendMessage(\n conversationToken: string,\n input: { body: string; clientId?: string },\n ): Promise<ChatMessage>\n history(\n conversationToken: string,\n opts?: { after?: string; afterEvent?: number },\n ): Promise<{ messages: ChatMessage[]; status: ConversationStatus }>\n}\n\nexport type LiveChatCreateSessionOptions = {\n /** The server-half bootstrap (same object `install()` takes) —\n * supplies the publishable key (and, when the API advertises it,\n * the realtime endpoint — passed through wholesale). */\n config: { enabled: boolean; publishableKey?: string; realtimeUrl?: string }\n /** Signed identity — SAME shape and rules as `install()`. A bad\n * signature downgrades to anonymous server-side; it is never a\n * second identity path. */\n visitor?: Record<string, unknown>\n /** BCP-47 tag for greeting/away localization. Defaults to\n * `<html lang>` then browser language, like the widget. */\n locale?: string\n /** Defaults to `location.href`. */\n pageUrl?: string\n referrer?: string\n /** Transcript refresh cadence. Polling is the transport today; a\n * realtime upgrade will keep this as the fallback. */\n pollIntervalMs?: number\n}\n\nexport type LiveChatSession = {\n /** Current transcript (same array identity as the latest snapshot). */\n readonly messages: readonly ChatMessage[]\n readonly state: LiveChatSessionState\n send(body: string): Promise<ChatMessage>\n /** Listener fires on every snapshot change. Returns unsubscribe. */\n subscribe(listener: (s: LiveChatSessionSnapshot) => void): () => void\n getSnapshot(): LiveChatSessionSnapshot\n /** Stop polling, abort in-flight work, drop listeners. Idempotent. */\n close(): void\n /** Drop the stored conversation token and start a fresh thread —\n * identity switch on a shared browser. */\n reset(): Promise<void>\n}\n\n// ── Storage (MUST stay in lockstep with packages/widget-live-chat) ──\n\nconst SESSION_STORAGE_KEY = 'bf-live-chat-session'\n\nfunction tokenStorageKey(publishableKey: string) {\n return `bf-live-chat-token:${publishableKey.slice(-12)}`\n}\n\nfunction getVisitorSessionId(): string {\n try {\n const existing = localStorage.getItem(SESSION_STORAGE_KEY)\n if (existing) return existing\n const generated =\n typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID().replace(/-/g, '')\n : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n localStorage.setItem(SESSION_STORAGE_KEY, generated)\n return generated\n } catch {\n return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n }\n}\n\nfunction readStoredToken(publishableKey: string): string | undefined {\n try {\n return localStorage.getItem(tokenStorageKey(publishableKey)) ?? undefined\n } catch {\n return undefined\n }\n}\n\nfunction writeStoredToken(publishableKey: string, token: string | null) {\n try {\n if (token === null) localStorage.removeItem(tokenStorageKey(publishableKey))\n else localStorage.setItem(tokenStorageKey(publishableKey), token)\n } catch {\n /* storage blocked — resume just won't survive reloads */\n }\n}\n\nfunction resolvePageLocale(explicit?: string): string | undefined {\n if (explicit) return explicit\n if (typeof document === 'undefined') return undefined\n const htmlLang = document.documentElement.lang?.trim()\n if (htmlLang) return htmlLang\n return typeof navigator !== 'undefined'\n ? navigator.language || undefined\n : undefined\n}\n\n// ── The session ─────────────────────────────────────────────────────\n\nexport async function createLiveChatSession(\n wire: LiveChatWire,\n opts: LiveChatCreateSessionOptions,\n): Promise<LiveChatSession> {\n if (typeof window === 'undefined') {\n throw new Error(\n 'liveChat.createSession() is browser-only — build the transcript UI ' +\n 'client-side; fetch the bootstrap with getConfig() on the server.',\n )\n }\n const publishableKey = opts.config.enabled\n ? opts.config.publishableKey\n : undefined\n\n const locale = resolvePageLocale(opts.locale)\n const pollMs = Math.max(1000, opts.pollIntervalMs ?? 5000)\n\n let snapshot: LiveChatSessionSnapshot = {\n messages: [],\n status: 'CONNECTING',\n online: false,\n greeting: null,\n offlineMessage: null,\n sending: false,\n error: null,\n }\n const listeners = new Set<(s: LiveChatSessionSnapshot) => void>()\n const seen = new Set<string>()\n let cursor: string | undefined\n let lastEventId = 0\n let token: string | undefined\n let timer: ReturnType<typeof setInterval> | undefined\n let closed = false\n let ticking = false\n const aborter = new AbortController()\n\n // ── Realtime transport state (see startRealtime below) ──\n let realtimeUrl: string | undefined = opts.config.realtimeUrl\n let ws: WebSocket | undefined\n let wsLive = false\n let wsFailures = 0\n let wsGivenUp = false\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined\n let pingTimer: ReturnType<typeof setInterval> | undefined\n\n function emit(patch: Partial<LiveChatSessionSnapshot>) {\n snapshot = { ...snapshot, ...patch }\n for (const listener of listeners) listener(snapshot)\n }\n\n function appendMessages(incoming: ChatMessage[]) {\n const fresh = incoming.filter((m) => !seen.has(m.id))\n if (fresh.length === 0) {\n // Even a full dupe batch may advance the event cursor (e.g. the\n // socket redelivers what a poll already fetched).\n for (const m of incoming) {\n if (typeof m.eventId === 'number' && m.eventId > lastEventId) {\n lastEventId = m.eventId\n }\n }\n return\n }\n for (const m of fresh) {\n seen.add(m.id)\n if (typeof m.eventId === 'number' && m.eventId > lastEventId) {\n lastEventId = m.eventId\n }\n }\n const messages = [...snapshot.messages, ...fresh]\n cursor = messages[messages.length - 1]!.createdAt\n emit({ messages })\n }\n\n function markClosed() {\n if (snapshot.status !== 'CLOSED') emit({ status: 'CLOSED' })\n stopPolling()\n teardownRealtime()\n wsGivenUp = true // a closed thread never reconnects; reset() re-arms\n }\n\n async function tick() {\n // Re-entrancy guard: a slow response must not overlap the next\n // interval firing (double-fetch → possible reorder).\n if (ticking || closed || !token) return\n ticking = true\n try {\n const result = await wire.history(\n token,\n // The eventId cursor is exact (no equal-millisecond drop\n // window); the timestamp cursor remains for old APIs that\n // never sent eventIds.\n lastEventId > 0 ? { afterEvent: lastEventId } : { after: cursor },\n )\n if (closed) return\n appendMessages(result.messages)\n if (result.status === 'CLOSED') markClosed()\n } catch {\n /* transient poll failure — next tick retries */\n } finally {\n ticking = false\n }\n }\n\n function startPolling() {\n stopPolling()\n timer = setInterval(() => void tick(), pollMs)\n }\n function stopPolling() {\n if (timer !== undefined) clearInterval(timer)\n timer = undefined\n }\n\n // ── Realtime transport (delivery-only WebSocket) ────────────────\n //\n // Invisible above the session API by contract: same subscribe,\n // same snapshots. While the socket is live, polling stops; the\n // moment it isn't (drop, backoff wait, permanent failure, no\n // WebSocket in the runtime, no realtimeUrl advertised), polling\n // covers the gap exactly as before. Sends stay on REST.\n\n function teardownRealtime() {\n if (reconnectTimer !== undefined) clearTimeout(reconnectTimer)\n reconnectTimer = undefined\n if (pingTimer !== undefined) clearInterval(pingTimer)\n pingTimer = undefined\n wsLive = false\n if (ws) {\n const socket = ws\n ws = undefined\n try {\n socket.onopen = socket.onmessage = socket.onclose = socket.onerror =\n null\n socket.close()\n } catch {\n /* already closed */\n }\n }\n }\n\n function scheduleReconnect() {\n if (closed || wsGivenUp || reconnectTimer !== undefined) return\n // 1s → 30s exponential with jitter; after 5 consecutive\n // failures, stay on polling for the session's lifetime — a\n // socket a corporate proxy kills will never open, and the user\n // still gets their messages.\n if (wsFailures >= 5) {\n wsGivenUp = true\n return\n }\n const base = Math.min(30_000, 1_000 * 2 ** wsFailures)\n const delay = base / 2 + Math.random() * (base / 2)\n reconnectTimer = setTimeout(() => {\n reconnectTimer = undefined\n startRealtime()\n }, delay)\n }\n\n function handleSocketDown() {\n teardownRealtime()\n if (closed || wsGivenUp || snapshot.status === 'CLOSED') return\n // Polling covers the gap from the FIRST millisecond of the\n // outage; a later successful socket auth stops it again.\n startPolling()\n wsFailures += 1\n scheduleReconnect()\n }\n\n function startRealtime() {\n if (\n closed ||\n wsGivenUp ||\n wsLive ||\n ws !== undefined ||\n !realtimeUrl ||\n !token ||\n !publishableKey ||\n typeof WebSocket === 'undefined' ||\n snapshot.status === 'CLOSED'\n ) {\n return\n }\n let socket: WebSocket\n try {\n socket = new WebSocket(realtimeUrl)\n } catch {\n wsFailures += 1\n scheduleReconnect()\n return\n }\n ws = socket\n socket.onopen = () => {\n // Auth in the FIRST FRAME, never the URL — tokens must not\n // land in proxy logs or browser history.\n try {\n socket.send(\n JSON.stringify({\n type: 'auth',\n publishableKey,\n conversationToken: token,\n lastEventId,\n }),\n )\n } catch {\n handleSocketDown()\n }\n }\n socket.onmessage = (event) => {\n let frame: { type?: string } & Record<string, unknown>\n try {\n frame = JSON.parse(String(event.data)) as typeof frame\n } catch {\n return // unknown/garbage frames are ignored by contract\n }\n switch (frame.type) {\n case 'ready': {\n wsLive = true\n wsFailures = 0\n stopPolling() // the socket is now the transport\n if (pingTimer === undefined) {\n pingTimer = setInterval(() => {\n try {\n socket.send(JSON.stringify({ type: 'ping' }))\n } catch {\n /* close race — the onclose handler recovers */\n }\n }, 30_000)\n }\n break\n }\n case 'message': {\n const m = frame.message as ChatMessage | undefined\n if (m && typeof m.id === 'string') {\n appendMessages([\n {\n ...m,\n eventId:\n typeof frame.eventId === 'number' ? frame.eventId : m.eventId,\n },\n ])\n }\n break\n }\n case 'status': {\n if (frame.status === 'CLOSED') markClosed()\n break\n }\n case 'error': {\n // The server refused us (bad auth, socket caps…). Fall\n // back to polling permanently — retrying a refusal is\n // just noise, and delivery still works.\n wsGivenUp = true\n teardownRealtime()\n if (!closed && snapshot.status !== 'CLOSED') startPolling()\n break\n }\n default:\n break // pong + future frame types\n }\n }\n socket.onclose = () => handleSocketDown()\n socket.onerror = () => {\n /* onclose always follows; avoid double-handling */\n }\n }\n\n async function connect(resumeToken: string | undefined) {\n if (!publishableKey) {\n emit({ status: 'CLOSED' })\n return\n }\n // Config first — greeting/away/online, localized.\n try {\n const cfg = await wire.runtimeConfig(locale)\n if (closed) return\n if (!cfg.enabled) {\n emit({ status: 'CLOSED' })\n return\n }\n emit({\n greeting: cfg.greeting,\n offlineMessage: cfg.offlineMessage,\n online: cfg.online !== false,\n })\n // Runtime config wins over the build-time bootstrap — it's\n // fetched live, so switching realtime off takes effect on the\n // next page load without a redeploy.\n if (cfg.realtimeUrl !== undefined) realtimeUrl = cfg.realtimeUrl\n } catch (error) {\n if (closed) return\n emit({ status: 'ERROR', error: error as Error })\n return\n }\n\n try {\n const started = await wire.startConversation({\n visitorSessionId: getVisitorSessionId(),\n conversationToken: resumeToken,\n pageUrl:\n opts.pageUrl ??\n (typeof location !== 'undefined'\n ? location.href.slice(0, 2048)\n : undefined),\n referrer: opts.referrer,\n locale,\n visitor: opts.visitor,\n })\n if (closed) return\n if (!started.enabled) {\n emit({ status: 'CLOSED' })\n return\n }\n token = started.conversationToken\n writeStoredToken(publishableKey, token)\n if (started.online !== undefined) emit({ online: started.online })\n emit({ status: 'OPEN' })\n await tick() // initial history before the first interval\n if (!closed) {\n startPolling()\n startRealtime() // upgrades in place; polling stops on `ready`\n }\n } catch (error) {\n if (closed) return\n emit({ status: 'ERROR', error: error as Error })\n }\n }\n\n const session: LiveChatSession = {\n get messages() {\n return snapshot.messages\n },\n get state() {\n return snapshot.status\n },\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n async send(body) {\n const trimmed = body.trim()\n if (!trimmed) throw new Error('send(): message body is empty')\n if (!token) throw new Error('send(): session is not connected')\n if (snapshot.status === 'CLOSED') {\n throw new Error('send(): conversation is closed — call reset()')\n }\n emit({ sending: true })\n try {\n // Idempotency key: if the response is lost mid-flight, a\n // consumer retry with the same body can't duplicate — the\n // server replays the original row for this clientId.\n const clientId =\n typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID()\n : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n const message = await wire.sendMessage(token, {\n body: trimmed,\n clientId,\n })\n appendMessages([message])\n return message\n } finally {\n emit({ sending: false })\n }\n },\n close() {\n if (closed) return\n closed = true\n stopPolling()\n teardownRealtime()\n aborter.abort()\n listeners.clear()\n },\n async reset() {\n // Socket FIRST — a socket left attached to the old conversation\n // would defeat the shared-browser guarantee reset() exists for.\n teardownRealtime()\n wsFailures = 0\n wsGivenUp = false\n if (publishableKey) writeStoredToken(publishableKey, null)\n token = undefined\n cursor = undefined\n lastEventId = 0\n seen.clear()\n stopPolling()\n emit({ messages: [], status: 'CONNECTING', error: null })\n await connect(undefined)\n },\n }\n\n await connect(publishableKey ? readStoredToken(publishableKey) : undefined)\n return session\n}\n","/**\n * `createBrandfineClient` — the SDK's entry point.\n *\n * Returns a stateless, multi-instance-safe handle scoped to a\n * single `(baseUrl, apiKey)` pair. Pattern follows the Stripe /\n * Algolia / OpenAI SDKs — explicit construction with config,\n * namespaced methods (`bf.posts.list(...)`, `bf.workspace.get()`),\n * no module-level singletons.\n *\n * Why factory not module-level state: multi-tenant consumers\n * sometimes need two clients in the same process (e.g. main site\n * + admin preview). Module-level env reading makes that impossible\n * without monkey-patching.\n */\n\nimport type {\n BrandfineCategory,\n BrandfineNavigation,\n BrandfinePost,\n BrandfinePostListResponse,\n BrandfineWorkspace,\n ListCategoriesOptions,\n ListPostsOptions,\n} from './types'\n\nimport {\n createLiveChatSession,\n type ChatMessage,\n type ConversationStatus,\n type LiveChatCreateSessionOptions,\n type LiveChatRuntimeConfig,\n type LiveChatSession,\n type LiveChatWire,\n type StartConversationResult,\n} from './live-chat-session'\n\nexport type {\n ChatMessage,\n ChatMessageSender,\n ConversationStatus,\n LiveChatCreateSessionOptions,\n LiveChatRuntimeConfig,\n LiveChatSession,\n LiveChatSessionSnapshot,\n LiveChatSessionState,\n StartConversationResult,\n} from './live-chat-session'\n\nexport type BrandfineClientConfig = {\n /** Base URL of the Brandfine API. OPTIONAL — defaults to the\n * production API (`https://api.brandfine.co`); on the server the\n * `BRANDFINE_API_URL` env var overrides the default (set it for\n * local dev / staging). Explicit option wins over both. No\n * trailing slash — the client trims one if you pass it anyway. */\n baseUrl?: string\n /** Workspace-scoped API key. Generated from the cms's Workspace\n * settings; identifies which workspace the client talks to. */\n apiKey: string\n /** Optional fetch override. Useful for tests (inject a stub),\n * for runtimes that need a custom implementation (edge workers\n * with non-standard fetch), or to add cross-cutting concerns\n * like tracing / retries. Defaults to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch\n /** Optional User-Agent header. Falls back to a generic SDK tag. */\n userAgent?: string\n}\n\n/**\n * Structured error thrown by every request helper on non-2xx\n * responses. Carries the raw body so consumers can log it for\n * debugging without re-fetching.\n */\nexport class BrandfineApiError extends Error {\n override readonly name = 'BrandfineApiError'\n readonly status: number\n readonly statusText: string\n readonly body: string\n readonly url: string\n\n constructor(args: {\n status: number\n statusText: string\n body: string\n url: string\n }) {\n super(\n `[brandfine] ${args.status} ${args.statusText} on ${args.url} — ${args.body.slice(0, 200)}`,\n )\n this.status = args.status\n this.statusText = args.statusText\n this.body = args.body\n this.url = args.url\n }\n}\n\ntype RequestOptions = {\n /** When true and the response is 404, return `null` instead of\n * throwing. Used by endpoints where 404 is a meaningful empty\n * state (navigation by key, single post by slug). */\n nullable404?: boolean\n signal?: AbortSignal\n}\n\nexport type BrandfineClient = {\n /** Low-level GET. Reserved for endpoints we don't have a typed\n * helper for yet. Adds the X-Api-Key header automatically. */\n get: <T>(path: string, opts?: RequestOptions) => Promise<T>\n posts: PostsApi\n categories: CategoriesApi\n workspace: WorkspaceApi\n navigations: NavigationsApi\n analytics: AnalyticsApi\n submissions: SubmissionsApi\n appointments: AppointmentsApi\n liveChat: LiveChatApi\n}\n\ntype PostsApi = {\n /** Paginated list of published posts. Handles the cms's\n * pagination transparently — caller gets a flat array. */\n list: <TConfig = unknown>(\n opts?: ListPostsOptions,\n ) => Promise<BrandfinePost<TConfig>[]>\n /** Single post by per-locale URL slug, scoped to the active\n * locale on the workspace's content. Returns `null` for 404 so\n * callers can render their own \"not found\" page without try/catch. */\n getBySlug: <TConfig = unknown>(\n slug: string,\n ) => Promise<BrandfinePost<TConfig> | null>\n}\n\ntype CategoriesApi = {\n list: (opts?: ListCategoriesOptions) => Promise<BrandfineCategory[]>\n}\n\ntype WorkspaceApi = {\n get: <\n TCustomConfig = Record<string, unknown>,\n TSchemaOrg = Record<string, unknown>,\n >() => Promise<BrandfineWorkspace<TCustomConfig, TSchemaOrg>>\n}\n\ntype NavigationsApi = {\n /** Navigation by its workspace-scoped `key` (e.g. `'header'`).\n * Returns `null` for 404 so consumers can fall back to a\n * hardcoded default without try/catch. `TConfig` narrows each\n * item's `customConfig` (default `unknown`). */\n get: <TConfig = unknown>(\n key: string,\n ) => Promise<BrandfineNavigation<TConfig> | null>\n}\n\nexport type CreateSubmissionInput = {\n /** Required. Display name of the submitter. */\n name: string\n /** Required. Validated server-side. */\n email: string\n /** Optional. Free-text up to 40 chars. */\n phone?: string\n /** Optional. Free-text up to 200 chars. */\n subject?: string\n /** Required. The message body — up to 10,000 chars. */\n message: string\n /** Optional. Where the submission came from — e.g. a route path\n * like `/contact`, or a marketing campaign label. Up to 500 chars. */\n source?: string\n /** Optional. Free-form JSON metadata the consumer attaches; the\n * cms surfaces it verbatim in the submissions admin view. */\n metadata?: Record<string, unknown>\n}\n\nexport type Submission = {\n id: string\n createdAt: string\n}\n\ntype SubmissionsApi = {\n /**\n * Posts a contact-form submission to `POST /external/submissions`\n * for this workspace. The cms surfaces the submission in the\n * Submissions inbox.\n *\n * Throws `BrandfineApiError` on validation failures (400) or\n * any other non-2xx — caller decides whether to surface that as\n * a user-visible error or a silent retry.\n */\n create: (input: CreateSubmissionInput) => Promise<Submission>\n}\n\n// ----------------------------------------------------------------\n// Appointments plugin SDK — pairs with the Appointments embed\n// widget. Consumers who want full control over the booking UI use\n// these methods directly; consumers who want the drop-in widget\n// use the `<script>` embed (which itself uses these methods under\n// the hood). The same `BrandfineClient` instance powers both.\n// ----------------------------------------------------------------\n\nexport type AppointmentSlot = {\n /** UTC ISO 8601 timestamp of the slot start. */\n start: string\n /** UTC ISO 8601 timestamp of the slot end. */\n end: string\n}\n\nexport type AppointmentAvailability = {\n /** False = plugin not activated, or activation row's `enabled`\n * flag is off. Widgets should render a \"not accepting bookings\"\n * state, not throw. */\n enabled: boolean\n /** Source-of-truth IANA timezone for the workspace's business\n * hours. Visitors see slots in their local TZ — use this for\n * the \"(workspace local: HH:MM)\" subtext. */\n timezone: string\n slotDurationMinutes: number\n leadTimeHours: number\n bookingWindowDays: number\n policyText: string | null\n slots: AppointmentSlot[]\n /** UTC ISO 8601. Useful for the widget's date range label. */\n windowStart: string\n windowEnd: string\n}\n\nexport type CreateAppointmentRequestInput = {\n visitorName: string\n visitorEmail: string\n visitorPhone?: string\n visitorMessage?: string\n /** UTC ISO 8601 of the requested slot start. Server re-validates\n * against business hours + busy ranges before accepting. */\n requestedAt: string\n /** Optional cookie-derived session id from the consumer site. */\n visitorSessionId?: string\n /** Signed host-app identity — the SAME payload shape and signature\n * Live Chat accepts, so one `liveChat.identityToken(externalId)`\n * call signs for both plugins. When present and valid, the booking\n * is attributed to the person (verified name/email take precedence\n * over the free-text fields above, and the booking links to their\n * chat threads via the shared externalId). Invalid or absent →\n * the booking proceeds anonymously — it is never rejected. */\n visitor?: VerifiedVisitor\n}\n\nexport type CreatedAppointmentRequest = {\n id: string\n createdAt: string\n requestedAt: string\n durationMinutes: number\n status: 'PENDING'\n /** True iff `visitor.identityToken` HMAC-verified — your signal\n * that the booking landed attributed rather than anonymous. */\n identityVerified: boolean\n /** Visitor's self-cancel token. Embed it in confirmation\n * emails / on-page UI so the visitor can cancel without an\n * account. One-time use; revoked once any party acts. */\n cancellationToken: string | null\n}\n\nexport type AppointmentStatus =\n | 'PENDING'\n | 'CONFIRMED'\n | 'REJECTED'\n | 'CANCELLED'\n\n/** One appointment as the identity-scoped read-back returns it —\n * the visitor-safe projection (\"where does my request stand?\"). */\nexport type Appointment = {\n id: string\n status: AppointmentStatus\n /** UTC ISO 8601 of the requested slot start (reflects the current\n * slot after a reschedule). */\n requestedAt: string\n /** The confirmed slot — non-null only once CONFIRMED. */\n scheduledAt: string | null\n durationMinutes: number\n /** Workspace's IANA timezone for local rendering. */\n timezone: string\n /** Customer's note — populated only on REJECTED. */\n declineReason: string | null\n rescheduleCount: number\n respondedAt: string | null\n createdAt: string\n}\n\n/** Proof of identity for read-back calls: the same externalId +\n * `liveChat.identityToken(externalId)` pair used when booking. */\nexport type AppointmentIdentity = {\n externalId: string\n identityToken: string\n}\n\ntype AppointmentsApi = {\n /**\n * Available slots for the workspace's booking window.\n * `from` / `to` are optional clamps inside the workspace's\n * configured window — the server ignores ranges outside.\n */\n getAvailability: (opts?: {\n from?: Date | string\n to?: Date | string\n }) => Promise<AppointmentAvailability>\n /**\n * Submit a visitor's appointment request. Server-side validates\n * the slot is still bookable; if it isn't, throws\n * `BrandfineApiError` with status 404 / 409.\n *\n * Pass `visitor` (signed with `liveChat.identityToken()`) to book\n * as a known person — that unlocks `list`/`get` read-back below.\n * Anonymous bookings remain fully supported; their status flow\n * stays email-driven via the cancellation-token link.\n */\n createRequest: (\n input: CreateAppointmentRequestInput,\n ) => Promise<CreatedAppointmentRequest>\n /**\n * Every appointment belonging to the verified person, newest\n * first. Requires a valid identity signature — throws\n * `BrandfineApiError` 401 on a bad one (reads need proof; there\n * is no anonymous downgrade for reading history). Server-side\n * only, like `identityToken()` itself.\n */\n list: (identity: AppointmentIdentity) => Promise<Appointment[]>\n /**\n * One appointment by id, identity-scoped. 404s when the id does\n * not exist OR belongs to someone else — indistinguishable by\n * design.\n */\n get: (id: string, identity: AppointmentIdentity) => Promise<Appointment>\n /**\n * Spend the one-time `cancellationToken` from `createRequest` to\n * cancel a still-PENDING request. The token IS the credential —\n * no id or API key needed. Throws `BrandfineApiError` 400 when\n * the request is no longer PENDING, 404 when the token is\n * unknown/already spent.\n */\n cancel: (\n cancellationToken: string,\n ) => Promise<{ id: string; status: 'CANCELLED' }>\n}\n\nexport type AnalyticsConfig =\n | {\n enabled: false\n /** GA4 Measurement ID — present when the workspace's Google\n * Analytics property was provisioned through Brandfine AND\n * the customer opted into tag injection. `install()` loads\n * gtag for it. Note gtag sets cookies: consent banners are\n * your site's responsibility. */\n gaMeasurementId?: string\n }\n | {\n enabled: true\n websiteId: string\n scriptUrl: string\n gaMeasurementId?: string\n }\n\nexport type AnalyticsOverviewRange = '24h' | '7d' | '30d' | '90d'\n\n/**\n * Composed traffic report for the workspace — summary KPIs +\n * bucketed chart data + top pages in one payload. Mirrors\n * `GET /external/analytics/overview` (see the API's\n * `ExternalAnalyticsOverview` type); additive changes only.\n *\n * Three shapes to handle:\n * - `{ enabled: false }` — analytics never enabled for the\n * workspace. Show an enable CTA.\n * - `{ enabled: true, verified: false }` — tracker provisioned\n * but no pageview recorded yet. Show \"waiting for first visit\".\n * - full payload — render the dashboard.\n */\nexport type AnalyticsOverview =\n | { enabled: false }\n | { enabled: true; verified: false }\n | {\n enabled: true\n verified: true\n range: AnalyticsOverviewRange\n summary: {\n visitors: number\n /** Fractional change vs the prior window (0.12 = +12%). */\n visitorsChange: number\n pageviews: number\n pageviewsChange: number\n visits: number\n visitsChange: number\n /** 0..1 fraction. */\n bounceRate: number\n bounceRateChange: number\n avgVisitSeconds: number\n avgVisitSecondsChange: number\n /** Visitors active in the last ~5 minutes. */\n activeNow: number\n }\n /** Bucketed chart data, oldest → newest. Hourly buckets for\n * `24h`, daily otherwise. `t` is an ISO-8601 bucket start. */\n timeseries: Array<{ t: string; visitors: number; pageviews: number }>\n /** Top 10 paths by views in the window. */\n topPages: Array<{ path: string; views: number; visitors: number }>\n /** Top 10 referrer sources by visitors. Empty-string source\n * means direct traffic. */\n sources: Array<{ source: string; visitors: number }>\n /** Top 10 visitor countries (ISO 3166-1 alpha-2 codes —\n * map to display names on your side, e.g. via\n * `Intl.DisplayNames`). */\n countries: Array<{ country: string; visitors: number }>\n /** Visitors by device class (`desktop` / `mobile` /\n * `tablet` / …). */\n devices: Array<{ device: string; visitors: number }>\n }\n\nexport type AnalyticsInstallResult =\n | { installed: false; reason: 'disabled' | 'ssr' | 'already-installed' }\n | { installed: true; websiteId: string }\n\nexport type InstallOptions = {\n /**\n * Pre-known config. When provided, `install()` skips the round-\n * trip to `/external/analytics-config` and injects the script\n * immediately. Use this when you've baked the values into your\n * build (env vars, CMS-side config dump, etc.) — typical for\n * static sites where the analytics state is decided at deploy\n * time, not per page load.\n *\n * Trade-off vs the default fetch path: if you disable analytics\n * in Brandfine, the tracker keeps loading until your next\n * deploy. That's usually the right trade for static sites\n * (which redeploy on every content change anyway) and the wrong\n * trade for dynamic sites where the api round-trip is cheap\n * relative to the rest of the page.\n *\n * Pass `{ enabled: false }` to force a no-op without touching\n * the api (e.g. to disable analytics for one environment without\n * changing Brandfine's state).\n */\n config?: AnalyticsConfig\n}\n\ntype AnalyticsApi = {\n /**\n * Injects the Brandfine analytics tracker into `document.head`\n * once. Safe to call on every page load — idempotent via a\n * marker attribute on the injected script tag.\n *\n * Two paths:\n * - `install()` — fetches the config from Brandfine, then\n * injects. Reflects enable/disable state on next page load.\n * - `install({ config })` — uses caller-provided config, skips\n * the fetch. Faster, no round-trip; ignores Brandfine state\n * changes until the consumer's next deploy.\n *\n * Returns details about what happened:\n * - `{ installed: true, websiteId }` — script was just injected.\n * - `{ installed: false, reason: 'disabled' }` — config says\n * analytics is off; no-op.\n * - `{ installed: false, reason: 'ssr' }` — no `document` in\n * scope (server-side). Call again on the client.\n * - `{ installed: false, reason: 'already-installed' }` — a\n * prior call (or another tab in the same SPA) already injected.\n *\n * Throws `BrandfineApiError` on non-2xx responses other than the\n * disabled case (which is a valid `{ enabled: false }` body).\n */\n install: (opts?: InstallOptions) => Promise<AnalyticsInstallResult>\n\n /** Lower-level helper — fetches the raw config without touching\n * the DOM. Useful when you want to inject the script yourself\n * (e.g. via a framework's <Script> component for nonce/csp). */\n getConfig: () => Promise<AnalyticsConfig>\n\n /**\n * Traffic report for the workspace — summary KPIs, bucketed\n * timeseries for charting, and top pages, in one round-trip.\n * This is a server-to-server read (it returns your site's\n * traffic data); call it from your backend or build step, not\n * from visitor-facing browser code.\n *\n * @param opts.range Window preset. Defaults to `'7d'`.\n */\n overview: (opts?: {\n range?: AnalyticsOverviewRange\n }) => Promise<AnalyticsOverview>\n}\n\nexport type LiveChatBootstrap =\n | { enabled: false }\n | {\n enabled: true\n /** The workspace's SCOPED publishable key — safe to bake into\n * public HTML; it can only start chat conversations. */\n publishableKey: string\n greeting: string | null\n offlineMessage: string | null\n theme: Record<string, string> | null\n /** Widget bundle path relative to the API base URL. */\n scriptPath: string\n /** Realtime endpoint (wss://…). OPTIONAL by design — this is\n * what makes the upgrade non-breaking: an older SDK ignores\n * it, a newer SDK against an older API sees it absent and\n * polls. Consumers pass `config` through wholesale, so it\n * reaches the browser with no change on their side. */\n realtimeUrl?: string\n }\n\nexport type LiveChatInstallResult =\n | { installed: false; reason: 'disabled' | 'ssr' | 'already-installed' }\n | { installed: true }\n\n/**\n * A signed visitor identity for Live Chat. Build it SERVER-SIDE:\n * compute `identityToken` with `liveChat.identityToken()` (or your\n * own HMAC_SHA256(identitySecret, externalId), hex) and pass the\n * whole object to `install()` — the widget forwards it verbatim and\n * the Brandfine API verifies the signature. An invalid or missing\n * token silently downgrades the conversation to anonymous.\n */\n/**\n * A signed host-app identity payload. Named per-plugin below for\n * discoverability, but it is ONE shape signed ONE way: a token from\n * `liveChat.identityToken(externalId)` is accepted by Live Chat AND\n * Appointments (`createRequest.visitor`, `list`/`get`).\n */\nexport type VerifiedVisitor = LiveChatVisitor\n\nexport type LiveChatVisitor = {\n /** Your app's stable id for this person (user id, lead reference…).\n * Conversations sharing an externalId are the same person across\n * devices and sessions. Max 128 chars. */\n externalId: string\n /** Display name shown in the Brandfine inbox. Max 120 chars. */\n name?: string\n /** Max 200 chars. */\n email?: string\n /** Small display-only key→string map (≤10 keys, values ≤100 chars). */\n attributes?: Record<string, string>\n /** hex HMAC_SHA256(identitySecret, externalId) — REQUIRED, computed\n * on your server. Never derive this in a browser. */\n identityToken: string\n}\n\nexport type LiveChatInstallOptions = {\n /**\n * Pre-known bootstrap. Same build-time/runtime trade-off as the\n * analytics `install()`: pass the value your server half fetched\n * (static-export sites bake it at build time), or omit — NOT\n * recommended in browsers, because fetching here would require\n * the broad key client-side. In practice: always pass `config`\n * from your server half.\n */\n config: LiveChatBootstrap\n /**\n * Already-signed visitor identity (see LiveChatVisitor). Optional —\n * omit for anonymous chat. The object must arrive from your server\n * with `identityToken` precomputed; `install()` only serializes it\n * onto the widget host, it performs no crypto.\n */\n visitor?: LiveChatVisitor\n /**\n * Locale for the widget's visitor-facing strings (greeting, away\n * message), e.g. your i18n router's active locale. Optional — the\n * widget falls back to the page's `<html lang>` and then the\n * browser language. Resolved server-side against the workspace's\n * configured translations; unknown locales get the default text.\n */\n locale?: string\n}\n\ntype LiveChatApi = {\n /**\n * Fetches the Live Chat bootstrap (publishable key + display\n * config) with the broad workspace key. SERVER-SIDE ONLY — call\n * from your backend, RSC, or build step, never from browser code.\n */\n getConfig: () => Promise<LiveChatBootstrap>\n\n /**\n * Injects the chat widget into the page: appends a host\n * `<div data-bf-live-chat>` carrying the publishable key and the\n * widget `<script>` tag. Idempotent via a marker attribute.\n * Browser-side half of the pair — receives the bootstrap your\n * server half fetched with `getConfig()`.\n *\n * Returns what happened, mirroring `analytics.install()`:\n * - `{ installed: true }` — widget just injected.\n * - `{ installed: false, reason: 'disabled' }` — chat is off.\n * - `{ installed: false, reason: 'ssr' }` — no `document`.\n * - `{ installed: false, reason: 'already-installed' }`.\n */\n install: (opts: LiveChatInstallOptions) => Promise<LiveChatInstallResult>\n\n /**\n * Computes the visitor identity token:\n * hex(HMAC_SHA256(signingSecret, externalId)). SERVER-ONLY — it\n * throws in a browser context rather than ever computing next to\n * the DOM.\n *\n * Signing secret, in order: `{ secret }` option →\n * `BRANDFINE_LIVE_CHAT_IDENTITY_SECRET` env → **derived from the\n * client's API key** (HMAC with a fixed domain-separation\n * constant; the Brandfine API derives the same value from its\n * stored copy). The derived path needs ZERO extra configuration —\n * an explicit secret is only for signers that shouldn't hold the\n * broad key, or legacy workspaces without a revealable key\n * (generate one in the CMS: Plugins → Live Chat → Integrate).\n */\n identityToken: (\n externalId: string,\n opts?: { secret?: string },\n ) => Promise<string>\n\n /**\n * Headless session — the conversation without the widget, for\n * building your own inline chat UI. Browser-only. Takes the same\n * `config` and signed `visitor` as `install()` (identical identity\n * rules and thread continuity — it IS the same conversation the\n * widget would join, sharing its storage keys). Transport today is\n * polling (`pollIntervalMs`, default 5000); a realtime upgrade\n * will keep this API and the poll as fallback.\n */\n createSession: (\n opts: LiveChatCreateSessionOptions,\n ) => Promise<LiveChatSession>\n\n /**\n * Raw, stateless wire methods for full control. NOTE on auth: the\n * config + start endpoints authenticate with this client's\n * `apiKey` header — in a browser, construct the client with the\n * PUBLISHABLE key (`createBrandfineClient({ apiKey: config.publishableKey })`),\n * never the broad key. Message endpoints authenticate with the\n * conversation token alone — treat it as a bearer credential:\n * don't log it, don't put it in URLs you share.\n */\n runtimeConfig: (locale?: string) => Promise<LiveChatRuntimeConfig>\n startConversation: (input: {\n visitorSessionId: string\n conversationToken?: string\n pageUrl?: string\n referrer?: string\n locale?: string\n visitor?: Record<string, unknown>\n }) => Promise<StartConversationResult>\n sendMessage: (\n conversationToken: string,\n input: { body: string; clientId?: string },\n ) => Promise<ChatMessage>\n history: (\n conversationToken: string,\n opts?: { after?: string; afterEvent?: number },\n ) => Promise<{ messages: ChatMessage[]; status: ConversationStatus }>\n}\n\n/** Attribute we stamp on the injected <script> so `install()` is\n * idempotent across re-renders and SPA route changes. */\nconst INSTALLED_MARKER = 'data-brandfine-analytics'\n\n/** Same idempotency contract for the live-chat widget injection. */\nconst LIVE_CHAT_MARKER = 'data-brandfine-live-chat'\n\n/**\n * Domain-separation constant for deriving the live-chat identity\n * signing secret from the workspace API key. MUST match the API's\n * constant (`live-chat-external.service.ts`) byte for byte — bump\n * the version suffix only in lockstep with a server release.\n */\nconst LIVE_CHAT_IDENTITY_CONTEXT = 'brandfine:live-chat:identity:v1'\n\n/** hex(HMAC_SHA256(key, message)) via WebCrypto — Node ≥20 global\n * AND browser-safe, so bundlers never see a node:crypto import. */\nasync function hmacHex(key: string, message: string): Promise<string> {\n const enc = new TextEncoder()\n const cryptoKey = await globalThis.crypto.subtle.importKey(\n 'raw',\n enc.encode(key),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n )\n const sig = await globalThis.crypto.subtle.sign(\n 'HMAC',\n cryptoKey,\n enc.encode(message),\n )\n return Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/** Marker for the injected Google tag — same idempotency contract. */\nconst GTAG_MARKER = 'data-brandfine-gtag'\n\n/**\n * Inject the Google tag (gtag.js) for an auto-provisioned GA4\n * property. No-ops when ANY gtag script is already on the page —\n * a site that hand-installed Google Analytics must not get a\n * second config (double-counted sessions are worse than a missing\n * tag). Safe to call repeatedly; the marker makes it idempotent.\n */\nfunction injectGoogleTag(measurementId: string): void {\n if (typeof document === 'undefined') return\n const existing = document.querySelector(\n `script[src*=\"googletagmanager.com/gtag/js\"], script[${GTAG_MARKER}]`,\n )\n if (existing) return\n\n const loader = document.createElement('script')\n loader.async = true\n loader.src = `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(measurementId)}`\n loader.setAttribute(GTAG_MARKER, measurementId)\n document.head.appendChild(loader)\n\n const w = window as unknown as { dataLayer?: unknown[] }\n w.dataLayer = w.dataLayer ?? []\n // gtag() must push `arguments` (an Arguments object), not a\n // plain array — GA's snippet relies on it.\n function gtag(..._args: unknown[]) {\n // eslint-disable-next-line prefer-rest-params\n w.dataLayer!.push(arguments)\n }\n gtag('js', new Date())\n gtag('config', measurementId)\n}\n\nconst DEFAULT_USER_AGENT = '@brandfine/client'\n\n/** Production API origin — the default when no `baseUrl` is given.\n * Consumers only configure a URL for local dev or staging. */\nconst DEFAULT_BASE_URL = 'https://api.brandfine.co'\n\n/**\n * Resolve the API origin: explicit option → `BRANDFINE_API_URL`\n * env (server-side only — browsers have no `process`, and an env\n * indirection in a browser bundle would be inlined at build time\n * anyway) → production default.\n */\nfunction resolveBaseUrl(explicit: string | undefined): string {\n if (explicit) return explicit\n if (typeof process !== 'undefined' && process.env?.BRANDFINE_API_URL) {\n return process.env.BRANDFINE_API_URL\n }\n return DEFAULT_BASE_URL\n}\n\nexport function createBrandfineClient(\n config: BrandfineClientConfig,\n): BrandfineClient {\n if (!config.apiKey)\n throw new Error('createBrandfineClient: `apiKey` is required')\n\n const baseUrl = resolveBaseUrl(config.baseUrl).replace(/\\/$/, '')\n const apiKey = config.apiKey\n // Resolve fetch lazily so consumers in environments without a\n // global fetch can polyfill before constructing the client.\n const fetchImpl: typeof fetch = config.fetch ?? globalThis.fetch\n const userAgent = config.userAgent ?? DEFAULT_USER_AGENT\n\n async function get<T>(path: string, opts: RequestOptions = {}): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method: 'GET',\n headers: {\n 'X-Api-Key': apiKey,\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n signal: opts.signal,\n })\n if (res.status === 404 && opts.nullable404) {\n // Drain the body so the underlying socket can be reused —\n // fetch implementations that don't auto-drain (older Node)\n // can leak otherwise.\n await res.text().catch(() => '')\n return null as T\n }\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body,\n url,\n })\n }\n return (await res.json()) as T\n }\n\n const posts: PostsApi = {\n async list<TConfig = unknown>(opts: ListPostsOptions = {}) {\n const out: BrandfinePost<TConfig>[] = []\n let page = 1\n const typeQuery = opts.type ? `&type=${encodeURIComponent(opts.type)}` : ''\n const localeQuery = opts.locale\n ? `&locale=${encodeURIComponent(opts.locale)}`\n : ''\n // Default pagination at the cms's 50-per-page cap. `forceLimit`\n // opts past it for content types that would otherwise need\n // many round-trips.\n const sizeQuery = opts.forceLimit\n ? `&force_limit=${opts.forceLimit}`\n : '&limit=50'\n // Pathological safety brake — 200 pages × 50 = 10k posts. If\n // a workspace ever needs more, callers should hit the API\n // directly with their own pagination logic.\n const MAX_PAGES = 200\n while (page <= MAX_PAGES) {\n const data = await get<BrandfinePostListResponse<TConfig>>(\n `/external/posts?include=content${sizeQuery}&page=${page}${typeQuery}${localeQuery}`,\n )\n out.push(...data.items)\n if (!data.pageInfo.hasNext) break\n page += 1\n }\n return out\n },\n async getBySlug<TConfig = unknown>(slug: string) {\n return get<BrandfinePost<TConfig> | null>(\n `/external/posts/${encodeURIComponent(slug)}`,\n { nullable404: true },\n )\n },\n }\n\n const categories: CategoriesApi = {\n async list(opts: ListCategoriesOptions = {}) {\n const qs = opts.locale ? `?locale=${encodeURIComponent(opts.locale)}` : ''\n const data = await get<{ items: BrandfineCategory[] }>(\n `/external/categories${qs}`,\n )\n return data.items\n },\n }\n\n const workspace: WorkspaceApi = {\n get<\n TCustomConfig = Record<string, unknown>,\n TSchemaOrg = Record<string, unknown>,\n >() {\n return get<BrandfineWorkspace<TCustomConfig, TSchemaOrg>>(\n '/external/workspace',\n )\n },\n }\n\n const navigations: NavigationsApi = {\n get<TConfig = unknown>(key: string) {\n return get<BrandfineNavigation<TConfig> | null>(\n `/external/navigations/${encodeURIComponent(key)}`,\n { nullable404: true },\n )\n },\n }\n\n const analytics: AnalyticsApi = {\n getConfig() {\n return get<AnalyticsConfig>('/external/analytics-config')\n },\n overview(opts = {}) {\n const range = opts.range ?? '7d'\n return get<AnalyticsOverview>(\n `/external/analytics/overview?range=${encodeURIComponent(range)}`,\n )\n },\n async install(opts: InstallOptions = {}) {\n // SSR safety: nothing to inject without a DOM. Consumers\n // call this from useEffect / onMount, but defensive anyway\n // (some frameworks still execute the file body on the server).\n if (typeof document === 'undefined') {\n return { installed: false, reason: 'ssr' as const }\n }\n\n // Use caller-provided config if present (build-time path),\n // otherwise fetch (runtime path).\n const cfg = opts.config ?? (await analytics.getConfig())\n\n // Google tag rides alongside the built-in tracker — injected\n // even when Brandfine analytics itself is off, because the\n // opt-in lives on the GA integration, not on the tracker.\n if (cfg.gaMeasurementId) {\n injectGoogleTag(cfg.gaMeasurementId)\n }\n\n if (!cfg.enabled) {\n return { installed: false, reason: 'disabled' as const }\n }\n\n // Idempotency: a prior call (StrictMode double-invoke, SPA\n // re-mount, second instance with the same workspace) may\n // have already injected. The marker attribute is the source\n // of truth — checking by script src would also miss the case\n // where two workspaces share the same scriptUrl.\n const existing = document.querySelector<HTMLScriptElement>(\n `script[${INSTALLED_MARKER}=\"${cfg.websiteId}\"]`,\n )\n if (existing) {\n return { installed: false, reason: 'already-installed' as const }\n }\n\n const script = document.createElement('script')\n script.defer = true\n script.src = cfg.scriptUrl\n script.setAttribute('data-website-id', cfg.websiteId)\n // The marker doubles as a sentinel + a debug aid (you can\n // grep the DOM for `data-brandfine-analytics` to confirm\n // an install).\n script.setAttribute(INSTALLED_MARKER, cfg.websiteId)\n document.head.appendChild(script)\n return { installed: true, websiteId: cfg.websiteId }\n },\n }\n\n /** Shared fetch for the live-chat public wire: optional key header\n * (publishable for config/start, none for token-authed message\n * endpoints), JSON in/out, BrandfineApiError on non-2xx. */\n async function liveChatWireRequest<T>(\n key: string | undefined,\n method: 'GET' | 'POST',\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method,\n headers: {\n Accept: 'application/json',\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...(key ? { 'X-Api-Key': key } : {}),\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body: text,\n url,\n })\n }\n return (await res.json()) as T\n }\n\n const liveChat: LiveChatApi = {\n getConfig() {\n return get<LiveChatBootstrap>('/external/live-chat/bootstrap')\n },\n async install(opts: LiveChatInstallOptions) {\n if (typeof document === 'undefined') {\n return { installed: false, reason: 'ssr' as const }\n }\n\n const cfg = opts.config\n if (!cfg.enabled) {\n return { installed: false, reason: 'disabled' as const }\n }\n\n const existing = document.querySelector(`[${LIVE_CHAT_MARKER}]`)\n if (existing) {\n return { installed: false, reason: 'already-installed' as const }\n }\n\n // Host div the widget script mounts into. Theme vars ride as\n // inline custom properties — they inherit through the widget's\n // shadow boundary.\n const host = document.createElement('div')\n host.setAttribute('data-bf-live-chat', '')\n host.setAttribute('data-publishable-key', cfg.publishableKey)\n host.setAttribute('data-base-url', baseUrl)\n host.setAttribute(LIVE_CHAT_MARKER, '')\n // Signed visitor identity → data-visitor. Serialization only;\n // the signature was computed server-side (identityToken()).\n if (opts.visitor?.externalId && opts.visitor.identityToken) {\n host.setAttribute('data-visitor', JSON.stringify(opts.visitor))\n }\n if (opts.locale) {\n host.setAttribute('data-locale', opts.locale)\n }\n if (cfg.theme) {\n for (const [key, value] of Object.entries(cfg.theme)) {\n if (key.startsWith('--bf-chat-')) {\n host.style.setProperty(key, value)\n }\n }\n }\n document.body.appendChild(host)\n\n const script = document.createElement('script')\n script.defer = true\n script.src = `${baseUrl}${cfg.scriptPath}`\n script.setAttribute(LIVE_CHAT_MARKER, 'script')\n document.head.appendChild(script)\n\n return { installed: true }\n },\n\n createSession(opts: LiveChatCreateSessionOptions) {\n // The session's key-authed calls use the PUBLISHABLE key from\n // the passed config (the browser client is often constructed\n // with a placeholder apiKey for install()-style usage).\n const pk = opts.config.enabled ? opts.config.publishableKey : undefined\n const wire: LiveChatWire = {\n runtimeConfig: (locale) => liveChatWireRequest(pk, 'GET',\n `/external/live-chat/config${locale ? `?locale=${encodeURIComponent(locale)}` : ''}`),\n startConversation: (input) =>\n liveChatWireRequest(pk, 'POST', '/external/live-chat/conversations', input),\n sendMessage: (conversationToken, input) =>\n liveChatWireRequest(undefined, 'POST',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages`, input),\n history: (conversationToken, o = {}) =>\n liveChatWireRequest(undefined, 'GET',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages${\n o.afterEvent !== undefined\n ? `?afterEvent=${encodeURIComponent(String(o.afterEvent))}`\n : o.after\n ? `?after=${encodeURIComponent(o.after)}`\n : ''\n }`),\n }\n return createLiveChatSession(wire, opts)\n },\n\n runtimeConfig(locale) {\n return liveChatWireRequest(apiKey, 'GET',\n `/external/live-chat/config${locale ? `?locale=${encodeURIComponent(locale)}` : ''}`)\n },\n startConversation(input) {\n return liveChatWireRequest(apiKey, 'POST', '/external/live-chat/conversations', input)\n },\n sendMessage(conversationToken, input) {\n return liveChatWireRequest(undefined, 'POST',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages`, input)\n },\n history(conversationToken, o = {}) {\n // afterEvent (eventId cursor, exact) wins over the legacy\n // timestamp cursor when the session knows one.\n const qs =\n o.afterEvent !== undefined\n ? `?afterEvent=${encodeURIComponent(String(o.afterEvent))}`\n : o.after\n ? `?after=${encodeURIComponent(o.after)}`\n : ''\n return liveChatWireRequest(undefined, 'GET',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages${qs}`)\n },\n\n async identityToken(externalId, opts = {}) {\n // Refuse to run where the secret could be exfiltrated. A page\n // that needs a token must get it from ITS server, not compute\n // one next to the DOM.\n if (typeof document !== 'undefined' || typeof window !== 'undefined') {\n throw new Error(\n 'liveChat.identityToken() is server-only — never compute identity ' +\n 'tokens in a browser. Sign the visitor on your server and pass ' +\n 'the result to install({ visitor }).',\n )\n }\n if (!externalId) {\n throw new Error('liveChat.identityToken(): externalId is required.')\n }\n // Signing-secret resolution:\n // 1. Explicit { secret } / BRANDFINE_LIVE_CHAT_IDENTITY_SECRET —\n // the decoupled option (a signer that shouldn't hold the\n // broad key, or a legacy workspace).\n // 2. DERIVED from the API key this client already holds:\n // HMAC(apiKey, domain-separation constant). Zero extra\n // config — the server derives the same value from its\n // stored copy of the key. Rotating the API key rotates\n // signing with it, which is the lifecycle you want anyway.\n const explicit =\n opts.secret ??\n (typeof process !== 'undefined'\n ? process.env.BRANDFINE_LIVE_CHAT_IDENTITY_SECRET\n : undefined)\n const secret =\n explicit ?? (await hmacHex(apiKey, LIVE_CHAT_IDENTITY_CONTEXT))\n return hmacHex(secret, externalId)\n },\n }\n\n const submissions: SubmissionsApi = {\n async create(input: CreateSubmissionInput) {\n const url = `${baseUrl}/external/submissions`\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n 'X-Api-Key': apiKey,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n body: JSON.stringify(input),\n })\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body,\n url,\n })\n }\n return (await res.json()) as Submission\n },\n }\n\n /**\n * Shared POST helper for the appointments namespace. The main\n * `get()` helper handles GETs; submissions has its own inline\n * POST because it predates this refactor. New plugin namespaces\n * (appointments first, others to follow) share this one so the\n * error-handling shape stays consistent.\n */\n async function post<T>(\n path: string,\n body: unknown,\n opts: RequestOptions = {},\n ): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n 'X-Api-Key': apiKey,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n body: JSON.stringify(body),\n signal: opts.signal,\n })\n if (!res.ok) {\n const errBody = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body: errBody,\n url,\n })\n }\n if (res.status === 204) return undefined as T\n return (await res.json()) as T\n }\n\n const appointments: AppointmentsApi = {\n getAvailability(opts = {}) {\n const qs: string[] = []\n if (opts.from) qs.push(`from=${encodeURIComponent(toIso(opts.from))}`)\n if (opts.to) qs.push(`to=${encodeURIComponent(toIso(opts.to))}`)\n const suffix = qs.length ? `?${qs.join('&')}` : ''\n return get<AppointmentAvailability>(\n `/external/appointments/availability${suffix}`,\n )\n },\n createRequest(input) {\n return post<CreatedAppointmentRequest>(\n '/external/appointments/requests',\n input,\n )\n },\n async list(identity) {\n // POST (not GET) so the identityToken never lands in URLs,\n // proxy logs, or browser history.\n const res = await post<{ appointments: Appointment[] }>(\n '/external/appointments/requests/lookup',\n identity,\n )\n return res.appointments\n },\n async get(id, identity) {\n // Same lookup endpoint narrowed by id — the server 404s when\n // the id is missing or belongs to another person, so a\n // successful response always carries exactly one row. The\n // guard below is defensive symmetry, not a real path.\n const res = await post<{ appointments: Appointment[] }>(\n '/external/appointments/requests/lookup',\n { ...identity, id },\n )\n const row = res.appointments[0]\n if (!row) {\n throw new BrandfineApiError({\n status: 404,\n statusText: 'Not Found',\n body: 'Appointment not found.',\n url: '/external/appointments/requests/lookup',\n })\n }\n return row\n },\n cancel(cancellationToken) {\n return post<{ id: string; status: 'CANCELLED' }>(\n `/external/appointments/requests/${encodeURIComponent(cancellationToken)}/cancel`,\n {},\n )\n },\n }\n\n return {\n get,\n posts,\n categories,\n workspace,\n navigations,\n analytics,\n submissions,\n appointments,\n liveChat,\n }\n}\n\n/** Accepts a Date or an already-ISO string and returns ISO. Saves\n * every caller from `.toISOString()`-ing manually. */\nfunction toIso(d: Date | string): string {\n return typeof d === 'string' ? d : d.toISOString()\n}\n","/**\n * @brandfine/client — root entry.\n *\n * The full SDK surface is exposed here for \"import everything from\n * one place\" usage. Tree-shaking + `sideEffects: false` mean\n * consumers don't pay a bundle cost for what they don't import.\n *\n * Heavier or framework-coupled pieces still live under subpath\n * exports (`@brandfine/client/cache`, `/resolvers`, `/webhook`) so\n * consumers with poor tree-shaking — or who only need one slice —\n * can scope their imports.\n */\n\nexport const SDK_VERSION = '0.0.0' as const\n\nexport {\n BrandfineApiError,\n createBrandfineClient,\n type AnalyticsConfig,\n type AnalyticsInstallResult,\n type AnalyticsOverview,\n type AnalyticsOverviewRange,\n type Appointment,\n type AppointmentAvailability,\n type AppointmentIdentity,\n type AppointmentSlot,\n type AppointmentStatus,\n type BrandfineClient,\n type BrandfineClientConfig,\n type ChatMessage,\n type CreateAppointmentRequestInput,\n type CreatedAppointmentRequest,\n type ChatMessageSender,\n type ConversationStatus,\n type CreateSubmissionInput,\n type InstallOptions,\n type LiveChatBootstrap,\n type LiveChatCreateSessionOptions,\n type LiveChatInstallOptions,\n type LiveChatInstallResult,\n type LiveChatRuntimeConfig,\n type LiveChatSession,\n type LiveChatSessionSnapshot,\n type LiveChatSessionState,\n type LiveChatVisitor,\n type StartConversationResult,\n type Submission,\n type VerifiedVisitor,\n} from './client'\n\nexport {\n createCache,\n createKeyedCache,\n type Cache,\n type CacheOptions,\n type KeyedCache,\n type KeyedCacheOptions,\n} from './cache/index'\n\nexport {\n isLocale,\n localizePath,\n pickLocale,\n resolveNavigation,\n stripLocalePrefix,\n type HydratedNav,\n type HydratedNavItem,\n type LocaleOptions,\n type ResolveNavigationOptions,\n} from './resolvers/index'\n\nexport {\n createBrandfineWebhookHandler,\n parseWebhookPayload,\n verifyWebhookSecret,\n type BrandfineWebhookEvent,\n type BrandfineWebhookHandlerOptions,\n type BrandfineWebhookPayload,\n} from './webhook/index'\n\nexport type {\n BrandfineCategory,\n BrandfineNavItem,\n BrandfineNavItemType,\n BrandfineNavPost,\n BrandfineNavigation,\n BrandfinePost,\n BrandfinePostListResponse,\n BrandfinePostTranslation,\n BrandfineWorkspace,\n ListCategoriesOptions,\n ListPostsOptions,\n} from './types'\n"]}
1
+ {"version":3,"sources":["../src/live-chat-session.ts","../src/client.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Headless Live Chat session — the conversation WITHOUT the widget.\n *\n * `bf.liveChat.createSession()` gives a consumer everything needed to\n * render chat inline in their own design system: transcript state, a\n * send method, and a subscribe API shaped for\n * `useSyncExternalStore` / Svelte stores / Vue refs. Brandfine keeps\n * owning transport, identity, threading and storage; the consumer\n * owns pixels.\n *\n * Storage keys are IDENTICAL to the floating widget's, so a visitor\n * who talks through the widget on one page and an inline panel on\n * another continues the same thread.\n */\n\n/**\n * `BOT` = AI-assistant replies (arriving once a workspace enables the\n * live-chat AI agent). Render `BOT` on the business/agent side, ideally\n * with an \"AI\" label — and treat any FUTURE unknown sender value the\n * same way (agent-side, generic label) rather than switching\n * exhaustively: the server may add sender kinds before your copy of\n * this SDK learns their names.\n */\nexport type ChatMessageSender = 'VISITOR' | 'AGENT' | 'SYSTEM' | 'BOT'\n\nexport type ChatMessage = {\n id: string\n body: string\n sender: ChatMessageSender\n createdAt: string\n /** Monotonic per-conversation sequence — the realtime resume\n * cursor. Optional: older APIs don't send it. */\n eventId?: number\n}\n\nexport type ConversationStatus = 'OPEN' | 'CLOSED'\n\nexport type LiveChatSessionState = 'CONNECTING' | 'OPEN' | 'CLOSED' | 'ERROR'\n\n/** The `/external/live-chat/config` shape — the RUNTIME config the\n * widget fetches per page load. Distinct from the server-side\n * `LiveChatBootstrap` (which additionally carries the publishable\n * key + script path). */\nexport type LiveChatRuntimeConfig =\n | { enabled: false }\n | {\n enabled: true\n greeting: string | null\n offlineMessage: string | null\n theme: Record<string, string> | null\n online?: boolean\n /** Realtime endpoint (wss://…). Absent = poll (older API, or\n * the transport is off). The session upgrades automatically\n * when present; consumers never touch it. */\n realtimeUrl?: string\n }\n\nexport type LiveChatSessionSnapshot = {\n messages: ChatMessage[]\n status: LiveChatSessionState\n /** Agents available right now (business-hours based today; agent\n * presence once the realtime transport lands). */\n online: boolean\n /** Localized via the session's `locale`. */\n greeting: string | null\n offlineMessage: string | null\n /** An outbound `send()` is in flight. */\n sending: boolean\n error: Error | null\n}\n\nexport type StartConversationResult =\n | { enabled: false }\n | {\n enabled: true\n conversationId: string\n conversationToken: string\n greeting: string | null\n resumed: boolean\n online?: boolean\n }\n\n/** Wire access the session needs — provided by the SDK client so the\n * session never hardcodes endpoints or headers. */\nexport type LiveChatWire = {\n runtimeConfig(locale?: string): Promise<LiveChatRuntimeConfig>\n startConversation(input: {\n visitorSessionId: string\n conversationToken?: string\n pageUrl?: string\n referrer?: string\n locale?: string\n visitor?: Record<string, unknown>\n }): Promise<StartConversationResult>\n sendMessage(\n conversationToken: string,\n input: { body: string; clientId?: string },\n ): Promise<ChatMessage>\n history(\n conversationToken: string,\n opts?: { after?: string; afterEvent?: number },\n ): Promise<{ messages: ChatMessage[]; status: ConversationStatus }>\n}\n\nexport type LiveChatCreateSessionOptions = {\n /** The server-half bootstrap (same object `install()` takes) —\n * supplies the publishable key (and, when the API advertises it,\n * the realtime endpoint — passed through wholesale). */\n config: { enabled: boolean; publishableKey?: string; realtimeUrl?: string }\n /** Signed identity — SAME shape and rules as `install()`. A bad\n * signature downgrades to anonymous server-side; it is never a\n * second identity path. */\n visitor?: Record<string, unknown>\n /** BCP-47 tag for greeting/away localization. Defaults to\n * `<html lang>` then browser language, like the widget. */\n locale?: string\n /** Defaults to `location.href`. */\n pageUrl?: string\n referrer?: string\n /** Transcript refresh cadence. Polling is the transport today; a\n * realtime upgrade will keep this as the fallback. */\n pollIntervalMs?: number\n}\n\nexport type LiveChatSession = {\n /** Current transcript (same array identity as the latest snapshot). */\n readonly messages: readonly ChatMessage[]\n readonly state: LiveChatSessionState\n send(body: string): Promise<ChatMessage>\n /** Listener fires on every snapshot change. Returns unsubscribe. */\n subscribe(listener: (s: LiveChatSessionSnapshot) => void): () => void\n getSnapshot(): LiveChatSessionSnapshot\n /** Stop polling, abort in-flight work, drop listeners. Idempotent. */\n close(): void\n /** Drop the stored conversation token and start a fresh thread —\n * identity switch on a shared browser. */\n reset(): Promise<void>\n}\n\n// ── Storage (MUST stay in lockstep with packages/widget-live-chat) ──\n\nconst SESSION_STORAGE_KEY = 'bf-live-chat-session'\n\nfunction tokenStorageKey(publishableKey: string) {\n return `bf-live-chat-token:${publishableKey.slice(-12)}`\n}\n\nfunction getVisitorSessionId(): string {\n try {\n const existing = localStorage.getItem(SESSION_STORAGE_KEY)\n if (existing) return existing\n const generated =\n typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID().replace(/-/g, '')\n : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n localStorage.setItem(SESSION_STORAGE_KEY, generated)\n return generated\n } catch {\n return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n }\n}\n\nfunction readStoredToken(publishableKey: string): string | undefined {\n try {\n return localStorage.getItem(tokenStorageKey(publishableKey)) ?? undefined\n } catch {\n return undefined\n }\n}\n\nfunction writeStoredToken(publishableKey: string, token: string | null) {\n try {\n if (token === null) localStorage.removeItem(tokenStorageKey(publishableKey))\n else localStorage.setItem(tokenStorageKey(publishableKey), token)\n } catch {\n /* storage blocked — resume just won't survive reloads */\n }\n}\n\nfunction resolvePageLocale(explicit?: string): string | undefined {\n if (explicit) return explicit\n if (typeof document === 'undefined') return undefined\n const htmlLang = document.documentElement.lang?.trim()\n if (htmlLang) return htmlLang\n return typeof navigator !== 'undefined'\n ? navigator.language || undefined\n : undefined\n}\n\n// ── The session ─────────────────────────────────────────────────────\n\nexport async function createLiveChatSession(\n wire: LiveChatWire,\n opts: LiveChatCreateSessionOptions,\n): Promise<LiveChatSession> {\n if (typeof window === 'undefined') {\n throw new Error(\n 'liveChat.createSession() is browser-only — build the transcript UI ' +\n 'client-side; fetch the bootstrap with getConfig() on the server.',\n )\n }\n const publishableKey = opts.config.enabled\n ? opts.config.publishableKey\n : undefined\n\n const locale = resolvePageLocale(opts.locale)\n const pollMs = Math.max(1000, opts.pollIntervalMs ?? 5000)\n\n let snapshot: LiveChatSessionSnapshot = {\n messages: [],\n status: 'CONNECTING',\n online: false,\n greeting: null,\n offlineMessage: null,\n sending: false,\n error: null,\n }\n const listeners = new Set<(s: LiveChatSessionSnapshot) => void>()\n const seen = new Set<string>()\n let cursor: string | undefined\n let lastEventId = 0\n let token: string | undefined\n let timer: ReturnType<typeof setInterval> | undefined\n let closed = false\n let ticking = false\n const aborter = new AbortController()\n\n // ── Realtime transport state (see startRealtime below) ──\n let realtimeUrl: string | undefined = opts.config.realtimeUrl\n let ws: WebSocket | undefined\n let wsLive = false\n let wsFailures = 0\n let wsGivenUp = false\n let reconnectTimer: ReturnType<typeof setTimeout> | undefined\n let pingTimer: ReturnType<typeof setInterval> | undefined\n\n function emit(patch: Partial<LiveChatSessionSnapshot>) {\n snapshot = { ...snapshot, ...patch }\n for (const listener of listeners) listener(snapshot)\n }\n\n function appendMessages(incoming: ChatMessage[]) {\n const fresh = incoming.filter((m) => !seen.has(m.id))\n if (fresh.length === 0) {\n // Even a full dupe batch may advance the event cursor (e.g. the\n // socket redelivers what a poll already fetched).\n for (const m of incoming) {\n if (typeof m.eventId === 'number' && m.eventId > lastEventId) {\n lastEventId = m.eventId\n }\n }\n return\n }\n for (const m of fresh) {\n seen.add(m.id)\n if (typeof m.eventId === 'number' && m.eventId > lastEventId) {\n lastEventId = m.eventId\n }\n }\n const messages = [...snapshot.messages, ...fresh]\n cursor = messages[messages.length - 1]!.createdAt\n emit({ messages })\n }\n\n function markClosed() {\n if (snapshot.status !== 'CLOSED') emit({ status: 'CLOSED' })\n stopPolling()\n teardownRealtime()\n wsGivenUp = true // a closed thread never reconnects; reset() re-arms\n }\n\n async function tick() {\n // Re-entrancy guard: a slow response must not overlap the next\n // interval firing (double-fetch → possible reorder).\n if (ticking || closed || !token) return\n ticking = true\n try {\n const result = await wire.history(\n token,\n // The eventId cursor is exact (no equal-millisecond drop\n // window); the timestamp cursor remains for old APIs that\n // never sent eventIds.\n lastEventId > 0 ? { afterEvent: lastEventId } : { after: cursor },\n )\n if (closed) return\n appendMessages(result.messages)\n if (result.status === 'CLOSED') markClosed()\n } catch {\n /* transient poll failure — next tick retries */\n } finally {\n ticking = false\n }\n }\n\n function startPolling() {\n stopPolling()\n timer = setInterval(() => void tick(), pollMs)\n }\n function stopPolling() {\n if (timer !== undefined) clearInterval(timer)\n timer = undefined\n }\n\n // ── Realtime transport (delivery-only WebSocket) ────────────────\n //\n // Invisible above the session API by contract: same subscribe,\n // same snapshots. While the socket is live, polling stops; the\n // moment it isn't (drop, backoff wait, permanent failure, no\n // WebSocket in the runtime, no realtimeUrl advertised), polling\n // covers the gap exactly as before. Sends stay on REST.\n\n function teardownRealtime() {\n if (reconnectTimer !== undefined) clearTimeout(reconnectTimer)\n reconnectTimer = undefined\n if (pingTimer !== undefined) clearInterval(pingTimer)\n pingTimer = undefined\n wsLive = false\n if (ws) {\n const socket = ws\n ws = undefined\n try {\n socket.onopen = socket.onmessage = socket.onclose = socket.onerror =\n null\n socket.close()\n } catch {\n /* already closed */\n }\n }\n }\n\n function scheduleReconnect() {\n if (closed || wsGivenUp || reconnectTimer !== undefined) return\n // 1s → 30s exponential with jitter; after 5 consecutive\n // failures, stay on polling for the session's lifetime — a\n // socket a corporate proxy kills will never open, and the user\n // still gets their messages.\n if (wsFailures >= 5) {\n wsGivenUp = true\n return\n }\n const base = Math.min(30_000, 1_000 * 2 ** wsFailures)\n const delay = base / 2 + Math.random() * (base / 2)\n reconnectTimer = setTimeout(() => {\n reconnectTimer = undefined\n startRealtime()\n }, delay)\n }\n\n function handleSocketDown() {\n teardownRealtime()\n if (closed || wsGivenUp || snapshot.status === 'CLOSED') return\n // Polling covers the gap from the FIRST millisecond of the\n // outage; a later successful socket auth stops it again.\n startPolling()\n wsFailures += 1\n scheduleReconnect()\n }\n\n function startRealtime() {\n if (\n closed ||\n wsGivenUp ||\n wsLive ||\n ws !== undefined ||\n !realtimeUrl ||\n !token ||\n !publishableKey ||\n typeof WebSocket === 'undefined' ||\n snapshot.status === 'CLOSED'\n ) {\n return\n }\n let socket: WebSocket\n try {\n socket = new WebSocket(realtimeUrl)\n } catch {\n wsFailures += 1\n scheduleReconnect()\n return\n }\n ws = socket\n socket.onopen = () => {\n // Auth in the FIRST FRAME, never the URL — tokens must not\n // land in proxy logs or browser history.\n try {\n socket.send(\n JSON.stringify({\n type: 'auth',\n publishableKey,\n conversationToken: token,\n lastEventId,\n }),\n )\n } catch {\n handleSocketDown()\n }\n }\n socket.onmessage = (event) => {\n let frame: { type?: string } & Record<string, unknown>\n try {\n frame = JSON.parse(String(event.data)) as typeof frame\n } catch {\n return // unknown/garbage frames are ignored by contract\n }\n switch (frame.type) {\n case 'ready': {\n wsLive = true\n wsFailures = 0\n stopPolling() // the socket is now the transport\n if (pingTimer === undefined) {\n pingTimer = setInterval(() => {\n try {\n socket.send(JSON.stringify({ type: 'ping' }))\n } catch {\n /* close race — the onclose handler recovers */\n }\n }, 30_000)\n }\n break\n }\n case 'message': {\n const m = frame.message as ChatMessage | undefined\n if (m && typeof m.id === 'string') {\n appendMessages([\n {\n ...m,\n eventId:\n typeof frame.eventId === 'number' ? frame.eventId : m.eventId,\n },\n ])\n }\n break\n }\n case 'status': {\n if (frame.status === 'CLOSED') markClosed()\n break\n }\n case 'error': {\n // The server refused us (bad auth, socket caps…). Fall\n // back to polling permanently — retrying a refusal is\n // just noise, and delivery still works.\n wsGivenUp = true\n teardownRealtime()\n if (!closed && snapshot.status !== 'CLOSED') startPolling()\n break\n }\n default:\n break // pong + future frame types\n }\n }\n socket.onclose = () => handleSocketDown()\n socket.onerror = () => {\n /* onclose always follows; avoid double-handling */\n }\n }\n\n async function connect(resumeToken: string | undefined) {\n if (!publishableKey) {\n emit({ status: 'CLOSED' })\n return\n }\n // Config first — greeting/away/online, localized.\n try {\n const cfg = await wire.runtimeConfig(locale)\n if (closed) return\n if (!cfg.enabled) {\n emit({ status: 'CLOSED' })\n return\n }\n emit({\n greeting: cfg.greeting,\n offlineMessage: cfg.offlineMessage,\n online: cfg.online !== false,\n })\n // Runtime config wins over the build-time bootstrap — it's\n // fetched live, so switching realtime off takes effect on the\n // next page load without a redeploy.\n if (cfg.realtimeUrl !== undefined) realtimeUrl = cfg.realtimeUrl\n } catch (error) {\n if (closed) return\n emit({ status: 'ERROR', error: error as Error })\n return\n }\n\n try {\n const started = await wire.startConversation({\n visitorSessionId: getVisitorSessionId(),\n conversationToken: resumeToken,\n pageUrl:\n opts.pageUrl ??\n (typeof location !== 'undefined'\n ? location.href.slice(0, 2048)\n : undefined),\n referrer: opts.referrer,\n locale,\n visitor: opts.visitor,\n })\n if (closed) return\n if (!started.enabled) {\n emit({ status: 'CLOSED' })\n return\n }\n token = started.conversationToken\n writeStoredToken(publishableKey, token)\n if (started.online !== undefined) emit({ online: started.online })\n emit({ status: 'OPEN' })\n await tick() // initial history before the first interval\n if (!closed) {\n startPolling()\n startRealtime() // upgrades in place; polling stops on `ready`\n }\n } catch (error) {\n if (closed) return\n emit({ status: 'ERROR', error: error as Error })\n }\n }\n\n const session: LiveChatSession = {\n get messages() {\n return snapshot.messages\n },\n get state() {\n return snapshot.status\n },\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n async send(body) {\n const trimmed = body.trim()\n if (!trimmed) throw new Error('send(): message body is empty')\n if (!token) throw new Error('send(): session is not connected')\n if (snapshot.status === 'CLOSED') {\n throw new Error('send(): conversation is closed — call reset()')\n }\n emit({ sending: true })\n try {\n // Idempotency key: if the response is lost mid-flight, a\n // consumer retry with the same body can't duplicate — the\n // server replays the original row for this clientId.\n const clientId =\n typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID()\n : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`\n const message = await wire.sendMessage(token, {\n body: trimmed,\n clientId,\n })\n appendMessages([message])\n return message\n } finally {\n emit({ sending: false })\n }\n },\n close() {\n if (closed) return\n closed = true\n stopPolling()\n teardownRealtime()\n aborter.abort()\n listeners.clear()\n },\n async reset() {\n // Socket FIRST — a socket left attached to the old conversation\n // would defeat the shared-browser guarantee reset() exists for.\n teardownRealtime()\n wsFailures = 0\n wsGivenUp = false\n if (publishableKey) writeStoredToken(publishableKey, null)\n token = undefined\n cursor = undefined\n lastEventId = 0\n seen.clear()\n stopPolling()\n emit({ messages: [], status: 'CONNECTING', error: null })\n await connect(undefined)\n },\n }\n\n await connect(publishableKey ? readStoredToken(publishableKey) : undefined)\n return session\n}\n","/**\n * `createBrandfineClient` — the SDK's entry point.\n *\n * Returns a stateless, multi-instance-safe handle scoped to a\n * single `(baseUrl, apiKey)` pair. Pattern follows the Stripe /\n * Algolia / OpenAI SDKs — explicit construction with config,\n * namespaced methods (`bf.posts.list(...)`, `bf.workspace.get()`),\n * no module-level singletons.\n *\n * Why factory not module-level state: multi-tenant consumers\n * sometimes need two clients in the same process (e.g. main site\n * + admin preview). Module-level env reading makes that impossible\n * without monkey-patching.\n */\n\nimport type {\n BrandfineCategory,\n BrandfineNavigation,\n BrandfinePost,\n BrandfinePostListResponse,\n BrandfineWorkspace,\n ListCategoriesOptions,\n ListPostsOptions,\n} from './types'\n\nimport {\n createLiveChatSession,\n type ChatMessage,\n type ConversationStatus,\n type LiveChatCreateSessionOptions,\n type LiveChatRuntimeConfig,\n type LiveChatSession,\n type LiveChatWire,\n type StartConversationResult,\n} from './live-chat-session'\n\nexport type {\n ChatMessage,\n ChatMessageSender,\n ConversationStatus,\n LiveChatCreateSessionOptions,\n LiveChatRuntimeConfig,\n LiveChatSession,\n LiveChatSessionSnapshot,\n LiveChatSessionState,\n StartConversationResult,\n} from './live-chat-session'\n\nexport type BrandfineClientConfig = {\n /** Base URL of the Brandfine API. OPTIONAL — defaults to the\n * production API (`https://api.brandfine.co`); on the server the\n * `BRANDFINE_API_URL` env var overrides the default (set it for\n * local dev / staging). Explicit option wins over both. No\n * trailing slash — the client trims one if you pass it anyway. */\n baseUrl?: string\n /** Workspace-scoped API key. Generated from the cms's Workspace\n * settings; identifies which workspace the client talks to. */\n apiKey: string\n /** Optional fetch override. Useful for tests (inject a stub),\n * for runtimes that need a custom implementation (edge workers\n * with non-standard fetch), or to add cross-cutting concerns\n * like tracing / retries. Defaults to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch\n /** Optional User-Agent header. Falls back to a generic SDK tag. */\n userAgent?: string\n}\n\n/**\n * Structured error thrown by every request helper on non-2xx\n * responses. Carries the raw body so consumers can log it for\n * debugging without re-fetching.\n */\nexport class BrandfineApiError extends Error {\n override readonly name = 'BrandfineApiError'\n readonly status: number\n readonly statusText: string\n readonly body: string\n readonly url: string\n\n constructor(args: {\n status: number\n statusText: string\n body: string\n url: string\n }) {\n super(\n `[brandfine] ${args.status} ${args.statusText} on ${args.url} — ${args.body.slice(0, 200)}`,\n )\n this.status = args.status\n this.statusText = args.statusText\n this.body = args.body\n this.url = args.url\n }\n}\n\ntype RequestOptions = {\n /** When true and the response is 404, return `null` instead of\n * throwing. Used by endpoints where 404 is a meaningful empty\n * state (navigation by key, single post by slug). */\n nullable404?: boolean\n signal?: AbortSignal\n}\n\nexport type BrandfineClient = {\n /** Low-level GET. Reserved for endpoints we don't have a typed\n * helper for yet. Adds the X-Api-Key header automatically. */\n get: <T>(path: string, opts?: RequestOptions) => Promise<T>\n posts: PostsApi\n categories: CategoriesApi\n workspace: WorkspaceApi\n navigations: NavigationsApi\n analytics: AnalyticsApi\n submissions: SubmissionsApi\n appointments: AppointmentsApi\n liveChat: LiveChatApi\n}\n\ntype PostsApi = {\n /** Paginated list of published posts. Handles the cms's\n * pagination transparently — caller gets a flat array. */\n list: <TConfig = unknown>(\n opts?: ListPostsOptions,\n ) => Promise<BrandfinePost<TConfig>[]>\n /** Single post by per-locale URL slug, scoped to the active\n * locale on the workspace's content. Returns `null` for 404 so\n * callers can render their own \"not found\" page without try/catch. */\n getBySlug: <TConfig = unknown>(\n slug: string,\n ) => Promise<BrandfinePost<TConfig> | null>\n}\n\ntype CategoriesApi = {\n list: (opts?: ListCategoriesOptions) => Promise<BrandfineCategory[]>\n}\n\ntype WorkspaceApi = {\n get: <\n TCustomConfig = Record<string, unknown>,\n TSchemaOrg = Record<string, unknown>,\n >() => Promise<BrandfineWorkspace<TCustomConfig, TSchemaOrg>>\n}\n\ntype NavigationsApi = {\n /** Navigation by its workspace-scoped `key` (e.g. `'header'`).\n * Returns `null` for 404 so consumers can fall back to a\n * hardcoded default without try/catch. `TConfig` narrows each\n * item's `customConfig` (default `unknown`). */\n get: <TConfig = unknown>(\n key: string,\n ) => Promise<BrandfineNavigation<TConfig> | null>\n}\n\nexport type CreateSubmissionInput = {\n /** Required. Display name of the submitter. */\n name: string\n /** Required. Validated server-side. */\n email: string\n /** Optional. Free-text up to 40 chars. */\n phone?: string\n /** Optional. Free-text up to 200 chars. */\n subject?: string\n /** Required. The message body — up to 10,000 chars. */\n message: string\n /** Optional. Where the submission came from — e.g. a route path\n * like `/contact`, or a marketing campaign label. Up to 500 chars. */\n source?: string\n /** Optional. Free-form JSON metadata the consumer attaches; the\n * cms surfaces it verbatim in the submissions admin view. */\n metadata?: Record<string, unknown>\n}\n\nexport type Submission = {\n id: string\n createdAt: string\n}\n\ntype SubmissionsApi = {\n /**\n * Posts a contact-form submission to `POST /external/submissions`\n * for this workspace. The cms surfaces the submission in the\n * Submissions inbox.\n *\n * Throws `BrandfineApiError` on validation failures (400) or\n * any other non-2xx — caller decides whether to surface that as\n * a user-visible error or a silent retry.\n */\n create: (input: CreateSubmissionInput) => Promise<Submission>\n}\n\n// ----------------------------------------------------------------\n// Appointments plugin SDK — pairs with the Appointments embed\n// widget. Consumers who want full control over the booking UI use\n// these methods directly; consumers who want the drop-in widget\n// use the `<script>` embed (which itself uses these methods under\n// the hood). The same `BrandfineClient` instance powers both.\n// ----------------------------------------------------------------\n\nexport type AppointmentSlot = {\n /** UTC ISO 8601 timestamp of the slot start. */\n start: string\n /** UTC ISO 8601 timestamp of the slot end. */\n end: string\n}\n\nexport type AppointmentAvailability = {\n /** False = plugin not activated, or activation row's `enabled`\n * flag is off. Widgets should render a \"not accepting bookings\"\n * state, not throw. */\n enabled: boolean\n /** Source-of-truth IANA timezone for the workspace's business\n * hours. Visitors see slots in their local TZ — use this for\n * the \"(workspace local: HH:MM)\" subtext. */\n timezone: string\n slotDurationMinutes: number\n leadTimeHours: number\n bookingWindowDays: number\n policyText: string | null\n slots: AppointmentSlot[]\n /** UTC ISO 8601. Useful for the widget's date range label. */\n windowStart: string\n windowEnd: string\n}\n\nexport type CreateAppointmentRequestInput = {\n visitorName: string\n visitorEmail: string\n visitorPhone?: string\n visitorMessage?: string\n /** UTC ISO 8601 of the requested slot start. Server re-validates\n * against business hours + busy ranges before accepting. */\n requestedAt: string\n /** Optional cookie-derived session id from the consumer site. */\n visitorSessionId?: string\n /** Signed host-app identity — the SAME payload shape and signature\n * Live Chat accepts, so one `liveChat.identityToken(externalId)`\n * call signs for both plugins. When present and valid, the booking\n * is attributed to the person (verified name/email take precedence\n * over the free-text fields above, and the booking links to their\n * chat threads via the shared externalId). Invalid or absent →\n * the booking proceeds anonymously — it is never rejected. */\n visitor?: VerifiedVisitor\n}\n\nexport type CreatedAppointmentRequest = {\n id: string\n createdAt: string\n requestedAt: string\n durationMinutes: number\n status: 'PENDING'\n /** True iff `visitor.identityToken` HMAC-verified — your signal\n * that the booking landed attributed rather than anonymous. */\n identityVerified: boolean\n /** Visitor's self-cancel token. Embed it in confirmation\n * emails / on-page UI so the visitor can cancel without an\n * account. One-time use; revoked once any party acts. */\n cancellationToken: string | null\n}\n\nexport type AppointmentStatus =\n | 'PENDING'\n | 'CONFIRMED'\n | 'REJECTED'\n | 'CANCELLED'\n\n/** One appointment as the identity-scoped read-back returns it —\n * the visitor-safe projection (\"where does my request stand?\"). */\nexport type Appointment = {\n id: string\n status: AppointmentStatus\n /** UTC ISO 8601 of the requested slot start (reflects the current\n * slot after a reschedule). */\n requestedAt: string\n /** The confirmed slot — non-null only once CONFIRMED. */\n scheduledAt: string | null\n durationMinutes: number\n /** Workspace's IANA timezone for local rendering. */\n timezone: string\n /** Customer's note — populated only on REJECTED. */\n declineReason: string | null\n rescheduleCount: number\n respondedAt: string | null\n createdAt: string\n}\n\n/** Proof of identity for read-back calls: the same externalId +\n * `liveChat.identityToken(externalId)` pair used when booking. */\nexport type AppointmentIdentity = {\n externalId: string\n identityToken: string\n}\n\ntype AppointmentsApi = {\n /**\n * Available slots for the workspace's booking window.\n * `from` / `to` are optional clamps inside the workspace's\n * configured window — the server ignores ranges outside.\n */\n getAvailability: (opts?: {\n from?: Date | string\n to?: Date | string\n }) => Promise<AppointmentAvailability>\n /**\n * Submit a visitor's appointment request. Server-side validates\n * the slot is still bookable; if it isn't, throws\n * `BrandfineApiError` with status 404 / 409.\n *\n * Pass `visitor` (signed with `liveChat.identityToken()`) to book\n * as a known person — that unlocks `list`/`get` read-back below.\n * Anonymous bookings remain fully supported; their status flow\n * stays email-driven via the cancellation-token link.\n */\n createRequest: (\n input: CreateAppointmentRequestInput,\n ) => Promise<CreatedAppointmentRequest>\n /**\n * Every appointment belonging to the verified person, newest\n * first. Requires a valid identity signature — throws\n * `BrandfineApiError` 401 on a bad one (reads need proof; there\n * is no anonymous downgrade for reading history). Server-side\n * only, like `identityToken()` itself.\n */\n list: (identity: AppointmentIdentity) => Promise<Appointment[]>\n /**\n * One appointment by id, identity-scoped. 404s when the id does\n * not exist OR belongs to someone else — indistinguishable by\n * design.\n */\n get: (id: string, identity: AppointmentIdentity) => Promise<Appointment>\n /**\n * Spend the one-time `cancellationToken` from `createRequest` to\n * cancel a still-PENDING request. The token IS the credential —\n * no id or API key needed. Throws `BrandfineApiError` 400 when\n * the request is no longer PENDING, 404 when the token is\n * unknown/already spent.\n */\n cancel: (\n cancellationToken: string,\n ) => Promise<{ id: string; status: 'CANCELLED' }>\n}\n\nexport type AnalyticsConfig =\n | {\n enabled: false\n /** GA4 Measurement ID — present when the workspace's Google\n * Analytics property was provisioned through Brandfine AND\n * the customer opted into tag injection. `install()` loads\n * gtag for it. Note gtag sets cookies: consent banners are\n * your site's responsibility. */\n gaMeasurementId?: string\n }\n | {\n enabled: true\n websiteId: string\n scriptUrl: string\n gaMeasurementId?: string\n }\n\nexport type AnalyticsOverviewRange = '24h' | '7d' | '30d' | '90d'\n\n/**\n * Composed traffic report for the workspace — summary KPIs +\n * bucketed chart data + top pages in one payload. Mirrors\n * `GET /external/analytics/overview` (see the API's\n * `ExternalAnalyticsOverview` type); additive changes only.\n *\n * Three shapes to handle:\n * - `{ enabled: false }` — analytics never enabled for the\n * workspace. Show an enable CTA.\n * - `{ enabled: true, verified: false }` — tracker provisioned\n * but no pageview recorded yet. Show \"waiting for first visit\".\n * - full payload — render the dashboard.\n */\nexport type AnalyticsOverview =\n | { enabled: false }\n | { enabled: true; verified: false }\n | {\n enabled: true\n verified: true\n range: AnalyticsOverviewRange\n summary: {\n visitors: number\n /** Fractional change vs the prior window (0.12 = +12%). */\n visitorsChange: number\n pageviews: number\n pageviewsChange: number\n visits: number\n visitsChange: number\n /** 0..1 fraction. */\n bounceRate: number\n bounceRateChange: number\n avgVisitSeconds: number\n avgVisitSecondsChange: number\n /** Visitors active in the last ~5 minutes. */\n activeNow: number\n }\n /** Bucketed chart data, oldest → newest. Hourly buckets for\n * `24h`, daily otherwise. `t` is an ISO-8601 bucket start. */\n timeseries: Array<{ t: string; visitors: number; pageviews: number }>\n /** Top 10 paths by views in the window. */\n topPages: Array<{ path: string; views: number; visitors: number }>\n /** Top 10 referrer sources by visitors. Empty-string source\n * means direct traffic. */\n sources: Array<{ source: string; visitors: number }>\n /** Top 10 visitor countries (ISO 3166-1 alpha-2 codes —\n * map to display names on your side, e.g. via\n * `Intl.DisplayNames`). */\n countries: Array<{ country: string; visitors: number }>\n /** Visitors by device class (`desktop` / `mobile` /\n * `tablet` / …). */\n devices: Array<{ device: string; visitors: number }>\n }\n\nexport type AnalyticsInstallResult =\n | { installed: false; reason: 'disabled' | 'ssr' | 'already-installed' }\n | { installed: true; websiteId: string }\n\nexport type InstallOptions = {\n /**\n * Pre-known config. When provided, `install()` skips the round-\n * trip to `/external/analytics-config` and injects the script\n * immediately. Use this when you've baked the values into your\n * build (env vars, CMS-side config dump, etc.) — typical for\n * static sites where the analytics state is decided at deploy\n * time, not per page load.\n *\n * Trade-off vs the default fetch path: if you disable analytics\n * in Brandfine, the tracker keeps loading until your next\n * deploy. That's usually the right trade for static sites\n * (which redeploy on every content change anyway) and the wrong\n * trade for dynamic sites where the api round-trip is cheap\n * relative to the rest of the page.\n *\n * Pass `{ enabled: false }` to force a no-op without touching\n * the api (e.g. to disable analytics for one environment without\n * changing Brandfine's state).\n */\n config?: AnalyticsConfig\n}\n\ntype AnalyticsApi = {\n /**\n * Injects the Brandfine analytics tracker into `document.head`\n * once. Safe to call on every page load — idempotent via a\n * marker attribute on the injected script tag.\n *\n * Two paths:\n * - `install()` — fetches the config from Brandfine, then\n * injects. Reflects enable/disable state on next page load.\n * - `install({ config })` — uses caller-provided config, skips\n * the fetch. Faster, no round-trip; ignores Brandfine state\n * changes until the consumer's next deploy.\n *\n * Returns details about what happened:\n * - `{ installed: true, websiteId }` — script was just injected.\n * - `{ installed: false, reason: 'disabled' }` — config says\n * analytics is off; no-op.\n * - `{ installed: false, reason: 'ssr' }` — no `document` in\n * scope (server-side). Call again on the client.\n * - `{ installed: false, reason: 'already-installed' }` — a\n * prior call (or another tab in the same SPA) already injected.\n *\n * Throws `BrandfineApiError` on non-2xx responses other than the\n * disabled case (which is a valid `{ enabled: false }` body).\n */\n install: (opts?: InstallOptions) => Promise<AnalyticsInstallResult>\n\n /** Lower-level helper — fetches the raw config without touching\n * the DOM. Useful when you want to inject the script yourself\n * (e.g. via a framework's <Script> component for nonce/csp). */\n getConfig: () => Promise<AnalyticsConfig>\n\n /**\n * Traffic report for the workspace — summary KPIs, bucketed\n * timeseries for charting, and top pages, in one round-trip.\n * This is a server-to-server read (it returns your site's\n * traffic data); call it from your backend or build step, not\n * from visitor-facing browser code.\n *\n * @param opts.range Window preset. Defaults to `'7d'`.\n */\n overview: (opts?: {\n range?: AnalyticsOverviewRange\n }) => Promise<AnalyticsOverview>\n}\n\nexport type LiveChatBootstrap =\n | { enabled: false }\n | {\n enabled: true\n /** The workspace's SCOPED publishable key — safe to bake into\n * public HTML; it can only start chat conversations. */\n publishableKey: string\n greeting: string | null\n offlineMessage: string | null\n theme: Record<string, string> | null\n /** Widget bundle path relative to the API base URL. */\n scriptPath: string\n /** Realtime endpoint (wss://…). OPTIONAL by design — this is\n * what makes the upgrade non-breaking: an older SDK ignores\n * it, a newer SDK against an older API sees it absent and\n * polls. Consumers pass `config` through wholesale, so it\n * reaches the browser with no change on their side. */\n realtimeUrl?: string\n }\n\nexport type LiveChatInstallResult =\n | { installed: false; reason: 'disabled' | 'ssr' | 'already-installed' }\n | { installed: true }\n\n/**\n * A signed visitor identity for Live Chat. Build it SERVER-SIDE:\n * compute `identityToken` with `liveChat.identityToken()` (or your\n * own HMAC_SHA256(identitySecret, externalId), hex) and pass the\n * whole object to `install()` — the widget forwards it verbatim and\n * the Brandfine API verifies the signature. An invalid or missing\n * token silently downgrades the conversation to anonymous.\n */\n/**\n * A signed host-app identity payload. Named per-plugin below for\n * discoverability, but it is ONE shape signed ONE way: a token from\n * `liveChat.identityToken(externalId)` is accepted by Live Chat AND\n * Appointments (`createRequest.visitor`, `list`/`get`).\n */\nexport type VerifiedVisitor = LiveChatVisitor\n\nexport type LiveChatVisitor = {\n /** Your app's stable id for this person (user id, lead reference…).\n * Conversations sharing an externalId are the same person across\n * devices and sessions. Max 128 chars. */\n externalId: string\n /** Display name shown in the Brandfine inbox. Max 120 chars. */\n name?: string\n /** Max 200 chars. */\n email?: string\n /** Small display-only key→string map (≤10 keys, values ≤100 chars). */\n attributes?: Record<string, string>\n /** hex HMAC_SHA256(identitySecret, externalId) — REQUIRED, computed\n * on your server. Never derive this in a browser. */\n identityToken: string\n}\n\nexport type LiveChatInstallOptions = {\n /**\n * Pre-known bootstrap. Same build-time/runtime trade-off as the\n * analytics `install()`: pass the value your server half fetched\n * (static-export sites bake it at build time), or omit — NOT\n * recommended in browsers, because fetching here would require\n * the broad key client-side. In practice: always pass `config`\n * from your server half.\n */\n config: LiveChatBootstrap\n /**\n * Already-signed visitor identity (see LiveChatVisitor). Optional —\n * omit for anonymous chat. The object must arrive from your server\n * with `identityToken` precomputed; `install()` only serializes it\n * onto the widget host, it performs no crypto.\n */\n visitor?: LiveChatVisitor\n /**\n * Locale for the widget's visitor-facing strings (greeting, away\n * message), e.g. your i18n router's active locale. Optional — the\n * widget falls back to the page's `<html lang>` and then the\n * browser language. Resolved server-side against the workspace's\n * configured translations; unknown locales get the default text.\n */\n locale?: string\n}\n\ntype LiveChatApi = {\n /**\n * Fetches the Live Chat bootstrap (publishable key + display\n * config) with the broad workspace key. SERVER-SIDE ONLY — call\n * from your backend, RSC, or build step, never from browser code.\n */\n getConfig: () => Promise<LiveChatBootstrap>\n\n /**\n * Injects the chat widget into the page: appends a host\n * `<div data-bf-live-chat>` carrying the publishable key and the\n * widget `<script>` tag. Idempotent via a marker attribute.\n * Browser-side half of the pair — receives the bootstrap your\n * server half fetched with `getConfig()`.\n *\n * Returns what happened, mirroring `analytics.install()`:\n * - `{ installed: true }` — widget just injected.\n * - `{ installed: false, reason: 'disabled' }` — chat is off.\n * - `{ installed: false, reason: 'ssr' }` — no `document`.\n * - `{ installed: false, reason: 'already-installed' }`.\n */\n install: (opts: LiveChatInstallOptions) => Promise<LiveChatInstallResult>\n\n /**\n * Computes the visitor identity token:\n * hex(HMAC_SHA256(signingSecret, externalId)). SERVER-ONLY — it\n * throws in a browser context rather than ever computing next to\n * the DOM.\n *\n * Signing secret, in order: `{ secret }` option →\n * `BRANDFINE_LIVE_CHAT_IDENTITY_SECRET` env → **derived from the\n * client's API key** (HMAC with a fixed domain-separation\n * constant; the Brandfine API derives the same value from its\n * stored copy). The derived path needs ZERO extra configuration —\n * an explicit secret is only for signers that shouldn't hold the\n * broad key, or legacy workspaces without a revealable key\n * (generate one in the CMS: Plugins → Live Chat → Integrate).\n */\n identityToken: (\n externalId: string,\n opts?: { secret?: string },\n ) => Promise<string>\n\n /**\n * Headless session — the conversation without the widget, for\n * building your own inline chat UI. Browser-only. Takes the same\n * `config` and signed `visitor` as `install()` (identical identity\n * rules and thread continuity — it IS the same conversation the\n * widget would join, sharing its storage keys). Transport today is\n * polling (`pollIntervalMs`, default 5000); a realtime upgrade\n * will keep this API and the poll as fallback.\n */\n createSession: (\n opts: LiveChatCreateSessionOptions,\n ) => Promise<LiveChatSession>\n\n /**\n * Raw, stateless wire methods for full control. NOTE on auth: the\n * config + start endpoints authenticate with this client's\n * `apiKey` header — in a browser, construct the client with the\n * PUBLISHABLE key (`createBrandfineClient({ apiKey: config.publishableKey })`),\n * never the broad key. Message endpoints authenticate with the\n * conversation token alone — treat it as a bearer credential:\n * don't log it, don't put it in URLs you share.\n */\n runtimeConfig: (locale?: string) => Promise<LiveChatRuntimeConfig>\n startConversation: (input: {\n visitorSessionId: string\n conversationToken?: string\n pageUrl?: string\n referrer?: string\n locale?: string\n visitor?: Record<string, unknown>\n }) => Promise<StartConversationResult>\n sendMessage: (\n conversationToken: string,\n input: { body: string; clientId?: string },\n ) => Promise<ChatMessage>\n history: (\n conversationToken: string,\n opts?: { after?: string; afterEvent?: number },\n ) => Promise<{ messages: ChatMessage[]; status: ConversationStatus }>\n}\n\n/** Attribute we stamp on the injected <script> so `install()` is\n * idempotent across re-renders and SPA route changes. */\nconst INSTALLED_MARKER = 'data-brandfine-analytics'\n\n/** Same idempotency contract for the live-chat widget injection. */\nconst LIVE_CHAT_MARKER = 'data-brandfine-live-chat'\n\n/**\n * Domain-separation constant for deriving the live-chat identity\n * signing secret from the workspace API key. MUST match the API's\n * constant (`live-chat-external.service.ts`) byte for byte — bump\n * the version suffix only in lockstep with a server release.\n */\nconst LIVE_CHAT_IDENTITY_CONTEXT = 'brandfine:live-chat:identity:v1'\n\n/** hex(HMAC_SHA256(key, message)) via WebCrypto — Node ≥20 global\n * AND browser-safe, so bundlers never see a node:crypto import. */\nasync function hmacHex(key: string, message: string): Promise<string> {\n const enc = new TextEncoder()\n const cryptoKey = await globalThis.crypto.subtle.importKey(\n 'raw',\n enc.encode(key),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n )\n const sig = await globalThis.crypto.subtle.sign(\n 'HMAC',\n cryptoKey,\n enc.encode(message),\n )\n return Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/** Marker for the injected Google tag — same idempotency contract. */\nconst GTAG_MARKER = 'data-brandfine-gtag'\n\n/**\n * Inject the Google tag (gtag.js) for an auto-provisioned GA4\n * property. No-ops when ANY gtag script is already on the page —\n * a site that hand-installed Google Analytics must not get a\n * second config (double-counted sessions are worse than a missing\n * tag). Safe to call repeatedly; the marker makes it idempotent.\n */\nfunction injectGoogleTag(measurementId: string): void {\n if (typeof document === 'undefined') return\n const existing = document.querySelector(\n `script[src*=\"googletagmanager.com/gtag/js\"], script[${GTAG_MARKER}]`,\n )\n if (existing) return\n\n const loader = document.createElement('script')\n loader.async = true\n loader.src = `https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(measurementId)}`\n loader.setAttribute(GTAG_MARKER, measurementId)\n document.head.appendChild(loader)\n\n const w = window as unknown as { dataLayer?: unknown[] }\n w.dataLayer = w.dataLayer ?? []\n // gtag() must push `arguments` (an Arguments object), not a\n // plain array — GA's snippet relies on it.\n function gtag(..._args: unknown[]) {\n // eslint-disable-next-line prefer-rest-params\n w.dataLayer!.push(arguments)\n }\n gtag('js', new Date())\n gtag('config', measurementId)\n}\n\nconst DEFAULT_USER_AGENT = '@brandfine/client'\n\n/** Production API origin — the default when no `baseUrl` is given.\n * Consumers only configure a URL for local dev or staging. */\nconst DEFAULT_BASE_URL = 'https://api.brandfine.co'\n\n/**\n * Resolve the API origin: explicit option → `BRANDFINE_API_URL`\n * env (server-side only — browsers have no `process`, and an env\n * indirection in a browser bundle would be inlined at build time\n * anyway) → production default.\n */\nfunction resolveBaseUrl(explicit: string | undefined): string {\n if (explicit) return explicit\n if (typeof process !== 'undefined' && process.env?.BRANDFINE_API_URL) {\n return process.env.BRANDFINE_API_URL\n }\n return DEFAULT_BASE_URL\n}\n\nexport function createBrandfineClient(\n config: BrandfineClientConfig,\n): BrandfineClient {\n if (!config.apiKey)\n throw new Error('createBrandfineClient: `apiKey` is required')\n\n const baseUrl = resolveBaseUrl(config.baseUrl).replace(/\\/$/, '')\n const apiKey = config.apiKey\n // Resolve fetch lazily so consumers in environments without a\n // global fetch can polyfill before constructing the client.\n const fetchImpl: typeof fetch = config.fetch ?? globalThis.fetch\n const userAgent = config.userAgent ?? DEFAULT_USER_AGENT\n\n async function get<T>(path: string, opts: RequestOptions = {}): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method: 'GET',\n headers: {\n 'X-Api-Key': apiKey,\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n signal: opts.signal,\n })\n if (res.status === 404 && opts.nullable404) {\n // Drain the body so the underlying socket can be reused —\n // fetch implementations that don't auto-drain (older Node)\n // can leak otherwise.\n await res.text().catch(() => '')\n return null as T\n }\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body,\n url,\n })\n }\n return (await res.json()) as T\n }\n\n const posts: PostsApi = {\n async list<TConfig = unknown>(opts: ListPostsOptions = {}) {\n const out: BrandfinePost<TConfig>[] = []\n let page = 1\n const typeQuery = opts.type ? `&type=${encodeURIComponent(opts.type)}` : ''\n const localeQuery = opts.locale\n ? `&locale=${encodeURIComponent(opts.locale)}`\n : ''\n // Default pagination at the cms's 50-per-page cap. `forceLimit`\n // opts past it for content types that would otherwise need\n // many round-trips.\n const sizeQuery = opts.forceLimit\n ? `&force_limit=${opts.forceLimit}`\n : '&limit=50'\n // Pathological safety brake — 200 pages × 50 = 10k posts. If\n // a workspace ever needs more, callers should hit the API\n // directly with their own pagination logic.\n const MAX_PAGES = 200\n while (page <= MAX_PAGES) {\n const data = await get<BrandfinePostListResponse<TConfig>>(\n `/external/posts?include=content${sizeQuery}&page=${page}${typeQuery}${localeQuery}`,\n )\n out.push(...data.items)\n if (!data.pageInfo.hasNext) break\n page += 1\n }\n return out\n },\n async getBySlug<TConfig = unknown>(slug: string) {\n return get<BrandfinePost<TConfig> | null>(\n `/external/posts/${encodeURIComponent(slug)}`,\n { nullable404: true },\n )\n },\n }\n\n const categories: CategoriesApi = {\n async list(opts: ListCategoriesOptions = {}) {\n const qs = opts.locale ? `?locale=${encodeURIComponent(opts.locale)}` : ''\n const data = await get<{ items: BrandfineCategory[] }>(\n `/external/categories${qs}`,\n )\n return data.items\n },\n }\n\n const workspace: WorkspaceApi = {\n get<\n TCustomConfig = Record<string, unknown>,\n TSchemaOrg = Record<string, unknown>,\n >() {\n return get<BrandfineWorkspace<TCustomConfig, TSchemaOrg>>(\n '/external/workspace',\n )\n },\n }\n\n const navigations: NavigationsApi = {\n get<TConfig = unknown>(key: string) {\n return get<BrandfineNavigation<TConfig> | null>(\n `/external/navigations/${encodeURIComponent(key)}`,\n { nullable404: true },\n )\n },\n }\n\n const analytics: AnalyticsApi = {\n getConfig() {\n return get<AnalyticsConfig>('/external/analytics-config')\n },\n overview(opts = {}) {\n const range = opts.range ?? '7d'\n return get<AnalyticsOverview>(\n `/external/analytics/overview?range=${encodeURIComponent(range)}`,\n )\n },\n async install(opts: InstallOptions = {}) {\n // SSR safety: nothing to inject without a DOM. Consumers\n // call this from useEffect / onMount, but defensive anyway\n // (some frameworks still execute the file body on the server).\n if (typeof document === 'undefined') {\n return { installed: false, reason: 'ssr' as const }\n }\n\n // Use caller-provided config if present (build-time path),\n // otherwise fetch (runtime path).\n const cfg = opts.config ?? (await analytics.getConfig())\n\n // Google tag rides alongside the built-in tracker — injected\n // even when Brandfine analytics itself is off, because the\n // opt-in lives on the GA integration, not on the tracker.\n if (cfg.gaMeasurementId) {\n injectGoogleTag(cfg.gaMeasurementId)\n }\n\n if (!cfg.enabled) {\n return { installed: false, reason: 'disabled' as const }\n }\n\n // Idempotency: a prior call (StrictMode double-invoke, SPA\n // re-mount, second instance with the same workspace) may\n // have already injected. The marker attribute is the source\n // of truth — checking by script src would also miss the case\n // where two workspaces share the same scriptUrl.\n const existing = document.querySelector<HTMLScriptElement>(\n `script[${INSTALLED_MARKER}=\"${cfg.websiteId}\"]`,\n )\n if (existing) {\n return { installed: false, reason: 'already-installed' as const }\n }\n\n const script = document.createElement('script')\n script.defer = true\n script.src = cfg.scriptUrl\n script.setAttribute('data-website-id', cfg.websiteId)\n // The marker doubles as a sentinel + a debug aid (you can\n // grep the DOM for `data-brandfine-analytics` to confirm\n // an install).\n script.setAttribute(INSTALLED_MARKER, cfg.websiteId)\n document.head.appendChild(script)\n return { installed: true, websiteId: cfg.websiteId }\n },\n }\n\n /** Shared fetch for the live-chat public wire: optional key header\n * (publishable for config/start, none for token-authed message\n * endpoints), JSON in/out, BrandfineApiError on non-2xx. */\n async function liveChatWireRequest<T>(\n key: string | undefined,\n method: 'GET' | 'POST',\n path: string,\n body?: unknown,\n ): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method,\n headers: {\n Accept: 'application/json',\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...(key ? { 'X-Api-Key': key } : {}),\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body: text,\n url,\n })\n }\n return (await res.json()) as T\n }\n\n const liveChat: LiveChatApi = {\n getConfig() {\n return get<LiveChatBootstrap>('/external/live-chat/bootstrap')\n },\n async install(opts: LiveChatInstallOptions) {\n if (typeof document === 'undefined') {\n return { installed: false, reason: 'ssr' as const }\n }\n\n const cfg = opts.config\n if (!cfg.enabled) {\n return { installed: false, reason: 'disabled' as const }\n }\n\n const existing = document.querySelector(`[${LIVE_CHAT_MARKER}]`)\n if (existing) {\n return { installed: false, reason: 'already-installed' as const }\n }\n\n // Host div the widget script mounts into. Theme vars ride as\n // inline custom properties — they inherit through the widget's\n // shadow boundary.\n const host = document.createElement('div')\n host.setAttribute('data-bf-live-chat', '')\n host.setAttribute('data-publishable-key', cfg.publishableKey)\n host.setAttribute('data-base-url', baseUrl)\n host.setAttribute(LIVE_CHAT_MARKER, '')\n // Signed visitor identity → data-visitor. Serialization only;\n // the signature was computed server-side (identityToken()).\n if (opts.visitor?.externalId && opts.visitor.identityToken) {\n host.setAttribute('data-visitor', JSON.stringify(opts.visitor))\n }\n if (opts.locale) {\n host.setAttribute('data-locale', opts.locale)\n }\n if (cfg.theme) {\n for (const [key, value] of Object.entries(cfg.theme)) {\n if (key.startsWith('--bf-chat-')) {\n host.style.setProperty(key, value)\n }\n }\n }\n document.body.appendChild(host)\n\n const script = document.createElement('script')\n script.defer = true\n script.src = `${baseUrl}${cfg.scriptPath}`\n script.setAttribute(LIVE_CHAT_MARKER, 'script')\n document.head.appendChild(script)\n\n return { installed: true }\n },\n\n createSession(opts: LiveChatCreateSessionOptions) {\n // The session's key-authed calls use the PUBLISHABLE key from\n // the passed config (the browser client is often constructed\n // with a placeholder apiKey for install()-style usage).\n const pk = opts.config.enabled ? opts.config.publishableKey : undefined\n const wire: LiveChatWire = {\n runtimeConfig: (locale) => liveChatWireRequest(pk, 'GET',\n `/external/live-chat/config${locale ? `?locale=${encodeURIComponent(locale)}` : ''}`),\n startConversation: (input) =>\n liveChatWireRequest(pk, 'POST', '/external/live-chat/conversations', input),\n sendMessage: (conversationToken, input) =>\n liveChatWireRequest(undefined, 'POST',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages`, input),\n history: (conversationToken, o = {}) =>\n liveChatWireRequest(undefined, 'GET',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages${\n o.afterEvent !== undefined\n ? `?afterEvent=${encodeURIComponent(String(o.afterEvent))}`\n : o.after\n ? `?after=${encodeURIComponent(o.after)}`\n : ''\n }`),\n }\n return createLiveChatSession(wire, opts)\n },\n\n runtimeConfig(locale) {\n return liveChatWireRequest(apiKey, 'GET',\n `/external/live-chat/config${locale ? `?locale=${encodeURIComponent(locale)}` : ''}`)\n },\n startConversation(input) {\n return liveChatWireRequest(apiKey, 'POST', '/external/live-chat/conversations', input)\n },\n sendMessage(conversationToken, input) {\n return liveChatWireRequest(undefined, 'POST',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages`, input)\n },\n history(conversationToken, o = {}) {\n // afterEvent (eventId cursor, exact) wins over the legacy\n // timestamp cursor when the session knows one.\n const qs =\n o.afterEvent !== undefined\n ? `?afterEvent=${encodeURIComponent(String(o.afterEvent))}`\n : o.after\n ? `?after=${encodeURIComponent(o.after)}`\n : ''\n return liveChatWireRequest(undefined, 'GET',\n `/external/live-chat/conversations/${encodeURIComponent(conversationToken)}/messages${qs}`)\n },\n\n async identityToken(externalId, opts = {}) {\n // Refuse to run where the secret could be exfiltrated. A page\n // that needs a token must get it from ITS server, not compute\n // one next to the DOM.\n if (typeof document !== 'undefined' || typeof window !== 'undefined') {\n throw new Error(\n 'liveChat.identityToken() is server-only — never compute identity ' +\n 'tokens in a browser. Sign the visitor on your server and pass ' +\n 'the result to install({ visitor }).',\n )\n }\n if (!externalId) {\n throw new Error('liveChat.identityToken(): externalId is required.')\n }\n // Signing-secret resolution:\n // 1. Explicit { secret } / BRANDFINE_LIVE_CHAT_IDENTITY_SECRET —\n // the decoupled option (a signer that shouldn't hold the\n // broad key, or a legacy workspace).\n // 2. DERIVED from the API key this client already holds:\n // HMAC(apiKey, domain-separation constant). Zero extra\n // config — the server derives the same value from its\n // stored copy of the key. Rotating the API key rotates\n // signing with it, which is the lifecycle you want anyway.\n const explicit =\n opts.secret ??\n (typeof process !== 'undefined'\n ? process.env.BRANDFINE_LIVE_CHAT_IDENTITY_SECRET\n : undefined)\n const secret =\n explicit ?? (await hmacHex(apiKey, LIVE_CHAT_IDENTITY_CONTEXT))\n return hmacHex(secret, externalId)\n },\n }\n\n const submissions: SubmissionsApi = {\n async create(input: CreateSubmissionInput) {\n const url = `${baseUrl}/external/submissions`\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n 'X-Api-Key': apiKey,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n body: JSON.stringify(input),\n })\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body,\n url,\n })\n }\n return (await res.json()) as Submission\n },\n }\n\n /**\n * Shared POST helper for the appointments namespace. The main\n * `get()` helper handles GETs; submissions has its own inline\n * POST because it predates this refactor. New plugin namespaces\n * (appointments first, others to follow) share this one so the\n * error-handling shape stays consistent.\n */\n async function post<T>(\n path: string,\n body: unknown,\n opts: RequestOptions = {},\n ): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: {\n 'X-Api-Key': apiKey,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n body: JSON.stringify(body),\n signal: opts.signal,\n })\n if (!res.ok) {\n const errBody = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body: errBody,\n url,\n })\n }\n if (res.status === 204) return undefined as T\n return (await res.json()) as T\n }\n\n const appointments: AppointmentsApi = {\n getAvailability(opts = {}) {\n const qs: string[] = []\n if (opts.from) qs.push(`from=${encodeURIComponent(toIso(opts.from))}`)\n if (opts.to) qs.push(`to=${encodeURIComponent(toIso(opts.to))}`)\n const suffix = qs.length ? `?${qs.join('&')}` : ''\n return get<AppointmentAvailability>(\n `/external/appointments/availability${suffix}`,\n )\n },\n createRequest(input) {\n return post<CreatedAppointmentRequest>(\n '/external/appointments/requests',\n input,\n )\n },\n async list(identity) {\n // POST (not GET) so the identityToken never lands in URLs,\n // proxy logs, or browser history.\n const res = await post<{ appointments: Appointment[] }>(\n '/external/appointments/requests/lookup',\n identity,\n )\n return res.appointments\n },\n async get(id, identity) {\n // Same lookup endpoint narrowed by id — the server 404s when\n // the id is missing or belongs to another person, so a\n // successful response always carries exactly one row. The\n // guard below is defensive symmetry, not a real path.\n const res = await post<{ appointments: Appointment[] }>(\n '/external/appointments/requests/lookup',\n { ...identity, id },\n )\n const row = res.appointments[0]\n if (!row) {\n throw new BrandfineApiError({\n status: 404,\n statusText: 'Not Found',\n body: 'Appointment not found.',\n url: '/external/appointments/requests/lookup',\n })\n }\n return row\n },\n cancel(cancellationToken) {\n return post<{ id: string; status: 'CANCELLED' }>(\n `/external/appointments/requests/${encodeURIComponent(cancellationToken)}/cancel`,\n {},\n )\n },\n }\n\n return {\n get,\n posts,\n categories,\n workspace,\n navigations,\n analytics,\n submissions,\n appointments,\n liveChat,\n }\n}\n\n/** Accepts a Date or an already-ISO string and returns ISO. Saves\n * every caller from `.toISOString()`-ing manually. */\nfunction toIso(d: Date | string): string {\n return typeof d === 'string' ? d : d.toISOString()\n}\n","/**\n * @brandfine/client — root entry.\n *\n * The full SDK surface is exposed here for \"import everything from\n * one place\" usage. Tree-shaking + `sideEffects: false` mean\n * consumers don't pay a bundle cost for what they don't import.\n *\n * Heavier or framework-coupled pieces still live under subpath\n * exports (`@brandfine/client/cache`, `/resolvers`, `/webhook`) so\n * consumers with poor tree-shaking — or who only need one slice —\n * can scope their imports.\n */\n\nexport const SDK_VERSION = '0.0.0' as const\n\nexport {\n BrandfineApiError,\n createBrandfineClient,\n type AnalyticsConfig,\n type AnalyticsInstallResult,\n type AnalyticsOverview,\n type AnalyticsOverviewRange,\n type Appointment,\n type AppointmentAvailability,\n type AppointmentIdentity,\n type AppointmentSlot,\n type AppointmentStatus,\n type BrandfineClient,\n type BrandfineClientConfig,\n type ChatMessage,\n type CreateAppointmentRequestInput,\n type CreatedAppointmentRequest,\n type ChatMessageSender,\n type ConversationStatus,\n type CreateSubmissionInput,\n type InstallOptions,\n type LiveChatBootstrap,\n type LiveChatCreateSessionOptions,\n type LiveChatInstallOptions,\n type LiveChatInstallResult,\n type LiveChatRuntimeConfig,\n type LiveChatSession,\n type LiveChatSessionSnapshot,\n type LiveChatSessionState,\n type LiveChatVisitor,\n type StartConversationResult,\n type Submission,\n type VerifiedVisitor,\n} from './client'\n\nexport {\n createCache,\n createKeyedCache,\n type Cache,\n type CacheOptions,\n type KeyedCache,\n type KeyedCacheOptions,\n} from './cache/index'\n\nexport {\n isLocale,\n localizePath,\n pickLocale,\n resolveNavigation,\n stripLocalePrefix,\n type HydratedNav,\n type HydratedNavItem,\n type LocaleOptions,\n type ResolveNavigationOptions,\n} from './resolvers/index'\n\nexport {\n createBrandfineWebhookHandler,\n parseWebhookPayload,\n verifyWebhookSecret,\n type BrandfineWebhookEvent,\n type BrandfineWebhookHandlerOptions,\n type BrandfineWebhookPayload,\n} from './webhook/index'\n\nexport type {\n BrandfineCategory,\n BrandfineNavItem,\n BrandfineNavItemType,\n BrandfineNavPost,\n BrandfineNavigation,\n BrandfinePost,\n BrandfinePostListResponse,\n BrandfinePostTranslation,\n BrandfineWorkspace,\n ListCategoriesOptions,\n ListPostsOptions,\n} from './types'\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brandfine/client",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "description": "Brandfine consumer SDK — typed HTTP client, server-side caches, locale + navigation resolvers, and webhook helpers for landing-page integrations.",
5
5
  "license": "MIT",
6
6
  "type": "module",