ts-fsrs 2.1.1 → 3.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 +9 -0
- package/dist/default.d.ts +16 -0
- package/dist/default.js +58 -0
- package/dist/fsrs.d.ts +29 -21
- package/dist/fsrs.js +71 -55
- package/dist/help.d.ts +1 -7
- package/dist/index.d.ts +6 -25
- package/dist/index.js +17 -56
- package/dist/models.d.ts +5 -19
- package/dist/models.js +5 -13
- package/dist/scheduler.d.ts +4 -4
- package/dist/type.d.ts +13 -0
- package/dist/type.js +2 -0
- package/package.json +3 -2
- package/dist/fsrs_version.d.ts +0 -2
- package/dist/fsrs_version.js +0 -7
package/README.md
CHANGED
|
@@ -9,6 +9,15 @@ developers apply FSRS to their flashcard applications, there by improving the us
|
|
|
9
9
|
npm install ts-fsrs
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
+
# Environment Variables
|
|
13
|
+
If you need to customize default parameters, you can modify the values using `.env`/`.env.local`/`.env.production`/`.env.development`.
|
|
14
|
+
|
|
15
|
+
Copy the [.env.local.example](./example/.env.local.example) file in this directory to .env.local (which will be ignored by Git):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cp .env.local.example .env.local
|
|
19
|
+
```
|
|
20
|
+
|
|
12
21
|
# Example
|
|
13
22
|
|
|
14
23
|
```typescript
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Card, FSRSParameters, FSRS } from "./index";
|
|
2
|
+
import { EnvParams } from "./type";
|
|
3
|
+
export declare const envParams: EnvParams;
|
|
4
|
+
export declare const default_request_retention: number;
|
|
5
|
+
export declare const default_maximum_interval: number;
|
|
6
|
+
export declare const default_w: number[];
|
|
7
|
+
export declare const default_enable_fuzz: boolean;
|
|
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
|
+
};
|
|
15
|
+
export declare const createEmptyCard: (now?: Date) => Card;
|
|
16
|
+
export declare const fsrs: (params?: Partial<FSRSParameters>) => FSRS;
|
package/dist/default.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
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.fsrs = exports.createEmptyCard = exports.generatorParameters = exports.FSRSVersion = exports.default_enable_fuzz = exports.default_w = exports.default_maximum_interval = exports.default_request_retention = exports.envParams = void 0;
|
|
7
|
+
const index_1 = require("./index");
|
|
8
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
9
|
+
dotenv_1.default.config({ path: `./.env.local` });
|
|
10
|
+
dotenv_1.default.config({ path: `./.env.production` });
|
|
11
|
+
dotenv_1.default.config({ path: `./.env.` });
|
|
12
|
+
dotenv_1.default.config({ path: `./.env.development` });
|
|
13
|
+
exports.envParams = {
|
|
14
|
+
FSRS_REQUEST_RETENTION: Number(process.env.FSRS_REQUEST_RETENTION),
|
|
15
|
+
FSRS_MAXIMUM_INTERVAL: Number(process.env.FSRS_MAXIMUM_INTERVAL),
|
|
16
|
+
FSRS_W: process.env.FSRS_W
|
|
17
|
+
? JSON.parse(process.env.FSRS_W)
|
|
18
|
+
: undefined,
|
|
19
|
+
FSRS_ENABLE_FUZZ: Boolean(process.env.FSRS_ENABLE_FUZZ),
|
|
20
|
+
};
|
|
21
|
+
exports.default_request_retention = !isNaN(exports.envParams.FSRS_REQUEST_RETENTION)
|
|
22
|
+
? exports.envParams.FSRS_REQUEST_RETENTION
|
|
23
|
+
: 0.9;
|
|
24
|
+
exports.default_maximum_interval = !isNaN(exports.envParams.FSRS_MAXIMUM_INTERVAL)
|
|
25
|
+
? exports.envParams.FSRS_MAXIMUM_INTERVAL
|
|
26
|
+
: 36500;
|
|
27
|
+
exports.default_w = exports.envParams.FSRS_W || [
|
|
28
|
+
0.4, 0.6, 2.4, 5.8, 4.93, 0.94, 0.86, 0.01, 1.49, 0.14, 0.94, 2.18, 0.05,
|
|
29
|
+
0.34, 1.26, 0.29, 2.61,
|
|
30
|
+
];
|
|
31
|
+
exports.default_enable_fuzz = exports.envParams.FSRS_ENABLE_FUZZ || false;
|
|
32
|
+
exports.FSRSVersion = "3.0.0";
|
|
33
|
+
const generatorParameters = (props) => {
|
|
34
|
+
return {
|
|
35
|
+
request_retention: props?.request_retention || exports.default_request_retention,
|
|
36
|
+
maximum_interval: props?.maximum_interval || exports.default_maximum_interval,
|
|
37
|
+
w: props?.w || exports.default_w,
|
|
38
|
+
enable_fuzz: props?.enable_fuzz || exports.default_enable_fuzz,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
exports.generatorParameters = generatorParameters;
|
|
42
|
+
const createEmptyCard = (now) => {
|
|
43
|
+
return {
|
|
44
|
+
due: now || new Date(),
|
|
45
|
+
stability: 0,
|
|
46
|
+
difficulty: 0,
|
|
47
|
+
elapsed_days: 0,
|
|
48
|
+
scheduled_days: 0,
|
|
49
|
+
reps: 0,
|
|
50
|
+
lapses: 0,
|
|
51
|
+
state: index_1.State.New,
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
exports.createEmptyCard = createEmptyCard;
|
|
55
|
+
const fsrs = (params) => {
|
|
56
|
+
return new index_1.FSRS(params || {});
|
|
57
|
+
};
|
|
58
|
+
exports.fsrs = fsrs;
|
package/dist/fsrs.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { SchedulingCard } from "./index";
|
|
2
|
+
import { FSRSParameters, Card, State, Rating } from "./models";
|
|
3
|
+
import type { int } from "./type";
|
|
4
|
+
export declare class FSRS {
|
|
4
5
|
private param;
|
|
5
6
|
private readonly intervalModifier;
|
|
6
7
|
private seed?;
|
|
7
|
-
constructor(param
|
|
8
|
+
constructor(param: Partial<FSRSParameters>);
|
|
8
9
|
repeat: (card: Card, now: Date) => {
|
|
9
|
-
|
|
10
|
+
1: {
|
|
10
11
|
card: Card;
|
|
11
12
|
log: {
|
|
12
13
|
rating: Rating;
|
|
@@ -16,7 +17,7 @@ export default class FSRS {
|
|
|
16
17
|
review: Date;
|
|
17
18
|
};
|
|
18
19
|
};
|
|
19
|
-
|
|
20
|
+
2: {
|
|
20
21
|
card: Card;
|
|
21
22
|
log: {
|
|
22
23
|
rating: Rating;
|
|
@@ -26,7 +27,7 @@ export default class FSRS {
|
|
|
26
27
|
review: Date;
|
|
27
28
|
};
|
|
28
29
|
};
|
|
29
|
-
|
|
30
|
+
3: {
|
|
30
31
|
card: Card;
|
|
31
32
|
log: {
|
|
32
33
|
rating: Rating;
|
|
@@ -36,7 +37,7 @@ export default class FSRS {
|
|
|
36
37
|
review: Date;
|
|
37
38
|
};
|
|
38
39
|
};
|
|
39
|
-
|
|
40
|
+
4: {
|
|
40
41
|
card: Card;
|
|
41
42
|
log: {
|
|
42
43
|
rating: Rating;
|
|
@@ -59,28 +60,34 @@ export default class FSRS {
|
|
|
59
60
|
next_ds(s: SchedulingCard, last_d: number, last_s: number, retrievability: number): void;
|
|
60
61
|
/**
|
|
61
62
|
* The formula used is :
|
|
62
|
-
* $$S_0(G) =
|
|
63
|
+
* $$S_0(G) = w_{G-1}$$
|
|
63
64
|
* $$\max \{S_0,0.1\}$$
|
|
64
|
-
* @param g Grade (rating at Anki) [
|
|
65
|
+
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
65
66
|
* @return Stability (interval when R=90%)
|
|
66
67
|
*/
|
|
67
68
|
init_stability(g: number): number;
|
|
68
69
|
/**
|
|
69
70
|
* The formula used is :
|
|
70
|
-
* $$D_0(G) =
|
|
71
|
+
* $$D_0(G) = w_4 - (G-3) \cdot w_5$$
|
|
71
72
|
* $$\min \{\max \{D_0(G),1\},10\}$$
|
|
72
|
-
*
|
|
73
|
+
* where the D_0(3)=w_4 when the first rating is good.
|
|
74
|
+
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
73
75
|
* @return Difficulty D \in [1,10]
|
|
74
76
|
*/
|
|
75
77
|
init_difficulty(g: number): number;
|
|
76
78
|
apply_fuzz(ivl: number): number;
|
|
79
|
+
/**
|
|
80
|
+
* Ref:
|
|
81
|
+
* constructor(param: Partial<FSRSParameters>)
|
|
82
|
+
* this.intervalModifier = 9 * (1 / this.param.request_retention - 1);
|
|
83
|
+
*/
|
|
77
84
|
next_interval(s: number): int;
|
|
78
85
|
/**
|
|
79
86
|
* The formula used is :
|
|
80
|
-
* $$next_d = D
|
|
87
|
+
* $$next_d = D - w_6 \cdot (R - 2)$$
|
|
81
88
|
* $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot next_d$$
|
|
82
89
|
* @param d
|
|
83
|
-
* @param g Grade (
|
|
90
|
+
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
84
91
|
* @return next_D
|
|
85
92
|
*/
|
|
86
93
|
next_difficulty(d: number, g: number): number;
|
|
@@ -91,24 +98,25 @@ export default class FSRS {
|
|
|
91
98
|
constrain_difficulty(difficulty: number): number;
|
|
92
99
|
/**
|
|
93
100
|
* The formula used is :
|
|
94
|
-
* $$
|
|
95
|
-
* @param init $$w_2 : D_0(
|
|
96
|
-
* @param current $$D
|
|
101
|
+
* $$w_7 \cdot init +(1 - w_7) \cdot current$$
|
|
102
|
+
* @param init $$w_2 : D_0(3) = w_2 + (R-2) \cdot w_3= w_2$$
|
|
103
|
+
* @param current $$D - w_6 \cdot (R - 2)$$
|
|
97
104
|
* @return difficulty
|
|
98
105
|
*/
|
|
99
106
|
mean_reversion(init: number, current: number): number;
|
|
100
107
|
/**
|
|
101
108
|
* The formula used is :
|
|
102
|
-
* $$S^\prime_r(D,S,R) = S\cdot(e^{
|
|
109
|
+
* $$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)$$
|
|
103
110
|
* @param d Difficulty D \in [1,10]
|
|
104
111
|
* @param s Stability (interval when R=90%)
|
|
105
112
|
* @param r Retrievability (probability of recall)
|
|
113
|
+
* @param g Grade (Rating[0.again,1.hard,2.good,3.easy])
|
|
106
114
|
* @return S^\prime_r new stability after recall
|
|
107
115
|
*/
|
|
108
|
-
next_recall_stability(d: number, s: number, r: number): number;
|
|
116
|
+
next_recall_stability(d: number, s: number, r: number, g: Rating): number;
|
|
109
117
|
/**
|
|
110
118
|
* The formula used is :
|
|
111
|
-
* $$S^\prime_f(D,S,R) =
|
|
119
|
+
* $$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)}.$$
|
|
112
120
|
* @param d Difficulty D \in [1,10]
|
|
113
121
|
* @param s Stability (interval when R=90%)
|
|
114
122
|
* @param r Retrievability (probability of recall)
|
|
@@ -117,7 +125,7 @@ export default class FSRS {
|
|
|
117
125
|
next_forget_stability(d: number, s: number, r: number): number;
|
|
118
126
|
/**
|
|
119
127
|
* The formula used is :
|
|
120
|
-
* $$R(t,S) =
|
|
128
|
+
* $$R(t,S) = (1 + \frac{t}{9 \cdot S})^{-1},$$
|
|
121
129
|
* @param t t days since the last review
|
|
122
130
|
* @param s Stability (interval when R=90%)
|
|
123
131
|
* @return r Retrievability (probability of recall)
|
package/dist/fsrs.js
CHANGED
|
@@ -3,25 +3,30 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FSRS = void 0;
|
|
6
7
|
const seedrandom_1 = __importDefault(require("seedrandom"));
|
|
7
8
|
const index_1 = require("./index");
|
|
8
9
|
const help_1 = require("./help");
|
|
10
|
+
const models_1 = require("./models");
|
|
11
|
+
// Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-v4
|
|
9
12
|
class FSRS {
|
|
10
13
|
param;
|
|
11
14
|
intervalModifier;
|
|
12
15
|
seed;
|
|
13
16
|
constructor(param) {
|
|
14
|
-
this.param =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
this.param = (0, index_1.generatorParameters)(param);
|
|
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);
|
|
17
21
|
}
|
|
18
22
|
repeat = (card, now) => {
|
|
19
23
|
card = {
|
|
20
24
|
...card,
|
|
25
|
+
last_review: card.last_review ? (0, help_1.fixDate)(card.last_review) : undefined,
|
|
21
26
|
};
|
|
22
27
|
now = new Date((0, help_1.fixDate)(now));
|
|
23
28
|
card.elapsed_days =
|
|
24
|
-
card.state ===
|
|
29
|
+
card.state === models_1.State.New ? 0 : now.diff(card.last_review, "days"); //相距时间
|
|
25
30
|
card.last_review = now; // 上次复习时间
|
|
26
31
|
card.reps += 1;
|
|
27
32
|
const s = new index_1.SchedulingCard(card);
|
|
@@ -29,33 +34,33 @@ class FSRS {
|
|
|
29
34
|
this.seed = String(card.last_review.getTime()) + String(card.elapsed_days);
|
|
30
35
|
let easy_interval, good_interval, hard_interval;
|
|
31
36
|
switch (card.state) {
|
|
32
|
-
case
|
|
37
|
+
case models_1.State.New:
|
|
33
38
|
this.init_ds(s);
|
|
34
39
|
s.again.due = now.scheduler(1);
|
|
35
40
|
s.hard.due = now.scheduler(5);
|
|
36
41
|
s.good.due = now.scheduler(10);
|
|
37
|
-
easy_interval = this.next_interval(s.easy.stability
|
|
42
|
+
easy_interval = this.next_interval(s.easy.stability);
|
|
38
43
|
s.easy.scheduled_days = easy_interval;
|
|
39
44
|
s.easy.due = now.scheduler(easy_interval, true);
|
|
40
45
|
break;
|
|
41
|
-
case
|
|
42
|
-
case
|
|
46
|
+
case models_1.State.Learning:
|
|
47
|
+
case models_1.State.Relearning:
|
|
43
48
|
hard_interval = 0;
|
|
44
49
|
good_interval = this.next_interval(s.good.stability);
|
|
45
|
-
easy_interval = Math.max(this.next_interval(s.easy.stability
|
|
50
|
+
easy_interval = Math.max(this.next_interval(s.easy.stability), good_interval + 1);
|
|
46
51
|
s.schedule(now, hard_interval, good_interval, easy_interval);
|
|
47
52
|
break;
|
|
48
|
-
case
|
|
53
|
+
case models_1.State.Review: {
|
|
49
54
|
const interval = card.elapsed_days;
|
|
50
55
|
const last_d = card.difficulty;
|
|
51
56
|
const last_s = card.stability;
|
|
52
57
|
const retrievability = this.current_retrievability(interval, last_s);
|
|
53
58
|
this.next_ds(s, last_d, last_s, retrievability);
|
|
54
|
-
hard_interval = this.next_interval(
|
|
59
|
+
hard_interval = this.next_interval(s.hard.stability);
|
|
55
60
|
good_interval = this.next_interval(s.good.stability);
|
|
56
61
|
hard_interval = Math.min(hard_interval, good_interval);
|
|
57
62
|
good_interval = Math.max(good_interval, hard_interval + 1);
|
|
58
|
-
easy_interval = Math.max(this.next_interval(s.easy.stability
|
|
63
|
+
easy_interval = Math.max(this.next_interval(s.easy.stability), good_interval + 1);
|
|
59
64
|
s.schedule(now, hard_interval, good_interval, easy_interval);
|
|
60
65
|
break;
|
|
61
66
|
}
|
|
@@ -63,21 +68,21 @@ class FSRS {
|
|
|
63
68
|
return s.record_log(card, now);
|
|
64
69
|
};
|
|
65
70
|
get_retrievability = (card, now) => {
|
|
66
|
-
if (card.state !==
|
|
71
|
+
if (card.state !== models_1.State.Review) {
|
|
67
72
|
return undefined;
|
|
68
73
|
}
|
|
69
74
|
const t = Math.max(now.diff(card.last_review, "days"), 0);
|
|
70
75
|
return ((this.current_retrievability(t, card.stability) * 100).toFixed(2) + "%");
|
|
71
76
|
};
|
|
72
77
|
init_ds(s) {
|
|
73
|
-
s.again.difficulty = this.init_difficulty(
|
|
74
|
-
s.again.stability = this.init_stability(
|
|
75
|
-
s.hard.difficulty = this.init_difficulty(
|
|
76
|
-
s.hard.stability = this.init_stability(
|
|
77
|
-
s.good.difficulty = this.init_difficulty(
|
|
78
|
-
s.good.stability = this.init_stability(
|
|
79
|
-
s.easy.difficulty = this.init_difficulty(
|
|
80
|
-
s.easy.stability = this.init_stability(
|
|
78
|
+
s.again.difficulty = this.init_difficulty(models_1.Rating.Again);
|
|
79
|
+
s.again.stability = this.init_stability(models_1.Rating.Again);
|
|
80
|
+
s.hard.difficulty = this.init_difficulty(models_1.Rating.Hard);
|
|
81
|
+
s.hard.stability = this.init_stability(models_1.Rating.Hard);
|
|
82
|
+
s.good.difficulty = this.init_difficulty(models_1.Rating.Good);
|
|
83
|
+
s.good.stability = this.init_stability(models_1.Rating.Good);
|
|
84
|
+
s.easy.difficulty = this.init_difficulty(models_1.Rating.Easy);
|
|
85
|
+
s.easy.stability = this.init_stability(models_1.Rating.Easy);
|
|
81
86
|
}
|
|
82
87
|
/**
|
|
83
88
|
*
|
|
@@ -87,34 +92,35 @@ class FSRS {
|
|
|
87
92
|
* @param retrievability Retrievability
|
|
88
93
|
*/
|
|
89
94
|
next_ds(s, last_d, last_s, retrievability) {
|
|
90
|
-
s.again.difficulty = this.next_difficulty(last_d,
|
|
95
|
+
s.again.difficulty = this.next_difficulty(last_d, models_1.Rating.Again);
|
|
91
96
|
s.again.stability = this.next_forget_stability(s.again.difficulty, last_s, retrievability);
|
|
92
|
-
s.hard.difficulty = this.next_difficulty(last_d,
|
|
93
|
-
s.hard.stability = this.next_recall_stability(s.hard.difficulty, last_s, retrievability);
|
|
94
|
-
s.good.difficulty = this.next_difficulty(last_d,
|
|
95
|
-
s.good.stability = this.next_recall_stability(s.good.difficulty, last_s, retrievability);
|
|
96
|
-
s.easy.difficulty = this.next_difficulty(last_d,
|
|
97
|
-
s.easy.stability = this.next_recall_stability(s.easy.difficulty, last_s, retrievability);
|
|
97
|
+
s.hard.difficulty = this.next_difficulty(last_d, models_1.Rating.Hard);
|
|
98
|
+
s.hard.stability = this.next_recall_stability(s.hard.difficulty, last_s, retrievability, models_1.Rating.Hard);
|
|
99
|
+
s.good.difficulty = this.next_difficulty(last_d, models_1.Rating.Good);
|
|
100
|
+
s.good.stability = this.next_recall_stability(s.good.difficulty, last_s, retrievability, models_1.Rating.Good);
|
|
101
|
+
s.easy.difficulty = this.next_difficulty(last_d, models_1.Rating.Easy);
|
|
102
|
+
s.easy.stability = this.next_recall_stability(s.easy.difficulty, last_s, retrievability, models_1.Rating.Easy);
|
|
98
103
|
}
|
|
99
104
|
/**
|
|
100
105
|
* The formula used is :
|
|
101
|
-
* $$S_0(G) =
|
|
106
|
+
* $$S_0(G) = w_{G-1}$$
|
|
102
107
|
* $$\max \{S_0,0.1\}$$
|
|
103
|
-
* @param g Grade (rating at Anki) [
|
|
108
|
+
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
104
109
|
* @return Stability (interval when R=90%)
|
|
105
110
|
*/
|
|
106
111
|
init_stability(g) {
|
|
107
|
-
return Math.max(this.param.w[
|
|
112
|
+
return Math.max(this.param.w[g - 1], 0.1);
|
|
108
113
|
}
|
|
109
114
|
/**
|
|
110
115
|
* The formula used is :
|
|
111
|
-
* $$D_0(G) =
|
|
116
|
+
* $$D_0(G) = w_4 - (G-3) \cdot w_5$$
|
|
112
117
|
* $$\min \{\max \{D_0(G),1\},10\}$$
|
|
113
|
-
*
|
|
118
|
+
* where the D_0(3)=w_4 when the first rating is good.
|
|
119
|
+
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
114
120
|
* @return Difficulty D \in [1,10]
|
|
115
121
|
*/
|
|
116
122
|
init_difficulty(g) {
|
|
117
|
-
return Math.min(Math.max(this.param.w[
|
|
123
|
+
return Math.min(Math.max(this.param.w[4] - (g - 3) * this.param.w[5], 1), 10);
|
|
118
124
|
}
|
|
119
125
|
apply_fuzz(ivl) {
|
|
120
126
|
if (!this.param.enable_fuzz || ivl < 2.5)
|
|
@@ -126,21 +132,26 @@ class FSRS {
|
|
|
126
132
|
const max_ivl = Math.round(ivl * 1.05 + 1);
|
|
127
133
|
return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl);
|
|
128
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Ref:
|
|
137
|
+
* constructor(param: Partial<FSRSParameters>)
|
|
138
|
+
* this.intervalModifier = 9 * (1 / this.param.request_retention - 1);
|
|
139
|
+
*/
|
|
129
140
|
next_interval(s) {
|
|
130
141
|
const newInterval = this.apply_fuzz(s * this.intervalModifier);
|
|
131
142
|
return Math.min(Math.max(Math.round(newInterval), 1), this.param.maximum_interval);
|
|
132
143
|
}
|
|
133
144
|
/**
|
|
134
145
|
* The formula used is :
|
|
135
|
-
* $$next_d = D
|
|
146
|
+
* $$next_d = D - w_6 \cdot (R - 2)$$
|
|
136
147
|
* $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot next_d$$
|
|
137
148
|
* @param d
|
|
138
|
-
* @param g Grade (
|
|
149
|
+
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
139
150
|
* @return next_D
|
|
140
151
|
*/
|
|
141
152
|
next_difficulty(d, g) {
|
|
142
|
-
const next_d = d
|
|
143
|
-
return this.constrain_difficulty(this.mean_reversion(this.param.w[
|
|
153
|
+
const next_d = d - this.param.w[6] * (g - 3);
|
|
154
|
+
return this.constrain_difficulty(this.mean_reversion(this.param.w[4], next_d));
|
|
144
155
|
}
|
|
145
156
|
/**
|
|
146
157
|
* The formula used is :
|
|
@@ -151,53 +162,58 @@ class FSRS {
|
|
|
151
162
|
}
|
|
152
163
|
/**
|
|
153
164
|
* The formula used is :
|
|
154
|
-
* $$
|
|
155
|
-
* @param init $$w_2 : D_0(
|
|
156
|
-
* @param current $$D
|
|
165
|
+
* $$w_7 \cdot init +(1 - w_7) \cdot current$$
|
|
166
|
+
* @param init $$w_2 : D_0(3) = w_2 + (R-2) \cdot w_3= w_2$$
|
|
167
|
+
* @param current $$D - w_6 \cdot (R - 2)$$
|
|
157
168
|
* @return difficulty
|
|
158
169
|
*/
|
|
159
170
|
mean_reversion(init, current) {
|
|
160
|
-
return this.param.w[
|
|
171
|
+
return this.param.w[7] * init + (1 - this.param.w[7]) * current;
|
|
161
172
|
}
|
|
162
173
|
/**
|
|
163
174
|
* The formula used is :
|
|
164
|
-
* $$S^\prime_r(D,S,R) = S\cdot(e^{
|
|
175
|
+
* $$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)$$
|
|
165
176
|
* @param d Difficulty D \in [1,10]
|
|
166
177
|
* @param s Stability (interval when R=90%)
|
|
167
178
|
* @param r Retrievability (probability of recall)
|
|
179
|
+
* @param g Grade (Rating[0.again,1.hard,2.good,3.easy])
|
|
168
180
|
* @return S^\prime_r new stability after recall
|
|
169
181
|
*/
|
|
170
|
-
next_recall_stability(d, s, r) {
|
|
182
|
+
next_recall_stability(d, s, r, g) {
|
|
183
|
+
const hard_penalty = models_1.Rating.Hard === g ? this.param.w[15] : 1;
|
|
184
|
+
const easy_bound = models_1.Rating.Easy === g ? this.param.w[16] : 1;
|
|
171
185
|
return (s *
|
|
172
186
|
(1 +
|
|
173
|
-
Math.exp(this.param.w[
|
|
187
|
+
Math.exp(this.param.w[8]) *
|
|
174
188
|
(11 - d) *
|
|
175
|
-
Math.pow(s, this.param.w[
|
|
176
|
-
(Math.exp((1 - r) * this.param.w[
|
|
189
|
+
Math.pow(s, -this.param.w[9]) *
|
|
190
|
+
(Math.exp((1 - r) * this.param.w[10]) - 1) *
|
|
191
|
+
hard_penalty *
|
|
192
|
+
easy_bound));
|
|
177
193
|
}
|
|
178
194
|
/**
|
|
179
195
|
* The formula used is :
|
|
180
|
-
* $$S^\prime_f(D,S,R) =
|
|
196
|
+
* $$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)}.$$
|
|
181
197
|
* @param d Difficulty D \in [1,10]
|
|
182
198
|
* @param s Stability (interval when R=90%)
|
|
183
199
|
* @param r Retrievability (probability of recall)
|
|
184
200
|
* @return S^\prime_f new stability after forgetting
|
|
185
201
|
*/
|
|
186
202
|
next_forget_stability(d, s, r) {
|
|
187
|
-
return (this.param.w[
|
|
188
|
-
Math.pow(d, this.param.w[
|
|
189
|
-
Math.pow(s, this.param.w[
|
|
190
|
-
Math.exp((1 - r) * this.param.w[
|
|
203
|
+
return (this.param.w[11] *
|
|
204
|
+
Math.pow(d, -this.param.w[12]) *
|
|
205
|
+
(Math.pow(s + 1, this.param.w[13]) - 1) *
|
|
206
|
+
Math.exp((1 - r) * this.param.w[14]));
|
|
191
207
|
}
|
|
192
208
|
/**
|
|
193
209
|
* The formula used is :
|
|
194
|
-
* $$R(t,S) =
|
|
210
|
+
* $$R(t,S) = (1 + \frac{t}{9 \cdot S})^{-1},$$
|
|
195
211
|
* @param t t days since the last review
|
|
196
212
|
* @param s Stability (interval when R=90%)
|
|
197
213
|
* @return r Retrievability (probability of recall)
|
|
198
214
|
*/
|
|
199
215
|
current_retrievability(t, s) {
|
|
200
|
-
return Math.
|
|
216
|
+
return Math.pow(1 + t / (9 * s), -1);
|
|
201
217
|
}
|
|
202
218
|
}
|
|
203
|
-
exports.
|
|
219
|
+
exports.FSRS = FSRS;
|
package/dist/help.d.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export type int = number & {
|
|
3
|
-
__int__: void;
|
|
4
|
-
};
|
|
5
|
-
export type double = number & {
|
|
6
|
-
__double__: void;
|
|
7
|
-
};
|
|
1
|
+
import type { int, unit } from "./type";
|
|
8
2
|
declare global {
|
|
9
3
|
export interface Date {
|
|
10
4
|
scheduler(t: int, isDay?: boolean): Date;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,26 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { SchedulingCard } from "./scheduler";
|
|
4
|
-
import FSRSVersion from "./fsrs_version";
|
|
5
|
-
declare const fsrs: (param?: FSRSParameters) => FSRS;
|
|
6
|
-
declare const createEmptyCard: (now?: Date) => Card;
|
|
7
|
-
declare const generatorParameters: (props?: {
|
|
8
|
-
request_retention?: number;
|
|
9
|
-
maximum_interval?: number;
|
|
10
|
-
easy_bonus?: number;
|
|
11
|
-
hard_factor?: number;
|
|
12
|
-
w?: number[];
|
|
13
|
-
enable_fuzz?: boolean;
|
|
14
|
-
}) => {
|
|
15
|
-
request_retention: number;
|
|
16
|
-
maximum_interval: number;
|
|
17
|
-
easy_bonus: number;
|
|
18
|
-
hard_factor: number;
|
|
19
|
-
w: number[];
|
|
20
|
-
enable_fuzz: boolean;
|
|
21
|
-
};
|
|
22
|
-
export { fsrs, FSRSVersion, State, Rating, SchedulingCard, createEmptyCard, generatorParameters, };
|
|
23
|
-
export type { StateType, RatingType, ReviewLog, Card, SchedulingLog, FSRSParameters, };
|
|
24
|
-
export { default_request_retention, default_maximum_interval, default_easy_bonus, default_hard_factor, default_w, default_enable_fuzz, };
|
|
1
|
+
export { SchedulingCard } from "./scheduler";
|
|
2
|
+
export { default_request_retention, default_maximum_interval, default_w, default_enable_fuzz, FSRSVersion, generatorParameters, createEmptyCard, fsrs, envParams } from "./default";
|
|
25
3
|
export { date_scheduler, date_diff, formatDate, show_diff_message, } from "./help";
|
|
26
|
-
export type { int, double } from "./
|
|
4
|
+
export type { int, double } from "./type";
|
|
5
|
+
export type { FSRSParameters, Card, ReviewLog, StateType, RatingType, } from "./models";
|
|
6
|
+
export { State, Rating } from "./models";
|
|
7
|
+
export { FSRS } from "./fsrs";
|
package/dist/index.js
CHANGED
|
@@ -1,64 +1,25 @@
|
|
|
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
|
-
exports.
|
|
7
|
-
|
|
8
|
-
const models_1 = require("./models");
|
|
9
|
-
Object.defineProperty(exports, "default_easy_bonus", { enumerable: true, get: function () { return models_1.default_easy_bonus; } });
|
|
10
|
-
Object.defineProperty(exports, "default_enable_fuzz", { enumerable: true, get: function () { return models_1.default_enable_fuzz; } });
|
|
11
|
-
Object.defineProperty(exports, "default_hard_factor", { enumerable: true, get: function () { return models_1.default_hard_factor; } });
|
|
12
|
-
Object.defineProperty(exports, "default_maximum_interval", { enumerable: true, get: function () { return models_1.default_maximum_interval; } });
|
|
13
|
-
Object.defineProperty(exports, "default_request_retention", { enumerable: true, get: function () { return models_1.default_request_retention; } });
|
|
14
|
-
Object.defineProperty(exports, "default_w", { enumerable: true, get: function () { return models_1.default_w; } });
|
|
15
|
-
Object.defineProperty(exports, "Rating", { enumerable: true, get: function () { return models_1.Rating; } });
|
|
16
|
-
Object.defineProperty(exports, "State", { enumerable: true, get: function () { return models_1.State; } });
|
|
17
|
-
const scheduler_1 = require("./scheduler");
|
|
3
|
+
exports.FSRS = exports.Rating = exports.State = exports.show_diff_message = exports.formatDate = exports.date_diff = exports.date_scheduler = exports.envParams = exports.fsrs = exports.createEmptyCard = exports.generatorParameters = exports.FSRSVersion = exports.default_enable_fuzz = exports.default_w = exports.default_maximum_interval = exports.default_request_retention = exports.SchedulingCard = void 0;
|
|
4
|
+
var scheduler_1 = require("./scheduler");
|
|
18
5
|
Object.defineProperty(exports, "SchedulingCard", { enumerable: true, get: function () { return scheduler_1.SchedulingCard; } });
|
|
19
|
-
|
|
20
|
-
exports
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
};
|
|
24
|
-
exports.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
difficulty: 0,
|
|
30
|
-
elapsed_days: 0,
|
|
31
|
-
scheduled_days: 0,
|
|
32
|
-
reps: 0,
|
|
33
|
-
lapses: 0,
|
|
34
|
-
state: models_1.State.New,
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
exports.createEmptyCard = createEmptyCard;
|
|
38
|
-
const generatorParameters = (props) => {
|
|
39
|
-
if (!props) {
|
|
40
|
-
return {
|
|
41
|
-
request_retention: models_1.default_request_retention,
|
|
42
|
-
maximum_interval: models_1.default_maximum_interval,
|
|
43
|
-
easy_bonus: models_1.default_easy_bonus,
|
|
44
|
-
hard_factor: models_1.default_hard_factor,
|
|
45
|
-
w: models_1.default_w,
|
|
46
|
-
enable_fuzz: models_1.default_enable_fuzz,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
const { w, request_retention, hard_factor, maximum_interval, enable_fuzz, easy_bonus, } = props;
|
|
50
|
-
return {
|
|
51
|
-
request_retention: request_retention || models_1.default_request_retention,
|
|
52
|
-
maximum_interval: maximum_interval || models_1.default_maximum_interval,
|
|
53
|
-
easy_bonus: easy_bonus || models_1.default_easy_bonus,
|
|
54
|
-
hard_factor: hard_factor || models_1.default_hard_factor,
|
|
55
|
-
w: w || models_1.default_w,
|
|
56
|
-
enable_fuzz: enable_fuzz || models_1.default_enable_fuzz,
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
exports.generatorParameters = generatorParameters;
|
|
6
|
+
var default_1 = require("./default");
|
|
7
|
+
Object.defineProperty(exports, "default_request_retention", { enumerable: true, get: function () { return default_1.default_request_retention; } });
|
|
8
|
+
Object.defineProperty(exports, "default_maximum_interval", { enumerable: true, get: function () { return default_1.default_maximum_interval; } });
|
|
9
|
+
Object.defineProperty(exports, "default_w", { enumerable: true, get: function () { return default_1.default_w; } });
|
|
10
|
+
Object.defineProperty(exports, "default_enable_fuzz", { enumerable: true, get: function () { return default_1.default_enable_fuzz; } });
|
|
11
|
+
Object.defineProperty(exports, "FSRSVersion", { enumerable: true, get: function () { return default_1.FSRSVersion; } });
|
|
12
|
+
Object.defineProperty(exports, "generatorParameters", { enumerable: true, get: function () { return default_1.generatorParameters; } });
|
|
13
|
+
Object.defineProperty(exports, "createEmptyCard", { enumerable: true, get: function () { return default_1.createEmptyCard; } });
|
|
14
|
+
Object.defineProperty(exports, "fsrs", { enumerable: true, get: function () { return default_1.fsrs; } });
|
|
15
|
+
Object.defineProperty(exports, "envParams", { enumerable: true, get: function () { return default_1.envParams; } });
|
|
60
16
|
var help_1 = require("./help");
|
|
61
17
|
Object.defineProperty(exports, "date_scheduler", { enumerable: true, get: function () { return help_1.date_scheduler; } });
|
|
62
18
|
Object.defineProperty(exports, "date_diff", { enumerable: true, get: function () { return help_1.date_diff; } });
|
|
63
19
|
Object.defineProperty(exports, "formatDate", { enumerable: true, get: function () { return help_1.formatDate; } });
|
|
64
20
|
Object.defineProperty(exports, "show_diff_message", { enumerable: true, get: function () { return help_1.show_diff_message; } });
|
|
21
|
+
var models_1 = require("./models");
|
|
22
|
+
Object.defineProperty(exports, "State", { enumerable: true, get: function () { return models_1.State; } });
|
|
23
|
+
Object.defineProperty(exports, "Rating", { enumerable: true, get: function () { return models_1.Rating; } });
|
|
24
|
+
var fsrs_1 = require("./fsrs");
|
|
25
|
+
Object.defineProperty(exports, "FSRS", { enumerable: true, get: function () { return fsrs_1.FSRS; } });
|
package/dist/models.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type StateType = "
|
|
1
|
+
export type StateType = "New" | "Learning" | "Review" | "Relearning";
|
|
2
2
|
export declare enum State {
|
|
3
3
|
New = 0,
|
|
4
4
|
Learning = 1,
|
|
@@ -7,10 +7,10 @@ export declare enum State {
|
|
|
7
7
|
}
|
|
8
8
|
export type RatingType = "Again" | "Hard" | "Good" | "Easy";
|
|
9
9
|
export declare enum Rating {
|
|
10
|
-
Again =
|
|
11
|
-
Hard =
|
|
12
|
-
Good =
|
|
13
|
-
Easy =
|
|
10
|
+
Again = 1,
|
|
11
|
+
Hard = 2,
|
|
12
|
+
Good = 3,
|
|
13
|
+
Easy = 4
|
|
14
14
|
}
|
|
15
15
|
export interface ReviewLog {
|
|
16
16
|
rating: Rating;
|
|
@@ -30,23 +30,9 @@ export interface Card {
|
|
|
30
30
|
state: State;
|
|
31
31
|
last_review?: Date;
|
|
32
32
|
}
|
|
33
|
-
export interface SchedulingLog {
|
|
34
|
-
[key: number]: {
|
|
35
|
-
card: Card;
|
|
36
|
-
log: ReviewLog;
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
33
|
export interface FSRSParameters {
|
|
40
34
|
request_retention: number;
|
|
41
35
|
maximum_interval: number;
|
|
42
|
-
easy_bonus: number;
|
|
43
|
-
hard_factor: number;
|
|
44
36
|
w: number[];
|
|
45
37
|
enable_fuzz: boolean;
|
|
46
38
|
}
|
|
47
|
-
export declare const default_request_retention = 0.9;
|
|
48
|
-
export declare const default_maximum_interval = 36500;
|
|
49
|
-
export declare const default_easy_bonus = 1.3;
|
|
50
|
-
export declare const default_hard_factor = 1.2;
|
|
51
|
-
export declare const default_w: number[];
|
|
52
|
-
export declare const default_enable_fuzz = false;
|
package/dist/models.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.Rating = exports.State = void 0;
|
|
4
4
|
var State;
|
|
5
5
|
(function (State) {
|
|
6
6
|
State[State["New"] = 0] = "New";
|
|
@@ -10,16 +10,8 @@ var State;
|
|
|
10
10
|
})(State || (exports.State = State = {}));
|
|
11
11
|
var Rating;
|
|
12
12
|
(function (Rating) {
|
|
13
|
-
Rating[Rating["Again"] =
|
|
14
|
-
Rating[Rating["Hard"] =
|
|
15
|
-
Rating[Rating["Good"] =
|
|
16
|
-
Rating[Rating["Easy"] =
|
|
13
|
+
Rating[Rating["Again"] = 1] = "Again";
|
|
14
|
+
Rating[Rating["Hard"] = 2] = "Hard";
|
|
15
|
+
Rating[Rating["Good"] = 3] = "Good";
|
|
16
|
+
Rating[Rating["Easy"] = 4] = "Easy";
|
|
17
17
|
})(Rating || (exports.Rating = Rating = {}));
|
|
18
|
-
exports.default_request_retention = 0.9;
|
|
19
|
-
exports.default_maximum_interval = 36500;
|
|
20
|
-
exports.default_easy_bonus = 1.3;
|
|
21
|
-
exports.default_hard_factor = 1.2;
|
|
22
|
-
exports.default_w = [
|
|
23
|
-
1, 1, 5, -0.5, -0.5, 0.2, 1.4, -0.12, 0.8, 2, -0.2, 0.2, 1,
|
|
24
|
-
];
|
|
25
|
-
exports.default_enable_fuzz = false;
|
package/dist/scheduler.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare class SchedulingCard {
|
|
|
9
9
|
update_state(state: State): this;
|
|
10
10
|
schedule(now: Date, hard_interval: number, good_interval: number, easy_interval: number): SchedulingCard;
|
|
11
11
|
record_log(card: Card, now: Date): {
|
|
12
|
-
|
|
12
|
+
1: {
|
|
13
13
|
card: Card;
|
|
14
14
|
log: {
|
|
15
15
|
rating: Rating;
|
|
@@ -19,7 +19,7 @@ export declare class SchedulingCard {
|
|
|
19
19
|
review: Date;
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
|
-
|
|
22
|
+
2: {
|
|
23
23
|
card: Card;
|
|
24
24
|
log: {
|
|
25
25
|
rating: Rating;
|
|
@@ -29,7 +29,7 @@ export declare class SchedulingCard {
|
|
|
29
29
|
review: Date;
|
|
30
30
|
};
|
|
31
31
|
};
|
|
32
|
-
|
|
32
|
+
3: {
|
|
33
33
|
card: Card;
|
|
34
34
|
log: {
|
|
35
35
|
rating: Rating;
|
|
@@ -39,7 +39,7 @@ export declare class SchedulingCard {
|
|
|
39
39
|
review: Date;
|
|
40
40
|
};
|
|
41
41
|
};
|
|
42
|
-
|
|
42
|
+
4: {
|
|
43
43
|
card: Card;
|
|
44
44
|
log: {
|
|
45
45
|
rating: Rating;
|
package/dist/type.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type unit = "days" | "minutes";
|
|
2
|
+
export type int = number & {
|
|
3
|
+
__int__: void;
|
|
4
|
+
};
|
|
5
|
+
export type double = number & {
|
|
6
|
+
__double__: void;
|
|
7
|
+
};
|
|
8
|
+
export interface EnvParams {
|
|
9
|
+
FSRS_REQUEST_RETENTION: number;
|
|
10
|
+
FSRS_MAXIMUM_INTERVAL: number;
|
|
11
|
+
FSRS_W?: number[];
|
|
12
|
+
FSRS_ENABLE_FUZZ?: boolean;
|
|
13
|
+
}
|
package/dist/type.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-fsrs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
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",
|
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
"FSRS"
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
|
+
"dotenv": "^16.3.1",
|
|
13
14
|
"seedrandom": "^3.0.5"
|
|
14
15
|
},
|
|
15
16
|
"devDependencies": {
|
|
16
|
-
"@types/node": "^20.6.2",
|
|
17
17
|
"@types/jest": "^29.5.5",
|
|
18
|
+
"@types/node": "^20.6.2",
|
|
18
19
|
"@types/seedrandom": "^3.0.5",
|
|
19
20
|
"@typescript-eslint/eslint-plugin": "^6.7.0",
|
|
20
21
|
"@typescript-eslint/parser": "^6.7.0",
|
package/dist/fsrs_version.d.ts
DELETED
package/dist/fsrs_version.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
4
|
-
// @ts-ignore
|
|
5
|
-
const package_json_1 = require("../../package.json");
|
|
6
|
-
const FSRSVersion = package_json_1.version;
|
|
7
|
-
exports.default = FSRSVersion;
|