up
All checks were successful
Deploy to Production / deploy (push) Successful in 8s

This commit is contained in:
lubukhu
2026-01-24 13:35:11 +07:00
parent 6c3e93636e
commit 65fd0158a3
145 changed files with 10262 additions and 0 deletions

View File

@@ -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

View File

@@ -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"}

View File

@@ -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

File diff suppressed because one or more lines are too long

View File

@@ -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<string, string>;
}
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<T> = (data: T) => void;
declare class SimpleEventEmitter<Events extends Record<string, any>> {
private handlers;
on<K extends keyof Events>(event: K, handler: EventHandler<Events[K]>): () => void;
off<K extends keyof Events>(event: K, handler: EventHandler<Events[K]>): void;
protected emit<K extends keyof Events>(event: K, data: Events[K]): void;
}
export declare class GameClientSDK extends SimpleEventEmitter<ClientSDKEvents> {
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

View File

@@ -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"}

View File

@@ -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

File diff suppressed because one or more lines are too long

View File

@@ -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<GameCode, any>;
/**
* 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

View File

@@ -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"}

View File

@@ -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

File diff suppressed because one or more lines are too long

View File

@@ -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

View File

@@ -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"}

View File

@@ -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

View File

@@ -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"}