broker-core-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # Core SDK - Adaptive Learning System
2
+
3
+ Đây là bộ công cụ lõi (Core SDK) chứa toàn bộ logic nghiệp vụ, thuật toán học tập, hằng số (constants) và các định nghĩa dữ liệu (types) dùng chung cho toàn bộ hệ thống Adaptive Learning. SDK này được thiết kế theo hướng **Functional Programming**, hỗ trợ **Tree-shaking** tối đa và đảm bảo tính nhất quán (Type-safety) giữa các nền tảng Web (Admin), Mobile (Learner) và Backend.
4
+
5
+ ---
6
+
7
+ ## 🚀 Tính năng chính
8
+
9
+ - **Constants Management**: Quản lý các biến hằng (như loại phần tử, mức độ tự tin) tập trung.
10
+ - **Learning Engine**: Thuật toán Spaced Repetition (Lặp lại ngắt quãng) và tính toán mức độ thành thạo (Mastery).
11
+ - **Game Engine**: Logic "trọng tài" kiểm tra kết quả Đúng/Sai thống nhất cho tất cả các dạng Game (Quiz, Sorting, Matching, Hotspot, Memory Card).
12
+ - **Slide Renderer Math**: Bộ công thức chuyển đổi tọa độ linh hoạt giữa giá trị `%` và `Pixel`.
13
+ - **Discriminated Union Types**: Hệ thống Type chặt chẽ, tự động ánh xạ cấu trúc `data` theo `type` của phần tử, hỗ trợ Autocomplete tối đa cho IDE.
14
+
15
+ ---
16
+
17
+ ## 📦 Cài đặt
18
+
19
+ ### Dành cho dự án trong cùng Monorepo (Workspace)
20
+ Thêm vào file `package.json` của project cần dùng:
21
+ ```json
22
+ "dependencies": {
23
+ "@broker/core-sdk": "workspace:*"
24
+ }
25
+ ```
26
+
27
+ ### Dành cho dự án độc lập
28
+ ```bash
29
+ npm install ../path-to/core-sdk
30
+ ```
31
+
32
+ ---
33
+
34
+ ## 🛠 Hướng dẫn sử dụng chi tiết
35
+
36
+ ### 1. Hệ thống Constants & Type
37
+ Bộ SDK cung cấp sẵn các object constants giúp bạn không phải fix cứng chuỗi (`"QUIZ"`, `"High"`,...) ở các dự án khác.
38
+
39
+ ```typescript
40
+ import { ELEMENT_TYPES, CONFIDENCE_LEVELS } from "@broker/core-sdk";
41
+ import type { ElementType, ConfidenceLevel } from "@broker/core-sdk";
42
+
43
+ // Sử dụng thay cho hardcode text
44
+ const myType: ElementType = ELEMENT_TYPES.QUIZ;
45
+ const conf: ConfidenceLevel = CONFIDENCE_LEVELS.HIGH;
46
+ ```
47
+
48
+ ### 2. SlideElement & Type Safety (Mới)
49
+ `SlideElement` hiện đã được chuyển sang dạng **Discriminated Union**. Khi bạn sử dụng trong code, IDE sẽ tự động gợi ý các trường trong `data` dựa trên `type`.
50
+
51
+ ```typescript
52
+ import type { SlideElement } from "@broker/core-sdk";
53
+
54
+ const element: SlideElement = {
55
+ id: "e1",
56
+ type: "QUIZ", // Khi type là QUIZ
57
+ position: { x: 0, y: 0, w: 100, h: 100 },
58
+ data: {
59
+ question: "TypeScript là gì?",
60
+ options: [{ id: "1", content: "Ngôn ngữ lập trình" }],
61
+ correctId: "1"
62
+ } // IDE sẽ autocomplete các trường của MultipleChoiceData
63
+ };
64
+ ```
65
+
66
+ ### 3. Learning Engine (Tính toán lộ trình học)
67
+ ```typescript
68
+ import { learningEngine, CONFIDENCE_LEVELS } from "@broker/core-sdk";
69
+
70
+ const daysToAdd = learningEngine.calculateNextReview(true, CONFIDENCE_LEVELS.HIGH); // Kết quả: 7
71
+ const isMastered = learningEngine.checkMastery(3, CONFIDENCE_LEVELS.HIGH); // Kết quả: true
72
+ ```
73
+
74
+ ---
75
+
76
+ ## 🏗 Cấu trúc SDK (Hệ thống Types mới)
77
+
78
+ Thư mục `src/types/` đã được quy hoạch lại để dễ quản lý:
79
+
80
+ - **`common.ts`**: Chứa các kiểu dữ liệu dùng chung cơ bản (như `Position`).
81
+ - **`data.ts`**: Tập trung toàn bộ Payload của phần tử (`TextData`, `VideoData`, `MultipleChoiceData`,... và `ElementDataMap`).
82
+ - **`elements.ts`**: Định nghĩa cấu trúc khung của phần tử (`SlideElement`, `ElementStyle`, `ElementType`).
83
+ - **`models.ts`**: Các thực thể lớn của hệ thống (`Slide`, `UserProgress`, `ActionLog`).
84
+
85
+ ---
86
+
87
+ ## 🧪 Phát triển và Kiểm thử
88
+
89
+ SDK là "trái tim" của hệ thống nên mọi thay đổi nhỏ đều có thể ảnh hưởng diện rộng.
90
+
91
+ 1. **Test Driven**:
92
+ ```bash
93
+ npm test
94
+ ```
95
+
96
+ 2. **Build & Verify Types**:
97
+ ```bash
98
+ npm run build
99
+ ```
100
+ (Lệnh này sử dụng `tsup` để đóng gói và tạo file `.d.ts`. Luôn chạy lệnh này để đảm bảo không có lỗi Type trước khi commit).
101
+
102
+ ---
103
+
104
+ ## 📝 Quy định khi mở rộng (Guidelines)
105
+
106
+ - **Không Magic Strings**: Luôn định nghĩa hằng số vào `elements.ts` (cho ELEMENT_TYPES) hoặc `models.ts` (cho CONFIDENCE_LEVELS).
107
+ - **Consolidate Data**: Khi thêm loại phần tử mới, hãy định nghĩa Interface data của nó trong `data.ts` và cập nhật vào `ElementDataMap`.
108
+ - **Discriminated Union**: Không sử dụng `any` cho trường `data` trong `SlideElement`. Hãy để hệ thống tự ánh xạ thông qua `ElementDataMap`.
109
+ - **Vanilla TS**: SDK không được phụ thuộc vào bất kỳ Framework nào (React/Node-specific).
110
+
111
+ ---
112
+
113
+ _© 2026 Broker Adaptive Learning Team - Tài liệu nội bộ._
@@ -0,0 +1,230 @@
1
+
2
+ === DIRECTORY TREE (max depth 3, source files only) ===
3
+ .gitignore
4
+ docs
5
+ docs\codebase
6
+ docs\codebase\.codebase-scan.txt
7
+ package-lock.json
8
+ package.json
9
+ README.md
10
+ src
11
+ src\data
12
+ src\engines
13
+ src\engines\game-engine.ts
14
+ src\engines\learning-engine.ts
15
+ src\index.ts
16
+ src\renderer
17
+ src\renderer\slide-renderer.ts
18
+ src\sync
19
+ src\sync\action-manager.ts
20
+ src\types
21
+ src\types\actions.ts
22
+ src\types\common.ts
23
+ src\types\data.ts
24
+ src\types\elements.ts
25
+ src\types\index.ts
26
+ src\types\models.ts
27
+ tests
28
+ tests\game-engine.test.ts
29
+ tests\learning-engine.test.ts
30
+ tests\slide-renderer.test.ts
31
+ tsconfig.json
32
+
33
+ === STACK DETECTION (manifest files) ===
34
+
35
+ --- package-lock.json ---
36
+ {
37
+ "name": "@broker/core-sdk",
38
+ "version": "1.0.0",
39
+ "lockfileVersion": 3,
40
+ "requires": true,
41
+ "packages": {
42
+ "": {
43
+ "name": "@broker/core-sdk",
44
+ "version": "1.0.0",
45
+ "license": "ISC",
46
+ "devDependencies": {
47
+ "tsup": "^8.5.1",
48
+ "typescript": "^5.9.3",
49
+ "vitest": "^4.1.5"
50
+ }
51
+ },
52
+ "node_modules/@emnapi/wasi-threads": {
53
+ "version": "1.2.1",
54
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
55
+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
56
+ "dev": true,
57
+ "license": "MIT",
58
+ "optional": true,
59
+ "dependencies": {
60
+ "tslib": "^2.4.0"
61
+ }
62
+ },
63
+ "node_modules/@esbuild/aix-ppc64": {
64
+ "version": "0.27.7",
65
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
66
+ "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
67
+ "cpu": [
68
+ "ppc64"
69
+ ],
70
+ "dev": true,
71
+ "license": "MIT",
72
+ "optional": true,
73
+ "os": [
74
+ "aix"
75
+ ],
76
+ "engines": {
77
+ "node": ">=18"
78
+ }
79
+ },
80
+ "node_modules/@esbuild/android-arm": {
81
+ "version": "0.27.7",
82
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
83
+ "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
84
+ "cpu": [
85
+ "arm"
86
+ ],
87
+ "dev": true,
88
+ "license": "MIT",
89
+ "optional": true,
90
+ "os": [
91
+ "android"
92
+ ],
93
+ "engines": {
94
+ "node": ">=18"
95
+ }
96
+ },
97
+ "node_modules/@esbuild/android-arm64": {
98
+ "version": "0.27.7",
99
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
100
+ "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
101
+ "cpu": [
102
+ "arm64"
103
+ ],
104
+ "dev": true,
105
+ "license": "MIT",
106
+ "optional": true,
107
+ "os": [
108
+ "android"
109
+ ],
110
+ "engines": {
111
+ "node": ">=18"
112
+ }
113
+ },
114
+ "node_modules/@esbuild/android-x64": {
115
+ "version": "0.27.7",
116
+
117
+ [TRUNCATED] Showing first 80 of 2601 lines.
118
+ --- package.json ---
119
+ {
120
+ "name": "@broker/core-sdk",
121
+ "type": "module",
122
+ "version": "1.0.0",
123
+ "description": "",
124
+ "main": "./dist/index.cjs",
125
+ "module": "./dist/index.js",
126
+ "types": "./dist/index.d.ts",
127
+ "exports": {
128
+ ".": {
129
+ "types": "./dist/index.d.ts",
130
+ "import": "./dist/index.js",
131
+ "require": "./dist/index.cjs"
132
+ }
133
+ },
134
+ "scripts": {
135
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
136
+ "test": "vitest"
137
+ },
138
+ "keywords": [],
139
+ "author": "",
140
+ "license": "ISC",
141
+ "devDependencies": {
142
+ "tsup": "^8.5.1",
143
+ "typescript": "^5.9.3",
144
+ "vitest": "^4.1.5"
145
+ }
146
+ }
147
+
148
+
149
+ === ENTRY POINTS ===
150
+ Found: src/index.ts
151
+
152
+ === LINTING AND FORMATTING CONFIG ===
153
+ Found: tsconfig.json
154
+
155
+ === ENVIRONMENT VARIABLE TEMPLATES ===
156
+ No .env.example or .env.template found. Identify required environment variables by searching the code and config for environment variable reads.
157
+
158
+ === TODO / FIXME / HACK (production code only, test dirs excluded) ===
159
+ None found.
160
+
161
+ === GIT RECENT COMMITS (last 20) ===
162
+ b929636 feat: export core engines, renderers, and types from index entry point
163
+ 691b6a2 feat: define interface structures and type maps for core element data types
164
+ dcc6a31 feat: implement game result validation engine and core element type definitions
165
+ e8689e3 docs: update installation and configuration instructions in README
166
+ 23345b5 feat: implement core types, models, and learning engine structure with scope renaming to @broker/core-sdk
167
+ b500b5a docs: update installation instructions and usage examples in README.md
168
+ 9eefc2b feat: add Slide, SlideElement, and ElementStyle type definitions
169
+ 2cc0159 feat: define Slide and UserProgress types for core SDK models
170
+ 602b2a4 feat: implement game engine validation logic and define core base and action logging types
171
+ 267d21e feat: initialize core-sdk with game engine, slide renderer, and common data models
172
+
173
+ === HIGH-CHURN FILES (last 90 days, top 20) ===
174
+ 4 README.md
175
+ 4 src/types/slide-element.ts
176
+ 3 src/engines/game-engine.ts
177
+ 3 src/types/index.ts
178
+ 3 src/engines/learning-engine.ts
179
+ 3 src/types/action-log.ts
180
+ 3 src/types/base.ts
181
+ 3 src/types/user-progress.ts
182
+ 2 src/index.ts
183
+ 2 src/types/data.ts
184
+ 2 src/types/elements.ts
185
+ 2 package-lock.json
186
+ 2 package.json
187
+ 1 src/types/actions.ts
188
+ 1 src/types/common.ts
189
+ 1 src/types/models.ts
190
+ 1 .gitignore
191
+ 1 src/renderer/slide-renderer.ts
192
+ 1 src/sync/action-manager.ts
193
+ 1 src/types/game-logic.ts
194
+
195
+ === MONOREPO SIGNALS ===
196
+ No monorepo signals detected.
197
+
198
+ === CODE METRICS ===
199
+ Total files scanned: 19
200
+ Total lines of code: 3261
201
+
202
+ Files by language:
203
+ TypeScript: 14
204
+ Other: 5
205
+
206
+ Top 10 largest files:
207
+ package-lock.json: 83.2KB
208
+ README.md: 4.7KB
209
+ tests\game-engine.test.ts: 3.2KB
210
+ src\types\actions.ts: 3.1KB
211
+ src\types\elements.ts: 1.9KB
212
+ src\types\data.ts: 1.6KB
213
+ src\engines\game-engine.ts: 1.5KB
214
+ tsconfig.json: 1.1KB
215
+ tests\learning-engine.test.ts: 1.1KB
216
+ src\engines\learning-engine.ts: 1.0KB
217
+
218
+ === CI/CD PIPELINES ===
219
+ No CI/CD pipelines detected.
220
+
221
+ === CONTAINERS & ORCHESTRATION ===
222
+ No containerization configs detected.
223
+
224
+ === SECURITY & COMPLIANCE ===
225
+ No security configs detected.
226
+
227
+ === PERFORMANCE & TESTING ===
228
+ No performance testing configs detected.
229
+
230
+ === SCAN COMPLETE ===
@@ -0,0 +1,37 @@
1
+ # Architecture
2
+
3
+ ## Core Sections (Required)
4
+
5
+ ### 1) Architectural Style
6
+
7
+ - Primary style: Library / SDK (Functional Programming)
8
+ - Why this classification: Stated in `README.md` that it is designed with Functional Programming and Tree-shaking.
9
+ - Primary constraints: Must be framework agnostic (Vanilla TS), no Magic Strings, must use strict Discriminated Union types.
10
+
11
+ ### 2) System Flow
12
+
13
+ ```text
14
+ [Consumer App] -> [Core SDK Export (src/index.ts)] -> [Pure Function Engine (e.g. game-engine.ts)] -> [Result]
15
+ ```
16
+
17
+ ### 3) Layer/Module Responsibilities
18
+
19
+ | Layer or module | Owns | Must not own | Evidence |
20
+ |-----------------|------|--------------|----------|
21
+ | `src/engines` | Algorithmic logic (Spaced Repetition, Game Checks) | State management, UI | `README.md` |
22
+ | `src/types` | Data definitions and Constants | Implementations | `README.md` |
23
+
24
+ ### 4) Reused Patterns
25
+
26
+ | Pattern | Where found | Why it exists |
27
+ |---------|-------------|---------------|
28
+ | Discriminated Union | `src/types/elements.ts` (`SlideElement`) | To map structures automatically and ensure Type-safety across the ecosystem. |
29
+
30
+ ### 5) Known Architectural Risks
31
+
32
+ - [TODO] Expanding types might require significant refactoring across the `ElementDataMap`.
33
+
34
+ ### 6) Evidence
35
+
36
+ - `README.md`
37
+ - `src/index.ts`
@@ -0,0 +1,19 @@
1
+ # Concerns and Technical Debt
2
+
3
+ ## Core Sections (Required)
4
+
5
+ ### 1) Technical Debt & TODOs
6
+
7
+ - NONE detected in code comments.
8
+
9
+ ### 2) High-Churn Areas
10
+
11
+ - `README.md` (4 commits)
12
+ - `src/types/slide-element.ts` (4 commits) - Indicates high activity around the core element types.
13
+ - `src/engines/game-engine.ts` (3 commits)
14
+ - `src/engines/learning-engine.ts` (3 commits)
15
+ - `src/types/index.ts` (3 commits)
16
+
17
+ ### 3) Evidence
18
+
19
+ - Scan output
@@ -0,0 +1,22 @@
1
+ # Coding Standards and Conventions
2
+
3
+ ## Core Sections (Required)
4
+
5
+ ### 1) Naming Conventions
6
+
7
+ - Files: kebab-case (e.g., `slide-renderer.ts`, `learning-engine.ts`)
8
+ - Variables/Functions: camelCase
9
+ - Constants: UPPER_SNAKE_CASE (e.g., `ELEMENT_TYPES`, `CONFIDENCE_LEVELS`)
10
+ - Types/Interfaces: PascalCase
11
+
12
+ ### 2) Coding Rules
13
+
14
+ - **No Magic Strings**: Always define constants in `src/types/elements.ts` or `src/types/models.ts`. (from `README.md`)
15
+ - **Consolidate Data**: When adding new element types, define the data interface in `data.ts` and add to `ElementDataMap`. (from `README.md`)
16
+ - **Strict Discriminated Unions**: Do not use `any` for the `data` field in `SlideElement`. (from `README.md`)
17
+ - **Framework Agnostic**: Code must be Vanilla TS without React/Node-specific dependencies. (from `README.md`)
18
+
19
+ ### 3) Evidence
20
+
21
+ - `README.md`
22
+ - Scan output
@@ -0,0 +1,12 @@
1
+ # Integrations
2
+
3
+ ## Core Sections (Required)
4
+
5
+ ### 1) External Services
6
+
7
+ - External APIs: NONE (It is a local SDK)
8
+ - Databases: NONE
9
+
10
+ ### 2) Evidence
11
+
12
+ - `package.json` (no dependencies)
@@ -0,0 +1,47 @@
1
+ # Technology Stack
2
+
3
+ ## Core Sections (Required)
4
+
5
+ ### 1) Runtime Summary
6
+
7
+ | Area | Value | Evidence |
8
+ |------|-------|----------|
9
+ | Primary language | TypeScript | `package.json`, `tsconfig.json` |
10
+ | Runtime + version | Node.js / Browser (Library) | `package.json` |
11
+ | Package manager | npm | `package-lock.json` |
12
+ | Module/build system | tsup | `package.json` (`scripts.build`) |
13
+
14
+ ### 2) Production Frameworks and Dependencies
15
+
16
+ List only high-impact production dependencies (frameworks, data, transport, auth).
17
+
18
+ | Dependency | Version | Role in system | Evidence |
19
+ |------------|---------|----------------|----------|
20
+ | None | N/A | Zero-dependency SDK | `package.json` (no `dependencies`) |
21
+
22
+ ### 3) Development Toolchain
23
+
24
+ | Tool | Purpose | Evidence |
25
+ |------|---------|----------|
26
+ | `typescript` | Type checking | `package.json` |
27
+ | `tsup` | Module bundler (CJS/ESM) | `package.json` |
28
+ | `vitest` | Unit testing | `package.json` |
29
+
30
+ ### 4) Key Commands
31
+
32
+ ```bash
33
+ npm install
34
+ npm run build
35
+ npm test
36
+ ```
37
+
38
+ ### 5) Environment and Config
39
+
40
+ - Config sources: `tsconfig.json`
41
+ - Required env vars: None detected.
42
+ - Deployment/runtime constraints: Deployed as an npm package via workspace or separate install, exports ESM and CommonJS. Framework-agnostic.
43
+
44
+ ### 6) Evidence
45
+
46
+ - `package.json`
47
+ - `tsconfig.json`
@@ -0,0 +1,41 @@
1
+ # Codebase Structure
2
+
3
+ ## Core Sections (Required)
4
+
5
+ ### 1) Top-Level Map
6
+
7
+ List only meaningful top-level directories and files.
8
+
9
+ | Path | Purpose | Evidence |
10
+ |------|---------|----------|
11
+ | `src/` | Source code root | Scan output |
12
+ | `src/types/` | Data definitions, Discriminated Unions, and models | `README.md`, Scan output |
13
+ | `src/engines/` | Core logic for game evaluation and spaced repetition | `README.md`, Scan output |
14
+ | `src/renderer/` | Slide rendering and calculation math | `README.md`, Scan output |
15
+ | `src/sync/` | Action manager handling | Scan output |
16
+ | `tests/` | Unit test files | Scan output |
17
+
18
+ ### 2) Entry Points
19
+
20
+ - Main runtime entry: `src/index.ts`
21
+ - Secondary entry points (worker/cli/jobs): NONE
22
+ - How entry is selected (script/config): Configured in `package.json` (`main`, `module`, `types`, `exports` fields).
23
+
24
+ ### 3) Module Boundaries
25
+
26
+ | Boundary | What belongs here | What must not be here |
27
+ |----------|-------------------|------------------------|
28
+ | `src/types/` | Interfaces, types, and constants | Business logic, algorithms |
29
+ | `src/engines/` | Pure functions for logic (Game Engine, Learning Engine) | UI rendering code, specific framework APIs |
30
+
31
+ ### 4) Naming and Organization Rules
32
+
33
+ - File naming pattern: kebab-case (e.g., `game-engine.ts`, `slide-renderer.ts`)
34
+ - Directory organization pattern: domain-based (`engines`, `types`, `renderer`)
35
+ - Import aliasing or path conventions: Relative imports within the SDK, exported centrally via `src/index.ts`.
36
+
37
+ ### 5) Evidence
38
+
39
+ - `docs/codebase/.codebase-scan.txt`
40
+ - `package.json`
41
+ - `README.md`
@@ -0,0 +1,22 @@
1
+ # Testing
2
+
3
+ ## Core Sections (Required)
4
+
5
+ ### 1) Testing Frameworks
6
+
7
+ - Test Runner: `vitest`
8
+
9
+ ### 2) Test Organization
10
+
11
+ - Location: Tests are colocated in the `tests/` directory at the project root (e.g., `tests/game-engine.test.ts`).
12
+
13
+ ### 3) Key Commands
14
+
15
+ ```bash
16
+ npm test
17
+ ```
18
+
19
+ ### 4) Evidence
20
+
21
+ - `package.json`
22
+ - Scan output
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "broker-core-sdk",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
+ "description": "",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "scripts": {
17
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
18
+ "test": "vitest"
19
+ },
20
+ "keywords": [],
21
+ "author": "",
22
+ "license": "ISC",
23
+ "devDependencies": {
24
+ "tsup": "^8.5.1",
25
+ "typescript": "^5.9.3",
26
+ "vitest": "^4.1.5"
27
+ }
28
+ }
@@ -0,0 +1,44 @@
1
+ import type { GameDataMap } from "../types/data.js";
2
+ import { GAME_TYPES, type GameType } from "../types/elements.js";
3
+
4
+ /**
5
+ * Validator nhận vào toàn bộ object cấu hình (GameDataMap[T]).
6
+ */
7
+ export const validateGameResult = <T extends GameType>(
8
+ type: T,
9
+ userAnswer: any,
10
+ correctData: GameDataMap[T], // Nhận cả object data thay vì chỉ string
11
+ ): boolean => {
12
+ switch (type) {
13
+ case GAME_TYPES.QUIZ: {
14
+ // Ép kiểu về MultipleChoiceData để truy cập correctId
15
+ const data = correctData as GameDataMap[typeof GAME_TYPES.QUIZ];
16
+ return userAnswer === data.correctId;
17
+ }
18
+
19
+ case GAME_TYPES.SORTING: {
20
+ const data = correctData as GameDataMap[typeof GAME_TYPES.SORTING];
21
+ return JSON.stringify(userAnswer) === JSON.stringify(data.correctOrder);
22
+ }
23
+
24
+ case GAME_TYPES.MATCHING: {
25
+ const data = correctData as GameDataMap[typeof GAME_TYPES.MATCHING];
26
+ const userSorted = [...userAnswer].sort();
27
+ const correctSorted = [...data.correctPairs].sort();
28
+ return JSON.stringify(userSorted) === JSON.stringify(correctSorted);
29
+ }
30
+
31
+ case GAME_TYPES.HOTSPOT: {
32
+ const data = correctData as GameDataMap[typeof GAME_TYPES.HOTSPOT];
33
+ const { x, y } = userAnswer;
34
+ const zone = data.zones.find((z) => z.id === data.correctZoneId);
35
+ if (!zone) return false;
36
+ return (
37
+ x >= zone.xMin && x <= zone.xMax && y >= zone.yMin && y <= zone.yMax
38
+ );
39
+ }
40
+
41
+ default:
42
+ return false;
43
+ }
44
+ };
@@ -0,0 +1,35 @@
1
+ import { CONFIDENCE_LEVELS } from "../types/models.js";
2
+ import type { ConfidenceLevel } from "../types/models.js";
3
+
4
+ /**
5
+ * Tính toán ngày review tiếp theo dựa trên kết quả và độ tự tin.
6
+ * - Đúng + Tự tin Cao: 7 ngày.
7
+ * - Đúng + Tự tin Vừa/Thấp: 1 ngày.
8
+ * - Sai: 0 ngày (ôn lại ngay).
9
+ */
10
+ export const calculateNextReview = (
11
+ isCorrect: boolean,
12
+ confidence: ConfidenceLevel,
13
+ ): number => {
14
+ if (!isCorrect) {
15
+ return 0; // Ôn lại ngay lập tức
16
+ }
17
+
18
+ if (confidence === CONFIDENCE_LEVELS.HIGH) {
19
+ return 7; // Giãn cách 7 ngày cho câu trả lời tự tin
20
+ }
21
+
22
+ // Trường hợp Medium hoặc Low: Chỉ giãn cách 1 ngày
23
+ return 1;
24
+ };
25
+
26
+ /**
27
+ * Điều kiện Mastery (Thành thạo):
28
+ * Chuỗi đúng (streak) >= 3 và lần trả lời gần nhất có độ tự tin Cao.
29
+ */
30
+ export const checkMastery = (
31
+ streak: number,
32
+ lastConfidence: ConfidenceLevel,
33
+ ): boolean => {
34
+ return streak >= 3 && lastConfidence === CONFIDENCE_LEVELS.HIGH;
35
+ };
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ import * as SlideRenderer from "./renderer/slide-renderer.js";
2
+ import * as learningEngine from "./engines/learning-engine.js";
3
+ import * as gameEngine from "./engines/game-engine.js";
4
+ export * from "./types/index.js";
5
+ export { SlideRenderer, learningEngine, gameEngine };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Chuyển đổi tọa độ từ Pixel sang % (Dùng cho Admin Builder)
3
+ * Canvas Builder & Percentage Positioning
4
+ */
5
+ export const calculatePercentPosition = (
6
+ pixelPos: { left: number; top: number; width: number; height: number },
7
+ canvasSize: { width: number; height: number },
8
+ ) => ({
9
+ x: (pixelPos.left / canvasSize.width) * 100,
10
+ y: (pixelPos.top / canvasSize.height) * 100,
11
+ w: (pixelPos.width / canvasSize.width) * 100,
12
+ h: (pixelPos.height / canvasSize.height) * 100,
13
+ });
14
+
15
+ /**
16
+ * Chuyển đổi tọa độ từ % sang Pixel thực tế trên thiết bị.
17
+ * AC 1: Renderer phải tính toán vị trí dựa trên % từ JSON
18
+ */
19
+ export const calculateRealPosition = (
20
+ percentPos: { x: number; y: number; w: number; h: number },
21
+ screenSize: { width: number; height: number },
22
+ ) => ({
23
+ left: (percentPos.x * screenSize.width) / 100,
24
+ top: (percentPos.y * screenSize.height) / 100,
25
+ width: (percentPos.w * screenSize.width) / 100,
26
+ height: (percentPos.h * screenSize.height) / 100,
27
+ });
@@ -0,0 +1,23 @@
1
+ import type { ActionLog } from "../index.js";
2
+
3
+ /**
4
+ * Quản lý hàng đợi hành động để đồng bộ Offline.
5
+ * Cơ chế Action Log (Tránh mất dữ liệu)
6
+ */
7
+ export const filterActionsToSync = (
8
+ localActions: ActionLog[],
9
+ lastSyncAtServer: number,
10
+ ): ActionLog[] => {
11
+ // AC 1: Chỉ gửi lên Server những hành động có timestamp lớn hơn thời điểm đồng bộ cuối
12
+ return localActions.filter((action) => action.timestamp > lastSyncAtServer);
13
+ };
14
+
15
+ /**
16
+ * Xử lý xung đột theo quy tắc Last Sync Wins.
17
+ */
18
+ export const resolveSyncConflict = (
19
+ localTimestamp: number,
20
+ serverTimestamp: number,
21
+ ): boolean => {
22
+ return localTimestamp > serverTimestamp;
23
+ };
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Tập hợp các sự kiện (events) từ người dùng hoặc hệ thống
3
+ * dùng để kích hoạt một hành động (action) nào đó trên Slide.
4
+ */
5
+ export const ACTION_TRIGGERS = {
6
+ ON_CLICK: "ON_CLICK",
7
+ ON_DOUBLE_CLICK: "ON_DOUBLE_CLICK",
8
+ ON_HOVER: "ON_HOVER",
9
+ ON_ENTER_VIEWPORT: "ON_ENTER_VIEWPORT",
10
+ } as const;
11
+
12
+ export type ActionTrigger =
13
+ (typeof ACTION_TRIGGERS)[keyof typeof ACTION_TRIGGERS];
14
+
15
+ /**
16
+ * Danh sách các loại hành động (side-effects) mà hệ thống hỗ trợ thực thi.
17
+ * Ví dụ: Chuyển trang, ẩn hiện component, phát nhạc, hoặc nộp bài.
18
+ */
19
+ export const ACTION_TYPES = {
20
+ NAVIGATE_SLIDE: "NAVIGATE_SLIDE",
21
+ TOGGLE_VISIBILITY: "TOGGLE_VISIBILITY",
22
+ PLAY_MEDIA: "PLAY_MEDIA",
23
+ EVALUATE_ANSWER: "EVALUATE_ANSWER",
24
+ } as const;
25
+
26
+ export type ActionType = (typeof ACTION_TYPES)[keyof typeof ACTION_TYPES];
27
+
28
+ /**
29
+ * Payload: Dữ liệu cấu hình cho hành động chuyển slide.
30
+ * - `targetSlideId`: ID của slide muốn nhảy tới (Dùng cho điều hướng tự do).
31
+ * - `direction`: Điều hướng tuần tự (Tiến/Lùi).
32
+ */
33
+ export interface NavigatePayload {
34
+ targetSlideId?: string;
35
+ direction?: "NEXT" | "PREV";
36
+ }
37
+
38
+ /**
39
+ * Payload: Dữ liệu cấu hình cho hành động thay đổi trạng thái hiển thị của Element.
40
+ * - `targetElementId`: ID của phần tử bị tác động.
41
+ * - `action`: Trạng thái muốn áp dụng (Hiển thị / Ẩn / Đảo ngược trạng thái).
42
+ */
43
+ export interface ToggleVisibilityPayload {
44
+ targetElementId: string;
45
+ action: "SHOW" | "HIDE" | "TOGGLE";
46
+ }
47
+
48
+ /**
49
+ * Payload: Dữ liệu cấu hình cho hành động chấm điểm và tính toán Spaced Repetition.
50
+ * - `targetElementId`: (Tùy chọn) ID của bài tập (Quiz, Matching,..) cần chấm. Nếu nút bấm nằm rời bên ngoài.
51
+ * - `conceptId`: (Tùy chọn) ID của kỹ năng/khái niệm để lưu tiến độ (mastery) cho học viên.
52
+ */
53
+ export interface EvaluatePayload {
54
+ targetElementId?: string;
55
+ conceptId?: string;
56
+ }
57
+
58
+ /**
59
+ * Từ điển ánh xạ (Mapping Map) cực kỳ quan trọng.
60
+ * Định nghĩa chính xác ActionType nào thì phải xài chung với Payload Interface nào.
61
+ */
62
+ export type ActionPayloadMap = {
63
+ [ACTION_TYPES.NAVIGATE_SLIDE]: NavigatePayload;
64
+ [ACTION_TYPES.TOGGLE_VISIBILITY]: ToggleVisibilityPayload;
65
+ [ACTION_TYPES.PLAY_MEDIA]: { mediaUrl: string; loop: boolean };
66
+ [ACTION_TYPES.EVALUATE_ANSWER]: EvaluatePayload;
67
+ };
68
+
69
+ /**
70
+ * Cấu trúc hoàn chỉnh của một Hành động gắn vào Element.
71
+ *
72
+ * @description Sử dụng pattern "Discriminated Union" của TypeScript:
73
+ * Khi dev set thuộc tính `type`, TypeScript sẽ tự động ép buộc `payload`
74
+ * phải chứa các trường dữ liệu tương ứng được định nghĩa trong `ActionPayloadMap`.
75
+ * Tránh tuyệt đối việc râu ông nọ cắm cằm bà kia (VD: type là NAVIGATE nhưng truyền mediaUrl).
76
+ */
77
+ export type ElementAction = {
78
+ [K in ActionType]: {
79
+ trigger: ActionTrigger;
80
+ type: K;
81
+ payload: ActionPayloadMap[K];
82
+ };
83
+ }[ActionType];
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Cấu trúc tọa độ cơ bản dựa trên tỷ lệ %.
3
+ */
4
+ export interface Position {
5
+ x: number; // Tọa độ ngang (0-100)
6
+ y: number; // Tọa độ dọc (0-100)
7
+ w: number; // Chiều rộng (0-100)
8
+ h: number; // Chiều cao (0-100)
9
+ }
@@ -0,0 +1,71 @@
1
+ // --- Standard Element Data ---
2
+
3
+ export interface TextData {
4
+ content: string;
5
+ }
6
+
7
+ export interface VideoData {
8
+ src: string;
9
+ poster?: string;
10
+ autoPlay?: boolean;
11
+ muted?: boolean;
12
+ loop?: boolean;
13
+ controls?: boolean;
14
+ isLive?: boolean;
15
+ }
16
+
17
+ // --- Game Element Data ---
18
+
19
+ // 1. Multiple Choice: Chọn 1 đáp án đúng
20
+ export interface MultipleChoiceData {
21
+ question: string;
22
+ options: { id: string; content: string }[];
23
+ correctId: string;
24
+ }
25
+
26
+ // 2. Sorting: Sắp xếp theo thứ tự mảng
27
+ export interface SortingData {
28
+ items: { id: string; content: string }[];
29
+ correctOrder: string[]; // Mảng chứa các ID theo thứ tự đúng
30
+ }
31
+
32
+ // 3. Matching: Nối cặp A - B
33
+ export interface MatchingData {
34
+ leftColumn: { id: string; content: string }[];
35
+ rightColumn: { id: string; content: string }[];
36
+ correctPairs: [string, string][]; // Mảng các cặp [id_trái, id_phải]
37
+ }
38
+
39
+ // 4. Hotspot: Click vào vùng ảnh
40
+ export interface HotspotData {
41
+ imageUri: string;
42
+ zones: {
43
+ id: string;
44
+ xMin: number;
45
+ yMin: number; // Đơn vị %
46
+ xMax: number;
47
+ yMax: number;
48
+ }[];
49
+ correctZoneId: string;
50
+ }
51
+
52
+ // --- Data Maps ---
53
+
54
+ /**
55
+ * Map để liên kết các Game Type với dữ liệu tương ứng.
56
+ */
57
+ export type GameDataMap = {
58
+ QUIZ: MultipleChoiceData;
59
+ SORTING: SortingData;
60
+ MATCHING: MatchingData;
61
+ HOTSPOT: HotspotData;
62
+ MEMORY_CARD: any; // Có thể mở rộng thêm sau
63
+ };
64
+
65
+ /**
66
+ * Map tổng hợp giữa tất cả ElementType và cấu trúc data tương ứng.
67
+ */
68
+ export type ElementDataMap = GameDataMap & {
69
+ TEXT: TextData;
70
+ VIDEO: VideoData;
71
+ };
@@ -0,0 +1,77 @@
1
+ import type { ElementAction } from "./actions.js";
2
+ import type { Position } from "./common.js";
3
+ import type { ElementDataMap } from "./data.js";
4
+
5
+ /**
6
+ * Định nghĩa tất cả các loại phần tử có thể xuất hiện trong một Slide.
7
+ */
8
+ export const ELEMENT_TYPES = {
9
+ TEXT: "TEXT",
10
+ VIDEO: "VIDEO",
11
+ QUIZ: "QUIZ",
12
+ MATCHING: "MATCHING",
13
+ SORTING: "SORTING",
14
+ MEMORY_CARD: "MEMORY_CARD",
15
+ HOTSPOT: "HOTSPOT",
16
+ } as const;
17
+
18
+ export type ElementType = (typeof ELEMENT_TYPES)[keyof typeof ELEMENT_TYPES];
19
+
20
+ /**
21
+ * Định nghĩa các loại game (tập con của ELEMENT_TYPES).
22
+ */
23
+ export const GAME_TYPES = {
24
+ QUIZ: "QUIZ",
25
+ MATCHING: "MATCHING",
26
+ SORTING: "SORTING",
27
+ MEMORY_CARD: "MEMORY_CARD",
28
+ HOTSPOT: "HOTSPOT",
29
+ } as const;
30
+
31
+ export type GameType = Extract<
32
+ ElementType,
33
+ (typeof GAME_TYPES)[keyof typeof GAME_TYPES]
34
+ >;
35
+
36
+ /**
37
+ * Định nghĩa style cho một phần tử.
38
+ */
39
+ export interface ElementStyle {
40
+ backgroundColor?: string;
41
+ color?: string;
42
+ fontSize?: number;
43
+ fontFamily?: string;
44
+ fontWeight?: string | number;
45
+ textAlign?: "left" | "center" | "right" | "justify";
46
+ border?: string;
47
+ borderRadius?: number;
48
+ opacity?: number;
49
+ zIndex?: number;
50
+ [key: string]: any; // Allow other CSS properties
51
+ }
52
+ /**
53
+ * Định nghĩa các loại animation hỗ trợ
54
+ */
55
+ export interface ElementAnimation {
56
+ type: "fade-in" | "slide-up" | "bounce" | "zoom-in";
57
+ duration: number; // Tính bằng ms
58
+ delay: number; // Độ trễ trước khi chạy (ms)
59
+ repeat?: boolean;
60
+ }
61
+
62
+ /**
63
+ * Sử dụng Discriminated Union để đảm bảo type-safety cho thuộc tính 'data'.
64
+ */
65
+ export type SlideElement = {
66
+ [K in keyof ElementDataMap]: {
67
+ id: string;
68
+ type: K;
69
+ position: Position;
70
+ style?: ElementStyle;
71
+ enterAnimation?: ElementAnimation;
72
+ exitAnimation?: ElementAnimation;
73
+ actions?: ElementAction[];
74
+ data: ElementDataMap[K];
75
+ groupId?: string;
76
+ };
77
+ }[keyof ElementDataMap];
@@ -0,0 +1,5 @@
1
+ export * from "./common.js";
2
+ export * from "./data.js";
3
+ export * from "./elements.js";
4
+ export * from "./models.js";
5
+ export * from "./actions.js";
@@ -0,0 +1,47 @@
1
+ import type { SlideElement } from "./elements.js";
2
+
3
+ // --- Slide Models ---
4
+
5
+ export interface SlideConfig {
6
+ aspectRatio: string;
7
+ theme: string;
8
+ }
9
+
10
+ export interface Slide {
11
+ id: string;
12
+ tenant_id: string;
13
+ course_id: string;
14
+ order: number;
15
+ config?: SlideConfig;
16
+ elements: SlideElement[];
17
+ }
18
+
19
+ // --- Tracking & Progress Models ---
20
+
21
+ export const CONFIDENCE_LEVELS = {
22
+ HIGH: "High",
23
+ MEDIUM: "Medium",
24
+ LOW: "Low",
25
+ } as const;
26
+
27
+ export type ConfidenceLevel = typeof CONFIDENCE_LEVELS[keyof typeof CONFIDENCE_LEVELS];
28
+
29
+ export interface ActionLog {
30
+ action_type: string;
31
+ timestamp: number;
32
+ duration: number;
33
+ result: {
34
+ is_correct: boolean;
35
+ confidence: ConfidenceLevel;
36
+ };
37
+ }
38
+
39
+ export interface UserProgress {
40
+ user_id: string;
41
+ tenant_id: string;
42
+ concept_id: string;
43
+ streak: number; // Số lần đúng liên tiếp
44
+ is_mastered: boolean;
45
+ next_review_at: string; // ISODate string
46
+ last_sync_at: string; // ISODate string
47
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ // Visit https://aka.ms/tsconfig to read more about this file
3
+ "compilerOptions": {
4
+ // File Layout
5
+ // "rootDir": "./src",
6
+ // "outDir": "./dist",
7
+
8
+ // Environment Settings
9
+ // See also https://aka.ms/tsconfig/module
10
+ "module": "nodenext",
11
+ "target": "esnext",
12
+ "types": [],
13
+ // For nodejs:
14
+ // "lib": ["esnext"],
15
+ // "types": ["node"],
16
+ // and npm install -D @types/node
17
+
18
+ // Other Outputs
19
+ "sourceMap": true,
20
+ "declaration": true,
21
+ "declarationMap": true,
22
+
23
+ // Stricter Typechecking Options
24
+ "noUncheckedIndexedAccess": true,
25
+ "exactOptionalPropertyTypes": true,
26
+
27
+ // Style Options
28
+ // "noImplicitReturns": true,
29
+ // "noImplicitOverride": true,
30
+ // "noUnusedLocals": true,
31
+ // "noUnusedParameters": true,
32
+ // "noFallthroughCasesInSwitch": true,
33
+ // "noPropertyAccessFromIndexSignature": true,
34
+
35
+ // Recommended Options
36
+ "strict": true,
37
+ "jsx": "react-jsx",
38
+ "verbatimModuleSyntax": true,
39
+ "isolatedModules": true,
40
+ "noUncheckedSideEffectImports": true,
41
+ "moduleDetection": "force",
42
+ "skipLibCheck": true,
43
+ }
44
+ }