// Parse the URL params the host appends when loading experience.url. // Source of truth on the host side: // src/app/features/bots/BotWidgetEmbed.ts (getBotWidgetUrl). // Keep this in sync if the host adds params. export type WidgetBootstrap = { widgetId: string; parentUrl: string; parentOrigin: string; roomId: string; userId: string; botId: string; botMxid: string; /** Bridge command prefix (e.g. `!wa`). Always non-empty — the host * validator (catalog.ts) defaults missing values to `!tg` and rejects * malformed overrides. The widget prepends ` ` to every * outbound command and form-field value (bridgev2/queue.go:118 strips * exactly `prefix+" "`). For mautrix-whatsapp the operator must set * `commandPrefix: "!wa"` in /config.json — connector.go ships * `DefaultCommandPrefix: "!wa"`. */ commandPrefix: string; theme: 'light' | 'dark'; clientLanguage: string; }; export type BootstrapResult = | { ok: true; bootstrap: WidgetBootstrap } | { ok: false; missing: string[] }; const REQUIRED = ['widgetId', 'parentUrl', 'roomId', 'userId', 'botMxid', 'commandPrefix'] as const; export const readBootstrap = (search: string): BootstrapResult => { const params = new URLSearchParams(search); const get = (k: string) => params.get(k) ?? ''; const missing = REQUIRED.filter((k) => !params.get(k)); if (missing.length > 0) return { ok: false, missing: [...missing] }; // Origin is what the widget validates against on incoming postMessage — // see widget-api.ts. Falling back to '*' would defeat the security // boundary, so a malformed parentUrl bails out as a missing-param error. let parentOrigin: string; try { parentOrigin = new URL(get('parentUrl')).origin; } catch { return { ok: false, missing: ['parentUrl'] }; } const themeRaw = get('theme'); const theme: 'light' | 'dark' = themeRaw === 'dark' ? 'dark' : 'light'; return { ok: true, bootstrap: { widgetId: get('widgetId'), parentUrl: get('parentUrl'), parentOrigin, roomId: get('roomId'), userId: get('userId'), botId: get('botId'), botMxid: get('botMxid'), commandPrefix: get('commandPrefix'), theme, clientLanguage: get('clientLanguage'), }, }; };