@coxwave/tap-kit 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,10 +4,10 @@
4
4
 
5
5
  ## 특징
6
6
 
7
- - **React 우선 지원** - `useTapKit` hook으로 간편한 통합
8
- - **타입 안전** - 완전한 TypeScript 지원
9
- - **경량** - 스마트한 CDN 로딩
10
- - **쉬운 마이그레이션** - tap-sdk와 100% API 호환
7
+ - **React 우선 지원** - `useTapKit` hook으로 간편한 통합
8
+ - **타입 안전** - 완전한 TypeScript 지원
9
+ - **경량** - 스마트한 CDN 로딩
10
+ - **쉬운 마이그레이션** - tap-sdk와 100% API 호환
11
11
 
12
12
  ## 설치
13
13
 
@@ -17,9 +17,7 @@ npm install @coxwave/tap-kit
17
17
 
18
18
  ## React에서 사용하기 (권장)
19
19
 
20
- ### 방법 1: TapKit 컴포넌트 (선언적)
21
-
22
- 가장 간단한 방법은 `<TapKit />` 컴포넌트를 사용하는 것입니다:
20
+ `<TapKit />` 컴포넌트와 `useTapKit` hook을 함께 사용합니다:
23
21
 
24
22
  ```tsx
25
23
  'use client'; // Next.js App Router 사용 시
@@ -48,126 +46,27 @@ function App() {
48
46
  }
49
47
  ```
50
48
 
51
- ### 방법 2: useTapKit Hook (고급)
52
-
53
- 직접 Web Component를 제어하려면 hook만 사용할 수 있습니다:
54
-
55
- ```tsx
56
- 'use client'; // Next.js App Router 사용 시
57
-
58
- import { useTapKit } from '@coxwave/tap-kit/react';
59
-
60
- function App() {
61
- const { elementRef, show, hide, isReady, error } = useTapKit({
62
- apiKey: 'your-api-key',
63
- userId: 'user-123',
64
- courseId: 'course-456',
65
- clipId: 'clip-789',
66
- });
67
-
68
- if (error) {
69
- return <div>에러 발생: {error.message}</div>;
70
- }
71
-
72
- return (
73
- <div>
74
- <button onClick={show} disabled={!isReady}>AI 튜터 열기</button>
75
- <button onClick={hide}>AI 튜터 닫기</button>
76
- <div ref={elementRef} />
77
- </div>
78
- );
79
- }
80
- ```
81
-
82
- ### 이벤트 핸들러
83
-
84
- TapKit은 다양한 이벤트 핸들러를 지원합니다:
85
-
86
- ```tsx
87
- const tapkit = useTapKit({
88
- apiKey: 'your-api-key',
89
-
90
- // SDK 준비 완료
91
- onReady: () => {
92
- console.log('TapKit이 준비되었습니다!');
93
- },
94
-
95
- // 에러 발생
96
- onError: (error) => {
97
- console.error('TapKit 에러:', error);
98
- },
99
-
100
- // 타임라인 탐색 (사용자가 영상 특정 구간으로 이동 요청)
101
- onTimelineSeek: (clipPlayHead, clipId) => {
102
- console.log(`${clipId}의 ${clipPlayHead}초로 이동`);
103
- // 여기서 비디오 플레이어를 제어할 수 있습니다
104
- videoPlayer.seekTo(clipPlayHead);
105
- },
106
-
107
- // 알람 표시 (AI가 중요 포인트 강조)
108
- onAlarmFadeIn: (messageInfo) => {
109
- console.log('알람 표시:', messageInfo);
110
- },
111
- });
112
- ```
113
-
114
- ### 동적 코스 변경
115
-
116
- ```tsx
117
- function VideoPlayer({ videoId }) {
118
- const tapkit = useTapKit({
119
- apiKey: 'your-api-key',
120
- userId: 'user-123',
121
- });
122
-
123
- // 비디오가 변경될 때 코스 업데이트
124
- useEffect(() => {
125
- if (tapkit.isReady) {
126
- tapkit.setCourse({
127
- courseId: 'course-456',
128
- clipId: videoId,
129
- clipPlayHead: 0,
130
- });
131
- }
132
- }, [videoId, tapkit]);
133
-
134
- return <TapKit control={tapkit.control} />;
135
- }
136
- ```
137
-
138
- ### Ref로 직접 제어
139
-
140
- TapKit 컴포넌트와 useTapKit hook 모두 ref를 통해 Web Component에 직접 접근할 수 있습니다:
49
+ ## Vanilla JS에서 사용하기
141
50
 
142
- ```tsx
143
- function App() {
144
- const tapkit = useTapKit({ apiKey: 'your-api-key' });
145
- const tapkitRef = useRef<TapKitElement>(null);
51
+ npm 패키지를 import하면 `<tap-kit>` Web Component를 사용할 수 있습니다:
146
52
 
147
- const handleCustomAction = () => {
148
- // 방법 1: hook의 메서드 사용
149
- tapkit.show();
53
+ ```javascript
54
+ import '@coxwave/tap-kit';
150
55
 
151
- // 방법 2: 컴포넌트 ref 사용
152
- tapkitRef.current?.show();
56
+ // Web Component 생성
57
+ const kit = document.createElement('tap-kit');
58
+ kit.apiKey = 'your-api-key'; // Property로 설정 (보안)
59
+ kit.userId = 'user-123';
60
+ kit.courseId = 'course-456';
61
+ kit.clipId = 'clip-789';
153
62
 
154
- // 방법 3: hook의 ref 사용
155
- tapkit.ref.current?.show();
156
- };
63
+ document.body.appendChild(kit);
157
64
 
158
- return (
159
- <>
160
- <button onClick={handleCustomAction}>열기</button>
161
- <TapKit ref={tapkitRef} control={tapkit.control} />
162
- </>
163
- );
164
- }
65
+ // 초기화 대기
66
+ await kit.ready;
67
+ kit.show();
165
68
  ```
166
69
 
167
- ## Vanilla JS에서 사용하기
168
-
169
- Vanilla JavaScript로 사용하려면 [TapKit 시작하기](https://edutap-ai-docs.vercel.app/docs/sdk/npm)를 참고하세요.
170
-
171
70
  ## CDN에서 사용하기
172
71
 
173
72
  npm 없이 CDN으로 직접 사용하려면 [CDN 사용 가이드](https://edutap-ai-docs.vercel.app/docs/sdk/cdn)를 참고하세요.
@@ -180,7 +79,6 @@ npm 없이 CDN으로 직접 사용하려면 [CDN 사용 가이드](https://eduta
180
79
 
181
80
  **Props:**
182
81
  - `control: TapKitControl` - useTapKit hook에서 반환된 control 객체 (필수)
183
- - `containerId?: string` - 커스텀 컨테이너 엘리먼트 ID (선택)
184
82
  - `style?: CSSProperties` - 인라인 스타일 (선택)
185
83
  - `className?: string` - CSS 클래스 (선택)
186
84
  - `ref?: Ref<TapKitElement>` - Web Component에 대한 ref (선택)
@@ -217,29 +115,118 @@ TapKit 인스턴스를 생성하고 관리하는 React Hook입니다.
217
115
  - `options.onAlarmFadeIn?: (messageInfo: unknown) => void` - 알람 표시 이벤트
218
116
 
219
117
  **Returns:**
118
+ - `control: TapKitControl` - TapKit 컴포넌트에 전달할 control 객체
119
+ - `isReady: boolean` - 로딩 완료 여부
120
+ - `error: Error | null` - 에러 객체
121
+ - `show: () => void` - 채팅 열기
122
+ - `hide: () => void` - 채팅 닫기
123
+ - `setCourse: (course) => void` - 강의 정보 변경
124
+ - `video: { bind, unbind }` - 비디오 동기화 컨트롤
220
125
 
221
- **인스턴스 접근:**
222
- - `element: TapKitElement | null` - Web Component 엘리먼트 (직접 접근용)
223
- - `ref: RefObject<TapKitElement | null>` - 엘리먼트 ref 객체
224
- - `elementRef: RefCallback<HTMLDivElement>` - 컨테이너 ref (Hook 패턴 전용)
126
+ ### 사용 예제
225
127
 
226
- **제어 객체:**
227
- - `control: TapKitControl` - TapKit 컴포넌트에 전달할 control 객체
128
+ #### 기본 사용
228
129
 
229
- **상태:**
230
- - `isReady: boolean` - TapKit 준비 완료 여부
231
- - `error: Error | null` - 초기화 에러
130
+ ```tsx
131
+ 'use client';
232
132
 
