import type { ConfigUpdateOptions, Course, TapKitConfig, TapKitInitParams, TapKitInstance } from "@coxwave/tap-kit-types"; import { loadCDNLoader } from "./loader"; /** @internal Symbol for internal configuration method - matches tap-kit-core's implementation */ const TAPKIT_CONFIG_SYMBOL = Symbol.for("tapkit.#config"); /** * TapSDK - Official TapKit Web SDK */ export class TapKit implements TapKitInstance { private instance: TapKitInstance | null = null; #loading: Promise; #config: TapKitConfig; private pendingConfig?: ConfigUpdateOptions; // Queue config until instance is ready constructor(config: TapKitConfig) { this.#config = config; this.#loading = this.load(); } private async load(): Promise { console.log("[TapSDK] Starting CDN loader..."); try { await loadCDNLoader(); console.log("[TapSDK] CDN loader completed"); } catch (err) { console.error("[TapSDK] CDN loader failed:", err); throw err; } if (!window.TapKit) { console.error("[TapSDK] window.TapKit not available after loading"); throw new Error("TapKit not available after loading CDN loader"); } console.log("[TapSDK] Creating TapKit instance from window.TapKit"); this.instance = new window.TapKit(this.#config); console.log("[TapSDK] TapKit instance created successfully"); // Apply pending config if any if (this.pendingConfig) { console.log("[TapSDK] Applying pending config:", this.pendingConfig); (this.instance as any)[TAPKIT_CONFIG_SYMBOL]?.(this.pendingConfig); this.pendingConfig = undefined; } } get ready(): Promise { return this.#loading.then(() => { if (!this.instance) { throw new Error("TapKit instance not initialized"); } return this.instance.ready; }); } get events() { if (!this.instance) { throw new Error("TapKit not loaded yet. Use await sdk.ready first."); } return this.instance.events; } get isOpen(): boolean { return this.instance?.isOpen ?? false; } get isInitialized(): boolean { return this.instance?.isInitialized ?? false; } get video() { if (!this.instance) { throw new Error("TapKit not loaded yet. Use await sdk.ready first."); } return this.instance.video; } async init(params: TapKitInitParams): Promise<() => void> { await this.#loading; return await this.instance!.init(params); } destroy(): void { if (this.instance) { this.instance.destroy(); this.instance = null; } } show(): void { if (!this.instance) { console.warn("[TapSDK] show() called before initialization"); return; } this.instance.show(); } hide(): void { if (!this.instance) { console.warn("[TapSDK] hide() called before initialization"); return; } this.instance.hide(); } setCourse(course: Partial & { courseId: string; clipId: string }): void { if (!this.instance) { console.warn("[TapSDK] setCourse() called before initialization"); return; } this.instance.setCourse(course); } setExternalReferenceId(id?: string): void { if (!this.instance) { console.warn("[TapSDK] setExternalReferenceId() called before initialization"); return; } this.instance.setExternalReferenceId(id); } setExternalReferenceData(data?: string): void { if (!this.instance) { console.warn("[TapSDK] setExternalReferenceData() called before initialization"); return; } this.instance.setExternalReferenceData(data); } sendContext(contextType: string, payload: Record): void { if (!this.instance) { console.warn("[TapSDK] sendContext() called before initialization"); return; } this.instance.sendContext(contextType, payload); } [TAPKIT_CONFIG_SYMBOL](options: ConfigUpdateOptions): void { if (!this.instance) { // Queue config until instance is ready console.log("[TapSDK] Queueing config (instance not loaded yet):", options); this.pendingConfig = options; return; } (this.instance as any)[TAPKIT_CONFIG_SYMBOL]?.(options); } get [Symbol.toStringTag]() { return "TapKit"; } }