{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// TODO: add logs when debug is enabled\nconst MONOCLE_SCRIPT_URL = 'https://mcl.spur.us/d/mcl.js'\n\nexport type MonocleEvents = 'assessment' | 'error' | 'load'\n\nexport interface MonocleOptions {\n  token: string // Authentication token for Monocle API\n  initTimeout?: number // Optional timeout for script loading (default: 5000ms)\n  debug?: boolean // Optional debug flag for logging\n}\n\n/**\n * Monocle integration loader and manager.\n * Dynamically injects the Monocle script and provides methods to interact with it.\n */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nexport default class Monocle {\n  private token: string\n  private initTimeout: number // Timeout for script loading\n  private _initialized = false\n  private _readyPromise: Promise<void> | null = null // Promise resolving when script is ready\n  private _script: HTMLScriptElement | null = null // <script> element reference\n  private _monocle: any = null // Global MCL object once loaded\n  private _eventTarget: EventTarget | null = null // EventTarget for custom events\n  private _handlers = new Map<\n    MonocleEvents,\n    Array<{ original: (detail: any) => void; wrapper: EventListener }>\n  >()\n  private _debug: boolean // Debug mode flag\n\n  /**\n   * @param options Configuration options, requiring a valid token\n   * @throws Error if no token is provided\n   */\n  constructor(options: MonocleOptions) {\n    if (!options.token) {\n      throw new Error('[Monocle] No token provided')\n    }\n    this.token = options.token\n    this.initTimeout = options.initTimeout || 5000 // Default timeout for script loading\n    if (options.debug) {\n      console.warn('[Monocle] Debug mode enabled')\n    }\n    this._debug = options.debug || false\n    this._eventTarget = new EventTarget()\n  }\n\n  /**\n   * Dispatches a custom Monocle event on the internal EventTarget.\n   */\n  private _dispatch(event: MonocleEvents, detail?: any) {\n    this._eventTarget?.dispatchEvent(new CustomEvent(event, { detail }))\n  }\n\n  /**\n   * Load the Monocle script into the document.\n   * Returns a promise that resolves when the script is loaded or rejects on failure or timeout.\n   */\n  public init(): Promise<void> {\n    if (typeof window === 'undefined') {\n      return Promise.reject(new Error('[Monocle] init() not supported in SSR'))\n    }\n\n    // If already initialized, return the existing promise\n    if (this._initialized) {\n      if (this._debug) {\n        console.warn('[Monocle] already initialized, init() ignored')\n      }\n      // To share the same loading promise:\n      return this._readyPromise as Promise<void>\n    }\n\n    this._initialized = true\n\n    // Creates and stores the static promise\n    this._readyPromise = new Promise((resolve, reject) => {\n      // Timer of timeout\n      const timer = setTimeout(() => {\n        // cleanup script\n        if (this._script?.parentNode) {\n          document.head.removeChild(this._script)\n        }\n        this._initialized = false\n        this._readyPromise = null\n\n        const err = new Error(`[Monocle] init() timeout after ${this.initTimeout} ms`)\n        this._dispatch('error', err)\n        reject(err)\n      }, this.initTimeout)\n\n      ;(window as any)._onAssessment = (jwt: string) => {\n        clearTimeout(timer)\n        this._dispatch('assessment', jwt)\n        // close init() promise\n        resolve()\n      }\n\n      // Script injection\n      const script = document.createElement('script') as HTMLScriptElement\n      // Only assign if not pre-set (allows tests to inject stub)\n      if (!this._script) {\n        this._script = script\n      }\n      script.id = '_mcl'\n      script.async = true\n      script.defer = true\n      script.src = `${MONOCLE_SCRIPT_URL}?tk=${encodeURIComponent(this.token)}`\n\n      script.addEventListener('load', () => {\n        // Store the global MCL object reference\n        this._monocle = (window as any).MCL\n        this._dispatch('load')\n        // resolve() to close init() promise at the end of the script loading, assessment is not yet available\n      })\n\n      script.addEventListener('error', () => {\n        clearTimeout(timer)\n        // Cleanup on failure\n        try {\n          document.head.removeChild(script)\n        } catch {\n          // ignore if not present\n        }\n        this._initialized = false\n        this._readyPromise = null\n\n        const err = new Error('[Monocle] Failed to load script')\n        this._dispatch('error', err)\n        reject(err)\n      })\n\n      script.setAttribute('onassessment', '_onAssessment')\n      script.setAttribute('onbundle', '_onAssessment')\n\n      document.head.appendChild(script)\n    })\n\n    return this._readyPromise\n  }\n\n  /**\n   * Returns the assessment JWT from Monocle.\n   * This method should only be called after the script is loaded and initialized.\n   * @throws Error if called on the server side or if no data is returned\n   */\n  public async getAssessment(): Promise<string> {\n    if (typeof window === 'undefined') {\n      throw new Error('[Monocle] getAssessment() is not available on the server side')\n    }\n\n    // Ensure the script is injected and MCL is defined\n    await this.init()\n\n    if (!this._monocle) {\n      throw new Error('[Monocle] MCL is not defined')\n    }\n\n    try {\n      await this._monocle.refresh()\n      const assessment = (await this._monocle.getAssessment()) as string | null\n      if (assessment) {\n        return assessment\n      }\n    } catch (err: any) {\n      this._dispatch('error', err)\n      throw err\n    }\n\n    const error = new Error('[Monocle] No data returned')\n    this._dispatch('error', error)\n    throw error\n  }\n\n  /**\n   * Register an event listener for Monocle events.\n   */\n  public on(event: MonocleEvents, handler: (detail: any) => void): void {\n    if (typeof window === 'undefined') return\n    if (!this._eventTarget) this._eventTarget = new EventTarget()\n\n    // Prevent duplicate registrations for same handler\n    const existing = this._handlers.get(event) || []\n    if (existing.some((e) => e.original === handler)) {\n      if (this._debug) console.warn(`[Monocle] handler already registered for ${event}`)\n      return\n    }\n\n    // Wrap handler to extract detail from CustomEvent\n    const wrapper: EventListener = (e: Event) => handler((e as CustomEvent).detail)\n    existing.push({ original: handler, wrapper })\n    this._handlers.set(event, existing)\n    this._eventTarget!.addEventListener(event, wrapper)\n  }\n\n  /**\n   * Unregister a previously added event listener.\n   */\n  public off(event: MonocleEvents, handler: (detail: any) => void): void {\n    if (typeof window === 'undefined') return\n    const list = this._handlers.get(event)\n    if (!list) return\n    // Remove all wrappers matching this handler\n    const remaining = [] as typeof list\n    list.forEach(({ original, wrapper }) => {\n      if (original === handler) {\n        this._eventTarget!.removeEventListener(event, wrapper)\n      } else {\n        remaining.push({ original, wrapper })\n      }\n    })\n    if (remaining.length) {\n      this._handlers.set(event, remaining)\n    } else {\n      this._handlers.delete(event)\n    }\n  }\n\n  /**\n   * Clean up the Monocle script and all associated resources.\n   */\n  public destroy(): void {\n    if (typeof window === 'undefined' || !this._initialized) return\n\n    // Remove the specific script instance\n    this._script?.parentNode?.removeChild(this._script)\n    // Remove any residual Monocle scripts by URL\n    document.head.querySelectorAll('script').forEach((s) => {\n      if (s.src.includes('mcl.spur.us')) s.remove()\n    })\n\n    // Remove global MCL references\n    delete (window as any)._onAssessment\n    delete (window as any).MCL\n\n    // Clean all listeners\n    if (this._eventTarget) {\n      this._handlers.forEach((arr, event) => {\n        arr.forEach(({ wrapper }) => this._eventTarget!.removeEventListener(event, wrapper))\n      })\n    }\n\n    // Reset internal state\n    this._eventTarget = null\n    this._monocle = null\n    this._script = null\n    this._handlers.clear()\n\n    // Re-authorizes a later re-init\n    this._initialized = false\n    this._readyPromise = null\n  }\n}\n"],"mappings":"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","names":[]}