233
- **메서드:**
234
- - `show: () => void` - 채팅 인터페이스 표시
235
- - `hide: () => void` - 채팅 인터페이스 숨김
236
- - `setCourse: (course) => void` - 코스 정보 업데이트
133
+ import { TapKit, useTapKit } from '@coxwave/tap-kit/react';
134
+
135
+ function App() {
136
+ const tapkit = useTapKit({
137
+ apiKey: 'your-api-key',
138
+ userId: 'user-123',
139
+ courseId: 'course-456',
140
+ clipId: 'clip-789',
141
+ });
142
+
143
+ return <TapKit control={tapkit.control} style={{ height: '600px' }} />;
144
+ }
145
+ ```
146
+
147
+ #### 강의 변경
148
+
149
+ ```tsx
150
+ 'use client';
151
+
152
+ import { TapKit, useTapKit } from '@coxwave/tap-kit/react';
153
+
154
+ function App() {
155
+ const tapkit = useTapKit({
156
+ apiKey: 'your-api-key',
157
+ userId: 'user-123',
158
+ courseId: 'course-456',
159
+ clipId: 'clip-789',
160
+ });
161
+
162
+ const handleClipChange = (newClipId: string) => {
163
+ tapkit.setCourse({
164
+ courseId: 'course-456',
165
+ clipId: newClipId,
166
+ });
167
+ };
168
+
169
+ return (
170
+ <div>
171
+ <button onClick={() => handleClipChange('clip-001')}>클립 1</button>
172
+ <button onClick={() => handleClipChange('clip-002')}>클립 2</button>
173
+ <TapKit control={tapkit.control} />
174
+ </div>
175
+ );
176
+ }
177
+ ```
178
+
179
+ #### 비디오 동기화
180
+
181
+ ```tsx
182
+ 'use client';
183
+
184
+ import { useRef, useEffect } from 'react';
185
+ import { TapKit, useTapKit } from '@coxwave/tap-kit/react';
186
+
187
+ function App() {
188
+ const videoRef = useRef<HTMLVideoElement>(null);
189
+
190
+ const tapkit = useTapKit({
191
+ apiKey: 'your-api-key',
192
+ userId: 'user-123',
193
+ courseId: 'course-456',
194
+ clipId: 'clip-789',
195
+ onTimelineSeek: (clipPlayHead, clipId) => {
196
+ if (videoRef.current) {
197
+ videoRef.current.currentTime = clipPlayHead;
198
+ }
199
+ },
200
+ });
201
+
202
+ useEffect(() => {
203
+ if (!tapkit.isReady || !videoRef.current) return;
204
+
205
+ tapkit.video.bind(
206
+ {
207
+ getCurrentTime: () => videoRef.current!.currentTime,
208
+ setCurrentTime: (time) => { videoRef.current!.currentTime = time; },
209
+ },
210
+ 'clip-789'
211
+ );
212
+
213
+ return () => tapkit.video.unbind();
214
+ }, [tapkit.isReady]);
215
+
216
+ return (
217
+ <div>
218
+ <video ref={videoRef} controls />
219
+ <TapKit control={tapkit.control} />
220
+ </div>
221
+ );
222
+ }
223
+ ```
237
224
 
238
225
  ## 문서
239
226
 
240
227
  - [전체 문서](https://edutap-ai-docs.vercel.app)
241
- - [React 가이드](https://edutap-ai-docs.vercel.app/docs/sdk/npm/react)
242
- - [API Reference](https://edutap-ai-docs.vercel.app/docs/sdk/npm/api-reference)
228
+ - [React 가이드](https://edutap-ai-docs.vercel.app/docs/sdk/guides/react)
229
+ - [API Reference](https://edutap-ai-docs.vercel.app/docs/sdk/api-reference)
243
230
 
244
231
  ## tap-sdk에서 마이그레이션
245
232
 
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _coxwave_tap_kit_types from '@coxwave/tap-kit-types';
2
2
  import { TapKitInstance, TapKitConfig, TapKitInitParams, TapButtonAttributes } from '@coxwave/tap-kit-types';
3
- export { AlarmMessageInstanceType, ContainerConfig, ContainerMode, Course, ITapButtonElement, PositionType, SeekTimelineParamsType, TapButtonAttributes, TapKitConfig, TapKitConstructor, TapKitInitParams, TapKitInstance } from '@coxwave/tap-kit-types';
3
+ export { AlarmMessageInstanceType, ContainerConfig, Course, ITapButtonElement, PositionType, SeekTimelineParamsType, TapButtonAttributes, TapKitConfig, TapKitConstructor, TapKitInitParams, TapKitInstance } from '@coxwave/tap-kit-types';
4
4
 
5
5
  /**
6
6
  * TapKit - Official TapKit Web SDK
@@ -60,10 +60,14 @@ declare class TapKit implements TapKitInstance {
60
60
  */
61
61
  get ready(): Promise<void>;
62
62
  /**
63
- * Create a proxy that automatically waits for the instance to be ready
64
- * before delegating to the actual property
63
+ * Create a proxy for video API that waits for ready before delegating
65
64
  */
66
- private createReadyProxy;
65
+ private createVideoProxy;
66
+ /**
67
+ * Create events API that returns unsubscribe functions synchronously
68
+ * Queues the actual subscription until ready, but returns unsubscribe immediately
69
+ */
70
+ private createEventsProxy;
67
71
  get events(): {
68
72
  seekTimeline: (params: _coxwave_tap_kit_types.SeekTimelineParamsType) => void;
69
73
  onTimelineSeek: (callback: (clipPlayHead: number, clipId: string) => void) => () => void;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _coxwave_tap_kit_types from '@coxwave/tap-kit-types';
2
2
  import { TapKitInstance, TapKitConfig, TapKitInitParams, TapButtonAttributes } from '@coxwave/tap-kit-types';
3
- export { AlarmMessageInstanceType, ContainerConfig, ContainerMode, Course, ITapButtonElement, PositionType, SeekTimelineParamsType, TapButtonAttributes, TapKitConfig, TapKitConstructor, TapKitInitParams, TapKitInstance } from '@coxwave/tap-kit-types';
3
+ export { AlarmMessageInstanceType, ContainerConfig, Course, ITapButtonElement, PositionType, SeekTimelineParamsType, TapButtonAttributes, TapKitConfig, TapKitConstructor, TapKitInitParams, TapKitInstance } from '@coxwave/tap-kit-types';
4
4
 
5
5
  /**
6
6
  * TapKit - Official TapKit Web SDK
@@ -60,10 +60,14 @@ declare class TapKit implements TapKitInstance {
60
60
  */
61
61
  get ready(): Promise<void>;
62
62
  /**
63
- * Create a proxy that automatically waits for the instance to be ready
64
- * before delegating to the actual property
63
+ * Create a proxy for video API that waits for ready before delegating
65
64
  */
66
- private createReadyProxy;
65
+ private createVideoProxy;
66
+ /**
67
+ * Create events API that returns unsubscribe functions synchronously
68
+ * Queues the actual subscription until ready, but returns unsubscribe immediately
69
+ */
70
+ private createEventsProxy;
67
71
  get events(): {
68
72
  seekTimeline: (params: _coxwave_tap_kit_types.SeekTimelineParamsType) => void;
69
73
  onTimelineSeek: (callback: (clipPlayHead: number, clipId: string) => void) => () => void;
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var I=Object.defineProperty;var h=t=>{throw TypeError(t)};var m=(t,e,i)=>e in t?I(t,e,{enumerable:true,configurable:true,writable:true,value:i}):t[e]=i;var A=(t,e,i)=>m(t,e+"",i),D=(t,e,i)=>e.has(t)||h("Cannot "+i);var n=(t,e,i)=>(D(t,e,"read from private field"),i?i.call(t):e.get(t)),u=(t,e,i)=>e.has(t)?h("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,i),d=(t,e,i,o)=>(D(t,e,"write to private field"),e.set(t,i),i);var O="https://files.edutap.ai/tap-sdk/loader.js";function y(){return window?.__TAP_KIT_LOADER_URL__?window.__TAP_KIT_LOADER_URL__:O}function v(){return typeof window<"u"&&!!window.__TAP_KIT_CORE_URL__}function R(){return window.__TAP_KIT_CORE_URL__||""}function K(t,e,i){let o=Date.now(),r=()=>{if(window.TapKit&&window.TapKitLoaded===true){window.__TAP_KIT_LOADER_LOADED__=true,window.__TAP_KIT_LOADER_LOADING__=void 0,t();return}if(Date.now()-o>i){window.__TAP_KIT_LOADER_LOADING__=void 0,e(new Error(`TapKit loader timeout: SDK not available after ${i}ms`));return}typeof requestIdleCallback<"u"?requestIdleCallback(r,{timeout:500}):setTimeout(r,500);};return r}function E(t=4e3){if(window.__TAP_KIT_LOADER_LOADED__&&window.TapKit)return Promise.resolve();if(window.__TAP_KIT_LOADER_LOADING__)return window.__TAP_KIT_LOADER_LOADING__;let e=new Promise((i,o)=>{if(typeof document>"u"){o(new Error("TapKit requires browser environment (document is undefined)"));return}if(v()){if(window.TapKit&&window.TapKitLoaded===true){window.__TAP_KIT_LOADER_LOADED__=true,window.__TAP_KIT_LOADER_LOADING__=void 0,i();return}let p=R(),w=document.createElement("script");w.src=p,w.async=true,w.onload=()=>{window.TapKit?(window.TapKitLoaded=true,window.__TAP_KIT_LOADER_LOADED__=true,window.__TAP_KIT_LOADER_LOADING__=void 0,i()):(window.__TAP_KIT_LOADER_LOADING__=void 0,o(new Error("TapKit not available after loading local core")));},w.onerror=()=>{window.__TAP_KIT_LOADER_LOADING__=void 0,o(new Error(`Failed to load local TapKit core: ${p}`));},document.head.appendChild(w);return}let r=y(),a=document.createElement("script");a.src=r,a.async=true,a.onload=()=>{K(i,o,t)();},a.onerror=()=>{window.__TAP_KIT_LOADER_LOADING__=void 0,o(new Error(`Failed to load TapKit CDN loader: ${r}`));};let L=document.querySelector(`script[src="${r}"]`);L?(L.addEventListener("load",()=>{K(i,o,t)();}),L.addEventListener("error",()=>o(new Error(`Failed to load TapKit CDN loader: ${r}`)))):document.head.appendChild(a);});return window.__TAP_KIT_LOADER_LOADING__=e,e}var _,c,s,T,l,f=class{constructor(e){A(this,"instance",null);u(this,_);u(this,c);u(this,s,null);u(this,T);u(this,l);d(this,c,e),d(this,_,this.load());}async load(){try{if(await E(),!window.TapKit)throw new Error("TapKit not available after loading CDN loader");this.instance=new window.TapKit(n(this,c));}catch(e){throw d(this,s,e instanceof Error?e:new Error(String(e))),n(this,s)}}get loaded(){return n(this,_).then(()=>{if(n(this,s))throw n(this,s);if(!this.instance)throw new Error(`TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${n(this,c).apiKey}`)})}get ready(){return n(this,_).then(()=>{if(n(this,s))throw n(this,s);if(!this.instance)throw new Error(`TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${n(this,c).apiKey}`);return this.instance.ready})}createReadyProxy(e){return new Proxy({},{get:(i,o)=>(...r)=>this.ready.then(()=>{let a=this.instance?.[e]?.[o];return typeof a=="function"?a(...r):a})})}get events(){return n(this,T)||d(this,T,this.createReadyProxy("events")),n(this,T)}get isOpen(){return this.instance?.isOpen??false}get isInitialized(){return this.instance?.isInitialized??false}get video(){return n(this,l)||d(this,l,this.createReadyProxy("video")),n(this,l)}async init(e){if(await n(this,_),!this.instance)throw new Error("TapKit instance not available after loading");return await this.instance.init(e)}destroy(){this.instance&&(this.instance.destroy(),this.instance=null),d(this,T,void 0),d(this,l,void 0);}show(){this.instance&&this.instance.show();}hide(){this.instance&&this.instance.hide();}get[Symbol.toStringTag](){return "TapKit"}};_=new WeakMap,c=new WeakMap,s=new WeakMap,T=new WeakMap,l=new WeakMap;
2
- exports.TapKit=f;exports.default=f;//# sourceMappingURL=index.js.map
1
+ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var I=Object.defineProperty;var L=i=>{throw TypeError(i)};var m=(i,e,t)=>e in i?I(i,e,{enumerable:true,configurable:true,writable:true,value:t}):i[e]=t;var A=(i,e,t)=>m(i,e+"",t),D=(i,e,t)=>e.has(i)||L("Cannot "+t);var r=(i,e,t)=>(D(i,e,"read from private field"),t?t.call(i):e.get(i)),T=(i,e,t)=>e.has(i)?L("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(i):e.set(i,t),s=(i,e,t,n)=>(D(i,e,"write to private field"),e.set(i,t),t);var v="https://files.edutap.ai/tap-sdk/loader.js";function O(){return window?.__TAP_KIT_LOADER_URL__?window.__TAP_KIT_LOADER_URL__:v}function y(){return typeof window<"u"&&!!window.__TAP_KIT_CORE_URL__}function P(){return window.__TAP_KIT_CORE_URL__||""}function K(i,e,t){let n=Date.now(),a=()=>{if(window.TapKit&&window.TapKitLoaded===true){window.__TAP_KIT_LOADER_LOADED__=true,window.__TAP_KIT_LOADER_LOADING__=void 0,i();return}if(Date.now()-n>t){window.__TAP_KIT_LOADER_LOADING__=void 0,e(new Error(`TapKit loader timeout: SDK not available after ${t}ms`));return}typeof requestIdleCallback<"u"?requestIdleCallback(a,{timeout:500}):setTimeout(a,500);};return a}function E(i=4e3){if(window.__TAP_KIT_LOADER_LOADED__&&window.TapKit)return Promise.resolve();if(window.__TAP_KIT_LOADER_LOADING__)return window.__TAP_KIT_LOADER_LOADING__;let e=new Promise((t,n)=>{if(typeof document>"u"){n(new Error("TapKit requires browser environment (document is undefined)"));return}if(y()){if(window.TapKit&&window.TapKitLoaded===true){window.__TAP_KIT_LOADER_LOADED__=true,window.__TAP_KIT_LOADER_LOADING__=void 0,t();return}let p=P(),w=document.createElement("script");w.src=p,w.async=true,w.onload=()=>{window.TapKit?(window.TapKitLoaded=true,window.__TAP_KIT_LOADER_LOADED__=true,window.__TAP_KIT_LOADER_LOADING__=void 0,t()):(window.__TAP_KIT_LOADER_LOADING__=void 0,n(new Error("TapKit not available after loading local core")));},w.onerror=()=>{window.__TAP_KIT_LOADER_LOADING__=void 0,n(new Error(`Failed to load local TapKit core: ${p}`));},document.head.appendChild(w);return}let a=O(),u=document.createElement("script");u.src=a,u.async=true,u.onload=()=>{K(t,n,i)();},u.onerror=()=>{window.__TAP_KIT_LOADER_LOADING__=void 0,n(new Error(`Failed to load TapKit CDN loader: ${a}`));};let f=document.querySelector(`script[src="${a}"]`);f?(f.addEventListener("load",()=>{K(t,n,i)();}),f.addEventListener("error",()=>n(new Error(`Failed to load TapKit CDN loader: ${a}`)))):document.head.appendChild(u);});return window.__TAP_KIT_LOADER_LOADING__=e,e}var d,_,o,c,l,h=class{constructor(e){A(this,"instance",null);T(this,d);T(this,_);T(this,o,null);T(this,c);T(this,l);s(this,_,e),s(this,d,this.load());}async load(){try{if(await E(),!window.TapKit)throw new Error("TapKit not available after loading CDN loader");this.instance=new window.TapKit(r(this,_));}catch(e){throw s(this,o,e instanceof Error?e:new Error(String(e))),r(this,o)}}get loaded(){return r(this,d).then(()=>{if(r(this,o))throw r(this,o);if(!this.instance)throw new Error(`TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${r(this,_).apiKey}`)})}get ready(){return r(this,d).then(()=>{if(r(this,o))throw r(this,o);if(!this.instance)throw new Error(`TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${r(this,_).apiKey}`);return this.instance.ready})}createVideoProxy(){return {bind:(e,t)=>{this.ready.then(()=>{this.instance?.video.bind(e,t);});},unbind:()=>{this.instance?.video?.unbind();}}}createEventsProxy(){return {seekTimeline:e=>{this.ready.then(()=>{this.instance?.events.seekTimeline(e);});},onTimelineSeek:e=>{let t,n=false;return this.ready.then(()=>{n||(t=this.instance?.events.onTimelineSeek(e));}),()=>{n=true,t?.();}},onAlarmFadeIn:e=>{let t,n=false;return this.ready.then(()=>{n||(t=this.instance?.events.onAlarmFadeIn(e));}),()=>{n=true,t?.();}}}}get events(){return r(this,c)||s(this,c,this.createEventsProxy()),r(this,c)}get isOpen(){return this.instance?.isOpen??false}get isInitialized(){return this.instance?.isInitialized??false}get video(){return r(this,l)||s(this,l,this.createVideoProxy()),r(this,l)}async init(e){if(await r(this,d),!this.instance)throw new Error("TapKit instance not available after loading");return await this.instance.init(e)}destroy(){this.instance&&(this.instance.destroy(),this.instance=null),s(this,c,void 0),s(this,l,void 0);}show(){this.instance&&this.instance.show();}hide(){this.instance&&this.instance.hide();}get[Symbol.toStringTag](){return "TapKit"}};d=new WeakMap,_=new WeakMap,o=new WeakMap,c=new WeakMap,l=new WeakMap;
2
+ exports.TapKit=h;exports.default=h;//# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/loader.ts","../src/kit.ts"],"names":["DEFAULT_CDN_LOADER_URL","getLoaderURL","isLocalCoreMode","getLocalCoreURL","createSDKChecker","resolve","reject","timeoutMs","startTime","checkSDK","loadCDNLoader","loadingPromise","coreURL","script","loaderURL","existingScript","_loading","_config","_loadError","_eventsProxy","_videoProxy","TapKit","config","__publicField","__privateAdd","__privateSet","__privateGet","err","propertyName","_target","prop","args","method","params"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * CDN loader for TapKit\n * Dynamically loads the TapKit SDK from CDN\n *\n * For local testing, you can override the loader URL:\n * window.__TAP_KIT_LOADER_URL__ = '/tap-kit-core/loader.js';\n *\n * For local development (bypass loader, load IIFE directly):\n * window.__TAP_KIT_CORE_URL__ = '/packages/tap-kit-core/dist/index.global.js';\n */\n\n// Build-time constant injected by tsup define\ndeclare const __DEFAULT_CDN_LOADER_URL__: string;\n\nconst DEFAULT_CDN_LOADER_URL =\n typeof __DEFAULT_CDN_LOADER_URL__ !== \"undefined\"\n ? __DEFAULT_CDN_LOADER_URL__\n : \"https://files.edutap.ai/tap-sdk/loader.js\";\nconst DEFAULT_TIMEOUT_MS = 4000; // 4 seconds total timeout\nconst IDLE_CALLBACK_TIMEOUT_MS = 500; // 500ms for requestIdleCallback\n\n/**\n * Get the loader URL from window override or default CDN\n */\nfunction getLoaderURL(): string {\n return window?.__TAP_KIT_LOADER_URL__\n ? window.__TAP_KIT_LOADER_URL__\n : DEFAULT_CDN_LOADER_URL;\n}\n\n/**\n * Check if local core mode is enabled\n * When __TAP_KIT_CORE_URL__ is set, directly load the IIFE bundle\n * This bypasses the loader.js and loads tap-kit-core directly\n */\nfunction isLocalCoreMode(): boolean {\n return typeof window !== \"undefined\" && !!window.__TAP_KIT_CORE_URL__;\n}\n\n/**\n * Get the local core URL\n */\nfunction getLocalCoreURL(): string {\n return window.__TAP_KIT_CORE_URL__ || \"\";\n}\n\n/**\n * Creates a SDK checker function with timeout and retry logic\n * Uses requestIdleCallback to avoid blocking browser rendering\n * @param resolve - Promise resolve function\n * @param reject - Promise reject function\n * @param timeoutMs - Maximum time to wait for SDK to load (milliseconds)\n * @returns Checker function to be called repeatedly\n */\nfunction createSDKChecker(\n resolve: (value: void | PromiseLike<void>) => void,\n reject: (reason?: unknown) => void,\n timeoutMs: number,\n): () => void {\n const startTime = Date.now();\n\n const checkSDK = (): void => {\n // Check if real TapKit is loaded (not just stub)\n // Stub has TapKitLoaded flag set to true by loader.js after real SDK loads\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const elapsed = Date.now() - startTime;\n\n // Check if exceeded timeout\n if (elapsed > timeoutMs) {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(\n new Error(\n `TapKit loader timeout: SDK not available after ${timeoutMs}ms`,\n ),\n );\n return;\n }\n\n // Use requestIdleCallback for better performance\n // Falls back to setTimeout if not available\n if (typeof requestIdleCallback !== \"undefined\") {\n requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });\n } else {\n setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);\n }\n };\n\n return checkSDK;\n}\n\n/**\n * Loads the CDN loader script\n * The loader will then fetch versions.json and load the appropriate SDK version\n *\n * If __TAP_KIT_CORE_URL__ is set, bypasses loader and loads IIFE directly\n *\n * @param timeoutMs - Maximum time to wait for SDK to load (default: 4000ms)\n * @returns Promise that resolves when SDK is loaded\n * @throws {Error} If loader fails to load or times out\n */\nexport function loadCDNLoader(\n timeoutMs: number = DEFAULT_TIMEOUT_MS,\n): Promise<void> {\n // If already loaded, return immediately\n if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {\n return Promise.resolve();\n }\n\n // If currently loading, return the existing promise\n if (window.__TAP_KIT_LOADER_LOADING__) {\n return window.__TAP_KIT_LOADER_LOADING__;\n }\n\n // Create loading promise\n const loadingPromise = new Promise<void>((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(\n new Error(\n \"TapKit requires browser environment (document is undefined)\",\n ),\n );\n return;\n }\n\n // Local core mode: Load IIFE directly\n if (isLocalCoreMode()) {\n // Check if TapKit is already loaded (from other pages in playground)\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const coreURL = getLocalCoreURL();\n\n const script = document.createElement(\"script\");\n script.src = coreURL;\n script.async = true;\n\n script.onload = () => {\n // IIFE directly sets window.TapKit\n // Set the loaded flag manually since we bypass loader.js\n if (window.TapKit) {\n window.TapKitLoaded = true;\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n } else {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(\"TapKit not available after loading local core\"));\n }\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load local TapKit core: ${coreURL}`));\n };\n\n document.head.appendChild(script);\n return;\n }\n\n // CDN mode: Load loader.js\n const loaderURL = getLoaderURL();\n const script = document.createElement(\"script\");\n script.src = loaderURL;\n script.async = true;\n\n script.onload = () => {\n // The loader script will load the actual SDK\n // We need to wait a bit for the loader to fetch and load the SDK\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));\n };\n\n // Check if script already exists\n const existingScript = document.querySelector(`script[src=\"${loaderURL}\"]`);\n\n if (existingScript) {\n // Script already added but not yet loaded\n existingScript.addEventListener(\"load\", () => {\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n });\n existingScript.addEventListener(\"error\", () =>\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)),\n );\n } else {\n document.head.appendChild(script);\n }\n });\n\n window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;\n return loadingPromise;\n}\n","import type {\n TapKitConfig,\n TapKitInitParams,\n TapKitInstance,\n} from \"@coxwave/tap-kit-types\";\nimport { loadCDNLoader } from \"./loader\";\n\n/**\n * TapKit - Official TapKit Web SDK\n *\n * @example\n * ```typescript\n * import TapKit from \"@coxwave/tap-kit\";\n *\n * const kit = new TapKit({ apiKey: 'your-api-key' });\n *\n * await kit.init({\n * buttonId: 'tap-button',\n * course: {\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * },\n * container: {\n * position: { top: '64px', right: '32px' },\n * width: '360px',\n * height: 'calc(100% - 128px)',\n * },\n * });\n * ```\n */\nexport class TapKit implements TapKitInstance {\n private instance: TapKitInstance | null = null;\n #loading: Promise<void>;\n #config: TapKitConfig;\n #loadError: Error | null = null;\n #eventsProxy?: TapKitInstance[\"events\"]; // Cached proxy for events API\n #videoProxy?: TapKitInstance[\"video\"]; // Cached proxy for video API\n\n constructor(config: TapKitConfig) {\n this.#config = config;\n this.#loading = this.load();\n }\n\n private async load(): Promise<void> {\n try {\n await loadCDNLoader();\n\n if (!window.TapKit) {\n throw new Error(\"TapKit not available after loading CDN loader\");\n }\n\n this.instance = new window.TapKit(this.#config);\n } catch (err) {\n this.#loadError = err instanceof Error ? err : new Error(String(err));\n throw this.#loadError;\n }\n }\n\n /**\n * Promise that resolves when CDN is loaded and TapKit instance is available\n * (ready to call init())\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.loaded; // CDN downloaded, window.TapKit available\n * await kit.init({...}); // Now safe to call init()\n * ```\n *\n * @see ready - For waiting until AFTER init() completes (iframe initialized)\n */\n get loaded(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${this.#config.apiKey}`,\n );\n }\n });\n }\n\n /**\n * Promise that resolves when SDK is fully initialized\n * (after init() is called and iframe is ready)\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.init({...});\n * await kit.ready; // iframe is fully initialized\n * ```\n *\n * @see loaded - For waiting until CDN load completes (before init)\n */\n get ready(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${this.#config.apiKey}`,\n );\n }\n return this.instance.ready;\n });\n }\n\n /**\n * Create a proxy that automatically waits for the instance to be ready\n * before delegating to the actual property\n */\n private createReadyProxy<T extends object>(\n propertyName: \"events\" | \"video\",\n ): T {\n return new Proxy({} as T, {\n get: (_target, prop: string) => {\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic method signature unknown at compile time\n return (...args: any[]) => {\n return this.ready.then(() => {\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic property access requires any\n const method = (this.instance?.[propertyName] as any)?.[prop];\n if (typeof method === \"function\") {\n return method(...args);\n }\n return method;\n });\n };\n },\n }) as T;\n }\n\n get events() {\n // Cache proxy to avoid creating new Proxy object on every access\n if (!this.#eventsProxy) {\n this.#eventsProxy =\n this.createReadyProxy<TapKitInstance[\"events\"]>(\"events\");\n }\n return this.#eventsProxy;\n }\n\n get isOpen(): boolean {\n return this.instance?.isOpen ?? false;\n }\n\n get isInitialized(): boolean {\n return this.instance?.isInitialized ?? false;\n }\n\n get video() {\n // Cache proxy to avoid creating new Proxy object on every access\n if (!this.#videoProxy) {\n this.#videoProxy =\n this.createReadyProxy<TapKitInstance[\"video\"]>(\"video\");\n }\n return this.#videoProxy;\n }\n\n async init(params: TapKitInitParams): Promise<() => void> {\n await this.#loading;\n if (!this.instance) {\n throw new Error(\"TapKit instance not available after loading\");\n }\n return await this.instance.init(params);\n }\n\n destroy(): void {\n if (this.instance) {\n this.instance.destroy();\n this.instance = null;\n }\n // Clear proxy references to prevent memory leaks\n this.#eventsProxy = undefined;\n this.#videoProxy = undefined;\n }\n\n show(): void {\n if (this.instance) {\n this.instance.show();\n }\n }\n\n hide(): void {\n if (this.instance) {\n this.instance.hide();\n }\n }\n\n get [Symbol.toStringTag]() {\n return \"TapKit\";\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/loader.ts","../src/kit.ts"],"names":["DEFAULT_CDN_LOADER_URL","getLoaderURL","isLocalCoreMode","getLocalCoreURL","createSDKChecker","resolve","reject","timeoutMs","startTime","checkSDK","loadCDNLoader","loadingPromise","coreURL","script","loaderURL","existingScript","_loading","_config","_loadError","_eventsProxy","_videoProxy","TapKit","config","__publicField","__privateAdd","__privateSet","__privateGet","err","clipId","params","callback","unsubscribe","cancelled","handler"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * CDN loader for TapKit\n * Dynamically loads the TapKit SDK from CDN\n *\n * For local testing, you can override the loader URL:\n * window.__TAP_KIT_LOADER_URL__ = '/tap-kit-core/loader.js';\n *\n * For local development (bypass loader, load IIFE directly):\n * window.__TAP_KIT_CORE_URL__ = '/packages/tap-kit-core/dist/index.global.js';\n */\n\n// Build-time constant injected by tsup define\ndeclare const __DEFAULT_CDN_LOADER_URL__: string;\n\nconst DEFAULT_CDN_LOADER_URL =\n typeof __DEFAULT_CDN_LOADER_URL__ !== \"undefined\"\n ? __DEFAULT_CDN_LOADER_URL__\n : \"https://files.edutap.ai/tap-sdk/loader.js\";\nconst DEFAULT_TIMEOUT_MS = 4000; // 4 seconds total timeout\nconst IDLE_CALLBACK_TIMEOUT_MS = 500; // 500ms for requestIdleCallback\n\n/**\n * Get the loader URL from window override or default CDN\n */\nfunction getLoaderURL(): string {\n return window?.__TAP_KIT_LOADER_URL__\n ? window.__TAP_KIT_LOADER_URL__\n : DEFAULT_CDN_LOADER_URL;\n}\n\n/**\n * Check if local core mode is enabled\n * When __TAP_KIT_CORE_URL__ is set, directly load the IIFE bundle\n * This bypasses the loader.js and loads tap-kit-core directly\n */\nfunction isLocalCoreMode(): boolean {\n return typeof window !== \"undefined\" && !!window.__TAP_KIT_CORE_URL__;\n}\n\n/**\n * Get the local core URL\n */\nfunction getLocalCoreURL(): string {\n return window.__TAP_KIT_CORE_URL__ || \"\";\n}\n\n/**\n * Creates a SDK checker function with timeout and retry logic\n * Uses requestIdleCallback to avoid blocking browser rendering\n * @param resolve - Promise resolve function\n * @param reject - Promise reject function\n * @param timeoutMs - Maximum time to wait for SDK to load (milliseconds)\n * @returns Checker function to be called repeatedly\n */\nfunction createSDKChecker(\n resolve: (value: void | PromiseLike<void>) => void,\n reject: (reason?: unknown) => void,\n timeoutMs: number,\n): () => void {\n const startTime = Date.now();\n\n const checkSDK = (): void => {\n // Check if real TapKit is loaded (not just stub)\n // Stub has TapKitLoaded flag set to true by loader.js after real SDK loads\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const elapsed = Date.now() - startTime;\n\n // Check if exceeded timeout\n if (elapsed > timeoutMs) {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(\n new Error(\n `TapKit loader timeout: SDK not available after ${timeoutMs}ms`,\n ),\n );\n return;\n }\n\n // Use requestIdleCallback for better performance\n // Falls back to setTimeout if not available\n if (typeof requestIdleCallback !== \"undefined\") {\n requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });\n } else {\n setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);\n }\n };\n\n return checkSDK;\n}\n\n/**\n * Loads the CDN loader script\n * The loader will then fetch versions.json and load the appropriate SDK version\n *\n * If __TAP_KIT_CORE_URL__ is set, bypasses loader and loads IIFE directly\n *\n * @param timeoutMs - Maximum time to wait for SDK to load (default: 4000ms)\n * @returns Promise that resolves when SDK is loaded\n * @throws {Error} If loader fails to load or times out\n */\nexport function loadCDNLoader(\n timeoutMs: number = DEFAULT_TIMEOUT_MS,\n): Promise<void> {\n // If already loaded, return immediately\n if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {\n return Promise.resolve();\n }\n\n // If currently loading, return the existing promise\n if (window.__TAP_KIT_LOADER_LOADING__) {\n return window.__TAP_KIT_LOADER_LOADING__;\n }\n\n // Create loading promise\n const loadingPromise = new Promise<void>((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(\n new Error(\n \"TapKit requires browser environment (document is undefined)\",\n ),\n );\n return;\n }\n\n // Local core mode: Load IIFE directly\n if (isLocalCoreMode()) {\n // Check if TapKit is already loaded (from other pages in playground)\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const coreURL = getLocalCoreURL();\n\n const script = document.createElement(\"script\");\n script.src = coreURL;\n script.async = true;\n\n script.onload = () => {\n // IIFE directly sets window.TapKit\n // Set the loaded flag manually since we bypass loader.js\n if (window.TapKit) {\n window.TapKitLoaded = true;\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n } else {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(\"TapKit not available after loading local core\"));\n }\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load local TapKit core: ${coreURL}`));\n };\n\n document.head.appendChild(script);\n return;\n }\n\n // CDN mode: Load loader.js\n const loaderURL = getLoaderURL();\n const script = document.createElement(\"script\");\n script.src = loaderURL;\n script.async = true;\n\n script.onload = () => {\n // The loader script will load the actual SDK\n // We need to wait a bit for the loader to fetch and load the SDK\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));\n };\n\n // Check if script already exists\n const existingScript = document.querySelector(`script[src=\"${loaderURL}\"]`);\n\n if (existingScript) {\n // Script already added but not yet loaded\n existingScript.addEventListener(\"load\", () => {\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n });\n existingScript.addEventListener(\"error\", () =>\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)),\n );\n } else {\n document.head.appendChild(script);\n }\n });\n\n window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;\n return loadingPromise;\n}\n","import type {\n TapKitConfig,\n TapKitInitParams,\n TapKitInstance,\n} from \"@coxwave/tap-kit-types\";\nimport { loadCDNLoader } from \"./loader\";\n\n/**\n * TapKit - Official TapKit Web SDK\n *\n * @example\n * ```typescript\n * import TapKit from \"@coxwave/tap-kit\";\n *\n * const kit = new TapKit({ apiKey: 'your-api-key' });\n *\n * await kit.init({\n * buttonId: 'tap-button',\n * course: {\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * },\n * container: {\n * position: { top: '64px', right: '32px' },\n * width: '360px',\n * height: 'calc(100% - 128px)',\n * },\n * });\n * ```\n */\nexport class TapKit implements TapKitInstance {\n private instance: TapKitInstance | null = null;\n #loading: Promise<void>;\n #config: TapKitConfig;\n #loadError: Error | null = null;\n #eventsProxy?: TapKitInstance[\"events\"]; // Cached proxy for events API\n #videoProxy?: TapKitInstance[\"video\"]; // Cached proxy for video API\n\n constructor(config: TapKitConfig) {\n this.#config = config;\n this.#loading = this.load();\n }\n\n private async load(): Promise<void> {\n try {\n await loadCDNLoader();\n\n if (!window.TapKit) {\n throw new Error(\"TapKit not available after loading CDN loader\");\n }\n\n this.instance = new window.TapKit(this.#config);\n } catch (err) {\n this.#loadError = err instanceof Error ? err : new Error(String(err));\n throw this.#loadError;\n }\n }\n\n /**\n * Promise that resolves when CDN is loaded and TapKit instance is available\n * (ready to call init())\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.loaded; // CDN downloaded, window.TapKit available\n * await kit.init({...}); // Now safe to call init()\n * ```\n *\n * @see ready - For waiting until AFTER init() completes (iframe initialized)\n */\n get loaded(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${this.#config.apiKey}`,\n );\n }\n });\n }\n\n /**\n * Promise that resolves when SDK is fully initialized\n * (after init() is called and iframe is ready)\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.init({...});\n * await kit.ready; // iframe is fully initialized\n * ```\n *\n * @see loaded - For waiting until CDN load completes (before init)\n */\n get ready(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${this.#config.apiKey}`,\n );\n }\n return this.instance.ready;\n });\n }\n\n /**\n * Create a proxy for video API that waits for ready before delegating\n */\n private createVideoProxy(): TapKitInstance[\"video\"] {\n return {\n bind: (config, clipId) => {\n // Queue the bind call - will execute after ready\n this.ready.then(() => {\n this.instance?.video.bind(config, clipId);\n });\n },\n unbind: () => {\n // Unbind can be called immediately if instance exists\n this.instance?.video?.unbind();\n },\n };\n }\n\n /**\n * Create events API that returns unsubscribe functions synchronously\n * Queues the actual subscription until ready, but returns unsubscribe immediately\n */\n private createEventsProxy(): TapKitInstance[\"events\"] {\n return {\n seekTimeline: (params) => {\n this.ready.then(() => {\n this.instance?.events.seekTimeline(params);\n });\n },\n onTimelineSeek: (callback) => {\n let unsubscribe: (() => void) | undefined;\n let cancelled = false;\n\n // Queue subscription\n this.ready.then(() => {\n if (cancelled) return;\n unsubscribe = this.instance?.events.onTimelineSeek(callback);\n });\n\n // Return synchronous unsubscribe function\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n },\n onAlarmFadeIn: (handler) => {\n let unsubscribe: (() => void) | undefined;\n let cancelled = false;\n\n this.ready.then(() => {\n if (cancelled) return;\n unsubscribe = this.instance?.events.onAlarmFadeIn(handler);\n });\n\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n },\n };\n }\n\n get events() {\n // Cache proxy to avoid creating new object on every access\n if (!this.#eventsProxy) {\n this.#eventsProxy = this.createEventsProxy();\n }\n return this.#eventsProxy;\n }\n\n get isOpen(): boolean {\n return this.instance?.isOpen ?? false;\n }\n\n get isInitialized(): boolean {\n return this.instance?.isInitialized ?? false;\n }\n\n get video() {\n // Cache proxy to avoid creating new object on every access\n if (!this.#videoProxy) {\n this.#videoProxy = this.createVideoProxy();\n }\n return this.#videoProxy;\n }\n\n async init(params: TapKitInitParams): Promise<() => void> {\n await this.#loading;\n if (!this.instance) {\n throw new Error(\"TapKit instance not available after loading\");\n }\n return await this.instance.init(params);\n }\n\n destroy(): void {\n if (this.instance) {\n this.instance.destroy();\n this.instance = null;\n }\n // Clear proxy references to prevent memory leaks\n this.#eventsProxy = undefined;\n this.#videoProxy = undefined;\n }\n\n show(): void {\n if (this.instance) {\n this.instance.show();\n }\n }\n\n hide(): void {\n if (this.instance) {\n this.instance.hide();\n }\n }\n\n get [Symbol.toStringTag]() {\n return \"TapKit\";\n }\n}\n"]}
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- var I=Object.defineProperty;var h=t=>{throw TypeError(t)};var m=(t,e,i)=>e in t?I(t,e,{enumerable:true,configurable:true,writable:true,value:i}):t[e]=i;var A=(t,e,i)=>m(t,e+"",i),D=(t,e,i)=>e.has(t)||h("Cannot "+i);var n=(t,e,i)=>(D(t,e,"read from private field"),i?i.call(t):e.get(t)),u=(t,e,i)=>e.has(t)?h("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,i),d=(t,e,i,o)=>(D(t,e,"write to private field"),e.set(t,i),i);var O="https://files.edutap.ai/tap-sdk/loader.js";function y(){return window?.__TAP_KIT_LOADER_URL__?window.__TAP_KIT_LOADER_URL__:O}function v(){return typeof window<"u"&&!!window.__TAP_KIT_CORE_URL__}function R(){return window.__TAP_KIT_CORE_URL__||""}function K(t,e,i){let o=Date.now(),r=()=>{if(window.TapKit&&window.TapKitLoaded===true){window.__TAP_KIT_LOADER_LOADED__=true,window.__TAP_KIT_LOADER_LOADING__=void 0,t();return}if(Date.now()-o>i){window.__TAP_KIT_LOADER_LOADING__=void 0,e(new Error(`TapKit loader timeout: SDK not available after ${i}ms`));return}typeof requestIdleCallback<"u"?requestIdleCallback(r,{timeout:500}):setTimeout(r,500);};return r}function E(t=4e3){if(window.__TAP_KIT_LOADER_LOADED__&&window.TapKit)return Promise.resolve();if(window.__TAP_KIT_LOADER_LOADING__)return window.__TAP_KIT_LOADER_LOADING__;let e=new Promise((i,o)=>{if(typeof document>"u"){o(new Error("TapKit requires browser environment (document is undefined)"));return}if(v()){if(window.TapKit&&window.TapKitLoaded===true){window.__TAP_KIT_LOADER_LOADED__=true,window.__TAP_KIT_LOADER_LOADING__=void 0,i();return}let p=R(),w=document.createElement("script");w.src=p,w.async=true,w.onload=()=>{window.TapKit?(window.TapKitLoaded=true,window.__TAP_KIT_LOADER_LOADED__=true,window.__TAP_KIT_LOADER_LOADING__=void 0,i()):(window.__TAP_KIT_LOADER_LOADING__=void 0,o(new Error("TapKit not available after loading local core")));},w.onerror=()=>{window.__TAP_KIT_LOADER_LOADING__=void 0,o(new Error(`Failed to load local TapKit core: ${p}`));},document.head.appendChild(w);return}let r=y(),a=document.createElement("script");a.src=r,a.async=true,a.onload=()=>{K(i,o,t)();},a.onerror=()=>{window.__TAP_KIT_LOADER_LOADING__=void 0,o(new Error(`Failed to load TapKit CDN loader: ${r}`));};let L=document.querySelector(`script[src="${r}"]`);L?(L.addEventListener("load",()=>{K(i,o,t)();}),L.addEventListener("error",()=>o(new Error(`Failed to load TapKit CDN loader: ${r}`)))):document.head.appendChild(a);});return window.__TAP_KIT_LOADER_LOADING__=e,e}var _,c,s,T,l,f=class{constructor(e){A(this,"instance",null);u(this,_);u(this,c);u(this,s,null);u(this,T);u(this,l);d(this,c,e),d(this,_,this.load());}async load(){try{if(await E(),!window.TapKit)throw new Error("TapKit not available after loading CDN loader");this.instance=new window.TapKit(n(this,c));}catch(e){throw d(this,s,e instanceof Error?e:new Error(String(e))),n(this,s)}}get loaded(){return n(this,_).then(()=>{if(n(this,s))throw n(this,s);if(!this.instance)throw new Error(`TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${n(this,c).apiKey}`)})}get ready(){return n(this,_).then(()=>{if(n(this,s))throw n(this,s);if(!this.instance)throw new Error(`TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${n(this,c).apiKey}`);return this.instance.ready})}createReadyProxy(e){return new Proxy({},{get:(i,o)=>(...r)=>this.ready.then(()=>{let a=this.instance?.[e]?.[o];return typeof a=="function"?a(...r):a})})}get events(){return n(this,T)||d(this,T,this.createReadyProxy("events")),n(this,T)}get isOpen(){return this.instance?.isOpen??false}get isInitialized(){return this.instance?.isInitialized??false}get video(){return n(this,l)||d(this,l,this.createReadyProxy("video")),n(this,l)}async init(e){if(await n(this,_),!this.instance)throw new Error("TapKit instance not available after loading");return await this.instance.init(e)}destroy(){this.instance&&(this.instance.destroy(),this.instance=null),d(this,T,void 0),d(this,l,void 0);}show(){this.instance&&this.instance.show();}hide(){this.instance&&this.instance.hide();}get[Symbol.toStringTag](){return "TapKit"}};_=new WeakMap,c=new WeakMap,s=new WeakMap,T=new WeakMap,l=new WeakMap;
2
- export{f as TapKit,f as default};//# sourceMappingURL=index.mjs.map
1
+ var I=Object.defineProperty;var L=i=>{throw TypeError(i)};var m=(i,e,t)=>e in i?I(i,e,{enumerable:true,configurable:true,writable:true,value:t}):i[e]=t;var A=(i,e,t)=>m(i,e+"",t),D=(i,e,t)=>e.has(i)||L("Cannot "+t);var r=(i,e,t)=>(D(i,e,"read from private field"),t?t.call(i):e.get(i)),T=(i,e,t)=>e.has(i)?L("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(i):e.set(i,t),s=(i,e,t,n)=>(D(i,e,"write to private field"),e.set(i,t),t);var v="https://files.edutap.ai/tap-sdk/loader.js";function O(){return window?.__TAP_KIT_LOADER_URL__?window.__TAP_KIT_LOADER_URL__:v}function y(){return typeof window<"u"&&!!window.__TAP_KIT_CORE_URL__}function P(){return window.__TAP_KIT_CORE_URL__||""}function K(i,e,t){let n=Date.now(),a=()=>{if(window.TapKit&&window.TapKitLoaded===true){window.__TAP_KIT_LOADER_LOADED__=true,window.__TAP_KIT_LOADER_LOADING__=void 0,i();return}if(Date.now()-n>t){window.__TAP_KIT_LOADER_LOADING__=void 0,e(new Error(`TapKit loader timeout: SDK not available after ${t}ms`));return}typeof requestIdleCallback<"u"?requestIdleCallback(a,{timeout:500}):setTimeout(a,500);};return a}function E(i=4e3){if(window.__TAP_KIT_LOADER_LOADED__&&window.TapKit)return Promise.resolve();if(window.__TAP_KIT_LOADER_LOADING__)return window.__TAP_KIT_LOADER_LOADING__;let e=new Promise((t,n)=>{if(typeof document>"u"){n(new Error("TapKit requires browser environment (document is undefined)"));return}if(y()){if(window.TapKit&&window.TapKitLoaded===true){window.__TAP_KIT_LOADER_LOADED__=true,window.__TAP_KIT_LOADER_LOADING__=void 0,t();return}let p=P(),w=document.createElement("script");w.src=p,w.async=true,w.onload=()=>{window.TapKit?(window.TapKitLoaded=true,window.__TAP_KIT_LOADER_LOADED__=true,window.__TAP_KIT_LOADER_LOADING__=void 0,t()):(window.__TAP_KIT_LOADER_LOADING__=void 0,n(new Error("TapKit not available after loading local core")));},w.onerror=()=>{window.__TAP_KIT_LOADER_LOADING__=void 0,n(new Error(`Failed to load local TapKit core: ${p}`));},document.head.appendChild(w);return}let a=O(),u=document.createElement("script");u.src=a,u.async=true,u.onload=()=>{K(t,n,i)();},u.onerror=()=>{window.__TAP_KIT_LOADER_LOADING__=void 0,n(new Error(`Failed to load TapKit CDN loader: ${a}`));};let f=document.querySelector(`script[src="${a}"]`);f?(f.addEventListener("load",()=>{K(t,n,i)();}),f.addEventListener("error",()=>n(new Error(`Failed to load TapKit CDN loader: ${a}`)))):document.head.appendChild(u);});return window.__TAP_KIT_LOADER_LOADING__=e,e}var d,_,o,c,l,h=class{constructor(e){A(this,"instance",null);T(this,d);T(this,_);T(this,o,null);T(this,c);T(this,l);s(this,_,e),s(this,d,this.load());}async load(){try{if(await E(),!window.TapKit)throw new Error("TapKit not available after loading CDN loader");this.instance=new window.TapKit(r(this,_));}catch(e){throw s(this,o,e instanceof Error?e:new Error(String(e))),r(this,o)}}get loaded(){return r(this,d).then(()=>{if(r(this,o))throw r(this,o);if(!this.instance)throw new Error(`TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${r(this,_).apiKey}`)})}get ready(){return r(this,d).then(()=>{if(r(this,o))throw r(this,o);if(!this.instance)throw new Error(`TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${r(this,_).apiKey}`);return this.instance.ready})}createVideoProxy(){return {bind:(e,t)=>{this.ready.then(()=>{this.instance?.video.bind(e,t);});},unbind:()=>{this.instance?.video?.unbind();}}}createEventsProxy(){return {seekTimeline:e=>{this.ready.then(()=>{this.instance?.events.seekTimeline(e);});},onTimelineSeek:e=>{let t,n=false;return this.ready.then(()=>{n||(t=this.instance?.events.onTimelineSeek(e));}),()=>{n=true,t?.();}},onAlarmFadeIn:e=>{let t,n=false;return this.ready.then(()=>{n||(t=this.instance?.events.onAlarmFadeIn(e));}),()=>{n=true,t?.();}}}}get events(){return r(this,c)||s(this,c,this.createEventsProxy()),r(this,c)}get isOpen(){return this.instance?.isOpen??false}get isInitialized(){return this.instance?.isInitialized??false}get video(){return r(this,l)||s(this,l,this.createVideoProxy()),r(this,l)}async init(e){if(await r(this,d),!this.instance)throw new Error("TapKit instance not available after loading");return await this.instance.init(e)}destroy(){this.instance&&(this.instance.destroy(),this.instance=null),s(this,c,void 0),s(this,l,void 0);}show(){this.instance&&this.instance.show();}hide(){this.instance&&this.instance.hide();}get[Symbol.toStringTag](){return "TapKit"}};d=new WeakMap,_=new WeakMap,o=new WeakMap,c=new WeakMap,l=new WeakMap;
2
+ export{h as TapKit,h as default};//# sourceMappingURL=index.mjs.map
3
3
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/loader.ts","../src/kit.ts"],"names":["DEFAULT_CDN_LOADER_URL","getLoaderURL","isLocalCoreMode","getLocalCoreURL","createSDKChecker","resolve","reject","timeoutMs","startTime","checkSDK","loadCDNLoader","loadingPromise","coreURL","script","loaderURL","existingScript","_loading","_config","_loadError","_eventsProxy","_videoProxy","TapKit","config","__publicField","__privateAdd","__privateSet","__privateGet","err","propertyName","_target","prop","args","method","params"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * CDN loader for TapKit\n * Dynamically loads the TapKit SDK from CDN\n *\n * For local testing, you can override the loader URL:\n * window.__TAP_KIT_LOADER_URL__ = '/tap-kit-core/loader.js';\n *\n * For local development (bypass loader, load IIFE directly):\n * window.__TAP_KIT_CORE_URL__ = '/packages/tap-kit-core/dist/index.global.js';\n */\n\n// Build-time constant injected by tsup define\ndeclare const __DEFAULT_CDN_LOADER_URL__: string;\n\nconst DEFAULT_CDN_LOADER_URL =\n typeof __DEFAULT_CDN_LOADER_URL__ !== \"undefined\"\n ? __DEFAULT_CDN_LOADER_URL__\n : \"https://files.edutap.ai/tap-sdk/loader.js\";\nconst DEFAULT_TIMEOUT_MS = 4000; // 4 seconds total timeout\nconst IDLE_CALLBACK_TIMEOUT_MS = 500; // 500ms for requestIdleCallback\n\n/**\n * Get the loader URL from window override or default CDN\n */\nfunction getLoaderURL(): string {\n return window?.__TAP_KIT_LOADER_URL__\n ? window.__TAP_KIT_LOADER_URL__\n : DEFAULT_CDN_LOADER_URL;\n}\n\n/**\n * Check if local core mode is enabled\n * When __TAP_KIT_CORE_URL__ is set, directly load the IIFE bundle\n * This bypasses the loader.js and loads tap-kit-core directly\n */\nfunction isLocalCoreMode(): boolean {\n return typeof window !== \"undefined\" && !!window.__TAP_KIT_CORE_URL__;\n}\n\n/**\n * Get the local core URL\n */\nfunction getLocalCoreURL(): string {\n return window.__TAP_KIT_CORE_URL__ || \"\";\n}\n\n/**\n * Creates a SDK checker function with timeout and retry logic\n * Uses requestIdleCallback to avoid blocking browser rendering\n * @param resolve - Promise resolve function\n * @param reject - Promise reject function\n * @param timeoutMs - Maximum time to wait for SDK to load (milliseconds)\n * @returns Checker function to be called repeatedly\n */\nfunction createSDKChecker(\n resolve: (value: void | PromiseLike<void>) => void,\n reject: (reason?: unknown) => void,\n timeoutMs: number,\n): () => void {\n const startTime = Date.now();\n\n const checkSDK = (): void => {\n // Check if real TapKit is loaded (not just stub)\n // Stub has TapKitLoaded flag set to true by loader.js after real SDK loads\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const elapsed = Date.now() - startTime;\n\n // Check if exceeded timeout\n if (elapsed > timeoutMs) {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(\n new Error(\n `TapKit loader timeout: SDK not available after ${timeoutMs}ms`,\n ),\n );\n return;\n }\n\n // Use requestIdleCallback for better performance\n // Falls back to setTimeout if not available\n if (typeof requestIdleCallback !== \"undefined\") {\n requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });\n } else {\n setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);\n }\n };\n\n return checkSDK;\n}\n\n/**\n * Loads the CDN loader script\n * The loader will then fetch versions.json and load the appropriate SDK version\n *\n * If __TAP_KIT_CORE_URL__ is set, bypasses loader and loads IIFE directly\n *\n * @param timeoutMs - Maximum time to wait for SDK to load (default: 4000ms)\n * @returns Promise that resolves when SDK is loaded\n * @throws {Error} If loader fails to load or times out\n */\nexport function loadCDNLoader(\n timeoutMs: number = DEFAULT_TIMEOUT_MS,\n): Promise<void> {\n // If already loaded, return immediately\n if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {\n return Promise.resolve();\n }\n\n // If currently loading, return the existing promise\n if (window.__TAP_KIT_LOADER_LOADING__) {\n return window.__TAP_KIT_LOADER_LOADING__;\n }\n\n // Create loading promise\n const loadingPromise = new Promise<void>((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(\n new Error(\n \"TapKit requires browser environment (document is undefined)\",\n ),\n );\n return;\n }\n\n // Local core mode: Load IIFE directly\n if (isLocalCoreMode()) {\n // Check if TapKit is already loaded (from other pages in playground)\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const coreURL = getLocalCoreURL();\n\n const script = document.createElement(\"script\");\n script.src = coreURL;\n script.async = true;\n\n script.onload = () => {\n // IIFE directly sets window.TapKit\n // Set the loaded flag manually since we bypass loader.js\n if (window.TapKit) {\n window.TapKitLoaded = true;\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n } else {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(\"TapKit not available after loading local core\"));\n }\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load local TapKit core: ${coreURL}`));\n };\n\n document.head.appendChild(script);\n return;\n }\n\n // CDN mode: Load loader.js\n const loaderURL = getLoaderURL();\n const script = document.createElement(\"script\");\n script.src = loaderURL;\n script.async = true;\n\n script.onload = () => {\n // The loader script will load the actual SDK\n // We need to wait a bit for the loader to fetch and load the SDK\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));\n };\n\n // Check if script already exists\n const existingScript = document.querySelector(`script[src=\"${loaderURL}\"]`);\n\n if (existingScript) {\n // Script already added but not yet loaded\n existingScript.addEventListener(\"load\", () => {\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n });\n existingScript.addEventListener(\"error\", () =>\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)),\n );\n } else {\n document.head.appendChild(script);\n }\n });\n\n window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;\n return loadingPromise;\n}\n","import type {\n TapKitConfig,\n TapKitInitParams,\n TapKitInstance,\n} from \"@coxwave/tap-kit-types\";\nimport { loadCDNLoader } from \"./loader\";\n\n/**\n * TapKit - Official TapKit Web SDK\n *\n * @example\n * ```typescript\n * import TapKit from \"@coxwave/tap-kit\";\n *\n * const kit = new TapKit({ apiKey: 'your-api-key' });\n *\n * await kit.init({\n * buttonId: 'tap-button',\n * course: {\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * },\n * container: {\n * position: { top: '64px', right: '32px' },\n * width: '360px',\n * height: 'calc(100% - 128px)',\n * },\n * });\n * ```\n */\nexport class TapKit implements TapKitInstance {\n private instance: TapKitInstance | null = null;\n #loading: Promise<void>;\n #config: TapKitConfig;\n #loadError: Error | null = null;\n #eventsProxy?: TapKitInstance[\"events\"]; // Cached proxy for events API\n #videoProxy?: TapKitInstance[\"video\"]; // Cached proxy for video API\n\n constructor(config: TapKitConfig) {\n this.#config = config;\n this.#loading = this.load();\n }\n\n private async load(): Promise<void> {\n try {\n await loadCDNLoader();\n\n if (!window.TapKit) {\n throw new Error(\"TapKit not available after loading CDN loader\");\n }\n\n this.instance = new window.TapKit(this.#config);\n } catch (err) {\n this.#loadError = err instanceof Error ? err : new Error(String(err));\n throw this.#loadError;\n }\n }\n\n /**\n * Promise that resolves when CDN is loaded and TapKit instance is available\n * (ready to call init())\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.loaded; // CDN downloaded, window.TapKit available\n * await kit.init({...}); // Now safe to call init()\n * ```\n *\n * @see ready - For waiting until AFTER init() completes (iframe initialized)\n */\n get loaded(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${this.#config.apiKey}`,\n );\n }\n });\n }\n\n /**\n * Promise that resolves when SDK is fully initialized\n * (after init() is called and iframe is ready)\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.init({...});\n * await kit.ready; // iframe is fully initialized\n * ```\n *\n * @see loaded - For waiting until CDN load completes (before init)\n */\n get ready(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${this.#config.apiKey}`,\n );\n }\n return this.instance.ready;\n });\n }\n\n /**\n * Create a proxy that automatically waits for the instance to be ready\n * before delegating to the actual property\n */\n private createReadyProxy<T extends object>(\n propertyName: \"events\" | \"video\",\n ): T {\n return new Proxy({} as T, {\n get: (_target, prop: string) => {\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic method signature unknown at compile time\n return (...args: any[]) => {\n return this.ready.then(() => {\n // biome-ignore lint/suspicious/noExplicitAny: Dynamic property access requires any\n const method = (this.instance?.[propertyName] as any)?.[prop];\n if (typeof method === \"function\") {\n return method(...args);\n }\n return method;\n });\n };\n },\n }) as T;\n }\n\n get events() {\n // Cache proxy to avoid creating new Proxy object on every access\n if (!this.#eventsProxy) {\n this.#eventsProxy =\n this.createReadyProxy<TapKitInstance[\"events\"]>(\"events\");\n }\n return this.#eventsProxy;\n }\n\n get isOpen(): boolean {\n return this.instance?.isOpen ?? false;\n }\n\n get isInitialized(): boolean {\n return this.instance?.isInitialized ?? false;\n }\n\n get video() {\n // Cache proxy to avoid creating new Proxy object on every access\n if (!this.#videoProxy) {\n this.#videoProxy =\n this.createReadyProxy<TapKitInstance[\"video\"]>(\"video\");\n }\n return this.#videoProxy;\n }\n\n async init(params: TapKitInitParams): Promise<() => void> {\n await this.#loading;\n if (!this.instance) {\n throw new Error(\"TapKit instance not available after loading\");\n }\n return await this.instance.init(params);\n }\n\n destroy(): void {\n if (this.instance) {\n this.instance.destroy();\n this.instance = null;\n }\n // Clear proxy references to prevent memory leaks\n this.#eventsProxy = undefined;\n this.#videoProxy = undefined;\n }\n\n show(): void {\n if (this.instance) {\n this.instance.show();\n }\n }\n\n hide(): void {\n if (this.instance) {\n this.instance.hide();\n }\n }\n\n get [Symbol.toStringTag]() {\n return \"TapKit\";\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/loader.ts","../src/kit.ts"],"names":["DEFAULT_CDN_LOADER_URL","getLoaderURL","isLocalCoreMode","getLocalCoreURL","createSDKChecker","resolve","reject","timeoutMs","startTime","checkSDK","loadCDNLoader","loadingPromise","coreURL","script","loaderURL","existingScript","_loading","_config","_loadError","_eventsProxy","_videoProxy","TapKit","config","__publicField","__privateAdd","__privateSet","__privateGet","err","clipId","params","callback","unsubscribe","cancelled","handler"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * CDN loader for TapKit\n * Dynamically loads the TapKit SDK from CDN\n *\n * For local testing, you can override the loader URL:\n * window.__TAP_KIT_LOADER_URL__ = '/tap-kit-core/loader.js';\n *\n * For local development (bypass loader, load IIFE directly):\n * window.__TAP_KIT_CORE_URL__ = '/packages/tap-kit-core/dist/index.global.js';\n */\n\n// Build-time constant injected by tsup define\ndeclare const __DEFAULT_CDN_LOADER_URL__: string;\n\nconst DEFAULT_CDN_LOADER_URL =\n typeof __DEFAULT_CDN_LOADER_URL__ !== \"undefined\"\n ? __DEFAULT_CDN_LOADER_URL__\n : \"https://files.edutap.ai/tap-sdk/loader.js\";\nconst DEFAULT_TIMEOUT_MS = 4000; // 4 seconds total timeout\nconst IDLE_CALLBACK_TIMEOUT_MS = 500; // 500ms for requestIdleCallback\n\n/**\n * Get the loader URL from window override or default CDN\n */\nfunction getLoaderURL(): string {\n return window?.__TAP_KIT_LOADER_URL__\n ? window.__TAP_KIT_LOADER_URL__\n : DEFAULT_CDN_LOADER_URL;\n}\n\n/**\n * Check if local core mode is enabled\n * When __TAP_KIT_CORE_URL__ is set, directly load the IIFE bundle\n * This bypasses the loader.js and loads tap-kit-core directly\n */\nfunction isLocalCoreMode(): boolean {\n return typeof window !== \"undefined\" && !!window.__TAP_KIT_CORE_URL__;\n}\n\n/**\n * Get the local core URL\n */\nfunction getLocalCoreURL(): string {\n return window.__TAP_KIT_CORE_URL__ || \"\";\n}\n\n/**\n * Creates a SDK checker function with timeout and retry logic\n * Uses requestIdleCallback to avoid blocking browser rendering\n * @param resolve - Promise resolve function\n * @param reject - Promise reject function\n * @param timeoutMs - Maximum time to wait for SDK to load (milliseconds)\n * @returns Checker function to be called repeatedly\n */\nfunction createSDKChecker(\n resolve: (value: void | PromiseLike<void>) => void,\n reject: (reason?: unknown) => void,\n timeoutMs: number,\n): () => void {\n const startTime = Date.now();\n\n const checkSDK = (): void => {\n // Check if real TapKit is loaded (not just stub)\n // Stub has TapKitLoaded flag set to true by loader.js after real SDK loads\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const elapsed = Date.now() - startTime;\n\n // Check if exceeded timeout\n if (elapsed > timeoutMs) {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(\n new Error(\n `TapKit loader timeout: SDK not available after ${timeoutMs}ms`,\n ),\n );\n return;\n }\n\n // Use requestIdleCallback for better performance\n // Falls back to setTimeout if not available\n if (typeof requestIdleCallback !== \"undefined\") {\n requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });\n } else {\n setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);\n }\n };\n\n return checkSDK;\n}\n\n/**\n * Loads the CDN loader script\n * The loader will then fetch versions.json and load the appropriate SDK version\n *\n * If __TAP_KIT_CORE_URL__ is set, bypasses loader and loads IIFE directly\n *\n * @param timeoutMs - Maximum time to wait for SDK to load (default: 4000ms)\n * @returns Promise that resolves when SDK is loaded\n * @throws {Error} If loader fails to load or times out\n */\nexport function loadCDNLoader(\n timeoutMs: number = DEFAULT_TIMEOUT_MS,\n): Promise<void> {\n // If already loaded, return immediately\n if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {\n return Promise.resolve();\n }\n\n // If currently loading, return the existing promise\n if (window.__TAP_KIT_LOADER_LOADING__) {\n return window.__TAP_KIT_LOADER_LOADING__;\n }\n\n // Create loading promise\n const loadingPromise = new Promise<void>((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(\n new Error(\n \"TapKit requires browser environment (document is undefined)\",\n ),\n );\n return;\n }\n\n // Local core mode: Load IIFE directly\n if (isLocalCoreMode()) {\n // Check if TapKit is already loaded (from other pages in playground)\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const coreURL = getLocalCoreURL();\n\n const script = document.createElement(\"script\");\n script.src = coreURL;\n script.async = true;\n\n script.onload = () => {\n // IIFE directly sets window.TapKit\n // Set the loaded flag manually since we bypass loader.js\n if (window.TapKit) {\n window.TapKitLoaded = true;\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n } else {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(\"TapKit not available after loading local core\"));\n }\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load local TapKit core: ${coreURL}`));\n };\n\n document.head.appendChild(script);\n return;\n }\n\n // CDN mode: Load loader.js\n const loaderURL = getLoaderURL();\n const script = document.createElement(\"script\");\n script.src = loaderURL;\n script.async = true;\n\n script.onload = () => {\n // The loader script will load the actual SDK\n // We need to wait a bit for the loader to fetch and load the SDK\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));\n };\n\n // Check if script already exists\n const existingScript = document.querySelector(`script[src=\"${loaderURL}\"]`);\n\n if (existingScript) {\n // Script already added but not yet loaded\n existingScript.addEventListener(\"load\", () => {\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n });\n existingScript.addEventListener(\"error\", () =>\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)),\n );\n } else {\n document.head.appendChild(script);\n }\n });\n\n window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;\n return loadingPromise;\n}\n","import type {\n TapKitConfig,\n TapKitInitParams,\n TapKitInstance,\n} from \"@coxwave/tap-kit-types\";\nimport { loadCDNLoader } from \"./loader\";\n\n/**\n * TapKit - Official TapKit Web SDK\n *\n * @example\n * ```typescript\n * import TapKit from \"@coxwave/tap-kit\";\n *\n * const kit = new TapKit({ apiKey: 'your-api-key' });\n *\n * await kit.init({\n * buttonId: 'tap-button',\n * course: {\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * },\n * container: {\n * position: { top: '64px', right: '32px' },\n * width: '360px',\n * height: 'calc(100% - 128px)',\n * },\n * });\n * ```\n */\nexport class TapKit implements TapKitInstance {\n private instance: TapKitInstance | null = null;\n #loading: Promise<void>;\n #config: TapKitConfig;\n #loadError: Error | null = null;\n #eventsProxy?: TapKitInstance[\"events\"]; // Cached proxy for events API\n #videoProxy?: TapKitInstance[\"video\"]; // Cached proxy for video API\n\n constructor(config: TapKitConfig) {\n this.#config = config;\n this.#loading = this.load();\n }\n\n private async load(): Promise<void> {\n try {\n await loadCDNLoader();\n\n if (!window.TapKit) {\n throw new Error(\"TapKit not available after loading CDN loader\");\n }\n\n this.instance = new window.TapKit(this.#config);\n } catch (err) {\n this.#loadError = err instanceof Error ? err : new Error(String(err));\n throw this.#loadError;\n }\n }\n\n /**\n * Promise that resolves when CDN is loaded and TapKit instance is available\n * (ready to call init())\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.loaded; // CDN downloaded, window.TapKit available\n * await kit.init({...}); // Now safe to call init()\n * ```\n *\n * @see ready - For waiting until AFTER init() completes (iframe initialized)\n */\n get loaded(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${this.#config.apiKey}`,\n );\n }\n });\n }\n\n /**\n * Promise that resolves when SDK is fully initialized\n * (after init() is called and iframe is ready)\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.init({...});\n * await kit.ready; // iframe is fully initialized\n * ```\n *\n * @see loaded - For waiting until CDN load completes (before init)\n */\n get ready(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${this.#config.apiKey}`,\n );\n }\n return this.instance.ready;\n });\n }\n\n /**\n * Create a proxy for video API that waits for ready before delegating\n */\n private createVideoProxy(): TapKitInstance[\"video\"] {\n return {\n bind: (config, clipId) => {\n // Queue the bind call - will execute after ready\n this.ready.then(() => {\n this.instance?.video.bind(config, clipId);\n });\n },\n unbind: () => {\n // Unbind can be called immediately if instance exists\n this.instance?.video?.unbind();\n },\n };\n }\n\n /**\n * Create events API that returns unsubscribe functions synchronously\n * Queues the actual subscription until ready, but returns unsubscribe immediately\n */\n private createEventsProxy(): TapKitInstance[\"events\"] {\n return {\n seekTimeline: (params) => {\n this.ready.then(() => {\n this.instance?.events.seekTimeline(params);\n });\n },\n onTimelineSeek: (callback) => {\n let unsubscribe: (() => void) | undefined;\n let cancelled = false;\n\n // Queue subscription\n this.ready.then(() => {\n if (cancelled) return;\n unsubscribe = this.instance?.events.onTimelineSeek(callback);\n });\n\n // Return synchronous unsubscribe function\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n },\n onAlarmFadeIn: (handler) => {\n let unsubscribe: (() => void) | undefined;\n let cancelled = false;\n\n this.ready.then(() => {\n if (cancelled) return;\n unsubscribe = this.instance?.events.onAlarmFadeIn(handler);\n });\n\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n },\n };\n }\n\n get events() {\n // Cache proxy to avoid creating new object on every access\n if (!this.#eventsProxy) {\n this.#eventsProxy = this.createEventsProxy();\n }\n return this.#eventsProxy;\n }\n\n get isOpen(): boolean {\n return this.instance?.isOpen ?? false;\n }\n\n get isInitialized(): boolean {\n return this.instance?.isInitialized ?? false;\n }\n\n get video() {\n // Cache proxy to avoid creating new object on every access\n if (!this.#videoProxy) {\n this.#videoProxy = this.createVideoProxy();\n }\n return this.#videoProxy;\n }\n\n async init(params: TapKitInitParams): Promise<() => void> {\n await this.#loading;\n if (!this.instance) {\n throw new Error(\"TapKit instance not available after loading\");\n }\n return await this.instance.init(params);\n }\n\n destroy(): void {\n if (this.instance) {\n this.instance.destroy();\n this.instance = null;\n }\n // Clear proxy references to prevent memory leaks\n this.#eventsProxy = undefined;\n this.#videoProxy = undefined;\n }\n\n show(): void {\n if (this.instance) {\n this.instance.show();\n }\n }\n\n hide(): void {\n if (this.instance) {\n this.instance.hide();\n }\n }\n\n get [Symbol.toStringTag]() {\n return \"TapKit\";\n }\n}\n"]}