@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 +21 -0
- package/README.md +271 -0
- package/example.js +27 -0
- package/index.js +2 -0
- package/package.json +20 -0
- package/src/models.js +32 -0
- package/src/reviewManager.js +39 -0
- package/src/scheduler.js +25 -0
- package/src/wordBank.js +29 -0
- package/src/wordManager.js +39 -0
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
|
+
[](https://www.npmjs.com/package/wb-sdk)
|
|
6
|
+
[](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
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;
|
package/src/scheduler.js
ADDED
|
@@ -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;
|
package/src/wordBank.js
ADDED
|
@@ -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;
|