@zephyr424/wb-sdk 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zephyr424
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,271 @@
1
+ # wb-sdk
2
+
3
+ > A developer-friendly spaced repetition engine for vocabulary building
4
+
5
+ [![npm version](https://img.shields.io/npm/v/wb-sdk.svg)](https://www.npmjs.com/package/wb-sdk)
6
+ [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Zephyr424/wb-sdk/blob/main/LICENSE)
7
+
8
+ **wb-sdk** is a lightweight, plug-and-play spaced repetition engine designed for developers who want to integrate intelligent vocabulary review into their applications.
9
+
10
+ Stop reinventing the wheel — just `npm install wb-sdk` and start building.
11
+
12
+ ---
13
+
14
+ ## ✨ Features
15
+
16
+ - 🧠 **SM-2 Algorithm** — Industry-standard spaced repetition, proven to optimize memory retention
17
+ - 📚 **Built-in Word Management** — Add, search, remove, and organize words effortlessly
18
+ - 🔁 **Review Scheduling** — Automatically calculates what to review and when
19
+ - 📊 **Progress Tracking** — Get stats on mastered words, retention rate, and future review forecasts
20
+ - 🧩 **Minimal API** — Intuitive `wb.word` and `wb.review` interfaces
21
+ - 📦 **Zero Dependencies** — Lightweight and fast, no bloat
22
+
23
+ ---
24
+
25
+ ## 🚀 Installation
26
+
27
+ ```bash
28
+ npm install wb-sdk
29
+ ```
30
+ ### Or with Yarn:
31
+ ```bash
32
+ yarn add wb-sdk
33
+ ```
34
+
35
+ ---
36
+
37
+ ## 🏃 Quick Start
38
+ ```javascripts
39
+ const { WordBank } = require('wb-sdk');
40
+
41
+ // Create a new word bank
42
+ const wb = new WordBank();
43
+
44
+ // Add some words
45
+ wb.word.add({ id: '1', word: 'apple', definition: 'a fruit' });
46
+ wb.word.add({ id: '2', word: 'benevolent', definition: 'kind and generous' });
47
+ wb.word.add({ id: '3', word: 'ephemeral', definition: 'lasting for a short time' });
48
+
49
+ // See all words
50
+ console.log(wb.word.list());
51
+
52
+ // Get today's review queue
53
+ const due = wb.review.getDue();
54
+ console.log(`📚 ${due.length} words due for review today:`);
55
+ due.forEach(w => console.log(` - ${w.word}: ${w.definition}`));
56
+
57
+ // Submit a review (quality: 0 = forgot, 5 = perfect recall)
58
+ wb.review.submit('1', 4); // "apple" reviewed with quality 4/5
59
+
60
+ // Check your progress
61
+ console.log(wb.review.progress());
62
+ // { total: 3, mastered: 0, remaining: 3, daysToMaster: 1 }
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 📖 API Documentation
68
+
69
+ ### `wb.word` — Word Management
70
+
71
+ | Method | Parameters | Returns | Description |
72
+ | :--- | :--- | :--- | :--- |
73
+ | `list()` | — | `Word[]` | Returns all words in the bank |
74
+ | `get(id)` | `id: string` | `Word \| null` | Retrieves a word by ID, returns `null` if not found |
75
+ | `add(data)` | `data: WordData` | `Word` | Adds a new word to the bank |
76
+ | `remove(id)` | `id: string` | `void` | Deletes a word from the bank |
77
+ | `search(keyword)` | `keyword: string` | `Word[]` | Searches words by word or definition (case‑insensitive) |
78
+ | `top(n)` | `n: number` | `Word[]` | Returns the top `n` words with the most review repetitions |
79
+
80
+ **Usage Examples:**
81
+
82
+ ```javascript
83
+ // List all words
84
+ wb.word.list();
85
+
86
+ // Get by ID
87
+ wb.word.get('apple');
88
+
89
+ // Add a new word
90
+ wb.word.add({ id: 'grok', word: 'grok', definition: 'to understand intuitively' });
91
+
92
+ // Remove by ID
93
+ wb.word.remove('grok');
94
+
95
+ // Search
96
+ wb.word.search('bene'); // Returns ["benevolent", "beneficial", ...]
97
+
98
+ // Top 10 most practiced
99
+ wb.word.top(10);
100
+ ```
101
+ ---
102
+
103
+ ### `wb.review` — Review Scheduling
104
+
105
+ | Method | Parameters | Returns | Description |
106
+ | :--- | :--- | :--- | :--- |
107
+ | `getDue([today])` | `today?: Date` | `Word[]` | Returns all words due for review today (or on a custom date) |
108
+ | `submit(id, quality)` | `id: string, quality: number` | `Word` | Records your review performance and updates SM‑2 state |
109
+ | `forecast([days])` | `days?: number` | `Object` | Predicts how many words will be due each day for the next N days |
110
+ | `progress()` | — | `Object` | Returns an overview of your learning progress |
111
+
112
+ **Usage Examples:**
113
+
114
+ ```javascript
115
+ // Get today's review queue
116
+ const due = wb.review.getDue();
117
+ console.log(`📚 ${due.length} words due today`);
118
+
119
+ // Get due words on a specific date
120
+ const dueOnChristmas = wb.review.getDue(new Date('2026-12-25'));
121
+
122
+ // Submit a review with quality 4/5
123
+ const updated = wb.review.submit('apple', 4);
124
+ console.log(`Next review in ${updated.interval} days`);
125
+
126
+ // Forecast review load for the next 7 days
127
+ const forecast = wb.review.forecast(7);
128
+ console.log(forecast);
129
+ // { "2026-09-01": 5, "2026-09-02": 8, "2026-09-03": 3, ... }
130
+
131
+ // Get overall progress stats
132
+ const stats = wb.review.progress();
133
+ console.log(stats);
134
+ // { total: 5000, mastered: 1200, remaining: 3800, daysToMaster: 760 }
135
+ ```
136
+ **`quality` — Review Quality Scale:**
137
+
138
+ | Score | Meaning |
139
+ | :--- | :--- |
140
+ | `5` | Perfect recall |
141
+ | `4` | Good recall, slight hesitation |
142
+ | `3` | Recalled with difficulty |
143
+ | `2` | Forgot, but recognized |
144
+ | `1` | Almost forgot |
145
+ | `0` | Completely forgot |
146
+
147
+ **`progress()` — Return Fields:**
148
+
149
+ | Field | Type | Description |
150
+ | :--- | :--- | :--- |
151
+ | `total` | `number` | Total words in the bank |
152
+ | `mastered` | `number` | Words with interval ≥ 30 days |
153
+ | `remaining` | `number` | Words not yet mastered |
154
+ | `daysToMaster` | `number` | Estimated days until all words are mastered |
155
+
156
+ ### 📋 Summary Table — All Methods
157
+
158
+ | Module | Method | Brief |
159
+ | :--- | :--- | :--- |
160
+ | `wb.word` | `list()` | Get all words |
161
+ | `wb.word` | `get(id)` | Get word by ID |
162
+ | `wb.word` | `add(data)` | Add a new word |
163
+ | `wb.word` | `remove(id)` | Delete a word |
164
+ | `wb.word` | `search(keyword)` | Search words |
165
+ | `wb.word` | `top(n)` | Get most practiced words |
166
+ | `wb.review` | `getDue([today])` | Get review queue |
167
+ | `wb.review` | `submit(id, quality)` | Submit review |
168
+ | `wb.review` | `forecast([days])` | Predict review load |
169
+ | `wb.review` | `progress()` | Get learning stats |
170
+ | `—` | `loadPreset(name)` | Load built-in word list |
171
+
172
+ ---
173
+
174
+ ## 🧠 How It Works
175
+
176
+ wb-sdk implements the **SM-2 algorithm**, the same one used by Anki and other popular flashcard apps. Each time you review a word and rate your recall (0–5), the engine:
177
+
178
+ - Adjusts the word's **ease factor** based on your performance
179
+ - Calculates the optimal **next review interval**
180
+ - Tracks your **progress** and predicts future review workload
181
+
182
+ This ensures you spend time on words you're about to forget — maximizing efficiency.
183
+
184
+ ---
185
+
186
+ ## 📦 Built-in Example
187
+
188
+ The package includes an example script that demonstrates the core features of `wb-sdk` in action.
189
+
190
+ ### Run the example
191
+
192
+ Make sure you are in the project root directory, then execute:
193
+
194
+ ```bash
195
+ node example.js
196
+ ```
197
+
198
+ ### What it does
199
+
200
+ The example:
201
+ - Creates a new `WordBank` instance
202
+ - Loads a built-in preset of 3 demo words (`apple`, `book`, `cat`)
203
+ - Lists all words
204
+ - Retrieves today's due words (initially all words are due)
205
+ - Submits a review for the first word with quality `4` (good recall)
206
+ - Shows learning progress stats
207
+ - Prints a 7‑day forecast of review load
208
+
209
+ ### Expected output (sample)
210
+
211
+ ```text
212
+ All words: [
213
+ Word { id: '1', word: 'apple', definition: 'a fruit', ... },
214
+ Word { id: '2', word: 'book', definition: 'a set of pages', ... },
215
+ Word { id: '3', word: 'cat', definition: 'a small animal', ... }
216
+ ]
217
+
218
+ Words due for review today:
219
+ - apple: a fruit
220
+ - book: a set of pages
221
+ - cat: a small animal
222
+
223
+ Submitted review for apple (quality 4/5)
224
+
225
+ Learning progress: { total: 3, mastered: 0, remaining: 3, daysToMaster: 1 }
226
+
227
+ 7-day review forecast: {
228
+ '2026-09-01': 0,
229
+ '2026-09-02': 0,
230
+ '2026-09-03': 1,
231
+ '2026-09-04': 0,
232
+ '2026-09-05': 0,
233
+ '2026-09-06': 0,
234
+ '2026-09-07': 0
235
+ }
236
+ ```
237
+ **Note**: The actual output may vary slightly depending on the current date and your review history.
238
+
239
+ ---
240
+
241
+ ## 🤝 Contributing
242
+
243
+ Contributions are welcome! If you have ideas, bug fixes, or improvements, feel free to:
244
+
245
+ - Open an [issue](https://github.com/Zephyr424/wb-sdk/issues) to report bugs or suggest features
246
+ - Submit a [pull request](https://github.com/Zephyr424/wb-sdk/pulls) with your changes
247
+
248
+ Please make sure your code follows the existing style and includes appropriate tests if applicable.
249
+
250
+ Thank you for helping make `wb-sdk` better!
251
+
252
+ ---
253
+
254
+ ## 📄 License
255
+
256
+ MIT © [Zephyr424](https://github.com/Zephyr424)
257
+
258
+ See the [LICENSE](./LICENSE) file for details.;
259
+
260
+ ---
261
+
262
+ ## ⭐ Show Your Support
263
+
264
+ If you find `wb-sdk` useful, please consider giving it a star ⭐ on GitHub — it means a lot and helps others discover the project.
265
+
266
+ Thank you for using `wb-sdk`!
267
+
268
+ ---
269
+
270
+ > Built with ❤️ by a developer who believes learning should be smart, not hard.
271
+ ---
package/example.js ADDED
@@ -0,0 +1,27 @@
1
+ const { WordBank } = require('./index');
2
+
3
+ // 创建一个词库实例
4
+ const wb = new WordBank();
5
+
6
+ // 加载内置演示词库
7
+ wb.loadPreset('demo');
8
+
9
+ console.log('所有单词:', wb.word.list());
10
+
11
+ // 模拟复习
12
+ console.log('\n今天需要复习的单词:');
13
+ const due = wb.review.getDue();
14
+ due.forEach(w => console.log(`- ${w.word}: ${w.definition}`));
15
+
16
+ // 模拟提交复习反馈(假设对第一个词评价为4分)
17
+ if (due.length > 0) {
18
+ const first = due[0];
19
+ wb.review.submit(first.id, 4);
20
+ console.log(`\n已提交复习: ${first.word} (质量4/5)`);
21
+ }
22
+
23
+ // 查看进度
24
+ console.log('\n学习进度:', wb.review.progress());
25
+
26
+ // 显示预测未来7天复习量
27
+ console.log('\n未来7天预测复习量:', wb.review.forecast(7));
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ const WordBank = require('./src/wordBank');
2
+ module.exports = WordBank;
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@zephyr424/wb-sdk",
3
+ "version": "0.1.0",
4
+ "description": "A developer-friendly spaced repetition engine for vocabulary building",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "node example.js"
8
+ },
9
+ "keywords": ["vocabulary", "spaced-repetition", "flashcards", "learning"],
10
+ "author": "Your Name",
11
+ "license": "MIT",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/yourusername/wb-sdk.git"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/yourusername/wb-sdk/issues"
18
+ },
19
+ "homepage": "https://github.com/yourusername/wb-sdk#readme"
20
+ }
package/src/models.js ADDED
@@ -0,0 +1,32 @@
1
+ class Word {
2
+ constructor({ id, word, definition, repetitions = 0, interval = 0, easeFactor = 2.5, lastReviewed = null }) {
3
+ this.id = id;
4
+ this.word = word;
5
+ this.definition = definition;
6
+ this.repetitions = repetitions;
7
+ this.interval = interval;
8
+ this.easeFactor = easeFactor;
9
+ this.lastReviewed = lastReviewed ? new Date(lastReviewed) : null;
10
+ }
11
+
12
+ isDue(today = new Date()) {
13
+ if (!this.lastReviewed) return true;
14
+ const dueDate = new Date(this.lastReviewed);
15
+ dueDate.setDate(dueDate.getDate() + this.interval);
16
+ return today >= dueDate;
17
+ }
18
+
19
+ toJSON() {
20
+ return {
21
+ id: this.id,
22
+ word: this.word,
23
+ definition: this.definition,
24
+ repetitions: this.repetitions,
25
+ interval: this.interval,
26
+ easeFactor: this.easeFactor,
27
+ lastReviewed: this.lastReviewed ? this.lastReviewed.toISOString() : null
28
+ };
29
+ }
30
+ }
31
+
32
+ module.exports = Word;
@@ -0,0 +1,39 @@
1
+ const Scheduler = require('./scheduler');
2
+
3
+ class ReviewManager {
4
+ constructor(parent) {
5
+ this.parent = parent;
6
+ }
7
+
8
+ getDue(today = new Date()) {
9
+ return this.parent.word.list().filter(w => w.isDue(today));
10
+ }
11
+
12
+ submit(id, quality) {
13
+ const word = this.parent.word.get(id);
14
+ if (!word) throw new Error(`Word with id ${id} not found`);
15
+ return Scheduler.schedule(word, quality);
16
+ }
17
+
18
+ forecast(days = 7, from = new Date()) {
19
+ const forecast = {};
20
+ for (let i = 1; i <= days; i++) {
21
+ const d = new Date(from);
22
+ d.setDate(d.getDate() + i);
23
+ const due = this.parent.word.list().filter(w => w.isDue(d)).length;
24
+ forecast[d.toISOString().slice(0,10)] = due;
25
+ }
26
+ return forecast;
27
+ }
28
+
29
+ progress() {
30
+ const words = this.parent.word.list();
31
+ const total = words.length;
32
+ const mastered = words.filter(w => w.interval >= 30).length;
33
+ const remaining = total - mastered;
34
+ const daysToMaster = Math.ceil(remaining / 5);
35
+ return { total, mastered, remaining, daysToMaster };
36
+ }
37
+ }
38
+
39
+ module.exports = ReviewManager;
@@ -0,0 +1,25 @@
1
+ class Scheduler {
2
+ static schedule(word, quality) {
3
+ const now = new Date();
4
+ if (quality < 3) {
5
+ word.repetitions = 0;
6
+ word.interval = 1;
7
+ } else {
8
+ if (word.repetitions === 0) {
9
+ word.interval = 1;
10
+ } else if (word.repetitions === 1) {
11
+ word.interval = 6;
12
+ } else {
13
+ word.interval = Math.round(word.interval * word.easeFactor);
14
+ }
15
+ word.repetitions += 1;
16
+ let ease = word.easeFactor + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02));
17
+ ease = Math.min(2.5, Math.max(1.3, ease));
18
+ word.easeFactor = ease;
19
+ }
20
+ word.lastReviewed = now;
21
+ return word;
22
+ }
23
+ }
24
+
25
+ module.exports = Scheduler;
@@ -0,0 +1,29 @@
1
+ const WordManager = require('./wordManager');
2
+ const ReviewManager = require('./reviewManager');
3
+
4
+ class WordBank {
5
+ constructor(initialWords = []) {
6
+ this.words = {};
7
+ this.word = new WordManager(this);
8
+ this.review = new ReviewManager(this);
9
+
10
+ if (initialWords && initialWords.length) {
11
+ initialWords.forEach(w => this.word.add(w));
12
+ }
13
+ }
14
+
15
+ loadPreset(name) {
16
+ const presets = {
17
+ demo: [
18
+ { id: '1', word: 'apple', definition: 'a fruit' },
19
+ { id: '2', word: 'book', definition: 'a set of pages' },
20
+ { id: '3', word: 'cat', definition: 'a small animal' }
21
+ ]
22
+ };
23
+ const words = presets[name];
24
+ if (!words) throw new Error(`Preset ${name} not found`);
25
+ words.forEach(w => this.word.add(w));
26
+ }
27
+ }
28
+
29
+ module.exports = WordBank;
@@ -0,0 +1,39 @@
1
+ const Word = require('./models');
2
+
3
+ class WordManager {
4
+ constructor(parent) {
5
+ this.parent = parent;
6
+ }
7
+
8
+ list() {
9
+ return Object.values(this.parent.words);
10
+ }
11
+
12
+ get(id) {
13
+ return this.parent.words[id] || null;
14
+ }
15
+
16
+ add(wordData) {
17
+ const word = new Word(wordData);
18
+ this.parent.words[word.id] = word;
19
+ return word;
20
+ }
21
+
22
+ remove(id) {
23
+ delete this.parent.words[id];
24
+ }
25
+
26
+ search(keyword) {
27
+ const lower = keyword.toLowerCase();
28
+ return this.list().filter(w =>
29
+ w.word.toLowerCase().includes(lower) ||
30
+ w.definition.toLowerCase().includes(lower)
31
+ );
32
+ }
33
+
34
+ top(n) {
35
+ return this.list().sort((a, b) => b.repetitions - a.repetitions).slice(0, n);
36
+ }
37
+ }
38
+
39
+ module.exports = WordManager;