/** * BreakPilot Compliance SDK - Embed Script * * Usage: * * */ import { ComplianceClient } from '@breakpilot/compliance-sdk-core' import type { ConsentPurpose, DSRRequestType, } from '@breakpilot/compliance-sdk-types' import { createBannerElement, renderSettingsPanel, type BannerConfig, type BannerContext, } from './embed-banner' // ============================================================================ // Types // ============================================================================ export interface BreakPilotSDKConfig { apiEndpoint: string apiKey: string tenantId?: string autoInjectBanner?: boolean bannerConfig?: BannerConfig onConsentChange?: (consents: Record) => void onReady?: () => void onError?: (error: Error) => void debug?: boolean } export type { BannerConfig } from './embed-banner' // ============================================================================ // Internal State // ============================================================================ let _client: ComplianceClient | null = null let _config: BreakPilotSDKConfig | null = null let _consents: Record = { ESSENTIAL: true, FUNCTIONAL: false, ANALYTICS: false, MARKETING: false, PERSONALIZATION: false, THIRD_PARTY: false, } let _bannerElement: HTMLElement | null = null let _isInitialized = false // ============================================================================ // Utility Functions // ============================================================================ function log(message: string, ...args: unknown[]): void { if (_config?.debug) { console.log(`[BreakPilotSDK] ${message}`, ...args) } } function getStoredConsents(): Record | null { try { const stored = localStorage.getItem('breakpilot_consents') return stored ? JSON.parse(stored) : null } catch { return null } } function storeConsents(consents: Record): void { try { localStorage.setItem('breakpilot_consents', JSON.stringify(consents)) localStorage.setItem('breakpilot_consents_timestamp', Date.now().toString()) } catch { log('Failed to store consents') } } // ============================================================================ // Banner Orchestration // ============================================================================ function buildBannerContext(): BannerContext { return { config: _config?.bannerConfig || {}, consents: _consents, callbacks: { onAcceptAll: () => handleAcceptAll(), onRejectAll: () => handleRejectAll(), onShowSettings: () => showSettingsPanel(), onToggleCategory: (category, granted) => { _consents[category] = granted }, onSaveSettings: () => handleSaveSettings(), onBack: () => showMainBanner(), }, } } function showMainBanner(): void { if (_bannerElement) { _bannerElement.remove() } _bannerElement = createBannerElement(buildBannerContext()) document.body.appendChild(_bannerElement) } function showSettingsPanel(): void { if (!_bannerElement) return renderSettingsPanel(_bannerElement, buildBannerContext()) } function hideBanner(): void { if (_bannerElement) { _bannerElement.remove() _bannerElement = null } } // ============================================================================ // Consent Handlers // ============================================================================ function handleAcceptAll(): void { _consents = { ESSENTIAL: true, FUNCTIONAL: true, ANALYTICS: true, MARKETING: true, PERSONALIZATION: true, THIRD_PARTY: true, } applyConsents() } function handleRejectAll(): void { _consents = { ESSENTIAL: true, FUNCTIONAL: false, ANALYTICS: false, MARKETING: false, PERSONALIZATION: false, THIRD_PARTY: false, } applyConsents() } function handleSaveSettings(): void { applyConsents() } function applyConsents(): void { storeConsents(_consents) hideBanner() _config?.onConsentChange?.(_consents) log('Consents applied:', _consents) } // ============================================================================ // Public API // ============================================================================ function init(config: BreakPilotSDKConfig): void { if (_isInitialized) { log('SDK already initialized') return } _config = config log('Initializing with config:', config) try { _client = new ComplianceClient({ apiEndpoint: config.apiEndpoint, apiKey: config.apiKey, tenantId: config.tenantId ?? '', }) // Check for stored consents const storedConsents = getStoredConsents() if (storedConsents) { _consents = storedConsents log('Loaded stored consents:', _consents) config.onConsentChange?.(storedConsents) } else if (config.autoInjectBanner !== false) { // Show banner if no stored consents showMainBanner() } _isInitialized = true config.onReady?.() log('SDK initialized successfully') } catch (error) { const err = error instanceof Error ? error : new Error(String(error)) config.onError?.(err) log('SDK initialization failed:', err) } } function getConsents(): Record { return { ..._consents } } function updateConsent(purpose: ConsentPurpose, granted: boolean): void { if (purpose === 'ESSENTIAL') { log('Cannot change essential consent') return } _consents[purpose] = granted storeConsents(_consents) _config?.onConsentChange?.(_consents) log('Consent updated:', purpose, granted) } function showBanner(): void { showMainBanner() } function hasConsent(purpose: ConsentPurpose): boolean { return _consents[purpose] } function getClient(): ComplianceClient | null { return _client } async function submitDSR( type: DSRRequestType, email: string, name: string ): Promise<{ success: boolean; requestId?: string; error?: string }> { if (!_client) { return { success: false, error: 'SDK not initialized' } } try { // This would call the DSR endpoint log('Submitting DSR:', { type, email, name }) // In a real implementation, this would use the client to submit return { success: true, requestId: `dsr_${Date.now()}` } } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error' return { success: false, error: message } } } function destroy(): void { hideBanner() _client = null _config = null _isInitialized = false log('SDK destroyed') } // ============================================================================ // Export for IIFE // ============================================================================ export const BreakPilotSDK = { init, getConsents, updateConsent, showBanner, hideBanner, hasConsent, getClient, submitDSR, destroy, version: '0.0.1', } // Auto-attach to window for script tag usage if (typeof window !== 'undefined') { ;(window as unknown as Record).BreakPilotSDK = BreakPilotSDK } export default BreakPilotSDK