diff --git a/G102-sequence/index.html b/G102-sequence/index.html index f8781db..5e28211 100644 --- a/G102-sequence/index.html +++ b/G102-sequence/index.html @@ -135,5 +135,7 @@ } }; + + \ No newline at end of file diff --git a/G102-sequence/sdk/package/README.md b/G102-sequence/sdk/package/README.md new file mode 100644 index 0000000..5488234 --- /dev/null +++ b/G102-sequence/sdk/package/README.md @@ -0,0 +1,506 @@ +# SDK Message Protocol + +Game giao tiếp với SDK thông qua **postMessage** trong hidden iframe. Game chỉ cần biết các message types, payloads, và khi nào gửi/nhận. + +--- + +## 🔌 Architecture + +``` +┌──────────────────────────────┐ +│ Game (React/Vue/etc) │ +│ │ +│ window.parent.postMessage() │ +│ window.addEventListener() │ +└──────────────┬───────────────┘ + │ postMessage + │ (JSON) + ↓ + ┌──────────────────────┐ + │ Hidden Iframe │ + │ (sdk-iframe.html) │ + │ │ + │ - Sanitize data │ + │ - Verify answers │ + │ - Call API │ + │ - Send responses │ + └──────────────────────┘ +``` + +--- + +## 📨 Message Types + +### 1️⃣ **SDK_INIT** (Game → SDK) + +Game khởi tạo SDK với mode và game_code. + +**Gửi:** +```javascript +window.parent.postMessage({ + type: 'SDK_INIT', + payload: { + mode: 'dev' | 'preview' | 'live', + game_code: 'G001' | 'G002' | ... | 'G123', + + // LIVE mode only: + assignment_id?: string, + student_id?: string, + api_base_url?: string, + auth_token?: string + } +}, '*'); +``` + +**Ví dụ DEV mode:** +```javascript +window.parent.postMessage({ + type: 'SDK_INIT', + payload: { + mode: 'dev', + game_code: 'G001' + } +}, '*'); +``` + +**Ví dụ LIVE mode:** +```javascript +window.parent.postMessage({ + type: 'SDK_INIT', + payload: { + mode: 'live', + game_code: 'G001', + assignment_id: 'ASSIGN_123', + student_id: 'STU_456', + api_base_url: 'https://api.sena.tech', + auth_token: 'token_xyz' + } +}, '*'); +``` + +**Nhận (SDK gửi lại khi ready):** +```javascript +{ + type: 'SDK_READY', + payload: { + mode: 'dev' | 'preview' | 'live', + game_code: 'G001' + } +} +``` + +--- + +### 2️⃣ **SDK_DATA_READY** (SDK → Game) + +SDK gửi sanitized data cho game render. Game phải listen sự kiện này. + +**Nhận:** +```javascript +window.addEventListener('message', (event) => { + if (event.data.type === 'SDK_DATA_READY') { + const { + items, // Sanitized items (NO answers!) + total_questions, + completed_count, + resume_data // Optional: previous results + } = event.data.payload; + + // Render game + renderGame(items); + } +}); +``` + +**Payload items (tùy game_code):** + +#### Quiz Games (G001-G004): +```json +{ + "id": "q1", + "question": "What is 2+2?", + "options": [ + {"text": "5"}, + {"text": "4"}, + {"text": "3"} + ] +} +``` + +#### Sequence Games (G110-G123): +```json +{ + "id": "seq1", + "question": ["H", "", "L", "", "O"], + "options": ["L", "E"], + "audio_url": "https://..." // optional +} +``` + +--- + +### 3️⃣ **SDK_CHECK_ANSWER** (Game → SDK) + +Game gửi user's answer để SDK verify. + +**Gửi:** +```javascript +window.parent.postMessage({ + type: 'SDK_CHECK_ANSWER', + payload: { + question_id: 'q1', + choice: any, // Index (quiz) hoặc Array (sequence) + time_spent?: number // Milliseconds + } +}, '*'); +``` + +**Quiz example (choice = index):** +```javascript +window.parent.postMessage({ + type: 'SDK_CHECK_ANSWER', + payload: { + question_id: 'q1', + choice: 1, // User clicked option index 1 + time_spent: 5000 + } +}, '*'); +``` + +**Sequence example (choice = reordered array):** +```javascript +window.parent.postMessage({ + type: 'SDK_CHECK_ANSWER', + payload: { + question_id: 'seq1', + choice: ["H", "e", "l", "l", "o"], // Reordered + time_spent: 8000 + } +}, '*'); +``` + +--- + +### 4️⃣ **SDK_ANSWER_RESULT** (SDK → Game) + +SDK gửi kết quả verify. + +**Nhận:** +```javascript +window.addEventListener('message', (event) => { + if (event.data.type === 'SDK_ANSWER_RESULT') { + const { + question_id, + correct, // true/false + score, // 0-1 hoặc custom + synced, // true = already synced to server + feedback // Optional: "✅ Correct!" or "❌ Wrong" + } = event.data.payload; + + if (correct) { + showCorrectFeedback(question_id); + } else { + showWrongFeedback(question_id); + } + } +}); +``` + +--- + +### 5️⃣ **SDK_PUSH_DATA** (Game → SDK, PREVIEW only) + +Nếu PREVIEW mode, game có thể push data thay vì SDK fetch. + +**Gửi:** +```javascript +window.parent.postMessage({ + type: 'SDK_PUSH_DATA', + payload: { + items: [ + { + id: 'q1', + question: 'What is 2+2?', + options: [ + {text: '3'}, + {text: '4'}, + {text: '5'} + ], + answer: '4' // Server data (with answer) + }, + // ... more items + ] + } +}, '*'); +``` + +SDK sẽ sanitize (remove answer, shuffle options) rồi gửi SDK_DATA_READY. + +--- + +### 6️⃣ **SDK_ERROR** (SDK → Game) + +SDK gửi error notification. + +**Nhận:** +```javascript +window.addEventListener('message', (event) => { + if (event.data.type === 'SDK_ERROR') { + const { + code, // Error code + message, // Error message + details // Optional: more info + } = event.data.payload; + + console.error(`[SDK Error] ${code}: ${message}`); + } +}); +``` + +--- + +## 🎮 Data Structures by Game Type + +### Quiz Games (G001-G004) + +#### Sanitized (Game receives): +```json +{ + "id": "q1", + "question": "Audio URL or text", + "image_url": "Image URL (optional)", + "options": [ + {"text": "Option A"} or {"audio": "Audio URL"} + ] +} +``` + +#### User Answer Format: +```javascript +choice = 0 // Index of selected option +``` + +--- + +### Sequence Word (G110-G113) + +#### Sanitized (Game receives): +```json +{ + "id": "seq_word_1", + "question": ["H", "", "L", "", "O"], + "options": ["L", "E"], + "audio_url": "URL (optional)" +} +``` + +#### User Answer Format: +```javascript +choice = ["H", "e", "l", "l", "o"] // Reordered array +``` + +--- + +### Sequence Sentence (G120-G123) + +#### Sanitized (Game receives): +```json +{ + "id": "seq_sent_1", + "question": ["I", "", "reading", ""], + "options": ["love", "books"], + "audio_url": "URL (optional)" +} +``` + +#### User Answer Format: +```javascript +choice = ["I", "love", "reading", "books"] // Reordered +``` + +--- + +## 💻 Complete Game Implementation Example + +```javascript +// ============ INITIALIZE ============ + +// Listen for SDK messages +window.addEventListener('message', (event) => { + handleSdkMessage(event.data); +}); + +// Initialize SDK +function initGame() { + const mode = 'live'; + const gameCode = 'G001'; + + window.parent.postMessage({ + type: 'SDK_INIT', + payload: { + mode, + game_code: gameCode, + assignment_id: 'ASSIGN_123', + student_id: 'STU_456', + api_base_url: 'https://api.sena.tech' + } + }, '*'); +} + +// Handle all SDK messages +function handleSdkMessage(data) { + switch (data.type) { + case 'SDK_READY': + console.log('SDK initialized:', data.payload); + break; + + case 'SDK_DATA_READY': + const items = data.payload.items; + renderGameItems(items); + break; + + case 'SDK_ANSWER_RESULT': + const result = data.payload; + if (result.correct) { + showCorrectFeedback(result.question_id); + } else { + showWrongFeedback(result.question_id); + } + break; + + case 'SDK_ERROR': + console.error('SDK Error:', data.payload); + break; + } +} + +// ============ RENDER GAME ============ + +function renderGameItems(items) { + items.forEach(item => { + if (item.options) { + // Quiz game + renderQuizQuestion(item); + } else if (Array.isArray(item.question)) { + // Sequence game + renderSequenceGame(item); + } + }); +} + +// ============ HANDLE USER ANSWER ============ + +function submitAnswer(questionId, userChoice) { + window.parent.postMessage({ + type: 'SDK_CHECK_ANSWER', + payload: { + question_id: questionId, + choice: userChoice, // Index for quiz, array for sequence + time_spent: 5000 + } + }, '*'); +} + +// ============ GAME START ============ + +// Start when page loads +window.addEventListener('load', () => { + initGame(); +}); +``` + +--- + +## 🔄 Message Flow Examples + +### DEV Mode (Local Verify) +``` +Game: SDK_INIT + ↓ +SDK: SDK_READY + ↓ +SDK: SDK_DATA_READY (mock items) + ↓ +Game: SDK_CHECK_ANSWER + ↓ +SDK: SDK_ANSWER_RESULT (instantly) +``` + +### LIVE Mode (Server Verify) +``` +Game: SDK_INIT + ↓ +SDK: SDK_READY + ↓ +SDK: fetch from API → SDK_DATA_READY + ↓ +Game: SDK_CHECK_ANSWER + ↓ +SDK: POST to server + ↓ +SDK: SDK_ANSWER_RESULT (wait for server) +``` + +### PREVIEW Mode (Push Data) +``` +Game: SDK_INIT (mode='preview') + ↓ +SDK: SDK_READY + ↓ +Game: SDK_PUSH_DATA (items with answers) + ↓ +SDK: sanitize → SDK_DATA_READY + ↓ +Game: SDK_CHECK_ANSWER + ↓ +SDK: SDK_ANSWER_RESULT (local verify) +``` + +--- + +## ⚠️ Important Notes + +✅ **SDK Always Sanitizes** +- Removes `answer` field +- Removes `word`, `sentence`, `parts`, `missing_letter_count` +- Shuffles options (for quiz) + +✅ **Game Never Gets Answer** +- Game receives only question + options +- Answer is stored server-side + +✅ **Shuffled Options** +- Game receives shuffled options array +- Game user clicks index +- SDK internally resolves index → text + +✅ **Sequence Games** +- Random positions are blanked (based on `missing_letter_count`) +- Game receives question with blanks + missing items +- User reorders missing items + +--- + +## 🚀 Testing + +Test locally without SDK: +```javascript +// Simulate SDK messages for testing +function simulateSdkMessage(type, payload) { + const event = new MessageEvent('message', { + data: { type, payload } + }); + window.dispatchEvent(event); +} + +// Simulate SDK_DATA_READY +simulateSdkMessage('SDK_DATA_READY', { + items: [ + { + id: 'q1', + question: 'What is 2+2?', + options: [{text: '4'}, {text: '5'}, {text: '3'}] + } + ] +}); +``` diff --git a/G102-sequence/sdk/package/dist/EventEmitter.d.ts b/G102-sequence/sdk/package/dist/EventEmitter.d.ts new file mode 100644 index 0000000..8a6f8a3 --- /dev/null +++ b/G102-sequence/sdk/package/dist/EventEmitter.d.ts @@ -0,0 +1,29 @@ +/** + * Game Iframe SDK - Event Emitter + * Simple typed event emitter for SDK + */ +export type EventHandler = (data: T) => void; +export declare class EventEmitter> { + private handlers; + /** + * Subscribe to an event + */ + on(event: K, handler: EventHandler): () => void; + /** + * Subscribe to an event (once) + */ + once(event: K, handler: EventHandler): () => void; + /** + * Unsubscribe from an event + */ + off(event: K, handler: EventHandler): void; + /** + * Emit an event + */ + emit(event: K, data: Events[K]): void; + /** + * Remove all handlers for an event (or all events) + */ + removeAllListeners(event?: keyof Events): void; +} +//# sourceMappingURL=EventEmitter.d.ts.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/EventEmitter.d.ts.map b/G102-sequence/sdk/package/dist/EventEmitter.d.ts.map new file mode 100644 index 0000000..bb5561d --- /dev/null +++ b/G102-sequence/sdk/package/dist/EventEmitter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"EventEmitter.d.ts","sourceRoot":"","sources":["../src/EventEmitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;AAEtD,qBAAa,YAAY,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACxD,OAAO,CAAC,QAAQ,CAAmD;IAEnE;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAUlF;;OAEG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAQpF;;OAEG;IACH,GAAG,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAI7E;;OAEG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI;IAU7D;;OAEG;IACH,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI;CAOjD"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/EventEmitter.js b/G102-sequence/sdk/package/dist/EventEmitter.js new file mode 100644 index 0000000..72c9cdb --- /dev/null +++ b/G102-sequence/sdk/package/dist/EventEmitter.js @@ -0,0 +1,65 @@ +"use strict"; +/** + * Game Iframe SDK - Event Emitter + * Simple typed event emitter for SDK + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EventEmitter = void 0; +class EventEmitter { + constructor() { + this.handlers = new Map(); + } + /** + * Subscribe to an event + */ + on(event, handler) { + if (!this.handlers.has(event)) { + this.handlers.set(event, new Set()); + } + this.handlers.get(event).add(handler); + // Return unsubscribe function + return () => this.off(event, handler); + } + /** + * Subscribe to an event (once) + */ + once(event, handler) { + const wrappedHandler = (data) => { + this.off(event, wrappedHandler); + handler(data); + }; + return this.on(event, wrappedHandler); + } + /** + * Unsubscribe from an event + */ + off(event, handler) { + this.handlers.get(event)?.delete(handler); + } + /** + * Emit an event + */ + emit(event, data) { + this.handlers.get(event)?.forEach(handler => { + try { + handler(data); + } + catch (err) { + console.error(`[EventEmitter] Error in handler for "${String(event)}":`, err); + } + }); + } + /** + * Remove all handlers for an event (or all events) + */ + removeAllListeners(event) { + if (event) { + this.handlers.delete(event); + } + else { + this.handlers.clear(); + } + } +} +exports.EventEmitter = EventEmitter; +//# sourceMappingURL=EventEmitter.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/EventEmitter.js.map b/G102-sequence/sdk/package/dist/EventEmitter.js.map new file mode 100644 index 0000000..7d574fe --- /dev/null +++ b/G102-sequence/sdk/package/dist/EventEmitter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventEmitter.js","sourceRoot":"","sources":["../src/EventEmitter.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAIH,MAAa,YAAY;IAAzB;QACY,aAAQ,GAAyC,IAAI,GAAG,EAAE,CAAC;IAwDvE,CAAC;IAtDG;;OAEG;IACH,EAAE,CAAyB,KAAQ,EAAE,OAAgC;QACjE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAEvC,8BAA8B;QAC9B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,IAAI,CAAyB,KAAQ,EAAE,OAAgC;QACnE,MAAM,cAAc,GAAG,CAAC,IAAe,EAAE,EAAE;YACvC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;YAChC,OAAO,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC,CAAC;QACF,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,GAAG,CAAyB,KAAQ,EAAE,OAAgC;QAClE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,IAAI,CAAyB,KAAQ,EAAE,IAAe;QAClD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;YACxC,IAAI,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,wCAAwC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAClF,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;OAEG;IACH,kBAAkB,CAAC,KAAoB;QACnC,IAAI,KAAK,EAAE,CAAC;YACR,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC1B,CAAC;IACL,CAAC;CACJ;AAzDD,oCAyDC"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/GameIframeSDK.d.ts b/G102-sequence/sdk/package/dist/GameIframeSDK.d.ts new file mode 100644 index 0000000..7cec288 --- /dev/null +++ b/G102-sequence/sdk/package/dist/GameIframeSDK.d.ts @@ -0,0 +1,93 @@ +/** + * Game Iframe SDK - Core + * SDK chính - compose các layers: MessageHandler, MessageSender + */ +import { EventEmitter } from './EventEmitter'; +import { MessageHandler } from './MessageHandler'; +import { MessageSender } from './MessageSender'; +import { GameIframeSDKConfig, SDKEvents, PushDataPayload, LeaderboardData } from './types'; +/** + * GameIframeSDK - Main SDK class + * Composes MessageHandler và MessageSender + */ +export declare class GameIframeSDK extends EventEmitter { + private config; + private messageHandler; + private messageSender; + private pendingData; + private isReady; + constructor(config: GameIframeSDKConfig); + /** + * Set iframe element reference + */ + setIframe(iframe: HTMLIFrameElement | null): this; + /** + * Get current iframe + */ + getIframe(): HTMLIFrameElement | null; + /** + * Check if game is ready + */ + isGameReady(): boolean; + /** + * Check if sender is ready (iframe available) + */ + isSenderReady(): boolean; + /** + * Send game data to iframe + */ + sendGameData(data: PushDataPayload): boolean; + /** + * Send leaderboard data to iframe + */ + sendLeaderboard(data: LeaderboardData): boolean; + /** + * Queue data to be sent when game is ready + */ + queueGameData(data: PushDataPayload): this; + /** + * Clear queued data + */ + clearQueuedData(): this; + /** + * Force reload iframe + */ + reloadIframe(): boolean; + /** + * Cleanup and destroy SDK + */ + destroy(): void; + /** + * Get MessageHandler instance for advanced usage + */ + getMessageHandler(): MessageHandler; + /** + * Get MessageSender instance for advanced usage + */ + getMessageSender(): MessageSender; + /** + * Setup event forwarding from MessageHandler to SDK events + */ + private setupEventForwarding; + /** + * Send queued data + */ + private sendQueuedData; + /** + * Internal logging + */ + private log; +} +/** + * Create SDK instance + */ +export declare function createGameIframeSDK(config: GameIframeSDKConfig): GameIframeSDK; +/** + * Get or create default SDK instance + */ +export declare function getGameIframeSDK(config?: GameIframeSDKConfig): GameIframeSDK; +/** + * Destroy default instance + */ +export declare function destroyGameIframeSDK(): void; +//# sourceMappingURL=GameIframeSDK.d.ts.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/GameIframeSDK.d.ts.map b/G102-sequence/sdk/package/dist/GameIframeSDK.d.ts.map new file mode 100644 index 0000000..35f8e1a --- /dev/null +++ b/G102-sequence/sdk/package/dist/GameIframeSDK.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GameIframeSDK.d.ts","sourceRoot":"","sources":["../src/GameIframeSDK.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACH,mBAAmB,EAEnB,SAAS,EACT,eAAe,EACf,eAAe,EAClB,MAAM,SAAS,CAAC;AAEjB;;;GAGG;AACH,qBAAa,aAAc,SAAQ,YAAY,CAAC,SAAS,CAAC;IACtD,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,OAAO,CAAkB;gBAErB,MAAM,EAAE,mBAAmB;IA4BvC;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI;IAOjD;;OAEG;IACH,SAAS,IAAI,iBAAiB,GAAG,IAAI;IAIrC;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,aAAa,IAAI,OAAO;IAQxB;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO;IAa5C;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO;IAiB/C;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI;IAY1C;;OAEG;IACH,eAAe,IAAI,IAAI;IASvB;;OAEG;IACH,YAAY,IAAI,OAAO;IASvB;;OAEG;IACH,OAAO,IAAI,IAAI;IAYf;;OAEG;IACH,iBAAiB,IAAI,cAAc;IAInC;;OAEG;IACH,gBAAgB,IAAI,aAAa;IAQjC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAmC5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAOtB;;OAEG;IACH,OAAO,CAAC,GAAG;CAkBd;AAQD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa,CAE9E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,aAAa,CAQ5E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/GameIframeSDK.js b/G102-sequence/sdk/package/dist/GameIframeSDK.js new file mode 100644 index 0000000..edf6fa1 --- /dev/null +++ b/G102-sequence/sdk/package/dist/GameIframeSDK.js @@ -0,0 +1,254 @@ +"use strict"; +/** + * Game Iframe SDK - Core + * SDK chính - compose các layers: MessageHandler, MessageSender + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GameIframeSDK = void 0; +exports.createGameIframeSDK = createGameIframeSDK; +exports.getGameIframeSDK = getGameIframeSDK; +exports.destroyGameIframeSDK = destroyGameIframeSDK; +const EventEmitter_1 = require("./EventEmitter"); +const MessageHandler_1 = require("./MessageHandler"); +const MessageSender_1 = require("./MessageSender"); +const types_1 = require("./types"); +/** + * GameIframeSDK - Main SDK class + * Composes MessageHandler và MessageSender + */ +class GameIframeSDK extends EventEmitter_1.EventEmitter { + constructor(config) { + super(); + this.pendingData = null; + this.isReady = false; + this.config = { ...types_1.DEFAULT_CONFIG, ...config }; + // Initialize layers + this.messageHandler = new MessageHandler_1.MessageHandler({ + acceptedOrigin: this.config.iframeOrigin, + debug: this.config.debug, + }); + this.messageSender = new MessageSender_1.MessageSender({ + targetOrigin: this.config.iframeOrigin, + debug: this.config.debug, + }); + // Setup event forwarding + this.setupEventForwarding(); + // Start listening + this.messageHandler.start(); + this.log('info', 'SDK initialized', { config: this.config }); + } + // ========================================================================== + // PUBLIC API - Iframe Management + // ========================================================================== + /** + * Set iframe element reference + */ + setIframe(iframe) { + this.messageSender.setIframe(iframe); + this.isReady = false; + this.log('info', 'Iframe set', { hasIframe: !!iframe }); + return this; + } + /** + * Get current iframe + */ + getIframe() { + return this.messageSender.getIframe(); + } + /** + * Check if game is ready + */ + isGameReady() { + return this.isReady; + } + /** + * Check if sender is ready (iframe available) + */ + isSenderReady() { + return this.messageSender.isReady(); + } + // ========================================================================== + // PUBLIC API - Send Data + // ========================================================================== + /** + * Send game data to iframe + */ + sendGameData(data) { + const result = this.messageSender.sendGameData(data); + if (!result.success) { + this.emit('error', { + message: 'Failed to send game data', + error: result.error, + }); + } + return result.success; + } + /** + * Send leaderboard data to iframe + */ + sendLeaderboard(data) { + const result = this.messageSender.sendLeaderboard(data); + if (!result.success) { + this.emit('error', { + message: 'Failed to send leaderboard', + error: result.error, + }); + } + return result.success; + } + // ========================================================================== + // PUBLIC API - Queue & Auto-send + // ========================================================================== + /** + * Queue data to be sent when game is ready + */ + queueGameData(data) { + this.pendingData = data; + this.log('info', 'Data queued for when game is ready'); + // If already ready, send immediately + if (this.isReady) { + this.sendQueuedData(); + } + return this; + } + /** + * Clear queued data + */ + clearQueuedData() { + this.pendingData = null; + return this; + } + // ========================================================================== + // PUBLIC API - Iframe Control + // ========================================================================== + /** + * Force reload iframe + */ + reloadIframe() { + this.isReady = false; + return this.messageSender.reloadIframe(); + } + // ========================================================================== + // PUBLIC API - Lifecycle + // ========================================================================== + /** + * Cleanup and destroy SDK + */ + destroy() { + this.messageHandler.destroy(); + this.removeAllListeners(); + this.pendingData = null; + this.isReady = false; + this.log('info', 'SDK destroyed'); + } + // ========================================================================== + // PUBLIC API - Direct Layer Access (Advanced) + // ========================================================================== + /** + * Get MessageHandler instance for advanced usage + */ + getMessageHandler() { + return this.messageHandler; + } + /** + * Get MessageSender instance for advanced usage + */ + getMessageSender() { + return this.messageSender; + } + // ========================================================================== + // PRIVATE METHODS + // ========================================================================== + /** + * Setup event forwarding from MessageHandler to SDK events + */ + setupEventForwarding() { + // Forward gameReady + this.messageHandler.on('gameReady', () => { + this.isReady = true; + this.emit('gameReady', undefined); + // Auto-send queued data if enabled + if (this.config.autoSendOnReady && this.pendingData) { + setTimeout(() => { + this.sendQueuedData(); + }, this.config.readyDelay); + } + }); + // Forward answerReport + this.messageHandler.on('answerReport', (data) => { + this.emit('answerReport', data); + }); + // Forward finalResult + this.messageHandler.on('finalResult', (data) => { + this.emit('finalResult', data); + }); + // Forward leaderboardRequest + this.messageHandler.on('leaderboardRequest', (data) => { + this.emit('leaderboardRequest', data); + }); + // Forward errors + this.messageHandler.on('error', (error) => { + this.emit('error', error); + }); + } + /** + * Send queued data + */ + sendQueuedData() { + if (this.pendingData) { + this.sendGameData(this.pendingData); + this.pendingData = null; + } + } + /** + * Internal logging + */ + log(level, message, data) { + if (this.config.debug) { + const prefix = '[GameIframeSDK]'; + switch (level) { + case 'info': + console.log(prefix, message, data ?? ''); + break; + case 'warn': + console.warn(prefix, message, data ?? ''); + break; + case 'error': + console.error(prefix, message, data ?? ''); + break; + } + } + this.emit('log', { level, message, data }); + } +} +exports.GameIframeSDK = GameIframeSDK; +// ========================================================================== +// FACTORY / SINGLETON HELPERS +// ========================================================================== +let defaultInstance = null; +/** + * Create SDK instance + */ +function createGameIframeSDK(config) { + return new GameIframeSDK(config); +} +/** + * Get or create default SDK instance + */ +function getGameIframeSDK(config) { + if (!defaultInstance && config) { + defaultInstance = new GameIframeSDK(config); + } + if (!defaultInstance) { + throw new Error('GameIframeSDK not initialized. Call with config first.'); + } + return defaultInstance; +} +/** + * Destroy default instance + */ +function destroyGameIframeSDK() { + defaultInstance?.destroy(); + defaultInstance = null; +} +//# sourceMappingURL=GameIframeSDK.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/GameIframeSDK.js.map b/G102-sequence/sdk/package/dist/GameIframeSDK.js.map new file mode 100644 index 0000000..2bed23f --- /dev/null +++ b/G102-sequence/sdk/package/dist/GameIframeSDK.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GameIframeSDK.js","sourceRoot":"","sources":["../src/GameIframeSDK.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/MessageHandler.d.ts b/G102-sequence/sdk/package/dist/MessageHandler.d.ts new file mode 100644 index 0000000..ab2b7f4 --- /dev/null +++ b/G102-sequence/sdk/package/dist/MessageHandler.d.ts @@ -0,0 +1,70 @@ +/** + * Game Iframe SDK - Message Handler + * Xử lý message từ iframe + */ +import { AnswerReportData, FinalResultData } from './types'; +import { EventEmitter } from './EventEmitter'; +export interface MessageHandlerEvents { + gameReady: void; + answerReport: AnswerReportData; + finalResult: FinalResultData; + leaderboardRequest: { + top: number; + }; + unknownMessage: { + type: string; + data: any; + }; + error: { + message: string; + error?: any; + }; +} +export interface MessageHandlerConfig { + /** + * Accepted origin (use '*' to accept all - not recommended for production) + */ + acceptedOrigin: string; + /** + * Enable debug logging + */ + debug?: boolean; +} +/** + * MessageHandler - Xử lý incoming messages từ iframe + */ +export declare class MessageHandler extends EventEmitter { + private config; + private boundHandler; + private isListening; + constructor(config: MessageHandlerConfig); + /** + * Start listening for messages + */ + start(): this; + /** + * Stop listening for messages + */ + stop(): this; + /** + * Check if handler is listening + */ + isActive(): boolean; + /** + * Handle incoming message + */ + private handleMessage; + /** + * Check if origin is allowed + */ + private isOriginAllowed; + /** + * Debug log + */ + private log; + /** + * Cleanup + */ + destroy(): void; +} +//# sourceMappingURL=MessageHandler.d.ts.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/MessageHandler.d.ts.map b/G102-sequence/sdk/package/dist/MessageHandler.d.ts.map new file mode 100644 index 0000000..ece5d8f --- /dev/null +++ b/G102-sequence/sdk/package/dist/MessageHandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../src/MessageHandler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAiB,gBAAgB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,WAAW,oBAAoB;IACjC,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,EAAE,gBAAgB,CAAC;IAC/B,WAAW,EAAE,eAAe,CAAC;IAC7B,kBAAkB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,cAAc,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC;IAC5C,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;CAC3C;AAED,MAAM,WAAW,oBAAoB;IACjC;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,cAAe,SAAQ,YAAY,CAAC,oBAAoB,CAAC;IAClE,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,YAAY,CAAgD;IACpE,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,EAAE,oBAAoB;IAKxC;;OAEG;IACH,KAAK,IAAI,IAAI;IAab;;OAEG;IACH,IAAI,IAAI,IAAI;IAWZ;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,OAAO,CAAC,aAAa;IAyCrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,GAAG;IAMX;;OAEG;IACH,OAAO,IAAI,IAAI;CAIlB"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/MessageHandler.js b/G102-sequence/sdk/package/dist/MessageHandler.js new file mode 100644 index 0000000..a30bf16 --- /dev/null +++ b/G102-sequence/sdk/package/dist/MessageHandler.js @@ -0,0 +1,115 @@ +"use strict"; +/** + * Game Iframe SDK - Message Handler + * Xử lý message từ iframe + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageHandler = void 0; +const types_1 = require("./types"); +const EventEmitter_1 = require("./EventEmitter"); +/** + * MessageHandler - Xử lý incoming messages từ iframe + */ +class MessageHandler extends EventEmitter_1.EventEmitter { + constructor(config) { + super(); + this.boundHandler = null; + this.isListening = false; + this.config = config; + } + /** + * Start listening for messages + */ + start() { + if (this.isListening) { + return this; + } + this.boundHandler = this.handleMessage.bind(this); + window.addEventListener('message', this.boundHandler); + this.isListening = true; + this.log('MessageHandler started'); + return this; + } + /** + * Stop listening for messages + */ + stop() { + if (this.boundHandler) { + window.removeEventListener('message', this.boundHandler); + this.boundHandler = null; + } + this.isListening = false; + this.log('MessageHandler stopped'); + return this; + } + /** + * Check if handler is listening + */ + isActive() { + return this.isListening; + } + /** + * Handle incoming message + */ + handleMessage(event) { + // Origin check + if (!this.isOriginAllowed(event.origin)) { + return; + } + const { type, data } = event.data || {}; + if (!type) + return; + this.log(`Received: ${type}`, data); + try { + switch (type) { + case types_1.MESSAGE_TYPES.GAME_READY: + this.emit('gameReady', undefined); + break; + case types_1.MESSAGE_TYPES.ANSWER_REPORT: + // Raw data pass-through + this.emit('answerReport', data); + break; + case types_1.MESSAGE_TYPES.FINAL_RESULT: + // Raw data pass-through + this.emit('finalResult', data); + break; + case types_1.MESSAGE_TYPES.GET_LEADERBOARD: + this.emit('leaderboardRequest', { top: data?.top || 10 }); + break; + default: + this.emit('unknownMessage', { type, data }); + break; + } + } + catch (error) { + const err = error; + this.emit('error', { message: `Error handling ${type}`, error: err }); + } + } + /** + * Check if origin is allowed + */ + isOriginAllowed(origin) { + if (this.config.acceptedOrigin === '*') { + return true; + } + return origin === this.config.acceptedOrigin; + } + /** + * Debug log + */ + log(message, data) { + if (this.config.debug) { + console.log('[MessageHandler]', message, data ?? ''); + } + } + /** + * Cleanup + */ + destroy() { + this.stop(); + this.removeAllListeners(); + } +} +exports.MessageHandler = MessageHandler; +//# sourceMappingURL=MessageHandler.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/MessageHandler.js.map b/G102-sequence/sdk/package/dist/MessageHandler.js.map new file mode 100644 index 0000000..813f39f --- /dev/null +++ b/G102-sequence/sdk/package/dist/MessageHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"MessageHandler.js","sourceRoot":"","sources":["../src/MessageHandler.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/MessageSender.d.ts b/G102-sequence/sdk/package/dist/MessageSender.d.ts new file mode 100644 index 0000000..63d3175 --- /dev/null +++ b/G102-sequence/sdk/package/dist/MessageSender.d.ts @@ -0,0 +1,60 @@ +/** + * Game Iframe SDK - Message Sender + * Gửi message đến iframe + */ +import { PushDataPayload, LeaderboardData } from './types'; +export interface MessageSenderConfig { + /** + * Target origin for postMessage + */ + targetOrigin: string; + /** + * Enable debug logging + */ + debug?: boolean; +} +export interface SendResult { + success: boolean; + error?: Error; +} +/** + * MessageSender - Gửi messages đến iframe + */ +export declare class MessageSender { + private config; + private iframe; + constructor(config: MessageSenderConfig); + /** + * Set iframe element + */ + setIframe(iframe: HTMLIFrameElement | null): this; + /** + * Get current iframe + */ + getIframe(): HTMLIFrameElement | null; + /** + * Check if iframe is available + */ + isReady(): boolean; + /** + * Send raw message to iframe + */ + sendRaw(message: any): SendResult; + /** + * Send game data (SERVER_PUSH_DATA) + */ + sendGameData(payload: PushDataPayload): SendResult; + /** + * Send leaderboard (SERVER_PUSH_LEADERBOARD) + */ + sendLeaderboard(data: LeaderboardData): SendResult; + /** + * Reload iframe + */ + reloadIframe(): boolean; + /** + * Debug log + */ + private log; +} +//# sourceMappingURL=MessageSender.d.ts.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/MessageSender.d.ts.map b/G102-sequence/sdk/package/dist/MessageSender.d.ts.map new file mode 100644 index 0000000..a9fff30 --- /dev/null +++ b/G102-sequence/sdk/package/dist/MessageSender.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"MessageSender.d.ts","sourceRoot":"","sources":["../src/MessageSender.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAiB,MAAM,SAAS,CAAC;AAE1E,MAAM,WAAW,mBAAmB;IAChC;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;CACjB;AAED;;GAEG;AACH,qBAAa,aAAa;IACtB,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,MAAM,CAAkC;gBAEpC,MAAM,EAAE,mBAAmB;IAIvC;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI;IAKjD;;OAEG;IACH,SAAS,IAAI,iBAAiB,GAAG,IAAI;IAIrC;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;OAEG;IACH,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,UAAU;IAmBjC;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,eAAe,GAAG,UAAU;IAoBlD;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,eAAe,GAAG,UAAU;IAmBlD;;OAEG;IACH,YAAY,IAAI,OAAO;IAqBvB;;OAEG;IACH,OAAO,CAAC,GAAG;CAYd"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/MessageSender.js b/G102-sequence/sdk/package/dist/MessageSender.js new file mode 100644 index 0000000..672d5ac --- /dev/null +++ b/G102-sequence/sdk/package/dist/MessageSender.js @@ -0,0 +1,132 @@ +"use strict"; +/** + * Game Iframe SDK - Message Sender + * Gửi message đến iframe + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageSender = void 0; +const types_1 = require("./types"); +/** + * MessageSender - Gửi messages đến iframe + */ +class MessageSender { + constructor(config) { + this.iframe = null; + this.config = config; + } + /** + * Set iframe element + */ + setIframe(iframe) { + this.iframe = iframe; + return this; + } + /** + * Get current iframe + */ + getIframe() { + return this.iframe; + } + /** + * Check if iframe is available + */ + isReady() { + return !!this.iframe?.contentWindow; + } + /** + * Send raw message to iframe + */ + sendRaw(message) { + if (!this.iframe?.contentWindow) { + return { + success: false, + error: new Error('Iframe not available'), + }; + } + try { + this.iframe.contentWindow.postMessage(message, this.config.targetOrigin); + this.log('Sent message', { type: message.type }); + return { success: true }; + } + catch (error) { + const err = error; + this.log('Send failed', { error: err.message }); + return { success: false, error: err }; + } + } + /** + * Send game data (SERVER_PUSH_DATA) + */ + sendGameData(payload) { + // Inline message creation + const message = { + type: types_1.MESSAGE_TYPES.SERVER_PUSH_DATA, + jsonData: payload, + }; + const result = this.sendRaw(message); + if (result.success) { + const dataLength = payload.data?.length || 0; + this.log('Sent game data', { + game_id: payload.game_id, + items: dataLength, + }); + } + return result; + } + /** + * Send leaderboard (SERVER_PUSH_LEADERBOARD) + */ + sendLeaderboard(data) { + // Inline message creation + const message = { + type: types_1.MESSAGE_TYPES.SERVER_PUSH_LEADERBOARD, + leaderboardData: data, + }; + const result = this.sendRaw(message); + if (result.success) { + this.log('Sent leaderboard', { + players: data.top_players?.length || 0, + hasUserRank: !!data.user_rank, + }); + } + return result; + } + /** + * Reload iframe + */ + reloadIframe() { + if (!this.iframe) { + return false; + } + const currentSrc = this.iframe.src; + if (!currentSrc || currentSrc === 'about:blank') { + return false; + } + this.iframe.src = ''; + setTimeout(() => { + if (this.iframe) { + this.iframe.src = currentSrc; + this.log('Iframe reloaded'); + } + }, 100); + return true; + } + /** + * Debug log + */ + log(message, data) { + if (this.config.debug) { + console.log('[MessageSender]', message); + if (data) { + try { + console.log(JSON.stringify(data, null, 2)); + } + catch (e) { + console.log(data); + } + } + } + } +} +exports.MessageSender = MessageSender; +//# sourceMappingURL=MessageSender.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/MessageSender.js.map b/G102-sequence/sdk/package/dist/MessageSender.js.map new file mode 100644 index 0000000..069686d --- /dev/null +++ b/G102-sequence/sdk/package/dist/MessageSender.js.map @@ -0,0 +1 @@ +{"version":3,"file":"MessageSender.js","sourceRoot":"","sources":["../src/MessageSender.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/DataValidator.d.ts b/G102-sequence/sdk/package/dist/client/DataValidator.d.ts new file mode 100644 index 0000000..fcd3298 --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/DataValidator.d.ts @@ -0,0 +1,49 @@ +/** + * Data Validator + * Verify data structure cho từng game code + * + * Usage: + * ```typescript + * import { validateGameData, DataValidator } from 'game-iframe-sdk/client'; + * + * const result = validateGameData('G001', receivedData); + * if (!result.valid) { + * console.error('Invalid data:', result.errors); + * } + * ``` + */ +import { GameCode } from '../kit/GameDataHandler'; +export interface ValidationResult { + valid: boolean; + errors: string[]; + warnings: string[]; +} +export interface FieldSchema { + type: 'string' | 'number' | 'boolean' | 'array' | 'object' | 'any'; + required: boolean; + arrayItemType?: 'string' | 'number' | 'object' | 'any'; + description?: string; +} +export interface ItemSchema { + [field: string]: FieldSchema; +} +/** + * Validate game data payload + */ +export declare function validateGameData(gameCode: GameCode, payload: any): ValidationResult; +/** + * Get schema for a game code + */ +export declare function getSchema(gameCode: GameCode): ItemSchema | null; +/** + * Get schema documentation for a game code + */ +export declare function getSchemaDoc(gameCode: GameCode): string; +export declare class DataValidator { + private gameCode; + constructor(gameCode: GameCode); + validate(payload: any): ValidationResult; + getSchema(): ItemSchema | null; + getSchemaDoc(): string; +} +//# sourceMappingURL=DataValidator.d.ts.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/DataValidator.d.ts.map b/G102-sequence/sdk/package/dist/client/DataValidator.d.ts.map new file mode 100644 index 0000000..d6f461f --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/DataValidator.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DataValidator.d.ts","sourceRoot":"","sources":["../../src/client/DataValidator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,QAAQ,EAAc,MAAM,wBAAwB,CAAC;AAM9D,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnE,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACvB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC;CAChC;AAoJD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,GAAG,gBAAgB,CAmDnF;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU,GAAG,IAAI,CAE/D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAyBvD;AAMD,qBAAa,aAAa;IACtB,OAAO,CAAC,QAAQ,CAAW;gBAEf,QAAQ,EAAE,QAAQ;IAI9B,QAAQ,CAAC,OAAO,EAAE,GAAG,GAAG,gBAAgB;IAIxC,SAAS,IAAI,UAAU,GAAG,IAAI;IAI9B,YAAY,IAAI,MAAM;CAGzB"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/DataValidator.js b/G102-sequence/sdk/package/dist/client/DataValidator.js new file mode 100644 index 0000000..bf05190 --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/DataValidator.js @@ -0,0 +1,252 @@ +"use strict"; +/** + * Data Validator + * Verify data structure cho từng game code + * + * Usage: + * ```typescript + * import { validateGameData, DataValidator } from 'game-iframe-sdk/client'; + * + * const result = validateGameData('G001', receivedData); + * if (!result.valid) { + * console.error('Invalid data:', result.errors); + * } + * ``` + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DataValidator = void 0; +exports.validateGameData = validateGameData; +exports.getSchema = getSchema; +exports.getSchemaDoc = getSchemaDoc; +const GameDataHandler_1 = require("../kit/GameDataHandler"); +// ============================================================================= +// SCHEMAS FOR EACH GAME CODE +// ============================================================================= +const QUIZ_BASE_SCHEMA = { + id: { type: 'string', required: true, description: 'Unique question ID' }, + options: { type: 'array', required: true, arrayItemType: 'string', description: 'Answer options' }, + answer: { type: 'number', required: true, description: 'Correct answer index (0-based)' }, +}; +const SCHEMAS = { + // Quiz variants + G001: { + ...QUIZ_BASE_SCHEMA, + question: { type: 'string', required: true, description: 'Text question' }, + }, + G002: { + ...QUIZ_BASE_SCHEMA, + question_audio: { type: 'string', required: true, description: 'Audio URL for question' }, + }, + G003: { + ...QUIZ_BASE_SCHEMA, + question: { type: 'string', required: true, description: 'Text question' }, + // options are audio URLs + }, + G004: { + ...QUIZ_BASE_SCHEMA, + question_image: { type: 'string', required: true, description: 'Image URL for question' }, + question: { type: 'string', required: false, description: 'Optional text hint' }, + }, + // G005: Quiz Text-Image (options are image URLs, client picks index) + G005: { + ...QUIZ_BASE_SCHEMA, + question: { type: 'string', required: true, description: 'Text question' }, + // options are image URLs, answer is index pointing to correct image + }, + // Sequence Word variants + G110: { + id: { type: 'string', required: true }, + word: { type: 'string', required: true, description: 'The word to arrange' }, + parts: { type: 'array', required: true, arrayItemType: 'string', description: 'Letters/parts to arrange' }, + answer: { type: 'array', required: true, arrayItemType: 'string', description: 'Correct order' }, + }, + G111: { + id: { type: 'string', required: true }, + word: { type: 'string', required: true }, + parts: { type: 'array', required: true, arrayItemType: 'string' }, + answer: { type: 'array', required: true, arrayItemType: 'string' }, + audio_url: { type: 'string', required: true, description: 'Audio hint URL' }, + }, + G112: { + id: { type: 'string', required: true }, + word: { type: 'string', required: true }, + parts: { type: 'array', required: true, arrayItemType: 'string' }, + answer: { type: 'array', required: true, arrayItemType: 'string' }, + audio_url: { type: 'string', required: true }, + }, + G113: { + id: { type: 'string', required: true }, + word: { type: 'string', required: true }, + parts: { type: 'array', required: true, arrayItemType: 'string' }, + answer: { type: 'array', required: true, arrayItemType: 'string' }, + audio_url: { type: 'string', required: true }, + }, + // Sequence Sentence variants + G120: { + id: { type: 'string', required: true }, + sentence: { type: 'string', required: false, description: 'Full sentence (hint)' }, + parts: { type: 'array', required: true, arrayItemType: 'string', description: 'Words to arrange' }, + answer: { type: 'array', required: true, arrayItemType: 'string', description: 'Correct word order' }, + }, + G121: { + id: { type: 'string', required: true }, + sentence: { type: 'string', required: false }, + parts: { type: 'array', required: true, arrayItemType: 'string' }, + answer: { type: 'array', required: true, arrayItemType: 'string' }, + audio_url: { type: 'string', required: true }, + }, + G122: { + id: { type: 'string', required: true }, + sentence: { type: 'string', required: false }, + parts: { type: 'array', required: true, arrayItemType: 'string' }, + answer: { type: 'array', required: true, arrayItemType: 'string' }, + audio_url: { type: 'string', required: true }, + }, + G123: { + id: { type: 'string', required: true }, + sentence: { type: 'string', required: false }, + parts: { type: 'array', required: true, arrayItemType: 'string' }, + answer: { type: 'array', required: true, arrayItemType: 'string' }, + audio_url: { type: 'string', required: true }, + } +}; +// ============================================================================= +// VALIDATOR +// ============================================================================= +/** + * Validate a single item against schema + */ +function validateItem(item, schema, itemIndex) { + const errors = []; + if (!item || typeof item !== 'object') { + errors.push(`Item [${itemIndex}]: Must be an object`); + return errors; + } + for (const [field, fieldSchema] of Object.entries(schema)) { + const value = item[field]; + // Check required + if (fieldSchema.required && (value === undefined || value === null)) { + errors.push(`Item [${itemIndex}].${field}: Required field is missing`); + continue; + } + // Skip validation if optional and not present + if (!fieldSchema.required && (value === undefined || value === null)) { + continue; + } + // Check type + const actualType = Array.isArray(value) ? 'array' : typeof value; + if (fieldSchema.type !== 'any' && actualType !== fieldSchema.type) { + errors.push(`Item [${itemIndex}].${field}: Expected ${fieldSchema.type}, got ${actualType}`); + continue; + } + // Check array items + if (fieldSchema.type === 'array' && fieldSchema.arrayItemType && fieldSchema.arrayItemType !== 'any') { + for (let i = 0; i < value.length; i++) { + const itemType = typeof value[i]; + if (itemType !== fieldSchema.arrayItemType) { + errors.push(`Item [${itemIndex}].${field}[${i}]: Expected ${fieldSchema.arrayItemType}, got ${itemType}`); + } + } + } + } + return errors; +} +/** + * Validate game data payload + */ +function validateGameData(gameCode, payload) { + const errors = []; + const warnings = []; + // Check game code + if (!GameDataHandler_1.GAME_CODES[gameCode]) { + errors.push(`Unknown game code: ${gameCode}`); + return { valid: false, errors, warnings }; + } + // Check payload structure + if (!payload || typeof payload !== 'object') { + errors.push('Payload must be an object'); + return { valid: false, errors, warnings }; + } + // Check data array + const items = payload.data || payload.items || payload.questions; + if (!items) { + errors.push('Missing data array (expected "data", "items", or "questions")'); + return { valid: false, errors, warnings }; + } + if (!Array.isArray(items)) { + errors.push('"data" must be an array'); + return { valid: false, errors, warnings }; + } + if (items.length === 0) { + warnings.push('Data array is empty'); + } + // Validate each item + const schema = SCHEMAS[gameCode]; + for (let i = 0; i < items.length; i++) { + const itemErrors = validateItem(items[i], schema, i); + errors.push(...itemErrors); + } + // Check for duplicate IDs + const ids = items.map((item) => item.id).filter(Boolean); + const duplicates = ids.filter((id, index) => ids.indexOf(id) !== index); + if (duplicates.length > 0) { + warnings.push(`Duplicate IDs found: ${[...new Set(duplicates)].join(', ')}`); + } + return { + valid: errors.length === 0, + errors, + warnings, + }; +} +/** + * Get schema for a game code + */ +function getSchema(gameCode) { + return SCHEMAS[gameCode] ?? null; +} +/** + * Get schema documentation for a game code + */ +function getSchemaDoc(gameCode) { + const schema = SCHEMAS[gameCode]; + if (!schema) + return `Unknown game code: ${gameCode}`; + const gameInfo = GameDataHandler_1.GAME_CODES[gameCode]; + const lines = [ + `## ${gameCode}: ${gameInfo.name}`, + `Category: ${gameInfo.category}`, + '', + '### Fields:', + ]; + for (const [field, fieldSchema] of Object.entries(schema)) { + const required = fieldSchema.required ? '(required)' : '(optional)'; + let type = fieldSchema.type; + if (fieldSchema.arrayItemType) { + type = `${fieldSchema.type}<${fieldSchema.arrayItemType}>`; + } + lines.push(`- **${field}**: ${type} ${required}`); + if (fieldSchema.description) { + lines.push(` - ${fieldSchema.description}`); + } + } + return lines.join('\n'); +} +// ============================================================================= +// DATA VALIDATOR CLASS +// ============================================================================= +class DataValidator { + constructor(gameCode) { + this.gameCode = gameCode; + } + validate(payload) { + return validateGameData(this.gameCode, payload); + } + getSchema() { + return getSchema(this.gameCode); + } + getSchemaDoc() { + return getSchemaDoc(this.gameCode); + } +} +exports.DataValidator = DataValidator; +//# sourceMappingURL=DataValidator.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/DataValidator.js.map b/G102-sequence/sdk/package/dist/client/DataValidator.js.map new file mode 100644 index 0000000..a434717 --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/DataValidator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DataValidator.js","sourceRoot":"","sources":["../../src/client/DataValidator.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/GameClientSDK.d.ts b/G102-sequence/sdk/package/dist/client/GameClientSDK.d.ts new file mode 100644 index 0000000..f074e21 --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/GameClientSDK.d.ts @@ -0,0 +1,146 @@ +/** + * GameClientSDK - SDK dành cho Game Iframe + * + * Sử dụng trong game để: + * - Tự động xác định mode (preview/live) từ URL + * - Nhận data từ parent (preview) hoặc fetch API (live) + * - Verify answers locally + * - Report results về parent + */ +import { GameCode } from '../kit/GameDataHandler'; +import { ValidationResult } from './DataValidator'; +export type ClientMode = 'preview' | 'live' | 'dev'; +export interface ClientSDKConfig { + debug?: boolean; + apiBaseUrl?: string; + getAuthHeaders?: () => Record; +} +export interface URLParams { + mode: ClientMode; + gameCode: GameCode; + gameId?: string; + lid?: string; + studentId?: string; +} +export interface GameDataPayload { + game_id: string; + game_code: GameCode; + data: any[]; + completed_question_ids?: Array<{ + id: string; + result: 0 | 1; + }>; +} +export interface AnswerResult { + isCorrect: boolean; + score: number; + feedback?: string; +} +export interface FinalResult { + score: number; + total: number; + correct: number; + wrong: number; + details: Array<{ + question_id: string; + choice: any; + result: 0 | 1; + time_spent: number; + }>; +} +export interface ClientSDKEvents { + ready: void; + dataReceived: { + items: any[]; + resumeData?: any[]; + validation?: ValidationResult; + }; + error: { + message: string; + error?: any; + }; + modeDetected: { + mode: ClientMode; + params: URLParams; + }; + validationError: { + validation: ValidationResult; + }; +} +type EventHandler = (data: T) => void; +declare class SimpleEventEmitter> { + private handlers; + on(event: K, handler: EventHandler): () => void; + off(event: K, handler: EventHandler): void; + protected emit(event: K, data: Events[K]): void; +} +export declare class GameClientSDK extends SimpleEventEmitter { + private config; + private params; + private mode; + private originalItems; + private sanitizedItems; + private userAnswers; + private isInitialized; + private startTime; + constructor(config?: ClientSDKConfig); + /** + * Get current mode + */ + getMode(): ClientMode; + /** + * Get URL params + */ + getParams(): URLParams; + /** + * Get game code + */ + getGameCode(): GameCode; + /** + * Get sanitized items (safe for rendering) + */ + getItems(): any[]; + /** + * Submit an answer and get verification result + */ + submitAnswer(questionId: string, choice: any): AnswerResult; + /** + * Get final result + */ + getFinalResult(): FinalResult; + /** + * Report final result to parent + */ + reportFinalResult(result?: FinalResult): void; + /** + * Request leaderboard from parent + */ + requestLeaderboard(top?: number): void; + /** + * Cleanup + */ + destroy(): void; + private parseURLParams; + private setupMessageListener; + private handleMessage; + private initialize; + /** + * Load mock data for dev mode + */ + private loadMockData; + private sendGameReady; + private fetchLiveData; + private handleDataReceived; + private sendAnswerReport; + private log; +} +/** + * Get or create GameClientSDK instance + */ +export declare function getGameClientSDK(config?: ClientSDKConfig): GameClientSDK; +/** + * Destroy client instance + */ +export declare function destroyGameClientSDK(): void; +export {}; +//# sourceMappingURL=GameClientSDK.d.ts.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/GameClientSDK.d.ts.map b/G102-sequence/sdk/package/dist/client/GameClientSDK.d.ts.map new file mode 100644 index 0000000..6c51c12 --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/GameClientSDK.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GameClientSDK.d.ts","sourceRoot":"","sources":["../../src/client/GameClientSDK.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAA8C,MAAM,wBAAwB,CAAC;AAE9F,OAAO,EAAoB,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAMrE,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,QAAQ,CAAC;IACpB,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,sBAAsB,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAA;KAAE,CAAC,CAAC;CACjE;AAED,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,KAAK,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,GAAG,CAAC;QACZ,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;CACN;AAED,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,IAAI,CAAC;IACZ,YAAY,EAAE;QAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,GAAG,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,gBAAgB,CAAA;KAAE,CAAC;IAClF,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACxC,YAAY,EAAE;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,SAAS,CAAA;KAAE,CAAC;IACtD,eAAe,EAAE;QAAE,UAAU,EAAE,gBAAgB,CAAA;KAAE,CAAC;CACrD;AAMD,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;AAEzC,cAAM,kBAAkB,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACvD,OAAO,CAAC,QAAQ,CAAwD;IAExE,EAAE,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAQlF,GAAG,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAI7E,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI;CAS1E;AAMD,qBAAa,aAAc,SAAQ,kBAAkB,CAAC,eAAe,CAAC;IAClE,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,IAAI,CAAa;IAGzB,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,WAAW,CAAwE;IAE3F,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,SAAS,CAAK;gBAEV,MAAM,GAAE,eAAoB;IA6BxC;;OAEG;IACH,OAAO,IAAI,UAAU;IAIrB;;OAEG;IACH,SAAS,IAAI,SAAS;IAItB;;OAEG;IACH,WAAW,IAAI,QAAQ;IAIvB;;OAEG;IACH,QAAQ,IAAI,GAAG,EAAE;IAIjB;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,YAAY;IA2B3D;;OAEG;IACH,cAAc,IAAI,WAAW;IAoB7B;;OAEG;IACH,iBAAiB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI;IAW7C;;OAEG;IACH,kBAAkB,CAAC,GAAG,SAAK,GAAG,IAAI;IAOlC;;OAEG;IACH,OAAO,IAAI,IAAI;IAYf,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,aAAa;YAqBP,UAAU;IAiBxB;;OAEG;IACH,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,aAAa;YAMP,aAAa;IAqC3B,OAAO,CAAC,kBAAkB;IAgD1B,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,GAAG;CAkBd;AAQD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,aAAa,CAKxE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/GameClientSDK.js b/G102-sequence/sdk/package/dist/client/GameClientSDK.js new file mode 100644 index 0000000..3c99487 --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/GameClientSDK.js @@ -0,0 +1,365 @@ +"use strict"; +/** + * GameClientSDK - SDK dành cho Game Iframe + * + * Sử dụng trong game để: + * - Tự động xác định mode (preview/live) từ URL + * - Nhận data từ parent (preview) hoặc fetch API (live) + * - Verify answers locally + * - Report results về parent + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GameClientSDK = void 0; +exports.getGameClientSDK = getGameClientSDK; +exports.destroyGameClientSDK = destroyGameClientSDK; +const GameDataHandler_1 = require("../kit/GameDataHandler"); +const MockData_1 = require("./MockData"); +const DataValidator_1 = require("./DataValidator"); +class SimpleEventEmitter { + constructor() { + this.handlers = new Map(); + } + on(event, handler) { + if (!this.handlers.has(event)) { + this.handlers.set(event, new Set()); + } + this.handlers.get(event).add(handler); + return () => this.off(event, handler); + } + off(event, handler) { + this.handlers.get(event)?.delete(handler); + } + emit(event, data) { + this.handlers.get(event)?.forEach(handler => { + try { + handler(data); + } + catch (err) { + console.error(`[GameClientSDK] Error in ${String(event)} handler:`, err); + } + }); + } +} +// ============================================================================= +// GAME CLIENT SDK +// ============================================================================= +class GameClientSDK extends SimpleEventEmitter { + constructor(config = {}) { + super(); + // Data storage + this.originalItems = new Map(); // Có đáp án + this.sanitizedItems = []; // Không có đáp án + this.userAnswers = new Map(); + this.isInitialized = false; + this.startTime = 0; + this.config = { + debug: config.debug ?? false, + apiBaseUrl: config.apiBaseUrl ?? '', + getAuthHeaders: config.getAuthHeaders ?? (() => ({})), + }; + // Parse URL params + this.params = this.parseURLParams(); + this.mode = this.params.mode; + this.log('info', 'SDK created', { mode: this.mode, params: this.params }); + // Emit mode detected + this.emit('modeDetected', { mode: this.mode, params: this.params }); + // Setup message listener + this.setupMessageListener(); + // Auto-initialize based on mode + this.initialize(); + } + // ========================================================================== + // PUBLIC API + // ========================================================================== + /** + * Get current mode + */ + getMode() { + return this.mode; + } + /** + * Get URL params + */ + getParams() { + return { ...this.params }; + } + /** + * Get game code + */ + getGameCode() { + return this.params.gameCode; + } + /** + * Get sanitized items (safe for rendering) + */ + getItems() { + return this.sanitizedItems; + } + /** + * Submit an answer and get verification result + */ + submitAnswer(questionId, choice) { + const originalItem = this.originalItems.get(questionId); + if (!originalItem) { + this.log('warn', `Item not found: ${questionId}`); + return { isCorrect: false, score: 0, feedback: 'Question not found' }; + } + // Verify using GameDataHandler + const result = (0, GameDataHandler_1.checkAnswer)(this.params.gameCode, originalItem, choice); + // Store user answer + const timeSpent = Date.now() - (this.userAnswers.size === 0 ? this.startTime : Date.now()); + this.userAnswers.set(questionId, { + choice, + result: result.isCorrect ? 1 : 0, + time: timeSpent, + }); + // Report to parent + this.sendAnswerReport(questionId, choice, result.isCorrect ? 1 : 0, timeSpent); + this.log('info', `Answer submitted: ${questionId}`, { choice, result }); + return result; + } + /** + * Get final result + */ + getFinalResult() { + const details = Array.from(this.userAnswers.entries()).map(([id, data]) => ({ + question_id: id, + choice: data.choice, + result: data.result, + time_spent: data.time, + })); + const correct = details.filter(d => d.result === 1).length; + const total = this.originalItems.size; + return { + score: total > 0 ? Math.round((correct / total) * 100) : 0, + total, + correct, + wrong: total - correct, + details, + }; + } + /** + * Report final result to parent + */ + reportFinalResult(result) { + const finalResult = result ?? this.getFinalResult(); + window.parent.postMessage({ + type: 'FINAL_RESULT', + data: finalResult, + }, '*'); + this.log('info', 'Final result reported', finalResult); + } + /** + * Request leaderboard from parent + */ + requestLeaderboard(top = 10) { + window.parent.postMessage({ + type: 'GET_LEADERBOARD', + data: { top }, + }, '*'); + } + /** + * Cleanup + */ + destroy() { + window.removeEventListener('message', this.handleMessage); + this.originalItems.clear(); + this.sanitizedItems = []; + this.userAnswers.clear(); + this.log('info', 'SDK destroyed'); + } + // ========================================================================== + // PRIVATE METHODS + // ========================================================================== + parseURLParams() { + const searchParams = new URLSearchParams(window.location.search); + const mode = (searchParams.get('mode') || 'preview'); + const gameCode = (searchParams.get('game_code') || 'G001'); + const gameId = searchParams.get('game_id') || undefined; + const lid = searchParams.get('lid') || undefined; + const studentId = searchParams.get('student_id') || undefined; + // Validate mode + if (mode !== 'preview' && mode !== 'live' && mode !== 'dev') { + this.log('warn', `Invalid mode: ${mode}, defaulting to preview`); + } + // Validate game code + if (!GameDataHandler_1.GAME_CODES[gameCode]) { + this.log('warn', `Unknown game code: ${gameCode}`); + } + return { mode, gameCode, gameId, lid, studentId }; + } + setupMessageListener() { + this.handleMessage = this.handleMessage.bind(this); + window.addEventListener('message', this.handleMessage); + } + handleMessage(event) { + const { type, jsonData, leaderboardData } = event.data || {}; + this.log('debug', 'Message received', { type, hasData: !!jsonData }); + switch (type) { + case 'SERVER_PUSH_DATA': + if (jsonData) { + this.handleDataReceived(jsonData); + } + break; + case 'SERVER_PUSH_LEADERBOARD': + if (leaderboardData) { + this.log('info', 'Leaderboard received', leaderboardData); + // Could emit event here + } + break; + } + } + async initialize() { + // Send GAME_READY immediately + this.sendGameReady(); + if (this.mode === 'dev') { + // Dev mode: load mock data immediately + this.log('info', 'DEV MODE: Loading mock data...'); + this.loadMockData(); + } + else if (this.mode === 'live') { + // Live mode: fetch data from API + await this.fetchLiveData(); + } + else { + // Preview mode: wait for postMessage + this.log('info', 'Preview mode: waiting for SERVER_PUSH_DATA...'); + } + } + /** + * Load mock data for dev mode + */ + loadMockData() { + const mockData = (0, MockData_1.getMockData)(this.params.gameCode); + if (!mockData) { + this.emit('error', { + message: `No mock data available for game code: ${this.params.gameCode}` + }); + return; + } + this.log('info', `Loaded mock data for ${this.params.gameCode}`); + this.handleDataReceived(mockData); + } + sendGameReady() { + window.parent.postMessage({ type: 'GAME_READY' }, '*'); + this.emit('ready', undefined); + this.log('info', 'GAME_READY sent'); + } + async fetchLiveData() { + const { gameId, lid } = this.params; + if (!gameId || !lid) { + this.emit('error', { message: 'Live mode requires game_id and lid' }); + return; + } + if (!this.config.apiBaseUrl) { + this.emit('error', { message: 'Live mode requires apiBaseUrl' }); + return; + } + try { + const url = `${this.config.apiBaseUrl}/games/${gameId}?lid=${lid}`; + const headers = { + 'Content-Type': 'application/json', + ...this.config.getAuthHeaders(), + }; + this.log('info', `Fetching live data: ${url}`); + const response = await fetch(url, { headers }); + if (!response.ok) { + throw new Error(`API Error: ${response.status}`); + } + const data = await response.json(); + this.handleDataReceived(data); + } + catch (error) { + this.log('error', 'Failed to fetch live data', error); + this.emit('error', { message: 'Failed to fetch game data', error }); + } + } + handleDataReceived(payload) { + this.startTime = Date.now(); + // Update game code if provided + if (payload.game_code && GameDataHandler_1.GAME_CODES[payload.game_code]) { + this.params.gameCode = payload.game_code; + } + // Validate data structure + const validation = (0, DataValidator_1.validateGameData)(this.params.gameCode, payload); + if (!validation.valid) { + this.log('error', 'Data validation failed', validation.errors); + this.emit('validationError', { validation }); + // Continue anyway to allow partial rendering + } + if (validation.warnings.length > 0) { + this.log('warn', 'Data validation warnings', validation.warnings); + } + // Extract items from various payload formats + const items = payload.data || payload.items || payload.questions || []; + const resumeData = payload.completed_question_ids || []; + // Store original items (with answers) + this.originalItems.clear(); + items.forEach((item) => { + if (item.id) { + this.originalItems.set(item.id, item); + } + }); + // Sanitize for client (remove answers) + this.sanitizedItems = (0, GameDataHandler_1.sanitizeForClient)(this.params.gameCode, items); + this.isInitialized = true; + this.log('info', `Data received: ${items.length} items, ${resumeData.length} completed`); + // Emit event with validation result + this.emit('dataReceived', { + items: this.sanitizedItems, + resumeData, + validation, + }); + } + sendAnswerReport(questionId, choice, result, timeSpent) { + window.parent.postMessage({ + type: 'ANSWER_REPORT', + data: { + question_id: questionId, + question_index: Array.from(this.originalItems.keys()).indexOf(questionId), + choice, + result, + time_spent: timeSpent, + }, + }, '*'); + } + log(level, message, data) { + if (!this.config.debug && level === 'debug') + return; + const prefix = `[GameClientSDK:${this.mode}]`; + switch (level) { + case 'debug': + case 'info': + console.log(prefix, message, data ?? ''); + break; + case 'warn': + console.warn(prefix, message, data ?? ''); + break; + case 'error': + console.error(prefix, message, data ?? ''); + break; + } + } +} +exports.GameClientSDK = GameClientSDK; +// ============================================================================= +// FACTORY +// ============================================================================= +let clientInstance = null; +/** + * Get or create GameClientSDK instance + */ +function getGameClientSDK(config) { + if (!clientInstance) { + clientInstance = new GameClientSDK(config); + } + return clientInstance; +} +/** + * Destroy client instance + */ +function destroyGameClientSDK() { + clientInstance?.destroy(); + clientInstance = null; +} +//# sourceMappingURL=GameClientSDK.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/GameClientSDK.js.map b/G102-sequence/sdk/package/dist/client/GameClientSDK.js.map new file mode 100644 index 0000000..d495545 --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/GameClientSDK.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GameClientSDK.js","sourceRoot":"","sources":["../../src/client/GameClientSDK.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/MockData.d.ts b/G102-sequence/sdk/package/dist/client/MockData.d.ts new file mode 100644 index 0000000..926fc6d --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/MockData.d.ts @@ -0,0 +1,182 @@ +/** + * Mock Data cho từng Game Code + * Game developers dùng để test game standalone + * + * Usage: + * ```typescript + * import { MockData } from 'game-iframe-sdk/client'; + * + * // Lấy sample data cho Quiz Text-Text + * const quizData = MockData.G001; + * + * // Lấy sample data cho Sequence Word + * const seqData = MockData.G110; + * ``` + */ +import { GameCode } from '../kit/GameDataHandler'; +/** G001: Quiz Text-Text */ +export declare const MOCK_G001: { + game_code: GameCode; + game_id: string; + data: { + id: string; + question: string; + options: string[]; + answer: number; + }[]; +}; +/** G002: Quiz Audio-Text */ +export declare const MOCK_G002: { + game_code: GameCode; + game_id: string; + data: { + id: string; + question_audio: string; + options: string[]; + answer: number; + }[]; +}; +/** G003: Quiz Text-Audio */ +export declare const MOCK_G003: { + game_code: GameCode; + game_id: string; + data: { + id: string; + question: string; + options: string[]; + answer: number; + }[]; +}; +/** G004: Quiz Image-Text */ +export declare const MOCK_G004: { + game_code: GameCode; + game_id: string; + data: ({ + id: string; + question_image: string; + question: string; + options: string[]; + answer: number; + } | { + id: string; + question_image: string; + options: string[]; + answer: number; + question?: undefined; + })[]; +}; +/** G005: Quiz Text-Image */ +export declare const MOCK_G005: { + game_code: GameCode; + game_id: string; + data: { + id: string; + question: string; + options: string[]; + answer: number; + }[]; +}; +/** G110: Sequence Word - no audio */ +export declare const MOCK_G110: { + game_code: GameCode; + game_id: string; + data: { + id: string; + word: string; + parts: string[]; + answer: string[]; + }[]; +}; +/** G111: Sequence Word - audio, hide 2 */ +export declare const MOCK_G111: { + game_code: GameCode; + game_id: string; + data: { + id: string; + word: string; + parts: string[]; + answer: string[]; + audio_url: string; + }[]; +}; +/** G112: Sequence Word - audio, hide 4 */ +export declare const MOCK_G112: { + game_code: GameCode; + game_id: string; + data: { + id: string; + word: string; + parts: string[]; + answer: string[]; + audio_url: string; + }[]; +}; +/** G113: Sequence Word - audio, hide all */ +export declare const MOCK_G113: { + game_code: GameCode; + game_id: string; + data: { + id: string; + word: string; + parts: string[]; + answer: string[]; + audio_url: string; + }[]; +}; +/** G120: Sequence Sentence - no audio */ +export declare const MOCK_G120: { + game_code: GameCode; + game_id: string; + data: { + id: string; + sentence: string; + parts: string[]; + answer: string[]; + }[]; +}; +/** G121: Sequence Sentence - audio, hide 2 */ +export declare const MOCK_G121: { + game_code: GameCode; + game_id: string; + data: { + id: string; + sentence: string; + parts: string[]; + answer: string[]; + audio_url: string; + }[]; +}; +/** G122: Sequence Sentence - audio, hide 4 */ +export declare const MOCK_G122: { + game_code: GameCode; + game_id: string; + data: { + id: string; + sentence: string; + parts: string[]; + answer: string[]; + audio_url: string; + }[]; +}; +/** G123: Sequence Sentence - audio, hide all */ +export declare const MOCK_G123: { + game_code: GameCode; + game_id: string; + data: { + id: string; + sentence: string; + parts: string[]; + answer: string[]; + audio_url: string; + }[]; +}; +export declare const MockData: Record; +/** + * Get mock data for a game code + */ +export declare function getMockData(code: GameCode): any; +/** + * Get all available game codes + */ +export declare function getAvailableGameCodes(): GameCode[]; +//# sourceMappingURL=MockData.d.ts.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/MockData.d.ts.map b/G102-sequence/sdk/package/dist/client/MockData.d.ts.map new file mode 100644 index 0000000..ae91d18 --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/MockData.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"MockData.d.ts","sourceRoot":"","sources":["../../src/client/MockData.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAMlD,2BAA2B;AAC3B,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;CAsBhC,CAAC;AAEF,4BAA4B;AAC5B,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;CAgBhC,CAAC;AAEF,4BAA4B;AAC5B,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;CAchC,CAAC;AAEF,4BAA4B;AAC5B,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;;;;;;;;CAiBhC,CAAC;AAEF,4BAA4B;AAC5B,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;CAchC,CAAC;AAMF,qCAAqC;AACrC,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;CAsBhC,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;;CAWhC,CAAC;AAEF,0CAA0C;AAC1C,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;;CAWhC,CAAC;AAEF,4CAA4C;AAC5C,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;;CAWhC,CAAC;AAMF,yCAAyC;AACzC,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;CAgBhC,CAAC;AAEF,8CAA8C;AAC9C,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;;CAWhC,CAAC;AAEF,8CAA8C;AAC9C,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;;CAWhC,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,SAAS;eACG,QAAQ;;;;;;;;;CAWhC,CAAC;AAMF,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,CAiB1C,CAAC;AAEF;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,GAAG,CAE/C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,QAAQ,EAAE,CAElD"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/MockData.js b/G102-sequence/sdk/package/dist/client/MockData.js new file mode 100644 index 0000000..ea94250 --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/MockData.js @@ -0,0 +1,289 @@ +"use strict"; +/** + * Mock Data cho từng Game Code + * Game developers dùng để test game standalone + * + * Usage: + * ```typescript + * import { MockData } from 'game-iframe-sdk/client'; + * + * // Lấy sample data cho Quiz Text-Text + * const quizData = MockData.G001; + * + * // Lấy sample data cho Sequence Word + * const seqData = MockData.G110; + * ``` + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MockData = exports.MOCK_G123 = exports.MOCK_G122 = exports.MOCK_G121 = exports.MOCK_G120 = exports.MOCK_G113 = exports.MOCK_G112 = exports.MOCK_G111 = exports.MOCK_G110 = exports.MOCK_G005 = exports.MOCK_G004 = exports.MOCK_G003 = exports.MOCK_G002 = exports.MOCK_G001 = void 0; +exports.getMockData = getMockData; +exports.getAvailableGameCodes = getAvailableGameCodes; +// ============================================================================= +// QUIZ MOCK DATA +// ============================================================================= +/** G001: Quiz Text-Text */ +exports.MOCK_G001 = { + game_code: 'G001', + game_id: 'mock-quiz-text-text', + data: [ + { + id: 'q1', + question: 'Thủ đô của Việt Nam là gì?', + options: ['Hà Nội', 'Hồ Chí Minh', 'Đà Nẵng', 'Huế'], + answer: 0, // Index của đáp án đúng + }, + { + id: 'q2', + question: '2 + 2 = ?', + options: ['3', '4', '5', '6'], + answer: 1, + }, + { + id: 'q3', + question: 'Con vật nào biết bay?', + options: ['Chó', 'Mèo', 'Chim', 'Cá'], + answer: 2, + }, + ], +}; +/** G002: Quiz Audio-Text */ +exports.MOCK_G002 = { + game_code: 'G002', + game_id: 'mock-quiz-audio-text', + data: [ + { + id: 'q1', + question_audio: 'https://example.com/audio/question1.mp3', + options: ['Apple', 'Banana', 'Orange', 'Grape'], + answer: 0, + }, + { + id: 'q2', + question_audio: 'https://example.com/audio/question2.mp3', + options: ['Dog', 'Cat', 'Bird', 'Fish'], + answer: 2, + }, + ], +}; +/** G003: Quiz Text-Audio */ +exports.MOCK_G003 = { + game_code: 'G003', + game_id: 'mock-quiz-text-audio', + data: [ + { + id: 'q1', + question: 'Chọn phát âm đúng của từ "Hello"', + options: [ + 'https://example.com/audio/hello1.mp3', + 'https://example.com/audio/hello2.mp3', + 'https://example.com/audio/hello3.mp3', + ], + answer: 0, + }, + ], +}; +/** G004: Quiz Image-Text */ +exports.MOCK_G004 = { + game_code: 'G004', + game_id: 'mock-quiz-image-text', + data: [ + { + id: 'q1', + question_image: 'https://example.com/images/apple.jpg', + question: 'Đây là quả gì?', // Optional hint + options: ['Táo', 'Cam', 'Chuối', 'Nho'], + answer: 0, + }, + { + id: 'q2', + question_image: 'https://example.com/images/cat.jpg', + options: ['Chó', 'Mèo', 'Thỏ', 'Chuột'], + answer: 1, + }, + ], +}; +/** G005: Quiz Text-Image */ +exports.MOCK_G005 = { + game_code: 'G005', + game_id: 'mock-quiz-text-image', + data: [ + { + id: 'q1', + question: 'Chọn hình ảnh con mèo', + options: [ + 'https://example.com/images/dog.jpg', + 'https://example.com/images/cat.jpg', + 'https://example.com/images/bird.jpg', + ], + answer: 1, + }, + ], +}; +// ============================================================================= +// SEQUENCE WORD MOCK DATA +// ============================================================================= +/** G110: Sequence Word - no audio */ +exports.MOCK_G110 = { + game_code: 'G110', + game_id: 'mock-sequence-word', + data: [ + { + id: 'sw1', + word: 'APPLE', + parts: ['A', 'P', 'P', 'L', 'E'], // Đáp án đúng theo thứ tự + answer: ['A', 'P', 'P', 'L', 'E'], // SDK sẽ shuffle parts, giữ answer để verify + }, + { + id: 'sw2', + word: 'HELLO', + parts: ['H', 'E', 'L', 'L', 'O'], + answer: ['H', 'E', 'L', 'L', 'O'], + }, + { + id: 'sw3', + word: 'WORLD', + parts: ['W', 'O', 'R', 'L', 'D'], + answer: ['W', 'O', 'R', 'L', 'D'], + }, + ], +}; +/** G111: Sequence Word - audio, hide 2 */ +exports.MOCK_G111 = { + game_code: 'G111', + game_id: 'mock-sequence-word-audio-2', + data: [ + { + id: 'sw1', + word: 'BANANA', + parts: ['B', 'A', 'N', 'A', 'N', 'A'], + answer: ['B', 'A', 'N', 'A', 'N', 'A'], + audio_url: 'https://example.com/audio/banana.mp3', + }, + ], +}; +/** G112: Sequence Word - audio, hide 4 */ +exports.MOCK_G112 = { + game_code: 'G112', + game_id: 'mock-sequence-word-audio-4', + data: [ + { + id: 'sw1', + word: 'COMPUTER', + parts: ['C', 'O', 'M', 'P', 'U', 'T', 'E', 'R'], + answer: ['C', 'O', 'M', 'P', 'U', 'T', 'E', 'R'], + audio_url: 'https://example.com/audio/computer.mp3', + }, + ], +}; +/** G113: Sequence Word - audio, hide all */ +exports.MOCK_G113 = { + game_code: 'G113', + game_id: 'mock-sequence-word-audio-all', + data: [ + { + id: 'sw1', + word: 'ELEPHANT', + parts: ['E', 'L', 'E', 'P', 'H', 'A', 'N', 'T'], + answer: ['E', 'L', 'E', 'P', 'H', 'A', 'N', 'T'], + audio_url: 'https://example.com/audio/elephant.mp3', + }, + ], +}; +// ============================================================================= +// SEQUENCE SENTENCE MOCK DATA +// ============================================================================= +/** G120: Sequence Sentence - no audio */ +exports.MOCK_G120 = { + game_code: 'G120', + game_id: 'mock-sequence-sentence', + data: [ + { + id: 'ss1', + sentence: 'I love learning English.', + parts: ['I', 'love', 'learning', 'English.'], + answer: ['I', 'love', 'learning', 'English.'], + }, + { + id: 'ss2', + sentence: 'The cat is sleeping.', + parts: ['The', 'cat', 'is', 'sleeping.'], + answer: ['The', 'cat', 'is', 'sleeping.'], + }, + ], +}; +/** G121: Sequence Sentence - audio, hide 2 */ +exports.MOCK_G121 = { + game_code: 'G121', + game_id: 'mock-sequence-sentence-audio-2', + data: [ + { + id: 'ss1', + sentence: 'She goes to school every day.', + parts: ['She', 'goes', 'to', 'school', 'every', 'day.'], + answer: ['She', 'goes', 'to', 'school', 'every', 'day.'], + audio_url: 'https://example.com/audio/sentence1.mp3', + }, + ], +}; +/** G122: Sequence Sentence - audio, hide 4 */ +exports.MOCK_G122 = { + game_code: 'G122', + game_id: 'mock-sequence-sentence-audio-4', + data: [ + { + id: 'ss1', + sentence: 'My brother plays football in the park.', + parts: ['My', 'brother', 'plays', 'football', 'in', 'the', 'park.'], + answer: ['My', 'brother', 'plays', 'football', 'in', 'the', 'park.'], + audio_url: 'https://example.com/audio/sentence2.mp3', + }, + ], +}; +/** G123: Sequence Sentence - audio, hide all */ +exports.MOCK_G123 = { + game_code: 'G123', + game_id: 'mock-sequence-sentence-audio-all', + data: [ + { + id: 'ss1', + sentence: 'The quick brown fox jumps over the lazy dog.', + parts: ['The', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog.'], + answer: ['The', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog.'], + audio_url: 'https://example.com/audio/sentence3.mp3', + }, + ], +}; +// ============================================================================= +// MOCK DATA MAP +// ============================================================================= +exports.MockData = { + // Quiz + G001: exports.MOCK_G001, + G002: exports.MOCK_G002, + G003: exports.MOCK_G003, + G004: exports.MOCK_G004, + // Sequence Word + G110: exports.MOCK_G110, + G111: exports.MOCK_G111, + G112: exports.MOCK_G112, + G113: exports.MOCK_G113, + // Sequence Sentence + G120: exports.MOCK_G120, + G121: exports.MOCK_G121, + G122: exports.MOCK_G122, + G123: exports.MOCK_G123, + G005: exports.MOCK_G005, +}; +/** + * Get mock data for a game code + */ +function getMockData(code) { + return exports.MockData[code] ?? null; +} +/** + * Get all available game codes + */ +function getAvailableGameCodes() { + return Object.keys(exports.MockData); +} +//# sourceMappingURL=MockData.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/MockData.js.map b/G102-sequence/sdk/package/dist/client/MockData.js.map new file mode 100644 index 0000000..40b3d0c --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/MockData.js.map @@ -0,0 +1 @@ +{"version":3,"file":"MockData.js","sourceRoot":"","sources":["../../src/client/MockData.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/index.d.ts b/G102-sequence/sdk/package/dist/client/index.d.ts new file mode 100644 index 0000000..95297ca --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/index.d.ts @@ -0,0 +1,8 @@ +/** + * Client SDK exports + * SDK dành cho game developers sử dụng trong game iframe + */ +export { GameClientSDK, getGameClientSDK, destroyGameClientSDK, type ClientMode, type ClientSDKConfig, type URLParams, type GameDataPayload, type AnswerResult, type FinalResult, type ClientSDKEvents, } from './GameClientSDK'; +export { MockData, getMockData, getAvailableGameCodes, MOCK_G001, MOCK_G002, MOCK_G003, MOCK_G004, MOCK_G110, MOCK_G111, MOCK_G112, MOCK_G113, MOCK_G120, MOCK_G121, MOCK_G122, MOCK_G123, } from './MockData'; +export { validateGameData, getSchema, getSchemaDoc, DataValidator, type ValidationResult, type FieldSchema, type ItemSchema, } from './DataValidator'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/index.d.ts.map b/G102-sequence/sdk/package/dist/client/index.d.ts.map new file mode 100644 index 0000000..1918946 --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,eAAe,GACvB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACH,QAAQ,EACR,WAAW,EACX,qBAAqB,EACrB,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,GACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EACH,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,aAAa,EACb,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,UAAU,GAClB,MAAM,iBAAiB,CAAC"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/index.js b/G102-sequence/sdk/package/dist/client/index.js new file mode 100644 index 0000000..cc3f246 --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/index.js @@ -0,0 +1,35 @@ +"use strict"; +/** + * Client SDK exports + * SDK dành cho game developers sử dụng trong game iframe + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DataValidator = exports.getSchemaDoc = exports.getSchema = exports.validateGameData = exports.MOCK_G123 = exports.MOCK_G122 = exports.MOCK_G121 = exports.MOCK_G120 = exports.MOCK_G113 = exports.MOCK_G112 = exports.MOCK_G111 = exports.MOCK_G110 = exports.MOCK_G004 = exports.MOCK_G003 = exports.MOCK_G002 = exports.MOCK_G001 = exports.getAvailableGameCodes = exports.getMockData = exports.MockData = exports.destroyGameClientSDK = exports.getGameClientSDK = exports.GameClientSDK = void 0; +var GameClientSDK_1 = require("./GameClientSDK"); +Object.defineProperty(exports, "GameClientSDK", { enumerable: true, get: function () { return GameClientSDK_1.GameClientSDK; } }); +Object.defineProperty(exports, "getGameClientSDK", { enumerable: true, get: function () { return GameClientSDK_1.getGameClientSDK; } }); +Object.defineProperty(exports, "destroyGameClientSDK", { enumerable: true, get: function () { return GameClientSDK_1.destroyGameClientSDK; } }); +// Mock Data - sample data cho từng game code +var MockData_1 = require("./MockData"); +Object.defineProperty(exports, "MockData", { enumerable: true, get: function () { return MockData_1.MockData; } }); +Object.defineProperty(exports, "getMockData", { enumerable: true, get: function () { return MockData_1.getMockData; } }); +Object.defineProperty(exports, "getAvailableGameCodes", { enumerable: true, get: function () { return MockData_1.getAvailableGameCodes; } }); +Object.defineProperty(exports, "MOCK_G001", { enumerable: true, get: function () { return MockData_1.MOCK_G001; } }); +Object.defineProperty(exports, "MOCK_G002", { enumerable: true, get: function () { return MockData_1.MOCK_G002; } }); +Object.defineProperty(exports, "MOCK_G003", { enumerable: true, get: function () { return MockData_1.MOCK_G003; } }); +Object.defineProperty(exports, "MOCK_G004", { enumerable: true, get: function () { return MockData_1.MOCK_G004; } }); +Object.defineProperty(exports, "MOCK_G110", { enumerable: true, get: function () { return MockData_1.MOCK_G110; } }); +Object.defineProperty(exports, "MOCK_G111", { enumerable: true, get: function () { return MockData_1.MOCK_G111; } }); +Object.defineProperty(exports, "MOCK_G112", { enumerable: true, get: function () { return MockData_1.MOCK_G112; } }); +Object.defineProperty(exports, "MOCK_G113", { enumerable: true, get: function () { return MockData_1.MOCK_G113; } }); +Object.defineProperty(exports, "MOCK_G120", { enumerable: true, get: function () { return MockData_1.MOCK_G120; } }); +Object.defineProperty(exports, "MOCK_G121", { enumerable: true, get: function () { return MockData_1.MOCK_G121; } }); +Object.defineProperty(exports, "MOCK_G122", { enumerable: true, get: function () { return MockData_1.MOCK_G122; } }); +Object.defineProperty(exports, "MOCK_G123", { enumerable: true, get: function () { return MockData_1.MOCK_G123; } }); +// Data Validator - verify data structure +var DataValidator_1 = require("./DataValidator"); +Object.defineProperty(exports, "validateGameData", { enumerable: true, get: function () { return DataValidator_1.validateGameData; } }); +Object.defineProperty(exports, "getSchema", { enumerable: true, get: function () { return DataValidator_1.getSchema; } }); +Object.defineProperty(exports, "getSchemaDoc", { enumerable: true, get: function () { return DataValidator_1.getSchemaDoc; } }); +Object.defineProperty(exports, "DataValidator", { enumerable: true, get: function () { return DataValidator_1.DataValidator; } }); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/client/index.js.map b/G102-sequence/sdk/package/dist/client/index.js.map new file mode 100644 index 0000000..83f0913 --- /dev/null +++ b/G102-sequence/sdk/package/dist/client/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,iDAWyB;AAVrB,8GAAA,aAAa,OAAA;AACb,iHAAA,gBAAgB,OAAA;AAChB,qHAAA,oBAAoB,OAAA;AAUxB,6CAA6C;AAC7C,uCAgBoB;AAfhB,oGAAA,QAAQ,OAAA;AACR,uGAAA,WAAW,OAAA;AACX,iHAAA,qBAAqB,OAAA;AACrB,qGAAA,SAAS,OAAA;AACT,qGAAA,SAAS,OAAA;AACT,qGAAA,SAAS,OAAA;AACT,qGAAA,SAAS,OAAA;AACT,qGAAA,SAAS,OAAA;AACT,qGAAA,SAAS,OAAA;AACT,qGAAA,SAAS,OAAA;AACT,qGAAA,SAAS,OAAA;AACT,qGAAA,SAAS,OAAA;AACT,qGAAA,SAAS,OAAA;AACT,qGAAA,SAAS,OAAA;AACT,qGAAA,SAAS,OAAA;AAGb,yCAAyC;AACzC,iDAQyB;AAPrB,iHAAA,gBAAgB,OAAA;AAChB,0GAAA,SAAS,OAAA;AACT,6GAAA,YAAY,OAAA;AACZ,8GAAA,aAAa,OAAA"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/EventEmitter.js b/G102-sequence/sdk/package/dist/esm/EventEmitter.js new file mode 100644 index 0000000..3e651dd --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/EventEmitter.js @@ -0,0 +1,61 @@ +/** + * Game Iframe SDK - Event Emitter + * Simple typed event emitter for SDK + */ +export class EventEmitter { + constructor() { + this.handlers = new Map(); + } + /** + * Subscribe to an event + */ + on(event, handler) { + if (!this.handlers.has(event)) { + this.handlers.set(event, new Set()); + } + this.handlers.get(event).add(handler); + // Return unsubscribe function + return () => this.off(event, handler); + } + /** + * Subscribe to an event (once) + */ + once(event, handler) { + const wrappedHandler = (data) => { + this.off(event, wrappedHandler); + handler(data); + }; + return this.on(event, wrappedHandler); + } + /** + * Unsubscribe from an event + */ + off(event, handler) { + this.handlers.get(event)?.delete(handler); + } + /** + * Emit an event + */ + emit(event, data) { + this.handlers.get(event)?.forEach(handler => { + try { + handler(data); + } + catch (err) { + console.error(`[EventEmitter] Error in handler for "${String(event)}":`, err); + } + }); + } + /** + * Remove all handlers for an event (or all events) + */ + removeAllListeners(event) { + if (event) { + this.handlers.delete(event); + } + else { + this.handlers.clear(); + } + } +} +//# sourceMappingURL=EventEmitter.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/EventEmitter.js.map b/G102-sequence/sdk/package/dist/esm/EventEmitter.js.map new file mode 100644 index 0000000..06af93a --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/EventEmitter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventEmitter.js","sourceRoot":"","sources":["../../src/EventEmitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,OAAO,YAAY;IAAzB;QACY,aAAQ,GAAyC,IAAI,GAAG,EAAE,CAAC;IAwDvE,CAAC;IAtDG;;OAEG;IACH,EAAE,CAAyB,KAAQ,EAAE,OAAgC;QACjE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAEvC,8BAA8B;QAC9B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,IAAI,CAAyB,KAAQ,EAAE,OAAgC;QACnE,MAAM,cAAc,GAAG,CAAC,IAAe,EAAE,EAAE;YACvC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;YAChC,OAAO,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC,CAAC;QACF,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,GAAG,CAAyB,KAAQ,EAAE,OAAgC;QAClE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,IAAI,CAAyB,KAAQ,EAAE,IAAe;QAClD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;YACxC,IAAI,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,wCAAwC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAClF,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;OAEG;IACH,kBAAkB,CAAC,KAAoB;QACnC,IAAI,KAAK,EAAE,CAAC;YACR,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QAC1B,CAAC;IACL,CAAC;CACJ"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/GameIframeSDK.js b/G102-sequence/sdk/package/dist/esm/GameIframeSDK.js new file mode 100644 index 0000000..16cfa31 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/GameIframeSDK.js @@ -0,0 +1,247 @@ +/** + * Game Iframe SDK - Core + * SDK chính - compose các layers: MessageHandler, MessageSender + */ +import { EventEmitter } from './EventEmitter'; +import { MessageHandler } from './MessageHandler'; +import { MessageSender } from './MessageSender'; +import { DEFAULT_CONFIG, } from './types'; +/** + * GameIframeSDK - Main SDK class + * Composes MessageHandler và MessageSender + */ +export class GameIframeSDK extends EventEmitter { + constructor(config) { + super(); + this.pendingData = null; + this.isReady = false; + this.config = { ...DEFAULT_CONFIG, ...config }; + // Initialize layers + this.messageHandler = new MessageHandler({ + acceptedOrigin: this.config.iframeOrigin, + debug: this.config.debug, + }); + this.messageSender = new MessageSender({ + targetOrigin: this.config.iframeOrigin, + debug: this.config.debug, + }); + // Setup event forwarding + this.setupEventForwarding(); + // Start listening + this.messageHandler.start(); + this.log('info', 'SDK initialized', { config: this.config }); + } + // ========================================================================== + // PUBLIC API - Iframe Management + // ========================================================================== + /** + * Set iframe element reference + */ + setIframe(iframe) { + this.messageSender.setIframe(iframe); + this.isReady = false; + this.log('info', 'Iframe set', { hasIframe: !!iframe }); + return this; + } + /** + * Get current iframe + */ + getIframe() { + return this.messageSender.getIframe(); + } + /** + * Check if game is ready + */ + isGameReady() { + return this.isReady; + } + /** + * Check if sender is ready (iframe available) + */ + isSenderReady() { + return this.messageSender.isReady(); + } + // ========================================================================== + // PUBLIC API - Send Data + // ========================================================================== + /** + * Send game data to iframe + */ + sendGameData(data) { + const result = this.messageSender.sendGameData(data); + if (!result.success) { + this.emit('error', { + message: 'Failed to send game data', + error: result.error, + }); + } + return result.success; + } + /** + * Send leaderboard data to iframe + */ + sendLeaderboard(data) { + const result = this.messageSender.sendLeaderboard(data); + if (!result.success) { + this.emit('error', { + message: 'Failed to send leaderboard', + error: result.error, + }); + } + return result.success; + } + // ========================================================================== + // PUBLIC API - Queue & Auto-send + // ========================================================================== + /** + * Queue data to be sent when game is ready + */ + queueGameData(data) { + this.pendingData = data; + this.log('info', 'Data queued for when game is ready'); + // If already ready, send immediately + if (this.isReady) { + this.sendQueuedData(); + } + return this; + } + /** + * Clear queued data + */ + clearQueuedData() { + this.pendingData = null; + return this; + } + // ========================================================================== + // PUBLIC API - Iframe Control + // ========================================================================== + /** + * Force reload iframe + */ + reloadIframe() { + this.isReady = false; + return this.messageSender.reloadIframe(); + } + // ========================================================================== + // PUBLIC API - Lifecycle + // ========================================================================== + /** + * Cleanup and destroy SDK + */ + destroy() { + this.messageHandler.destroy(); + this.removeAllListeners(); + this.pendingData = null; + this.isReady = false; + this.log('info', 'SDK destroyed'); + } + // ========================================================================== + // PUBLIC API - Direct Layer Access (Advanced) + // ========================================================================== + /** + * Get MessageHandler instance for advanced usage + */ + getMessageHandler() { + return this.messageHandler; + } + /** + * Get MessageSender instance for advanced usage + */ + getMessageSender() { + return this.messageSender; + } + // ========================================================================== + // PRIVATE METHODS + // ========================================================================== + /** + * Setup event forwarding from MessageHandler to SDK events + */ + setupEventForwarding() { + // Forward gameReady + this.messageHandler.on('gameReady', () => { + this.isReady = true; + this.emit('gameReady', undefined); + // Auto-send queued data if enabled + if (this.config.autoSendOnReady && this.pendingData) { + setTimeout(() => { + this.sendQueuedData(); + }, this.config.readyDelay); + } + }); + // Forward answerReport + this.messageHandler.on('answerReport', (data) => { + this.emit('answerReport', data); + }); + // Forward finalResult + this.messageHandler.on('finalResult', (data) => { + this.emit('finalResult', data); + }); + // Forward leaderboardRequest + this.messageHandler.on('leaderboardRequest', (data) => { + this.emit('leaderboardRequest', data); + }); + // Forward errors + this.messageHandler.on('error', (error) => { + this.emit('error', error); + }); + } + /** + * Send queued data + */ + sendQueuedData() { + if (this.pendingData) { + this.sendGameData(this.pendingData); + this.pendingData = null; + } + } + /** + * Internal logging + */ + log(level, message, data) { + if (this.config.debug) { + const prefix = '[GameIframeSDK]'; + switch (level) { + case 'info': + console.log(prefix, message, data ?? ''); + break; + case 'warn': + console.warn(prefix, message, data ?? ''); + break; + case 'error': + console.error(prefix, message, data ?? ''); + break; + } + } + this.emit('log', { level, message, data }); + } +} +// ========================================================================== +// FACTORY / SINGLETON HELPERS +// ========================================================================== +let defaultInstance = null; +/** + * Create SDK instance + */ +export function createGameIframeSDK(config) { + return new GameIframeSDK(config); +} +/** + * Get or create default SDK instance + */ +export function getGameIframeSDK(config) { + if (!defaultInstance && config) { + defaultInstance = new GameIframeSDK(config); + } + if (!defaultInstance) { + throw new Error('GameIframeSDK not initialized. Call with config first.'); + } + return defaultInstance; +} +/** + * Destroy default instance + */ +export function destroyGameIframeSDK() { + defaultInstance?.destroy(); + defaultInstance = null; +} +//# sourceMappingURL=GameIframeSDK.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/GameIframeSDK.js.map b/G102-sequence/sdk/package/dist/esm/GameIframeSDK.js.map new file mode 100644 index 0000000..47416a3 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/GameIframeSDK.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GameIframeSDK.js","sourceRoot":"","sources":["../../src/GameIframeSDK.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/MessageHandler.js b/G102-sequence/sdk/package/dist/esm/MessageHandler.js new file mode 100644 index 0000000..7700394 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/MessageHandler.js @@ -0,0 +1,111 @@ +/** + * Game Iframe SDK - Message Handler + * Xử lý message từ iframe + */ +import { MESSAGE_TYPES } from './types'; +import { EventEmitter } from './EventEmitter'; +/** + * MessageHandler - Xử lý incoming messages từ iframe + */ +export class MessageHandler extends EventEmitter { + constructor(config) { + super(); + this.boundHandler = null; + this.isListening = false; + this.config = config; + } + /** + * Start listening for messages + */ + start() { + if (this.isListening) { + return this; + } + this.boundHandler = this.handleMessage.bind(this); + window.addEventListener('message', this.boundHandler); + this.isListening = true; + this.log('MessageHandler started'); + return this; + } + /** + * Stop listening for messages + */ + stop() { + if (this.boundHandler) { + window.removeEventListener('message', this.boundHandler); + this.boundHandler = null; + } + this.isListening = false; + this.log('MessageHandler stopped'); + return this; + } + /** + * Check if handler is listening + */ + isActive() { + return this.isListening; + } + /** + * Handle incoming message + */ + handleMessage(event) { + // Origin check + if (!this.isOriginAllowed(event.origin)) { + return; + } + const { type, data } = event.data || {}; + if (!type) + return; + this.log(`Received: ${type}`, data); + try { + switch (type) { + case MESSAGE_TYPES.GAME_READY: + this.emit('gameReady', undefined); + break; + case MESSAGE_TYPES.ANSWER_REPORT: + // Raw data pass-through + this.emit('answerReport', data); + break; + case MESSAGE_TYPES.FINAL_RESULT: + // Raw data pass-through + this.emit('finalResult', data); + break; + case MESSAGE_TYPES.GET_LEADERBOARD: + this.emit('leaderboardRequest', { top: data?.top || 10 }); + break; + default: + this.emit('unknownMessage', { type, data }); + break; + } + } + catch (error) { + const err = error; + this.emit('error', { message: `Error handling ${type}`, error: err }); + } + } + /** + * Check if origin is allowed + */ + isOriginAllowed(origin) { + if (this.config.acceptedOrigin === '*') { + return true; + } + return origin === this.config.acceptedOrigin; + } + /** + * Debug log + */ + log(message, data) { + if (this.config.debug) { + console.log('[MessageHandler]', message, data ?? ''); + } + } + /** + * Cleanup + */ + destroy() { + this.stop(); + this.removeAllListeners(); + } +} +//# sourceMappingURL=MessageHandler.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/MessageHandler.js.map b/G102-sequence/sdk/package/dist/esm/MessageHandler.js.map new file mode 100644 index 0000000..8800b58 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/MessageHandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"MessageHandler.js","sourceRoot":"","sources":["../../src/MessageHandler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAqC,MAAM,SAAS,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAuB9C;;GAEG;AACH,MAAM,OAAO,cAAe,SAAQ,YAAkC;IAKlE,YAAY,MAA4B;QACpC,KAAK,EAAE,CAAC;QAJJ,iBAAY,GAA2C,IAAI,CAAC;QAC5D,gBAAW,GAAG,KAAK,CAAC;QAIxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,KAAK;QACD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QACtD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QAEnC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,IAAI;QACA,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;YACzD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QAEnC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,QAAQ;QACJ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,KAAmB;QACrC,eAAe;QACf,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACtC,OAAO;QACX,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI;YAAE,OAAO;QAElB,IAAI,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;QAEpC,IAAI,CAAC;YACD,QAAQ,IAAI,EAAE,CAAC;gBACX,KAAK,aAAa,CAAC,UAAU;oBACzB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;oBAClC,MAAM;gBAEV,KAAK,aAAa,CAAC,aAAa;oBAC5B,wBAAwB;oBACxB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAwB,CAAC,CAAC;oBACpD,MAAM;gBAEV,KAAK,aAAa,CAAC,YAAY;oBAC3B,wBAAwB;oBACxB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAuB,CAAC,CAAC;oBAClD,MAAM;gBAEV,KAAK,aAAa,CAAC,eAAe;oBAC9B,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC;oBAC1D,MAAM;gBAEV;oBACI,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;oBAC5C,MAAM;YACd,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,KAAc,CAAC;YAC3B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,kBAAkB,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC1E,CAAC;IACL,CAAC;IAED;;OAEG;IACK,eAAe,CAAC,MAAc;QAClC,IAAI,IAAI,CAAC,MAAM,CAAC,cAAc,KAAK,GAAG,EAAE,CAAC;YACrC,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,OAAO,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC;IACjD,CAAC;IAED;;OAEG;IACK,GAAG,CAAC,OAAe,EAAE,IAAU;QACnC,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC;IACL,CAAC;IAED;;OAEG;IACH,OAAO;QACH,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC9B,CAAC;CACJ"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/MessageSender.js b/G102-sequence/sdk/package/dist/esm/MessageSender.js new file mode 100644 index 0000000..e30199c --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/MessageSender.js @@ -0,0 +1,128 @@ +/** + * Game Iframe SDK - Message Sender + * Gửi message đến iframe + */ +import { MESSAGE_TYPES } from './types'; +/** + * MessageSender - Gửi messages đến iframe + */ +export class MessageSender { + constructor(config) { + this.iframe = null; + this.config = config; + } + /** + * Set iframe element + */ + setIframe(iframe) { + this.iframe = iframe; + return this; + } + /** + * Get current iframe + */ + getIframe() { + return this.iframe; + } + /** + * Check if iframe is available + */ + isReady() { + return !!this.iframe?.contentWindow; + } + /** + * Send raw message to iframe + */ + sendRaw(message) { + if (!this.iframe?.contentWindow) { + return { + success: false, + error: new Error('Iframe not available'), + }; + } + try { + this.iframe.contentWindow.postMessage(message, this.config.targetOrigin); + this.log('Sent message', { type: message.type }); + return { success: true }; + } + catch (error) { + const err = error; + this.log('Send failed', { error: err.message }); + return { success: false, error: err }; + } + } + /** + * Send game data (SERVER_PUSH_DATA) + */ + sendGameData(payload) { + // Inline message creation + const message = { + type: MESSAGE_TYPES.SERVER_PUSH_DATA, + jsonData: payload, + }; + const result = this.sendRaw(message); + if (result.success) { + const dataLength = payload.data?.length || 0; + this.log('Sent game data', { + game_id: payload.game_id, + items: dataLength, + }); + } + return result; + } + /** + * Send leaderboard (SERVER_PUSH_LEADERBOARD) + */ + sendLeaderboard(data) { + // Inline message creation + const message = { + type: MESSAGE_TYPES.SERVER_PUSH_LEADERBOARD, + leaderboardData: data, + }; + const result = this.sendRaw(message); + if (result.success) { + this.log('Sent leaderboard', { + players: data.top_players?.length || 0, + hasUserRank: !!data.user_rank, + }); + } + return result; + } + /** + * Reload iframe + */ + reloadIframe() { + if (!this.iframe) { + return false; + } + const currentSrc = this.iframe.src; + if (!currentSrc || currentSrc === 'about:blank') { + return false; + } + this.iframe.src = ''; + setTimeout(() => { + if (this.iframe) { + this.iframe.src = currentSrc; + this.log('Iframe reloaded'); + } + }, 100); + return true; + } + /** + * Debug log + */ + log(message, data) { + if (this.config.debug) { + console.log('[MessageSender]', message); + if (data) { + try { + console.log(JSON.stringify(data, null, 2)); + } + catch (e) { + console.log(data); + } + } + } + } +} +//# sourceMappingURL=MessageSender.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/MessageSender.js.map b/G102-sequence/sdk/package/dist/esm/MessageSender.js.map new file mode 100644 index 0000000..8993a62 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/MessageSender.js.map @@ -0,0 +1 @@ +{"version":3,"file":"MessageSender.js","sourceRoot":"","sources":["../../src/MessageSender.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/client/DataValidator.js b/G102-sequence/sdk/package/dist/esm/client/DataValidator.js new file mode 100644 index 0000000..8a4fb75 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/client/DataValidator.js @@ -0,0 +1,245 @@ +/** + * Data Validator + * Verify data structure cho từng game code + * + * Usage: + * ```typescript + * import { validateGameData, DataValidator } from 'game-iframe-sdk/client'; + * + * const result = validateGameData('G001', receivedData); + * if (!result.valid) { + * console.error('Invalid data:', result.errors); + * } + * ``` + */ +import { GAME_CODES } from '../kit/GameDataHandler'; +// ============================================================================= +// SCHEMAS FOR EACH GAME CODE +// ============================================================================= +const QUIZ_BASE_SCHEMA = { + id: { type: 'string', required: true, description: 'Unique question ID' }, + options: { type: 'array', required: true, arrayItemType: 'string', description: 'Answer options' }, + answer: { type: 'number', required: true, description: 'Correct answer index (0-based)' }, +}; +const SCHEMAS = { + // Quiz variants + G001: { + ...QUIZ_BASE_SCHEMA, + question: { type: 'string', required: true, description: 'Text question' }, + }, + G002: { + ...QUIZ_BASE_SCHEMA, + question_audio: { type: 'string', required: true, description: 'Audio URL for question' }, + }, + G003: { + ...QUIZ_BASE_SCHEMA, + question: { type: 'string', required: true, description: 'Text question' }, + // options are audio URLs + }, + G004: { + ...QUIZ_BASE_SCHEMA, + question_image: { type: 'string', required: true, description: 'Image URL for question' }, + question: { type: 'string', required: false, description: 'Optional text hint' }, + }, + // G005: Quiz Text-Image (options are image URLs, client picks index) + G005: { + ...QUIZ_BASE_SCHEMA, + question: { type: 'string', required: true, description: 'Text question' }, + // options are image URLs, answer is index pointing to correct image + }, + // Sequence Word variants + G110: { + id: { type: 'string', required: true }, + word: { type: 'string', required: true, description: 'The word to arrange' }, + parts: { type: 'array', required: true, arrayItemType: 'string', description: 'Letters/parts to arrange' }, + answer: { type: 'array', required: true, arrayItemType: 'string', description: 'Correct order' }, + }, + G111: { + id: { type: 'string', required: true }, + word: { type: 'string', required: true }, + parts: { type: 'array', required: true, arrayItemType: 'string' }, + answer: { type: 'array', required: true, arrayItemType: 'string' }, + audio_url: { type: 'string', required: true, description: 'Audio hint URL' }, + }, + G112: { + id: { type: 'string', required: true }, + word: { type: 'string', required: true }, + parts: { type: 'array', required: true, arrayItemType: 'string' }, + answer: { type: 'array', required: true, arrayItemType: 'string' }, + audio_url: { type: 'string', required: true }, + }, + G113: { + id: { type: 'string', required: true }, + word: { type: 'string', required: true }, + parts: { type: 'array', required: true, arrayItemType: 'string' }, + answer: { type: 'array', required: true, arrayItemType: 'string' }, + audio_url: { type: 'string', required: true }, + }, + // Sequence Sentence variants + G120: { + id: { type: 'string', required: true }, + sentence: { type: 'string', required: false, description: 'Full sentence (hint)' }, + parts: { type: 'array', required: true, arrayItemType: 'string', description: 'Words to arrange' }, + answer: { type: 'array', required: true, arrayItemType: 'string', description: 'Correct word order' }, + }, + G121: { + id: { type: 'string', required: true }, + sentence: { type: 'string', required: false }, + parts: { type: 'array', required: true, arrayItemType: 'string' }, + answer: { type: 'array', required: true, arrayItemType: 'string' }, + audio_url: { type: 'string', required: true }, + }, + G122: { + id: { type: 'string', required: true }, + sentence: { type: 'string', required: false }, + parts: { type: 'array', required: true, arrayItemType: 'string' }, + answer: { type: 'array', required: true, arrayItemType: 'string' }, + audio_url: { type: 'string', required: true }, + }, + G123: { + id: { type: 'string', required: true }, + sentence: { type: 'string', required: false }, + parts: { type: 'array', required: true, arrayItemType: 'string' }, + answer: { type: 'array', required: true, arrayItemType: 'string' }, + audio_url: { type: 'string', required: true }, + } +}; +// ============================================================================= +// VALIDATOR +// ============================================================================= +/** + * Validate a single item against schema + */ +function validateItem(item, schema, itemIndex) { + const errors = []; + if (!item || typeof item !== 'object') { + errors.push(`Item [${itemIndex}]: Must be an object`); + return errors; + } + for (const [field, fieldSchema] of Object.entries(schema)) { + const value = item[field]; + // Check required + if (fieldSchema.required && (value === undefined || value === null)) { + errors.push(`Item [${itemIndex}].${field}: Required field is missing`); + continue; + } + // Skip validation if optional and not present + if (!fieldSchema.required && (value === undefined || value === null)) { + continue; + } + // Check type + const actualType = Array.isArray(value) ? 'array' : typeof value; + if (fieldSchema.type !== 'any' && actualType !== fieldSchema.type) { + errors.push(`Item [${itemIndex}].${field}: Expected ${fieldSchema.type}, got ${actualType}`); + continue; + } + // Check array items + if (fieldSchema.type === 'array' && fieldSchema.arrayItemType && fieldSchema.arrayItemType !== 'any') { + for (let i = 0; i < value.length; i++) { + const itemType = typeof value[i]; + if (itemType !== fieldSchema.arrayItemType) { + errors.push(`Item [${itemIndex}].${field}[${i}]: Expected ${fieldSchema.arrayItemType}, got ${itemType}`); + } + } + } + } + return errors; +} +/** + * Validate game data payload + */ +export function validateGameData(gameCode, payload) { + const errors = []; + const warnings = []; + // Check game code + if (!GAME_CODES[gameCode]) { + errors.push(`Unknown game code: ${gameCode}`); + return { valid: false, errors, warnings }; + } + // Check payload structure + if (!payload || typeof payload !== 'object') { + errors.push('Payload must be an object'); + return { valid: false, errors, warnings }; + } + // Check data array + const items = payload.data || payload.items || payload.questions; + if (!items) { + errors.push('Missing data array (expected "data", "items", or "questions")'); + return { valid: false, errors, warnings }; + } + if (!Array.isArray(items)) { + errors.push('"data" must be an array'); + return { valid: false, errors, warnings }; + } + if (items.length === 0) { + warnings.push('Data array is empty'); + } + // Validate each item + const schema = SCHEMAS[gameCode]; + for (let i = 0; i < items.length; i++) { + const itemErrors = validateItem(items[i], schema, i); + errors.push(...itemErrors); + } + // Check for duplicate IDs + const ids = items.map((item) => item.id).filter(Boolean); + const duplicates = ids.filter((id, index) => ids.indexOf(id) !== index); + if (duplicates.length > 0) { + warnings.push(`Duplicate IDs found: ${[...new Set(duplicates)].join(', ')}`); + } + return { + valid: errors.length === 0, + errors, + warnings, + }; +} +/** + * Get schema for a game code + */ +export function getSchema(gameCode) { + return SCHEMAS[gameCode] ?? null; +} +/** + * Get schema documentation for a game code + */ +export function getSchemaDoc(gameCode) { + const schema = SCHEMAS[gameCode]; + if (!schema) + return `Unknown game code: ${gameCode}`; + const gameInfo = GAME_CODES[gameCode]; + const lines = [ + `## ${gameCode}: ${gameInfo.name}`, + `Category: ${gameInfo.category}`, + '', + '### Fields:', + ]; + for (const [field, fieldSchema] of Object.entries(schema)) { + const required = fieldSchema.required ? '(required)' : '(optional)'; + let type = fieldSchema.type; + if (fieldSchema.arrayItemType) { + type = `${fieldSchema.type}<${fieldSchema.arrayItemType}>`; + } + lines.push(`- **${field}**: ${type} ${required}`); + if (fieldSchema.description) { + lines.push(` - ${fieldSchema.description}`); + } + } + return lines.join('\n'); +} +// ============================================================================= +// DATA VALIDATOR CLASS +// ============================================================================= +export class DataValidator { + constructor(gameCode) { + this.gameCode = gameCode; + } + validate(payload) { + return validateGameData(this.gameCode, payload); + } + getSchema() { + return getSchema(this.gameCode); + } + getSchemaDoc() { + return getSchemaDoc(this.gameCode); + } +} +//# sourceMappingURL=DataValidator.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/client/DataValidator.js.map b/G102-sequence/sdk/package/dist/esm/client/DataValidator.js.map new file mode 100644 index 0000000..46e14cd --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/client/DataValidator.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DataValidator.js","sourceRoot":"","sources":["../../../src/client/DataValidator.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/client/GameClientSDK.js b/G102-sequence/sdk/package/dist/esm/client/GameClientSDK.js new file mode 100644 index 0000000..2a9593e --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/client/GameClientSDK.js @@ -0,0 +1,359 @@ +/** + * GameClientSDK - SDK dành cho Game Iframe + * + * Sử dụng trong game để: + * - Tự động xác định mode (preview/live) từ URL + * - Nhận data từ parent (preview) hoặc fetch API (live) + * - Verify answers locally + * - Report results về parent + */ +import { checkAnswer, sanitizeForClient, GAME_CODES } from '../kit/GameDataHandler'; +import { getMockData } from './MockData'; +import { validateGameData } from './DataValidator'; +class SimpleEventEmitter { + constructor() { + this.handlers = new Map(); + } + on(event, handler) { + if (!this.handlers.has(event)) { + this.handlers.set(event, new Set()); + } + this.handlers.get(event).add(handler); + return () => this.off(event, handler); + } + off(event, handler) { + this.handlers.get(event)?.delete(handler); + } + emit(event, data) { + this.handlers.get(event)?.forEach(handler => { + try { + handler(data); + } + catch (err) { + console.error(`[GameClientSDK] Error in ${String(event)} handler:`, err); + } + }); + } +} +// ============================================================================= +// GAME CLIENT SDK +// ============================================================================= +export class GameClientSDK extends SimpleEventEmitter { + constructor(config = {}) { + super(); + // Data storage + this.originalItems = new Map(); // Có đáp án + this.sanitizedItems = []; // Không có đáp án + this.userAnswers = new Map(); + this.isInitialized = false; + this.startTime = 0; + this.config = { + debug: config.debug ?? false, + apiBaseUrl: config.apiBaseUrl ?? '', + getAuthHeaders: config.getAuthHeaders ?? (() => ({})), + }; + // Parse URL params + this.params = this.parseURLParams(); + this.mode = this.params.mode; + this.log('info', 'SDK created', { mode: this.mode, params: this.params }); + // Emit mode detected + this.emit('modeDetected', { mode: this.mode, params: this.params }); + // Setup message listener + this.setupMessageListener(); + // Auto-initialize based on mode + this.initialize(); + } + // ========================================================================== + // PUBLIC API + // ========================================================================== + /** + * Get current mode + */ + getMode() { + return this.mode; + } + /** + * Get URL params + */ + getParams() { + return { ...this.params }; + } + /** + * Get game code + */ + getGameCode() { + return this.params.gameCode; + } + /** + * Get sanitized items (safe for rendering) + */ + getItems() { + return this.sanitizedItems; + } + /** + * Submit an answer and get verification result + */ + submitAnswer(questionId, choice) { + const originalItem = this.originalItems.get(questionId); + if (!originalItem) { + this.log('warn', `Item not found: ${questionId}`); + return { isCorrect: false, score: 0, feedback: 'Question not found' }; + } + // Verify using GameDataHandler + const result = checkAnswer(this.params.gameCode, originalItem, choice); + // Store user answer + const timeSpent = Date.now() - (this.userAnswers.size === 0 ? this.startTime : Date.now()); + this.userAnswers.set(questionId, { + choice, + result: result.isCorrect ? 1 : 0, + time: timeSpent, + }); + // Report to parent + this.sendAnswerReport(questionId, choice, result.isCorrect ? 1 : 0, timeSpent); + this.log('info', `Answer submitted: ${questionId}`, { choice, result }); + return result; + } + /** + * Get final result + */ + getFinalResult() { + const details = Array.from(this.userAnswers.entries()).map(([id, data]) => ({ + question_id: id, + choice: data.choice, + result: data.result, + time_spent: data.time, + })); + const correct = details.filter(d => d.result === 1).length; + const total = this.originalItems.size; + return { + score: total > 0 ? Math.round((correct / total) * 100) : 0, + total, + correct, + wrong: total - correct, + details, + }; + } + /** + * Report final result to parent + */ + reportFinalResult(result) { + const finalResult = result ?? this.getFinalResult(); + window.parent.postMessage({ + type: 'FINAL_RESULT', + data: finalResult, + }, '*'); + this.log('info', 'Final result reported', finalResult); + } + /** + * Request leaderboard from parent + */ + requestLeaderboard(top = 10) { + window.parent.postMessage({ + type: 'GET_LEADERBOARD', + data: { top }, + }, '*'); + } + /** + * Cleanup + */ + destroy() { + window.removeEventListener('message', this.handleMessage); + this.originalItems.clear(); + this.sanitizedItems = []; + this.userAnswers.clear(); + this.log('info', 'SDK destroyed'); + } + // ========================================================================== + // PRIVATE METHODS + // ========================================================================== + parseURLParams() { + const searchParams = new URLSearchParams(window.location.search); + const mode = (searchParams.get('mode') || 'preview'); + const gameCode = (searchParams.get('game_code') || 'G001'); + const gameId = searchParams.get('game_id') || undefined; + const lid = searchParams.get('lid') || undefined; + const studentId = searchParams.get('student_id') || undefined; + // Validate mode + if (mode !== 'preview' && mode !== 'live' && mode !== 'dev') { + this.log('warn', `Invalid mode: ${mode}, defaulting to preview`); + } + // Validate game code + if (!GAME_CODES[gameCode]) { + this.log('warn', `Unknown game code: ${gameCode}`); + } + return { mode, gameCode, gameId, lid, studentId }; + } + setupMessageListener() { + this.handleMessage = this.handleMessage.bind(this); + window.addEventListener('message', this.handleMessage); + } + handleMessage(event) { + const { type, jsonData, leaderboardData } = event.data || {}; + this.log('debug', 'Message received', { type, hasData: !!jsonData }); + switch (type) { + case 'SERVER_PUSH_DATA': + if (jsonData) { + this.handleDataReceived(jsonData); + } + break; + case 'SERVER_PUSH_LEADERBOARD': + if (leaderboardData) { + this.log('info', 'Leaderboard received', leaderboardData); + // Could emit event here + } + break; + } + } + async initialize() { + // Send GAME_READY immediately + this.sendGameReady(); + if (this.mode === 'dev') { + // Dev mode: load mock data immediately + this.log('info', 'DEV MODE: Loading mock data...'); + this.loadMockData(); + } + else if (this.mode === 'live') { + // Live mode: fetch data from API + await this.fetchLiveData(); + } + else { + // Preview mode: wait for postMessage + this.log('info', 'Preview mode: waiting for SERVER_PUSH_DATA...'); + } + } + /** + * Load mock data for dev mode + */ + loadMockData() { + const mockData = getMockData(this.params.gameCode); + if (!mockData) { + this.emit('error', { + message: `No mock data available for game code: ${this.params.gameCode}` + }); + return; + } + this.log('info', `Loaded mock data for ${this.params.gameCode}`); + this.handleDataReceived(mockData); + } + sendGameReady() { + window.parent.postMessage({ type: 'GAME_READY' }, '*'); + this.emit('ready', undefined); + this.log('info', 'GAME_READY sent'); + } + async fetchLiveData() { + const { gameId, lid } = this.params; + if (!gameId || !lid) { + this.emit('error', { message: 'Live mode requires game_id and lid' }); + return; + } + if (!this.config.apiBaseUrl) { + this.emit('error', { message: 'Live mode requires apiBaseUrl' }); + return; + } + try { + const url = `${this.config.apiBaseUrl}/games/${gameId}?lid=${lid}`; + const headers = { + 'Content-Type': 'application/json', + ...this.config.getAuthHeaders(), + }; + this.log('info', `Fetching live data: ${url}`); + const response = await fetch(url, { headers }); + if (!response.ok) { + throw new Error(`API Error: ${response.status}`); + } + const data = await response.json(); + this.handleDataReceived(data); + } + catch (error) { + this.log('error', 'Failed to fetch live data', error); + this.emit('error', { message: 'Failed to fetch game data', error }); + } + } + handleDataReceived(payload) { + this.startTime = Date.now(); + // Update game code if provided + if (payload.game_code && GAME_CODES[payload.game_code]) { + this.params.gameCode = payload.game_code; + } + // Validate data structure + const validation = validateGameData(this.params.gameCode, payload); + if (!validation.valid) { + this.log('error', 'Data validation failed', validation.errors); + this.emit('validationError', { validation }); + // Continue anyway to allow partial rendering + } + if (validation.warnings.length > 0) { + this.log('warn', 'Data validation warnings', validation.warnings); + } + // Extract items from various payload formats + const items = payload.data || payload.items || payload.questions || []; + const resumeData = payload.completed_question_ids || []; + // Store original items (with answers) + this.originalItems.clear(); + items.forEach((item) => { + if (item.id) { + this.originalItems.set(item.id, item); + } + }); + // Sanitize for client (remove answers) + this.sanitizedItems = sanitizeForClient(this.params.gameCode, items); + this.isInitialized = true; + this.log('info', `Data received: ${items.length} items, ${resumeData.length} completed`); + // Emit event with validation result + this.emit('dataReceived', { + items: this.sanitizedItems, + resumeData, + validation, + }); + } + sendAnswerReport(questionId, choice, result, timeSpent) { + window.parent.postMessage({ + type: 'ANSWER_REPORT', + data: { + question_id: questionId, + question_index: Array.from(this.originalItems.keys()).indexOf(questionId), + choice, + result, + time_spent: timeSpent, + }, + }, '*'); + } + log(level, message, data) { + if (!this.config.debug && level === 'debug') + return; + const prefix = `[GameClientSDK:${this.mode}]`; + switch (level) { + case 'debug': + case 'info': + console.log(prefix, message, data ?? ''); + break; + case 'warn': + console.warn(prefix, message, data ?? ''); + break; + case 'error': + console.error(prefix, message, data ?? ''); + break; + } + } +} +// ============================================================================= +// FACTORY +// ============================================================================= +let clientInstance = null; +/** + * Get or create GameClientSDK instance + */ +export function getGameClientSDK(config) { + if (!clientInstance) { + clientInstance = new GameClientSDK(config); + } + return clientInstance; +} +/** + * Destroy client instance + */ +export function destroyGameClientSDK() { + clientInstance?.destroy(); + clientInstance = null; +} +//# sourceMappingURL=GameClientSDK.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/client/GameClientSDK.js.map b/G102-sequence/sdk/package/dist/esm/client/GameClientSDK.js.map new file mode 100644 index 0000000..0585b09 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/client/GameClientSDK.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GameClientSDK.js","sourceRoot":"","sources":["../../../src/client/GameClientSDK.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/client/MockData.js b/G102-sequence/sdk/package/dist/esm/client/MockData.js new file mode 100644 index 0000000..6e5379c --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/client/MockData.js @@ -0,0 +1,284 @@ +/** + * Mock Data cho từng Game Code + * Game developers dùng để test game standalone + * + * Usage: + * ```typescript + * import { MockData } from 'game-iframe-sdk/client'; + * + * // Lấy sample data cho Quiz Text-Text + * const quizData = MockData.G001; + * + * // Lấy sample data cho Sequence Word + * const seqData = MockData.G110; + * ``` + */ +// ============================================================================= +// QUIZ MOCK DATA +// ============================================================================= +/** G001: Quiz Text-Text */ +export const MOCK_G001 = { + game_code: 'G001', + game_id: 'mock-quiz-text-text', + data: [ + { + id: 'q1', + question: 'Thủ đô của Việt Nam là gì?', + options: ['Hà Nội', 'Hồ Chí Minh', 'Đà Nẵng', 'Huế'], + answer: 0, // Index của đáp án đúng + }, + { + id: 'q2', + question: '2 + 2 = ?', + options: ['3', '4', '5', '6'], + answer: 1, + }, + { + id: 'q3', + question: 'Con vật nào biết bay?', + options: ['Chó', 'Mèo', 'Chim', 'Cá'], + answer: 2, + }, + ], +}; +/** G002: Quiz Audio-Text */ +export const MOCK_G002 = { + game_code: 'G002', + game_id: 'mock-quiz-audio-text', + data: [ + { + id: 'q1', + question_audio: 'https://example.com/audio/question1.mp3', + options: ['Apple', 'Banana', 'Orange', 'Grape'], + answer: 0, + }, + { + id: 'q2', + question_audio: 'https://example.com/audio/question2.mp3', + options: ['Dog', 'Cat', 'Bird', 'Fish'], + answer: 2, + }, + ], +}; +/** G003: Quiz Text-Audio */ +export const MOCK_G003 = { + game_code: 'G003', + game_id: 'mock-quiz-text-audio', + data: [ + { + id: 'q1', + question: 'Chọn phát âm đúng của từ "Hello"', + options: [ + 'https://example.com/audio/hello1.mp3', + 'https://example.com/audio/hello2.mp3', + 'https://example.com/audio/hello3.mp3', + ], + answer: 0, + }, + ], +}; +/** G004: Quiz Image-Text */ +export const MOCK_G004 = { + game_code: 'G004', + game_id: 'mock-quiz-image-text', + data: [ + { + id: 'q1', + question_image: 'https://example.com/images/apple.jpg', + question: 'Đây là quả gì?', // Optional hint + options: ['Táo', 'Cam', 'Chuối', 'Nho'], + answer: 0, + }, + { + id: 'q2', + question_image: 'https://example.com/images/cat.jpg', + options: ['Chó', 'Mèo', 'Thỏ', 'Chuột'], + answer: 1, + }, + ], +}; +/** G005: Quiz Text-Image */ +export const MOCK_G005 = { + game_code: 'G005', + game_id: 'mock-quiz-text-image', + data: [ + { + id: 'q1', + question: 'Chọn hình ảnh con mèo', + options: [ + 'https://example.com/images/dog.jpg', + 'https://example.com/images/cat.jpg', + 'https://example.com/images/bird.jpg', + ], + answer: 1, + }, + ], +}; +// ============================================================================= +// SEQUENCE WORD MOCK DATA +// ============================================================================= +/** G110: Sequence Word - no audio */ +export const MOCK_G110 = { + game_code: 'G110', + game_id: 'mock-sequence-word', + data: [ + { + id: 'sw1', + word: 'APPLE', + parts: ['A', 'P', 'P', 'L', 'E'], // Đáp án đúng theo thứ tự + answer: ['A', 'P', 'P', 'L', 'E'], // SDK sẽ shuffle parts, giữ answer để verify + }, + { + id: 'sw2', + word: 'HELLO', + parts: ['H', 'E', 'L', 'L', 'O'], + answer: ['H', 'E', 'L', 'L', 'O'], + }, + { + id: 'sw3', + word: 'WORLD', + parts: ['W', 'O', 'R', 'L', 'D'], + answer: ['W', 'O', 'R', 'L', 'D'], + }, + ], +}; +/** G111: Sequence Word - audio, hide 2 */ +export const MOCK_G111 = { + game_code: 'G111', + game_id: 'mock-sequence-word-audio-2', + data: [ + { + id: 'sw1', + word: 'BANANA', + parts: ['B', 'A', 'N', 'A', 'N', 'A'], + answer: ['B', 'A', 'N', 'A', 'N', 'A'], + audio_url: 'https://example.com/audio/banana.mp3', + }, + ], +}; +/** G112: Sequence Word - audio, hide 4 */ +export const MOCK_G112 = { + game_code: 'G112', + game_id: 'mock-sequence-word-audio-4', + data: [ + { + id: 'sw1', + word: 'COMPUTER', + parts: ['C', 'O', 'M', 'P', 'U', 'T', 'E', 'R'], + answer: ['C', 'O', 'M', 'P', 'U', 'T', 'E', 'R'], + audio_url: 'https://example.com/audio/computer.mp3', + }, + ], +}; +/** G113: Sequence Word - audio, hide all */ +export const MOCK_G113 = { + game_code: 'G113', + game_id: 'mock-sequence-word-audio-all', + data: [ + { + id: 'sw1', + word: 'ELEPHANT', + parts: ['E', 'L', 'E', 'P', 'H', 'A', 'N', 'T'], + answer: ['E', 'L', 'E', 'P', 'H', 'A', 'N', 'T'], + audio_url: 'https://example.com/audio/elephant.mp3', + }, + ], +}; +// ============================================================================= +// SEQUENCE SENTENCE MOCK DATA +// ============================================================================= +/** G120: Sequence Sentence - no audio */ +export const MOCK_G120 = { + game_code: 'G120', + game_id: 'mock-sequence-sentence', + data: [ + { + id: 'ss1', + sentence: 'I love learning English.', + parts: ['I', 'love', 'learning', 'English.'], + answer: ['I', 'love', 'learning', 'English.'], + }, + { + id: 'ss2', + sentence: 'The cat is sleeping.', + parts: ['The', 'cat', 'is', 'sleeping.'], + answer: ['The', 'cat', 'is', 'sleeping.'], + }, + ], +}; +/** G121: Sequence Sentence - audio, hide 2 */ +export const MOCK_G121 = { + game_code: 'G121', + game_id: 'mock-sequence-sentence-audio-2', + data: [ + { + id: 'ss1', + sentence: 'She goes to school every day.', + parts: ['She', 'goes', 'to', 'school', 'every', 'day.'], + answer: ['She', 'goes', 'to', 'school', 'every', 'day.'], + audio_url: 'https://example.com/audio/sentence1.mp3', + }, + ], +}; +/** G122: Sequence Sentence - audio, hide 4 */ +export const MOCK_G122 = { + game_code: 'G122', + game_id: 'mock-sequence-sentence-audio-4', + data: [ + { + id: 'ss1', + sentence: 'My brother plays football in the park.', + parts: ['My', 'brother', 'plays', 'football', 'in', 'the', 'park.'], + answer: ['My', 'brother', 'plays', 'football', 'in', 'the', 'park.'], + audio_url: 'https://example.com/audio/sentence2.mp3', + }, + ], +}; +/** G123: Sequence Sentence - audio, hide all */ +export const MOCK_G123 = { + game_code: 'G123', + game_id: 'mock-sequence-sentence-audio-all', + data: [ + { + id: 'ss1', + sentence: 'The quick brown fox jumps over the lazy dog.', + parts: ['The', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog.'], + answer: ['The', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog.'], + audio_url: 'https://example.com/audio/sentence3.mp3', + }, + ], +}; +// ============================================================================= +// MOCK DATA MAP +// ============================================================================= +export const MockData = { + // Quiz + G001: MOCK_G001, + G002: MOCK_G002, + G003: MOCK_G003, + G004: MOCK_G004, + // Sequence Word + G110: MOCK_G110, + G111: MOCK_G111, + G112: MOCK_G112, + G113: MOCK_G113, + // Sequence Sentence + G120: MOCK_G120, + G121: MOCK_G121, + G122: MOCK_G122, + G123: MOCK_G123, + G005: MOCK_G005, +}; +/** + * Get mock data for a game code + */ +export function getMockData(code) { + return MockData[code] ?? null; +} +/** + * Get all available game codes + */ +export function getAvailableGameCodes() { + return Object.keys(MockData); +} +//# sourceMappingURL=MockData.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/client/MockData.js.map b/G102-sequence/sdk/package/dist/esm/client/MockData.js.map new file mode 100644 index 0000000..7bfcb60 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/client/MockData.js.map @@ -0,0 +1 @@ +{"version":3,"file":"MockData.js","sourceRoot":"","sources":["../../../src/client/MockData.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/client/index.js b/G102-sequence/sdk/package/dist/esm/client/index.js new file mode 100644 index 0000000..4ade702 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/client/index.js @@ -0,0 +1,10 @@ +/** + * Client SDK exports + * SDK dành cho game developers sử dụng trong game iframe + */ +export { GameClientSDK, getGameClientSDK, destroyGameClientSDK, } from './GameClientSDK'; +// Mock Data - sample data cho từng game code +export { MockData, getMockData, getAvailableGameCodes, MOCK_G001, MOCK_G002, MOCK_G003, MOCK_G004, MOCK_G110, MOCK_G111, MOCK_G112, MOCK_G113, MOCK_G120, MOCK_G121, MOCK_G122, MOCK_G123, } from './MockData'; +// Data Validator - verify data structure +export { validateGameData, getSchema, getSchemaDoc, DataValidator, } from './DataValidator'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/client/index.js.map b/G102-sequence/sdk/package/dist/esm/client/index.js.map new file mode 100644 index 0000000..9711281 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/client/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,aAAa,EACb,gBAAgB,EAChB,oBAAoB,GAQvB,MAAM,iBAAiB,CAAC;AAEzB,6CAA6C;AAC7C,OAAO,EACH,QAAQ,EACR,WAAW,EACX,qBAAqB,EACrB,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,EACT,SAAS,GACZ,MAAM,YAAY,CAAC;AAEpB,yCAAyC;AACzC,OAAO,EACH,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,aAAa,GAIhB,MAAM,iBAAiB,CAAC"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/game-bridge/GameBridge.js b/G102-sequence/sdk/package/dist/esm/game-bridge/GameBridge.js new file mode 100644 index 0000000..7bc5276 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/game-bridge/GameBridge.js @@ -0,0 +1,292 @@ +/** + * Game Bridge + * Wrapper đơn giản cho game developers để giao tiếp với SDK Iframe + * + * Usage: + * ```typescript + * import { GameBridge } from 'game-iframe-sdk/game-bridge'; + * + * const bridge = new GameBridge({ + * sdkIframeUrl: 'https://sdk.sena.tech/sdk-iframe.html', + * debug: true, + * }); + * + * // Init + * await bridge.init({ + * mode: 'preview', + * game_code: 'G001', + * }); + * + * // Listen for data + * bridge.on('dataReady', (data) => { + * renderGame(data.items); + * }); + * + * // Check answer + * bridge.checkAnswer('q1', userChoice).then(result => { + * showFeedback(result.correct); + * }); + * ``` + */ +import { SDK_MESSAGE_TYPES, createSdkMessage, isSdkMessage, } from '../sdk-iframe/types'; +// ============================================================================= +// GAME BRIDGE +// ============================================================================= +export class GameBridge { + constructor(config) { + this.sdkIframe = null; + this.sdkOrigin = ''; + this.isReady = false; + // Event handlers + this.handlers = new Map(); + // Pending requests (for promise-based API) + this.pendingRequests = new Map(); + this.requestCounter = 0; + this.config = { + sdkIframeUrl: config.sdkIframeUrl, + debug: config.debug ?? false, + timeout: config.timeout ?? 10000, + }; + // Extract origin from SDK URL + try { + const url = new URL(this.config.sdkIframeUrl); + this.sdkOrigin = url.origin; + } + catch { + this.sdkOrigin = '*'; + } + this.setupMessageListener(); + } + // ========================================================================== + // PUBLIC API - Init + // ========================================================================== + /** + * Create SDK Iframe and initialize + */ + async init(payload) { + // Create hidden iframe + this.sdkIframe = document.createElement('iframe'); + this.sdkIframe.src = this.config.sdkIframeUrl; + this.sdkIframe.style.cssText = 'position:absolute;width:0;height:0;border:0;visibility:hidden;'; + this.sdkIframe.setAttribute('aria-hidden', 'true'); + document.body.appendChild(this.sdkIframe); + this.log('info', 'SDK Iframe created'); + // Wait for iframe to load + await new Promise((resolve) => { + this.sdkIframe.onload = () => resolve(); + }); + // Send init + return this.sendRequest(SDK_MESSAGE_TYPES.SDK_INIT, payload); + } + /** + * Push data (preview mode) + */ + async pushData(payload) { + return this.sendRequest(SDK_MESSAGE_TYPES.SDK_PUSH_DATA, payload); + } + // ========================================================================== + // PUBLIC API - Game Actions + // ========================================================================== + /** + * Check answer - returns local result immediately + * Also triggers server sync in background + */ + async checkAnswer(questionId, choice, timeSpent) { + const payload = { + question_id: questionId, + choice, + time_spent: timeSpent, + }; + return this.sendRequest(SDK_MESSAGE_TYPES.SDK_CHECK_ANSWER, payload); + } + /** + * Get final result + */ + async getFinalResult() { + return this.sendRequest(SDK_MESSAGE_TYPES.SDK_GET_RESULT, {}); + } + /** + * Retry sync for a question + */ + async retrySync(questionId) { + return this.sendRequest(SDK_MESSAGE_TYPES.SDK_RETRY_SYNC, { + question_id: questionId, + }); + } + // ========================================================================== + // PUBLIC API - Events + // ========================================================================== + /** + * Subscribe to events + */ + on(event, handler) { + if (!this.handlers.has(event)) { + this.handlers.set(event, new Set()); + } + this.handlers.get(event).add(handler); + return () => this.off(event, handler); + } + /** + * Unsubscribe from events + */ + off(event, handler) { + this.handlers.get(event)?.delete(handler); + } + // ========================================================================== + // PUBLIC API - State + // ========================================================================== + /** + * Check if SDK is ready + */ + isSdkReady() { + return this.isReady; + } + /** + * Destroy bridge and cleanup + */ + destroy() { + // Clear pending requests + this.pendingRequests.forEach((pending) => { + clearTimeout(pending.timeout); + pending.reject(new Error('Bridge destroyed')); + }); + this.pendingRequests.clear(); + // Remove iframe + if (this.sdkIframe && this.sdkIframe.parentNode) { + this.sdkIframe.parentNode.removeChild(this.sdkIframe); + } + this.sdkIframe = null; + // Clear handlers + this.handlers.clear(); + this.log('info', 'Bridge destroyed'); + } + // ========================================================================== + // INTERNAL - Message Handling + // ========================================================================== + setupMessageListener() { + window.addEventListener('message', this.handleMessage.bind(this)); + } + handleMessage(event) { + // Validate origin (if not *) + if (this.sdkOrigin !== '*' && event.origin !== this.sdkOrigin) { + return; + } + const data = event.data; + if (!isSdkMessage(data)) { + return; + } + this.log('debug', `Received: ${data.type}`, data.payload); + // Handle pending request response + if (data.request_id && this.pendingRequests.has(data.request_id)) { + const pending = this.pendingRequests.get(data.request_id); + clearTimeout(pending.timeout); + this.pendingRequests.delete(data.request_id); + if (data.type === SDK_MESSAGE_TYPES.SDK_ERROR) { + pending.reject(data.payload); + } + else { + pending.resolve(data.payload); + } + return; + } + // Handle events + switch (data.type) { + case SDK_MESSAGE_TYPES.SDK_READY: + this.isReady = true; + this.emit('ready', data.payload); + break; + case SDK_MESSAGE_TYPES.SDK_DATA_READY: + this.emit('dataReady', data.payload); + break; + case SDK_MESSAGE_TYPES.SDK_ANSWER_RESULT: + this.emit('answerResult', data.payload); + break; + case SDK_MESSAGE_TYPES.SDK_SYNC_STATUS: + this.emit('syncStatus', data.payload); + break; + case SDK_MESSAGE_TYPES.SDK_SYNC_ERROR: + this.emit('syncError', data.payload); + break; + case SDK_MESSAGE_TYPES.SDK_FINAL_RESULT: + this.emit('finalResult', data.payload); + break; + case SDK_MESSAGE_TYPES.SDK_ERROR: + this.emit('error', data.payload); + break; + } + } + emit(event, data) { + this.handlers.get(event)?.forEach(handler => { + try { + handler(data); + } + catch (err) { + this.log('error', `Error in ${event} handler`, err); + } + }); + } + // ========================================================================== + // INTERNAL - Sending Messages + // ========================================================================== + sendRequest(type, payload) { + return new Promise((resolve, reject) => { + if (!this.sdkIframe?.contentWindow) { + reject(new Error('SDK Iframe not ready')); + return; + } + const requestId = `req_${++this.requestCounter}_${Date.now()}`; + const message = createSdkMessage(type, payload, requestId); + // Setup timeout + const timeout = setTimeout(() => { + this.pendingRequests.delete(requestId); + reject(new Error(`Request timeout: ${type}`)); + }, this.config.timeout); + // Store pending request + this.pendingRequests.set(requestId, { resolve, reject, timeout }); + // Send message + this.sdkIframe.contentWindow.postMessage(message, this.sdkOrigin); + this.log('debug', `Sent: ${type}`, payload); + }); + } + log(level, message, data) { + if (!this.config.debug && level === 'debug') + return; + const prefix = '[GameBridge]'; + switch (level) { + case 'debug': + case 'info': + console.log(prefix, message, data ?? ''); + break; + case 'warn': + console.warn(prefix, message, data ?? ''); + break; + case 'error': + console.error(prefix, message, data ?? ''); + break; + } + } +} +// ============================================================================= +// FACTORY +// ============================================================================= +let bridgeInstance = null; +/** + * Get or create GameBridge instance + */ +export function getGameBridge(config) { + if (!bridgeInstance && config) { + bridgeInstance = new GameBridge(config); + } + if (!bridgeInstance) { + throw new Error('GameBridge not initialized. Call with config first.'); + } + return bridgeInstance; +} +/** + * Destroy GameBridge instance + */ +export function destroyGameBridge() { + bridgeInstance?.destroy(); + bridgeInstance = null; +} +//# sourceMappingURL=GameBridge.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/game-bridge/GameBridge.js.map b/G102-sequence/sdk/package/dist/esm/game-bridge/GameBridge.js.map new file mode 100644 index 0000000..2288912 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/game-bridge/GameBridge.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GameBridge.js","sourceRoot":"","sources":["../../../src/game-bridge/GameBridge.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/game-bridge/index.js b/G102-sequence/sdk/package/dist/esm/game-bridge/index.js new file mode 100644 index 0000000..0984700 --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/game-bridge/index.js @@ -0,0 +1,8 @@ +/** + * Game Bridge exports + * Dành cho game developers tích hợp vào game + */ +export { GameBridge, getGameBridge, destroyGameBridge, } from './GameBridge'; +// Re-export types từ sdk-iframe +export { SDK_MESSAGE_TYPES, } from '../sdk-iframe/types'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/game-bridge/index.js.map b/G102-sequence/sdk/package/dist/esm/game-bridge/index.js.map new file mode 100644 index 0000000..ec9700e --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/game-bridge/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/game-bridge/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,UAAU,EACV,aAAa,EACb,iBAAiB,GAGpB,MAAM,cAAc,CAAC;AAEtB,gCAAgC;AAChC,OAAO,EACH,iBAAiB,GAYpB,MAAM,qBAAqB,CAAC"} \ No newline at end of file diff --git a/G102-sequence/sdk/package/dist/esm/index.js b/G102-sequence/sdk/package/dist/esm/index.js new file mode 100644 index 0000000..ef2756a --- /dev/null +++ b/G102-sequence/sdk/package/dist/esm/index.js @@ -0,0 +1,94 @@ +/** + * Game Iframe SDK - Main Entry Point + * + * @packageDocumentation + * @module game-iframe-sdk + * + * Architecture: + * - types.ts: Type definitions + * - mappers.ts: Data transformation/mapping + * - EventEmitter.ts: Simple typed event emitter + * - MessageHandler.ts: Handle incoming messages from iframe + * - MessageSender.ts: Send messages to iframe + * - GameIframeSDK.ts: Main SDK (composes above layers) + * - useGameIframeSDK.ts: React hook + * + * @example Browser/Vanilla JS + * ```typescript + * import { GameIframeSDK } from 'game-iframe-sdk'; + * + * const sdk = new GameIframeSDK({ + * iframeOrigin: 'http://senaai.vn:1357', + * debug: true + * }); + * + * sdk.setIframe(document.getElementById('gameIframe')); + * + * sdk.on('gameReady', () => { + * sdk.sendGameData({ game_id: 'xxx', user_id: 'yyy', questions: [...] }); + * }); + * ``` + * + * @example React + * ```tsx + * import { useGameIframeSDK } from 'game-iframe-sdk'; + * + * function GamePlayer() { + * const iframeRef = useRef(null); + * + * const { isReady, sendGameData } = useGameIframeSDK({ + * iframeRef, + * iframeOrigin: 'http://senaai.vn:1357', + * onAnswerReport: (data) => submitToServer(data), + * }); + * + * return