/** * Container Script Manager * Loads and manages third-party tracking scripts and pixels */ import type { SdkRemoteConfig } from './config'; /** * Identity snapshot read at the moment a third-party pixel initializes. * Aligns the browser Pixel's advanced matching with what CAPI sends server-side * (meta.js shovels all of {user_id, visitor_id, anonymous_id} into the CAPI * external_id array) — so any one of those hashes coincides between surfaces. * * `externalId` should be the SDK's distinct_id (user_id when identified, else * anonymous_id). `email` is only populated after identify(); when present, lets * the Pixel match on `em` too. */ export interface PixelIdentity { externalId?: string | null; email?: string | null; } export interface ContainerScript { id: string; name: string; type: 'inline' | 'external' | 'pixel'; content: string; trigger: 'page_load' | 'dom_ready' | 'window_load' | 'custom'; frequency: 'always' | 'once_per_page' | 'once_per_session'; enabled: boolean; conditions?: Array<{ type: string; operator: string; value: any; }>; settings?: { async?: boolean; defer?: boolean; integrity?: string; crossorigin?: string; }; } export interface PixelConfig { meta?: { enabled: boolean; pixel_id: string; enhanced_conversions?: boolean; }; google?: { enabled: boolean; tag_id: string; enhanced_conversions?: boolean; }; tiktok?: { enabled: boolean; pixel_id: string; }; whop?: { enabled: boolean; company_id: string; }; } export declare class ContainerManager { private scripts; private loadedScripts; private sessionLoadedScripts; private pixels; /** SDK runtime config from the /container-scripts `config` envelope (the * dashboard sdk_config + server-computed defaults). undefined if the worker * doesn't send it — caller then falls back to built-in defaults. */ private remoteConfig?; private workspaceId; private endpoint; private debug; private initialized; private disposed; private sandboxedIframes; private iframeCleanupTimeouts; private messageHandler; private getIdentity?; private canForward?; constructor(options: { workspaceId: string; endpoint?: string; debug?: boolean; getIdentity?: () => PixelIdentity | undefined; canForward?: () => boolean; }); /** * Initialize container and load scripts */ private forwardingAllowed; private readConfiguration; init(): Promise; /** * The SDK runtime config delivered by /container-scripts, or undefined if the * response omitted it. The SDK merges this under explicit init() options. */ getRemoteConfig(): SdkRemoteConfig | undefined; /** * Load scripts by trigger type */ private loadScriptsByTrigger; /** * Check if script should be loaded based on frequency and conditions */ private shouldLoadScript; /** * Evaluate script conditions */ private evaluateConditions; /** * Evaluate string condition */ private evaluateStringCondition; /** * Load a single script */ private loadScript; /** * Load inline JavaScript in sandboxed iframe * SECURITY: User-provided scripts run in isolated context to prevent XSS * FIXED (ISSUE-02): Added cleanup mechanism to prevent memory leaks */ private loadInlineScript; /** * Clean up a sandboxed iframe * FIXED (ISSUE-02): Prevents memory leaks from iframe accumulation */ private cleanupIframe; /** * Clean up all sandboxed iframes * FIXED (ISSUE-02): Called on SDK destroy to prevent memory leaks */ cleanupAllIframes(): void; /** * Load external JavaScript with SRI validation * SECURITY (Phase 1.2): SRI is now REQUIRED for external scripts */ private loadExternalScript; /** * Load tracking pixel */ private loadPixel; /** Initialize configured third-party pixels in the merchant's page context. */ private initializePixels; /** Initialize Whop's official first-party attribution pixel. */ private initializeWhopPixel; /** * Initialize Meta (Facebook) Pixel * * Async because we resolve SHA-256 hashes for advanced matching (Meta's * `external_id` / `em`) before calling fbq('init'). Aligning the hashes the * browser Pixel sends with what CAPI sends (meta.js shovels user_id / * visitor_id / anonymous_id into external_id[], and `em` is sha256 of the * lowercased email) is the dedup-quality lift the CAPI side can't fix alone. */ private initializeMetaPixel; /** * Initialize Google Tag */ private initializeGoogleTag; /** * Initialize TikTok Pixel */ private initializeTikTokPixel; /** * Whether the Meta Pixel is configured AND loaded (fbq present) — i.e. a Purchase * co-fire via trackToPixels() would actually reach Meta rather than silently no-op. * Used by the CC purchase-pixel dedup so its once-per-order guard isn't burned before * the pixel is live. (FSR-102) */ hasMetaPixel(): boolean; /** * Track event to all initialized pixels */ trackToPixels(eventName: string, properties?: any, eventId?: string): Promise; /** * Manually trigger a custom script */ triggerCustomScript(scriptId: string): void; /** * Get loaded scripts */ getLoadedScripts(): string[]; /** * SECURITY MODEL (Phase 1.1 Fix): * * Inline scripts run in sandboxed iframes with 'allow-scripts' only. * This prevents: * - Access to parent window/document * - Access to cookies and localStorage * - Cross-origin requests * - Popup creation * - Form submission * * External scripts MUST have SRI (Subresource Integrity) hashes. * This prevents: * - CDN compromise attacks * - Man-in-the-middle script injection * - Unauthorized script modifications * * Previous regex-based validation was removed because it's trivially bypassable. * Sandboxing provides defense-in-depth regardless of script content. */ /** * Validate script URL */ private isValidScriptUrl; /** * Sanitize event name - whitelist alphanumeric, underscore, dollar sign */ private sanitizeEventName; /** * Recursively sanitize properties object */ private sanitizeProperties; /** * Sanitize individual value */ private sanitizeValue; /** * Debug logging */ private log; } //# sourceMappingURL=container.d.ts.map