ts-fsrs 3.0.3 → 3.0.5
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 +88 -23
- package/dist/algorithm.d.ts +99 -0
- package/dist/algorithm.js +171 -0
- package/dist/default.d.ts +1 -6
- package/dist/default.js +1 -1
- package/dist/fsrs.d.ts +4 -131
- package/dist/fsrs.js +4 -156
- package/dist/help.d.ts +7 -0
- package/dist/help.js +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/models.d.ts +7 -0
- package/dist/scheduler.d.ts +2 -43
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,26 +30,91 @@ const now = new Date('2022-2-2 10:00:00');// new Date();
|
|
|
30
30
|
const scheduling_cards = f.repeat(card, now);
|
|
31
31
|
|
|
32
32
|
// console.log(scheduling_cards);
|
|
33
|
-
Object.keys(Rating)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
33
|
+
Object.keys(Rating)
|
|
34
|
+
.filter(key => !isNaN(Number(key)))
|
|
35
|
+
.map(key => Number(key) as Rating) // [Rating.Again, Rating.Hard, Rating.Good, Rating.Easy]
|
|
36
|
+
.forEach(grade => {
|
|
37
|
+
const { log, card } = scheduling_cards[grade];
|
|
38
|
+
console.group(`${Rating[grade]}`);
|
|
39
|
+
console.table({
|
|
40
|
+
[`card_${Rating[grade]}`]: {
|
|
41
|
+
...card,
|
|
42
|
+
due: formatDate(card.due),
|
|
43
|
+
last_review: formatDate(card.last_review as Date),
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
console.table({
|
|
47
|
+
[`log_${Rating[grade]}`]: {
|
|
48
|
+
...log,
|
|
49
|
+
review: formatDate(log.review),
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
console.groupEnd();
|
|
53
|
+
console.log('----------------------------------------------------------------');
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
> More examples refer to the [Example](https://github.com/ishiko732/ts-fsrs/blob/master/example/index.ts)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# Basic Use
|
|
61
|
+
|
|
62
|
+
## 1. **Initialization**:
|
|
63
|
+
To begin, create an empty card instance and set the current date:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { Card, createEmptyCard } from "ts-fsrs";
|
|
67
|
+
let card: Card = createEmptyCard();
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 2. **Parameter Configuration**:
|
|
71
|
+
The library allows for customization of SRS parameters. Use `generatorParameters` to produce the final set of parameters for the SRS algorithm. Here's an example setting a maximum interval:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { Card, createEmptyCard, generatorParameters, FSRSParameters } from "ts-fsrs";
|
|
75
|
+
let card: Card = createEmptyCard();
|
|
76
|
+
const params: FSRSParameters = generatorParameters({ maximum_interval: 1000 });
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 3. **Scheduling with FSRS**:
|
|
80
|
+
The core functionality lies in the `fsrs` function. When invoked, it returns a collection of cards scheduled based on different potential user ratings:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import {
|
|
84
|
+
Card,
|
|
85
|
+
createEmptyCard,
|
|
86
|
+
generatorParameters,
|
|
87
|
+
FSRSParameters,
|
|
88
|
+
FSRS,
|
|
89
|
+
RecordLog,
|
|
90
|
+
} from "ts-fsrs";
|
|
91
|
+
|
|
92
|
+
let card: Card = createEmptyCard();
|
|
93
|
+
const f: FSRS = new FSRS(); // or const f: FSRS = fsrs(params);
|
|
94
|
+
let scheduling_cards: RecordLog = f.repeat(card, new Date());
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 4. **Retrieving Scheduled Cards**:
|
|
98
|
+
Once you have the `scheduling_cards` object, you can retrieve cards based on user ratings. For instance, to access the card scheduled for a 'Good' rating:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const good: RecordLogItem = scheduling_cards[Rating.Good];
|
|
102
|
+
const newCard: Card = good.card;
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## 5. **Understanding Card Attributes**:
|
|
106
|
+
Each `Card` object consists of various attributes that determine its status, scheduling, and other metrics:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
type Card = {
|
|
110
|
+
due: Date; // Date when the card is next due for review
|
|
111
|
+
stability: number; // A measure of how well the information is retained
|
|
112
|
+
difficulty: number; // Reflects the inherent difficulty of the card content
|
|
113
|
+
elapsed_days: number; // Days since the card was last reviewed
|
|
114
|
+
scheduled_days: number; // The interval at which the card is next scheduled
|
|
115
|
+
reps: number; // Total number of times the card has been reviewed
|
|
116
|
+
lapses: number; // Times the card was forgotten or remembered incorrectly
|
|
117
|
+
state: State; // The current state of the card (New, Learning, Review, Relearning)
|
|
118
|
+
last_review?: Date; // The most recent review date, if applicable
|
|
119
|
+
};
|
|
120
|
+
```
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { SchedulingCard } from "./index";
|
|
2
|
+
import { FSRSParameters, Rating } from "./models";
|
|
3
|
+
import type { int } from "./type";
|
|
4
|
+
export declare class FSRSAlgorithm {
|
|
5
|
+
protected param: FSRSParameters;
|
|
6
|
+
private readonly intervalModifier;
|
|
7
|
+
protected seed?: string;
|
|
8
|
+
constructor(param: Partial<FSRSParameters>);
|
|
9
|
+
init_ds(s: SchedulingCard): void;
|
|
10
|
+
/**
|
|
11
|
+
* Updates the difficulty and stability values of the scheduling card based on the last difficulty,
|
|
12
|
+
* last stability, and the current retrievability.
|
|
13
|
+
* @param {SchedulingCard} s scheduling Card
|
|
14
|
+
* @param {number} last_d Difficulty
|
|
15
|
+
* @param {number} last_s Stability
|
|
16
|
+
* @param retrievability Retrievability
|
|
17
|
+
*/
|
|
18
|
+
next_ds(s: SchedulingCard, last_d: number, last_s: number, retrievability: number): void;
|
|
19
|
+
/**
|
|
20
|
+
* The formula used is :
|
|
21
|
+
* S_0(G) = w_{G-1}
|
|
22
|
+
* \max \{S_0,0.1\}
|
|
23
|
+
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
24
|
+
* @return Stability (interval when R=90%)
|
|
25
|
+
*/
|
|
26
|
+
init_stability(g: number): number;
|
|
27
|
+
/**
|
|
28
|
+
* The formula used is :
|
|
29
|
+
* $$D_0(G) = w_4 - (G-3) \cdot w_5$$
|
|
30
|
+
* $$\min \{\max \{D_0(G),1\},10\}$$
|
|
31
|
+
* where the D_0(3)=w_4 when the first rating is good.
|
|
32
|
+
* @param {number} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
33
|
+
* @return {number} Difficulty D \in [1,10]
|
|
34
|
+
*/
|
|
35
|
+
init_difficulty(g: number): number;
|
|
36
|
+
/**
|
|
37
|
+
* If fuzzing is disabled or ivl is less than 2.5, it returns the original interval.
|
|
38
|
+
* @param {number} ivl - The interval to be fuzzed.
|
|
39
|
+
* @return {number} - The fuzzed interval.
|
|
40
|
+
**/
|
|
41
|
+
apply_fuzz(ivl: number): number;
|
|
42
|
+
/**
|
|
43
|
+
* Ref:
|
|
44
|
+
* constructor(param: Partial<FSRSParameters>)
|
|
45
|
+
* this.intervalModifier = 9 * (1 / this.param.request_retention - 1);
|
|
46
|
+
* @param {number} s - Stability (interval when R=90%)
|
|
47
|
+
*/
|
|
48
|
+
next_interval(s: number): int;
|
|
49
|
+
/**
|
|
50
|
+
* The formula used is :
|
|
51
|
+
* $$next_d = D - w_6 \cdot (R - 2)$$
|
|
52
|
+
* $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot next_d$$
|
|
53
|
+
* @param {number} d Difficulty D \in [1,10]
|
|
54
|
+
* @param {Rating} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
55
|
+
* @return {number} next_D
|
|
56
|
+
*/
|
|
57
|
+
next_difficulty(d: number, g: number): number;
|
|
58
|
+
/**
|
|
59
|
+
* The formula used is :
|
|
60
|
+
* $$\min \{\max \{D_0,1\},10\}$$
|
|
61
|
+
* @param {number} difficulty D \in [1,10]
|
|
62
|
+
*/
|
|
63
|
+
constrain_difficulty(difficulty: number): number;
|
|
64
|
+
/**
|
|
65
|
+
* The formula used is :
|
|
66
|
+
* $$w_7 \cdot init +(1 - w_7) \cdot current$$
|
|
67
|
+
* @param {number} init $$w_2 : D_0(3) = w_2 + (R-2) \cdot w_3= w_2$$
|
|
68
|
+
* @param {number} current $$D - w_6 \cdot (R - 2)$$
|
|
69
|
+
* @return {number} difficulty
|
|
70
|
+
*/
|
|
71
|
+
mean_reversion(init: number, current: number): number;
|
|
72
|
+
/**
|
|
73
|
+
* The formula used is :
|
|
74
|
+
* $$S^\prime_r(D,S,R,G) = S\cdot(e^{w_8}\cdot (11-D)\cdot S^{-w_9}\cdot(e^{w_10\cdot(1-R)}-1)\cdot w_15(if G=2) \cdot w_16(if G=4)+1)$$
|
|
75
|
+
* @param {number} d Difficulty D \in [1,10]
|
|
76
|
+
* @param {number} s Stability (interval when R=90%)
|
|
77
|
+
* @param {number} r Retrievability (probability of recall)
|
|
78
|
+
* @param {Rating} g Grade (Rating[0.again,1.hard,2.good,3.easy])
|
|
79
|
+
* @return {number} S^\prime_r new stability after recall
|
|
80
|
+
*/
|
|
81
|
+
next_recall_stability(d: number, s: number, r: number, g: Rating): number;
|
|
82
|
+
/**
|
|
83
|
+
* The formula used is :
|
|
84
|
+
* $$S^\prime_f(D,S,R) = w_11\cdot D^{-w_{12}}\cdot ((S+1)^{w_{13}}-1) \cdot e^{w_{14}\cdot(1-R)}.$$
|
|
85
|
+
* @param {number} d Difficulty D \in [1,10]
|
|
86
|
+
* @param {number} s Stability (interval when R=90%)
|
|
87
|
+
* @param {number} r Retrievability (probability of recall)
|
|
88
|
+
* @return {number} S^\prime_f new stability after forgetting
|
|
89
|
+
*/
|
|
90
|
+
next_forget_stability(d: number, s: number, r: number): number;
|
|
91
|
+
/**
|
|
92
|
+
* The formula used is :
|
|
93
|
+
* $$R(t,S) = (1 + \frac{t}{9 \cdot S})^{-1},$$
|
|
94
|
+
* @param {number} t t days since the last review
|
|
95
|
+
* @param {number} s Stability (interval when R=90%)
|
|
96
|
+
* @return {number} r Retrievability (probability of recall)
|
|
97
|
+
*/
|
|
98
|
+
current_retrievability(t: number, s: number): number;
|
|
99
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FSRSAlgorithm = void 0;
|
|
7
|
+
const seedrandom_1 = __importDefault(require("seedrandom"));
|
|
8
|
+
const index_1 = require("./index");
|
|
9
|
+
const models_1 = require("./models");
|
|
10
|
+
// Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-v4
|
|
11
|
+
class FSRSAlgorithm {
|
|
12
|
+
param;
|
|
13
|
+
intervalModifier;
|
|
14
|
+
seed;
|
|
15
|
+
constructor(param) {
|
|
16
|
+
this.param = (0, index_1.generatorParameters)(param);
|
|
17
|
+
// Ref: https://github.com/open-spaced-repetition/py-fsrs/blob/ecd68e453611eb808c7367c7a5312d7cadeedf5c/src/fsrs/fsrs.py#L79
|
|
18
|
+
// The formula used is : I(r,s)=9 \cdot s \cdot (\frac{1}{r}-1)
|
|
19
|
+
this.intervalModifier = 9 * (1 / this.param.request_retention - 1);
|
|
20
|
+
}
|
|
21
|
+
init_ds(s) {
|
|
22
|
+
s.again.difficulty = this.init_difficulty(models_1.Rating.Again);
|
|
23
|
+
s.again.stability = this.init_stability(models_1.Rating.Again);
|
|
24
|
+
s.hard.difficulty = this.init_difficulty(models_1.Rating.Hard);
|
|
25
|
+
s.hard.stability = this.init_stability(models_1.Rating.Hard);
|
|
26
|
+
s.good.difficulty = this.init_difficulty(models_1.Rating.Good);
|
|
27
|
+
s.good.stability = this.init_stability(models_1.Rating.Good);
|
|
28
|
+
s.easy.difficulty = this.init_difficulty(models_1.Rating.Easy);
|
|
29
|
+
s.easy.stability = this.init_stability(models_1.Rating.Easy);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Updates the difficulty and stability values of the scheduling card based on the last difficulty,
|
|
33
|
+
* last stability, and the current retrievability.
|
|
34
|
+
* @param {SchedulingCard} s scheduling Card
|
|
35
|
+
* @param {number} last_d Difficulty
|
|
36
|
+
* @param {number} last_s Stability
|
|
37
|
+
* @param retrievability Retrievability
|
|
38
|
+
*/
|
|
39
|
+
next_ds(s, last_d, last_s, retrievability) {
|
|
40
|
+
s.again.difficulty = this.next_difficulty(last_d, models_1.Rating.Again);
|
|
41
|
+
s.again.stability = this.next_forget_stability(s.again.difficulty, last_s, retrievability);
|
|
42
|
+
s.hard.difficulty = this.next_difficulty(last_d, models_1.Rating.Hard);
|
|
43
|
+
s.hard.stability = this.next_recall_stability(s.hard.difficulty, last_s, retrievability, models_1.Rating.Hard);
|
|
44
|
+
s.good.difficulty = this.next_difficulty(last_d, models_1.Rating.Good);
|
|
45
|
+
s.good.stability = this.next_recall_stability(s.good.difficulty, last_s, retrievability, models_1.Rating.Good);
|
|
46
|
+
s.easy.difficulty = this.next_difficulty(last_d, models_1.Rating.Easy);
|
|
47
|
+
s.easy.stability = this.next_recall_stability(s.easy.difficulty, last_s, retrievability, models_1.Rating.Easy);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The formula used is :
|
|
51
|
+
* S_0(G) = w_{G-1}
|
|
52
|
+
* \max \{S_0,0.1\}
|
|
53
|
+
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
54
|
+
* @return Stability (interval when R=90%)
|
|
55
|
+
*/
|
|
56
|
+
init_stability(g) {
|
|
57
|
+
return Math.max(this.param.w[g - 1], 0.1);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The formula used is :
|
|
61
|
+
* $$D_0(G) = w_4 - (G-3) \cdot w_5$$
|
|
62
|
+
* $$\min \{\max \{D_0(G),1\},10\}$$
|
|
63
|
+
* where the D_0(3)=w_4 when the first rating is good.
|
|
64
|
+
* @param {number} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
65
|
+
* @return {number} Difficulty D \in [1,10]
|
|
66
|
+
*/
|
|
67
|
+
init_difficulty(g) {
|
|
68
|
+
return Math.min(Math.max(this.param.w[4] - (g - 3) * this.param.w[5], 1), 10);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* If fuzzing is disabled or ivl is less than 2.5, it returns the original interval.
|
|
72
|
+
* @param {number} ivl - The interval to be fuzzed.
|
|
73
|
+
* @return {number} - The fuzzed interval.
|
|
74
|
+
**/
|
|
75
|
+
apply_fuzz(ivl) {
|
|
76
|
+
if (!this.param.enable_fuzz || ivl < 2.5)
|
|
77
|
+
return ivl;
|
|
78
|
+
const generator = (0, seedrandom_1.default)(this.seed);
|
|
79
|
+
const fuzz_factor = generator();
|
|
80
|
+
ivl = Math.round(ivl);
|
|
81
|
+
const min_ivl = Math.max(2, Math.round(ivl * 0.95 - 1));
|
|
82
|
+
const max_ivl = Math.round(ivl * 1.05 + 1);
|
|
83
|
+
return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Ref:
|
|
87
|
+
* constructor(param: Partial<FSRSParameters>)
|
|
88
|
+
* this.intervalModifier = 9 * (1 / this.param.request_retention - 1);
|
|
89
|
+
* @param {number} s - Stability (interval when R=90%)
|
|
90
|
+
*/
|
|
91
|
+
next_interval(s) {
|
|
92
|
+
const newInterval = this.apply_fuzz(s * this.intervalModifier);
|
|
93
|
+
return Math.min(Math.max(Math.round(newInterval), 1), this.param.maximum_interval);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The formula used is :
|
|
97
|
+
* $$next_d = D - w_6 \cdot (R - 2)$$
|
|
98
|
+
* $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot next_d$$
|
|
99
|
+
* @param {number} d Difficulty D \in [1,10]
|
|
100
|
+
* @param {Rating} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
101
|
+
* @return {number} next_D
|
|
102
|
+
*/
|
|
103
|
+
next_difficulty(d, g) {
|
|
104
|
+
const next_d = d - this.param.w[6] * (g - 3);
|
|
105
|
+
return this.constrain_difficulty(this.mean_reversion(this.param.w[4], next_d));
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The formula used is :
|
|
109
|
+
* $$\min \{\max \{D_0,1\},10\}$$
|
|
110
|
+
* @param {number} difficulty D \in [1,10]
|
|
111
|
+
*/
|
|
112
|
+
constrain_difficulty(difficulty) {
|
|
113
|
+
return Math.min(Math.max(Number(difficulty.toFixed(2)), 1), 10);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The formula used is :
|
|
117
|
+
* $$w_7 \cdot init +(1 - w_7) \cdot current$$
|
|
118
|
+
* @param {number} init $$w_2 : D_0(3) = w_2 + (R-2) \cdot w_3= w_2$$
|
|
119
|
+
* @param {number} current $$D - w_6 \cdot (R - 2)$$
|
|
120
|
+
* @return {number} difficulty
|
|
121
|
+
*/
|
|
122
|
+
mean_reversion(init, current) {
|
|
123
|
+
return this.param.w[7] * init + (1 - this.param.w[7]) * current;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The formula used is :
|
|
127
|
+
* $$S^\prime_r(D,S,R,G) = S\cdot(e^{w_8}\cdot (11-D)\cdot S^{-w_9}\cdot(e^{w_10\cdot(1-R)}-1)\cdot w_15(if G=2) \cdot w_16(if G=4)+1)$$
|
|
128
|
+
* @param {number} d Difficulty D \in [1,10]
|
|
129
|
+
* @param {number} s Stability (interval when R=90%)
|
|
130
|
+
* @param {number} r Retrievability (probability of recall)
|
|
131
|
+
* @param {Rating} g Grade (Rating[0.again,1.hard,2.good,3.easy])
|
|
132
|
+
* @return {number} S^\prime_r new stability after recall
|
|
133
|
+
*/
|
|
134
|
+
next_recall_stability(d, s, r, g) {
|
|
135
|
+
const hard_penalty = models_1.Rating.Hard === g ? this.param.w[15] : 1;
|
|
136
|
+
const easy_bound = models_1.Rating.Easy === g ? this.param.w[16] : 1;
|
|
137
|
+
return (s *
|
|
138
|
+
(1 +
|
|
139
|
+
Math.exp(this.param.w[8]) *
|
|
140
|
+
(11 - d) *
|
|
141
|
+
Math.pow(s, -this.param.w[9]) *
|
|
142
|
+
(Math.exp((1 - r) * this.param.w[10]) - 1) *
|
|
143
|
+
hard_penalty *
|
|
144
|
+
easy_bound));
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The formula used is :
|
|
148
|
+
* $$S^\prime_f(D,S,R) = w_11\cdot D^{-w_{12}}\cdot ((S+1)^{w_{13}}-1) \cdot e^{w_{14}\cdot(1-R)}.$$
|
|
149
|
+
* @param {number} d Difficulty D \in [1,10]
|
|
150
|
+
* @param {number} s Stability (interval when R=90%)
|
|
151
|
+
* @param {number} r Retrievability (probability of recall)
|
|
152
|
+
* @return {number} S^\prime_f new stability after forgetting
|
|
153
|
+
*/
|
|
154
|
+
next_forget_stability(d, s, r) {
|
|
155
|
+
return (this.param.w[11] *
|
|
156
|
+
Math.pow(d, -this.param.w[12]) *
|
|
157
|
+
(Math.pow(s + 1, this.param.w[13]) - 1) *
|
|
158
|
+
Math.exp((1 - r) * this.param.w[14]));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The formula used is :
|
|
162
|
+
* $$R(t,S) = (1 + \frac{t}{9 \cdot S})^{-1},$$
|
|
163
|
+
* @param {number} t t days since the last review
|
|
164
|
+
* @param {number} s Stability (interval when R=90%)
|
|
165
|
+
* @return {number} r Retrievability (probability of recall)
|
|
166
|
+
*/
|
|
167
|
+
current_retrievability(t, s) {
|
|
168
|
+
return Math.pow(1 + t / (9 * s), -1);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
exports.FSRSAlgorithm = FSRSAlgorithm;
|
package/dist/default.d.ts
CHANGED
|
@@ -6,11 +6,6 @@ export declare const default_maximum_interval: number;
|
|
|
6
6
|
export declare const default_w: number[];
|
|
7
7
|
export declare const default_enable_fuzz: boolean;
|
|
8
8
|
export declare const FSRSVersion: string;
|
|
9
|
-
export declare const generatorParameters: (props?: Partial<FSRSParameters>) =>
|
|
10
|
-
request_retention: number;
|
|
11
|
-
maximum_interval: number;
|
|
12
|
-
w: number[];
|
|
13
|
-
enable_fuzz: boolean;
|
|
14
|
-
};
|
|
9
|
+
export declare const generatorParameters: (props?: Partial<FSRSParameters>) => FSRSParameters;
|
|
15
10
|
export declare const createEmptyCard: (now?: Date) => Card;
|
|
16
11
|
export declare const fsrs: (params?: Partial<FSRSParameters>) => FSRS;
|
package/dist/default.js
CHANGED
|
@@ -29,7 +29,7 @@ exports.default_w = exports.envParams.FSRS_W || [
|
|
|
29
29
|
0.34, 1.26, 0.29, 2.61,
|
|
30
30
|
];
|
|
31
31
|
exports.default_enable_fuzz = exports.envParams.FSRS_ENABLE_FUZZ || false;
|
|
32
|
-
exports.FSRSVersion = "3.0.
|
|
32
|
+
exports.FSRSVersion = "3.0.5";
|
|
33
33
|
const generatorParameters = (props) => {
|
|
34
34
|
return {
|
|
35
35
|
request_retention: props?.request_retention || exports.default_request_retention,
|
package/dist/fsrs.d.ts
CHANGED
|
@@ -1,138 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
export declare class FSRS {
|
|
5
|
-
private param;
|
|
6
|
-
private readonly intervalModifier;
|
|
7
|
-
private seed?;
|
|
1
|
+
import { FSRSParameters, Card, CardInput, DateInput, RecordLog } from "./models";
|
|
2
|
+
import { FSRSAlgorithm } from "./algorithm";
|
|
3
|
+
export declare class FSRS extends FSRSAlgorithm {
|
|
8
4
|
constructor(param: Partial<FSRSParameters>);
|
|
9
5
|
preProcess(_card: CardInput, _now: DateInput): {
|
|
10
6
|
card: Card;
|
|
11
7
|
now: Date;
|
|
12
8
|
};
|
|
13
|
-
repeat: (card: CardInput, now: DateInput) =>
|
|
14
|
-
1: {
|
|
15
|
-
card: Card;
|
|
16
|
-
log: {
|
|
17
|
-
rating: Rating;
|
|
18
|
-
state: State;
|
|
19
|
-
elapsed_days: number;
|
|
20
|
-
scheduled_days: number;
|
|
21
|
-
review: Date;
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
2: {
|
|
25
|
-
card: Card;
|
|
26
|
-
log: {
|
|
27
|
-
rating: Rating;
|
|
28
|
-
state: State;
|
|
29
|
-
elapsed_days: number;
|
|
30
|
-
scheduled_days: number;
|
|
31
|
-
review: Date;
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
3: {
|
|
35
|
-
card: Card;
|
|
36
|
-
log: {
|
|
37
|
-
rating: Rating;
|
|
38
|
-
state: State;
|
|
39
|
-
elapsed_days: number;
|
|
40
|
-
scheduled_days: number;
|
|
41
|
-
review: Date;
|
|
42
|
-
};
|
|
43
|
-
};
|
|
44
|
-
4: {
|
|
45
|
-
card: Card;
|
|
46
|
-
log: {
|
|
47
|
-
rating: Rating;
|
|
48
|
-
state: State;
|
|
49
|
-
elapsed_days: number;
|
|
50
|
-
scheduled_days: number;
|
|
51
|
-
review: Date;
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
};
|
|
9
|
+
repeat: (card: CardInput, now: DateInput) => RecordLog;
|
|
55
10
|
get_retrievability: (card: Card, now: Date) => undefined | string;
|
|
56
|
-
init_ds(s: SchedulingCard): void;
|
|
57
|
-
/**
|
|
58
|
-
*
|
|
59
|
-
* @param s scheduling Card
|
|
60
|
-
* @param last_d Difficulty
|
|
61
|
-
* @param last_s Stability
|
|
62
|
-
* @param retrievability Retrievability
|
|
63
|
-
*/
|
|
64
|
-
next_ds(s: SchedulingCard, last_d: number, last_s: number, retrievability: number): void;
|
|
65
|
-
/**
|
|
66
|
-
* The formula used is :
|
|
67
|
-
* $$S_0(G) = w_{G-1}$$
|
|
68
|
-
* $$\max \{S_0,0.1\}$$
|
|
69
|
-
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
70
|
-
* @return Stability (interval when R=90%)
|
|
71
|
-
*/
|
|
72
|
-
init_stability(g: number): number;
|
|
73
|
-
/**
|
|
74
|
-
* The formula used is :
|
|
75
|
-
* $$D_0(G) = w_4 - (G-3) \cdot w_5$$
|
|
76
|
-
* $$\min \{\max \{D_0(G),1\},10\}$$
|
|
77
|
-
* where the D_0(3)=w_4 when the first rating is good.
|
|
78
|
-
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
79
|
-
* @return Difficulty D \in [1,10]
|
|
80
|
-
*/
|
|
81
|
-
init_difficulty(g: number): number;
|
|
82
|
-
apply_fuzz(ivl: number): number;
|
|
83
|
-
/**
|
|
84
|
-
* Ref:
|
|
85
|
-
* constructor(param: Partial<FSRSParameters>)
|
|
86
|
-
* this.intervalModifier = 9 * (1 / this.param.request_retention - 1);
|
|
87
|
-
*/
|
|
88
|
-
next_interval(s: number): int;
|
|
89
|
-
/**
|
|
90
|
-
* The formula used is :
|
|
91
|
-
* $$next_d = D - w_6 \cdot (R - 2)$$
|
|
92
|
-
* $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot next_d$$
|
|
93
|
-
* @param d
|
|
94
|
-
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
95
|
-
* @return next_D
|
|
96
|
-
*/
|
|
97
|
-
next_difficulty(d: number, g: number): number;
|
|
98
|
-
/**
|
|
99
|
-
* The formula used is :
|
|
100
|
-
* $$\min \{\max \{D_0,1\},10\}$$
|
|
101
|
-
*/
|
|
102
|
-
constrain_difficulty(difficulty: number): number;
|
|
103
|
-
/**
|
|
104
|
-
* The formula used is :
|
|
105
|
-
* $$w_7 \cdot init +(1 - w_7) \cdot current$$
|
|
106
|
-
* @param init $$w_2 : D_0(3) = w_2 + (R-2) \cdot w_3= w_2$$
|
|
107
|
-
* @param current $$D - w_6 \cdot (R - 2)$$
|
|
108
|
-
* @return difficulty
|
|
109
|
-
*/
|
|
110
|
-
mean_reversion(init: number, current: number): number;
|
|
111
|
-
/**
|
|
112
|
-
* The formula used is :
|
|
113
|
-
* $$S^\prime_r(D,S,R,G) = S\cdot(e^{w_8}\cdot (11-D)\cdot S^{-w_9}\cdot(e^{w_10\cdot(1-R)}-1)\cdot w_15(if G=2) \cdot w_16(if G=4)+1)$$
|
|
114
|
-
* @param d Difficulty D \in [1,10]
|
|
115
|
-
* @param s Stability (interval when R=90%)
|
|
116
|
-
* @param r Retrievability (probability of recall)
|
|
117
|
-
* @param g Grade (Rating[0.again,1.hard,2.good,3.easy])
|
|
118
|
-
* @return S^\prime_r new stability after recall
|
|
119
|
-
*/
|
|
120
|
-
next_recall_stability(d: number, s: number, r: number, g: Rating): number;
|
|
121
|
-
/**
|
|
122
|
-
* The formula used is :
|
|
123
|
-
* $$S^\prime_f(D,S,R) = w_11\cdot D^{-w_{12}}\cdot ((S+1)^{w_{13}}-1) \cdot e^{w_{14}\cdot(1-R)}.$$
|
|
124
|
-
* @param d Difficulty D \in [1,10]
|
|
125
|
-
* @param s Stability (interval when R=90%)
|
|
126
|
-
* @param r Retrievability (probability of recall)
|
|
127
|
-
* @return S^\prime_f new stability after forgetting
|
|
128
|
-
*/
|
|
129
|
-
next_forget_stability(d: number, s: number, r: number): number;
|
|
130
|
-
/**
|
|
131
|
-
* The formula used is :
|
|
132
|
-
* $$R(t,S) = (1 + \frac{t}{9 \cdot S})^{-1},$$
|
|
133
|
-
* @param t t days since the last review
|
|
134
|
-
* @param s Stability (interval when R=90%)
|
|
135
|
-
* @return r Retrievability (probability of recall)
|
|
136
|
-
*/
|
|
137
|
-
current_retrievability(t: number, s: number): number;
|
|
138
11
|
}
|
package/dist/fsrs.js
CHANGED
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.FSRS = void 0;
|
|
7
|
-
const seedrandom_1 = __importDefault(require("seedrandom"));
|
|
8
4
|
const index_1 = require("./index");
|
|
9
5
|
const help_1 = require("./help");
|
|
10
6
|
const models_1 = require("./models");
|
|
11
|
-
|
|
12
|
-
class FSRS {
|
|
13
|
-
param;
|
|
14
|
-
intervalModifier;
|
|
15
|
-
seed;
|
|
7
|
+
const algorithm_1 = require("./algorithm");
|
|
8
|
+
class FSRS extends algorithm_1.FSRSAlgorithm {
|
|
16
9
|
constructor(param) {
|
|
17
|
-
|
|
18
|
-
// Ref: https://github.com/open-spaced-repetition/py-fsrs/blob/ecd68e453611eb808c7367c7a5312d7cadeedf5c/src/fsrs/fsrs.py#L79
|
|
19
|
-
// The formula used is : I(r,s)=9 \cdot s \cdot (\frac{1}{r}-1)
|
|
20
|
-
this.intervalModifier = 9 * (1 / this.param.request_retention - 1);
|
|
10
|
+
super(param);
|
|
21
11
|
}
|
|
22
12
|
preProcess(_card, _now) {
|
|
23
13
|
const card = {
|
|
@@ -37,8 +27,7 @@ class FSRS {
|
|
|
37
27
|
card.state === models_1.State.New ? 0 : now.diff(card.last_review, "days"); //相距时间
|
|
38
28
|
card.last_review = now; // 上次复习时间
|
|
39
29
|
card.reps += 1;
|
|
40
|
-
const s = new index_1.SchedulingCard(card);
|
|
41
|
-
s.update_state(card.state);
|
|
30
|
+
const s = new index_1.SchedulingCard(card).update_state(card.state);
|
|
42
31
|
this.seed = String(card.last_review.getTime()) + String(card.elapsed_days);
|
|
43
32
|
let easy_interval, good_interval, hard_interval;
|
|
44
33
|
switch (card.state) {
|
|
@@ -85,146 +74,5 @@ class FSRS {
|
|
|
85
74
|
const t = Math.max(now.diff(card.last_review, "days"), 0);
|
|
86
75
|
return ((this.current_retrievability(t, card.stability) * 100).toFixed(2) + "%");
|
|
87
76
|
};
|
|
88
|
-
init_ds(s) {
|
|
89
|
-
s.again.difficulty = this.init_difficulty(models_1.Rating.Again);
|
|
90
|
-
s.again.stability = this.init_stability(models_1.Rating.Again);
|
|
91
|
-
s.hard.difficulty = this.init_difficulty(models_1.Rating.Hard);
|
|
92
|
-
s.hard.stability = this.init_stability(models_1.Rating.Hard);
|
|
93
|
-
s.good.difficulty = this.init_difficulty(models_1.Rating.Good);
|
|
94
|
-
s.good.stability = this.init_stability(models_1.Rating.Good);
|
|
95
|
-
s.easy.difficulty = this.init_difficulty(models_1.Rating.Easy);
|
|
96
|
-
s.easy.stability = this.init_stability(models_1.Rating.Easy);
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
*
|
|
100
|
-
* @param s scheduling Card
|
|
101
|
-
* @param last_d Difficulty
|
|
102
|
-
* @param last_s Stability
|
|
103
|
-
* @param retrievability Retrievability
|
|
104
|
-
*/
|
|
105
|
-
next_ds(s, last_d, last_s, retrievability) {
|
|
106
|
-
s.again.difficulty = this.next_difficulty(last_d, models_1.Rating.Again);
|
|
107
|
-
s.again.stability = this.next_forget_stability(s.again.difficulty, last_s, retrievability);
|
|
108
|
-
s.hard.difficulty = this.next_difficulty(last_d, models_1.Rating.Hard);
|
|
109
|
-
s.hard.stability = this.next_recall_stability(s.hard.difficulty, last_s, retrievability, models_1.Rating.Hard);
|
|
110
|
-
s.good.difficulty = this.next_difficulty(last_d, models_1.Rating.Good);
|
|
111
|
-
s.good.stability = this.next_recall_stability(s.good.difficulty, last_s, retrievability, models_1.Rating.Good);
|
|
112
|
-
s.easy.difficulty = this.next_difficulty(last_d, models_1.Rating.Easy);
|
|
113
|
-
s.easy.stability = this.next_recall_stability(s.easy.difficulty, last_s, retrievability, models_1.Rating.Easy);
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* The formula used is :
|
|
117
|
-
* $$S_0(G) = w_{G-1}$$
|
|
118
|
-
* $$\max \{S_0,0.1\}$$
|
|
119
|
-
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
120
|
-
* @return Stability (interval when R=90%)
|
|
121
|
-
*/
|
|
122
|
-
init_stability(g) {
|
|
123
|
-
return Math.max(this.param.w[g - 1], 0.1);
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* The formula used is :
|
|
127
|
-
* $$D_0(G) = w_4 - (G-3) \cdot w_5$$
|
|
128
|
-
* $$\min \{\max \{D_0(G),1\},10\}$$
|
|
129
|
-
* where the D_0(3)=w_4 when the first rating is good.
|
|
130
|
-
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
131
|
-
* @return Difficulty D \in [1,10]
|
|
132
|
-
*/
|
|
133
|
-
init_difficulty(g) {
|
|
134
|
-
return Math.min(Math.max(this.param.w[4] - (g - 3) * this.param.w[5], 1), 10);
|
|
135
|
-
}
|
|
136
|
-
apply_fuzz(ivl) {
|
|
137
|
-
if (!this.param.enable_fuzz || ivl < 2.5)
|
|
138
|
-
return ivl;
|
|
139
|
-
const generator = (0, seedrandom_1.default)(this.seed);
|
|
140
|
-
const fuzz_factor = generator();
|
|
141
|
-
ivl = Math.round(ivl);
|
|
142
|
-
const min_ivl = Math.max(2, Math.round(ivl * 0.95 - 1));
|
|
143
|
-
const max_ivl = Math.round(ivl * 1.05 + 1);
|
|
144
|
-
return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl);
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Ref:
|
|
148
|
-
* constructor(param: Partial<FSRSParameters>)
|
|
149
|
-
* this.intervalModifier = 9 * (1 / this.param.request_retention - 1);
|
|
150
|
-
*/
|
|
151
|
-
next_interval(s) {
|
|
152
|
-
const newInterval = this.apply_fuzz(s * this.intervalModifier);
|
|
153
|
-
return Math.min(Math.max(Math.round(newInterval), 1), this.param.maximum_interval);
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* The formula used is :
|
|
157
|
-
* $$next_d = D - w_6 \cdot (R - 2)$$
|
|
158
|
-
* $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot next_d$$
|
|
159
|
-
* @param d
|
|
160
|
-
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
161
|
-
* @return next_D
|
|
162
|
-
*/
|
|
163
|
-
next_difficulty(d, g) {
|
|
164
|
-
const next_d = d - this.param.w[6] * (g - 3);
|
|
165
|
-
return this.constrain_difficulty(this.mean_reversion(this.param.w[4], next_d));
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* The formula used is :
|
|
169
|
-
* $$\min \{\max \{D_0,1\},10\}$$
|
|
170
|
-
*/
|
|
171
|
-
constrain_difficulty(difficulty) {
|
|
172
|
-
return Math.min(Math.max(Number(difficulty.toFixed(2)), 1), 10);
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* The formula used is :
|
|
176
|
-
* $$w_7 \cdot init +(1 - w_7) \cdot current$$
|
|
177
|
-
* @param init $$w_2 : D_0(3) = w_2 + (R-2) \cdot w_3= w_2$$
|
|
178
|
-
* @param current $$D - w_6 \cdot (R - 2)$$
|
|
179
|
-
* @return difficulty
|
|
180
|
-
*/
|
|
181
|
-
mean_reversion(init, current) {
|
|
182
|
-
return this.param.w[7] * init + (1 - this.param.w[7]) * current;
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* The formula used is :
|
|
186
|
-
* $$S^\prime_r(D,S,R,G) = S\cdot(e^{w_8}\cdot (11-D)\cdot S^{-w_9}\cdot(e^{w_10\cdot(1-R)}-1)\cdot w_15(if G=2) \cdot w_16(if G=4)+1)$$
|
|
187
|
-
* @param d Difficulty D \in [1,10]
|
|
188
|
-
* @param s Stability (interval when R=90%)
|
|
189
|
-
* @param r Retrievability (probability of recall)
|
|
190
|
-
* @param g Grade (Rating[0.again,1.hard,2.good,3.easy])
|
|
191
|
-
* @return S^\prime_r new stability after recall
|
|
192
|
-
*/
|
|
193
|
-
next_recall_stability(d, s, r, g) {
|
|
194
|
-
const hard_penalty = models_1.Rating.Hard === g ? this.param.w[15] : 1;
|
|
195
|
-
const easy_bound = models_1.Rating.Easy === g ? this.param.w[16] : 1;
|
|
196
|
-
return (s *
|
|
197
|
-
(1 +
|
|
198
|
-
Math.exp(this.param.w[8]) *
|
|
199
|
-
(11 - d) *
|
|
200
|
-
Math.pow(s, -this.param.w[9]) *
|
|
201
|
-
(Math.exp((1 - r) * this.param.w[10]) - 1) *
|
|
202
|
-
hard_penalty *
|
|
203
|
-
easy_bound));
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* The formula used is :
|
|
207
|
-
* $$S^\prime_f(D,S,R) = w_11\cdot D^{-w_{12}}\cdot ((S+1)^{w_{13}}-1) \cdot e^{w_{14}\cdot(1-R)}.$$
|
|
208
|
-
* @param d Difficulty D \in [1,10]
|
|
209
|
-
* @param s Stability (interval when R=90%)
|
|
210
|
-
* @param r Retrievability (probability of recall)
|
|
211
|
-
* @return S^\prime_f new stability after forgetting
|
|
212
|
-
*/
|
|
213
|
-
next_forget_stability(d, s, r) {
|
|
214
|
-
return (this.param.w[11] *
|
|
215
|
-
Math.pow(d, -this.param.w[12]) *
|
|
216
|
-
(Math.pow(s + 1, this.param.w[13]) - 1) *
|
|
217
|
-
Math.exp((1 - r) * this.param.w[14]));
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* The formula used is :
|
|
221
|
-
* $$R(t,S) = (1 + \frac{t}{9 \cdot S})^{-1},$$
|
|
222
|
-
* @param t t days since the last review
|
|
223
|
-
* @param s Stability (interval when R=90%)
|
|
224
|
-
* @return r Retrievability (probability of recall)
|
|
225
|
-
*/
|
|
226
|
-
current_retrievability(t, s) {
|
|
227
|
-
return Math.pow(1 + t / (9 * s), -1);
|
|
228
|
-
}
|
|
229
77
|
}
|
|
230
78
|
exports.FSRS = FSRS;
|
package/dist/help.d.ts
CHANGED
|
@@ -8,6 +8,13 @@ declare global {
|
|
|
8
8
|
dueFormat(last_review: Date, unit?: boolean): string;
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* 计算日期和时间的偏移,并返回一个新的日期对象。
|
|
13
|
+
* @param now 当前日期和时间
|
|
14
|
+
* @param t 时间偏移量,当 isDay 为 true 时表示天数,为 false 时表示分钟
|
|
15
|
+
* @param isDay (可选)是否按天数单位进行偏移,默认为 false,表示按分钟单位计算偏移
|
|
16
|
+
* @returns 偏移后的日期和时间对象
|
|
17
|
+
*/
|
|
11
18
|
export declare function date_scheduler(now: Date, t: number, isDay?: boolean): Date;
|
|
12
19
|
export declare function date_diff(now: Date, pre: Date, unit: unit): number;
|
|
13
20
|
export declare function formatDate(date: Date): string;
|
package/dist/help.js
CHANGED
|
@@ -19,6 +19,13 @@ Date.prototype.format = function () {
|
|
|
19
19
|
Date.prototype.dueFormat = function (last_review, unit) {
|
|
20
20
|
return show_diff_message(this, last_review, unit);
|
|
21
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* 计算日期和时间的偏移,并返回一个新的日期对象。
|
|
24
|
+
* @param now 当前日期和时间
|
|
25
|
+
* @param t 时间偏移量,当 isDay 为 true 时表示天数,为 false 时表示分钟
|
|
26
|
+
* @param isDay (可选)是否按天数单位进行偏移,默认为 false,表示按分钟单位计算偏移
|
|
27
|
+
* @returns 偏移后的日期和时间对象
|
|
28
|
+
*/
|
|
22
29
|
function date_scheduler(now, t, isDay) {
|
|
23
30
|
return new Date(isDay
|
|
24
31
|
? now.getTime() + t * 24 * 60 * 60 * 1000
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ export { SchedulingCard } from "./scheduler";
|
|
|
2
2
|
export { default_request_retention, default_maximum_interval, default_w, default_enable_fuzz, FSRSVersion, generatorParameters, createEmptyCard, fsrs, envParams } from "./default";
|
|
3
3
|
export { date_scheduler, date_diff, formatDate, show_diff_message, } from "./help";
|
|
4
4
|
export type { int, double } from "./type";
|
|
5
|
-
export type { FSRSParameters, Card, ReviewLog, StateType, RatingType, CardInput, DateInput } from "./models";
|
|
5
|
+
export type { FSRSParameters, Card, ReviewLog, RecordLog, RecordLogItem, StateType, RatingType, CardInput, DateInput } from "./models";
|
|
6
6
|
export { State, Rating } from "./models";
|
|
7
7
|
export { FSRS } from "./fsrs";
|
package/dist/models.d.ts
CHANGED
|
@@ -19,6 +19,13 @@ export interface ReviewLog {
|
|
|
19
19
|
scheduled_days: number;
|
|
20
20
|
review: Date;
|
|
21
21
|
}
|
|
22
|
+
export type RecordLogItem = {
|
|
23
|
+
card: Card;
|
|
24
|
+
log: ReviewLog;
|
|
25
|
+
};
|
|
26
|
+
export type RecordLog = {
|
|
27
|
+
[key in Rating]: RecordLogItem;
|
|
28
|
+
};
|
|
22
29
|
export interface Card {
|
|
23
30
|
due: Date;
|
|
24
31
|
stability: number;
|
package/dist/scheduler.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Card,
|
|
1
|
+
import { Card, RecordLog, State } from "./models";
|
|
2
2
|
export declare class SchedulingCard {
|
|
3
3
|
again: Card;
|
|
4
4
|
hard: Card;
|
|
@@ -8,46 +8,5 @@ export declare class SchedulingCard {
|
|
|
8
8
|
constructor(card: Card);
|
|
9
9
|
update_state(state: State): this;
|
|
10
10
|
schedule(now: Date, hard_interval: number, good_interval: number, easy_interval: number): SchedulingCard;
|
|
11
|
-
record_log(card: Card, now: Date):
|
|
12
|
-
1: {
|
|
13
|
-
card: Card;
|
|
14
|
-
log: {
|
|
15
|
-
rating: Rating;
|
|
16
|
-
state: State;
|
|
17
|
-
elapsed_days: number;
|
|
18
|
-
scheduled_days: number;
|
|
19
|
-
review: Date;
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
2: {
|
|
23
|
-
card: Card;
|
|
24
|
-
log: {
|
|
25
|
-
rating: Rating;
|
|
26
|
-
state: State;
|
|
27
|
-
elapsed_days: number;
|
|
28
|
-
scheduled_days: number;
|
|
29
|
-
review: Date;
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
3: {
|
|
33
|
-
card: Card;
|
|
34
|
-
log: {
|
|
35
|
-
rating: Rating;
|
|
36
|
-
state: State;
|
|
37
|
-
elapsed_days: number;
|
|
38
|
-
scheduled_days: number;
|
|
39
|
-
review: Date;
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
4: {
|
|
43
|
-
card: Card;
|
|
44
|
-
log: {
|
|
45
|
-
rating: Rating;
|
|
46
|
-
state: State;
|
|
47
|
-
elapsed_days: number;
|
|
48
|
-
scheduled_days: number;
|
|
49
|
-
review: Date;
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
};
|
|
11
|
+
record_log(card: Card, now: Date): RecordLog;
|
|
53
12
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-fsrs",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.5",
|
|
4
4
|
"description": "ts-fsrs is a TypeScript package used to implement the Free Spaced Repetition Scheduler (FSRS) algorithm. It helps developers apply FSRS to their flashcard applications, thereby improving the user learning experience.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|