// Parse the URL params the Phase 2 bot widget 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. // // Identical shape to apps/widget-telegram/src/bootstrap.ts on purpose — // the host emits the same param set for every bot. Differences between // telegram and discord live in the bridge protocol, not in bootstrap. export type WidgetBootstrap = { widgetId: string; parentUrl: string; parentOrigin: string; roomId: string; userId: string; botId: string; botMxid: string; /** Bridge command prefix (e.g. `!discord`). Always non-empty — the host * validator (catalog.ts) defaults missing values to `!tg` and rejects * malformed overrides, so the discord bot's /config.json entry MUST set * `experience.commandPrefix: "!discord"` to override the default. The * widget prepends ` ` to every outbound command. */ 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'), }, }; };