ts-fsrs 5.3.3 → 5.4.1
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/CHANGELOG.md +481 -0
- package/dist/index.cjs +62 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +62 -54
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +64 -54
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/index.umd.js
CHANGED
|
@@ -4,6 +4,23 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.FSRS = {}));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
|
+
class FSRSError extends Error {
|
|
8
|
+
constructor(message = "FSRS Error") {
|
|
9
|
+
var _a;
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "FSRSError";
|
|
12
|
+
(_a = Error.captureStackTrace) == null ? void 0 : _a.call(Error, this, FSRSError);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
class FSRSValidationError extends FSRSError {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
var _a;
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "FSRSValidationError";
|
|
20
|
+
(_a = Error.captureStackTrace) == null ? void 0 : _a.call(Error, this, FSRSValidationError);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
7
24
|
var State = /* @__PURE__ */ ((State2) => {
|
|
8
25
|
State2[State2["New"] = 0] = "New";
|
|
9
26
|
State2[State2["Learning"] = 1] = "Learning";
|
|
@@ -53,13 +70,13 @@
|
|
|
53
70
|
const restOfString = value.slice(1).toLowerCase();
|
|
54
71
|
const ret = Rating[`${firstLetter}${restOfString}`];
|
|
55
72
|
if (ret === void 0) {
|
|
56
|
-
throw new
|
|
73
|
+
throw new FSRSValidationError(`Invalid rating:[${value}]`);
|
|
57
74
|
}
|
|
58
75
|
return ret;
|
|
59
76
|
} else if (typeof value === "number") {
|
|
60
77
|
return value;
|
|
61
78
|
}
|
|
62
|
-
throw new
|
|
79
|
+
throw new FSRSValidationError(`Invalid rating:[${value}]`);
|
|
63
80
|
}
|
|
64
81
|
static state(value) {
|
|
65
82
|
if (typeof value === "string") {
|
|
@@ -67,13 +84,13 @@
|
|
|
67
84
|
const restOfString = value.slice(1).toLowerCase();
|
|
68
85
|
const ret = State[`${firstLetter}${restOfString}`];
|
|
69
86
|
if (ret === void 0) {
|
|
70
|
-
throw new
|
|
87
|
+
throw new FSRSValidationError(`Invalid state:[${value}]`);
|
|
71
88
|
}
|
|
72
89
|
return ret;
|
|
73
90
|
} else if (typeof value === "number") {
|
|
74
91
|
return value;
|
|
75
92
|
}
|
|
76
|
-
throw new
|
|
93
|
+
throw new FSRSValidationError(`Invalid state:[${value}]`);
|
|
77
94
|
}
|
|
78
95
|
static time(value) {
|
|
79
96
|
if (value instanceof Date) {
|
|
@@ -87,12 +104,12 @@
|
|
|
87
104
|
if (!Number.isNaN(timestamp)) {
|
|
88
105
|
return new Date(timestamp);
|
|
89
106
|
} else {
|
|
90
|
-
throw new
|
|
107
|
+
throw new FSRSValidationError(`Invalid date:[${value}]`);
|
|
91
108
|
}
|
|
92
109
|
} else if (typeof value === "number") {
|
|
93
110
|
return new Date(value);
|
|
94
111
|
}
|
|
95
|
-
throw new
|
|
112
|
+
throw new FSRSValidationError(`Invalid date:[${value}]`);
|
|
96
113
|
}
|
|
97
114
|
static review_log(log) {
|
|
98
115
|
return __spreadProps$2(__spreadValues$2({}, log), {
|
|
@@ -127,7 +144,7 @@
|
|
|
127
144
|
}
|
|
128
145
|
function date_diff(now, pre, unit) {
|
|
129
146
|
if (!now || !pre) {
|
|
130
|
-
throw new
|
|
147
|
+
throw new FSRSValidationError("Invalid date");
|
|
131
148
|
}
|
|
132
149
|
const diff = TypeConvert.time(now).getTime() - TypeConvert.time(pre).getTime();
|
|
133
150
|
let r = 0;
|
|
@@ -253,7 +270,7 @@
|
|
|
253
270
|
const unit = step.slice(-1);
|
|
254
271
|
const value = parseInt(step.slice(0, -1), 10);
|
|
255
272
|
if (Number.isNaN(value) || !Number.isFinite(value) || value < 0) {
|
|
256
|
-
throw new
|
|
273
|
+
throw new FSRSValidationError(`Invalid step value: ${step}`);
|
|
257
274
|
}
|
|
258
275
|
switch (unit) {
|
|
259
276
|
case "m":
|
|
@@ -263,7 +280,9 @@
|
|
|
263
280
|
case "d":
|
|
264
281
|
return value * 1440;
|
|
265
282
|
default:
|
|
266
|
-
throw new
|
|
283
|
+
throw new FSRSValidationError(
|
|
284
|
+
`Invalid step unit: ${step}, expected m/h/d`
|
|
285
|
+
);
|
|
267
286
|
}
|
|
268
287
|
};
|
|
269
288
|
const BasicLearningStepsStrategy = (params, state, cur_step) => {
|
|
@@ -364,8 +383,8 @@
|
|
|
364
383
|
this.init();
|
|
365
384
|
}
|
|
366
385
|
checkGrade(grade) {
|
|
367
|
-
if (!Number.isFinite(grade) || grade <
|
|
368
|
-
throw new
|
|
386
|
+
if (!Number.isFinite(grade) || grade < 1 || grade > 4) {
|
|
387
|
+
throw new FSRSValidationError(`Invalid grade "${grade}",expected 1-4`);
|
|
369
388
|
}
|
|
370
389
|
}
|
|
371
390
|
init() {
|
|
@@ -511,7 +530,7 @@
|
|
|
511
530
|
return prng;
|
|
512
531
|
}
|
|
513
532
|
|
|
514
|
-
const version="5.
|
|
533
|
+
const version="5.4.1";
|
|
515
534
|
|
|
516
535
|
const default_request_retention = 0.9;
|
|
517
536
|
const default_maximum_interval = 36500;
|
|
@@ -591,7 +610,11 @@
|
|
|
591
610
|
const w13 = clamp(parameters[13] || 0, clip[13][0], clip[13][1]);
|
|
592
611
|
const w14 = clamp(parameters[14] || 0, clip[14][0], clip[14][1]);
|
|
593
612
|
const value = -(Math.log(w11) + Math.log(Math.pow(2, w13) - 1) + w14 * 0.3) / numRelearningSteps;
|
|
594
|
-
const w17_w18_ceiling = clamp(
|
|
613
|
+
const w17_w18_ceiling = clamp(
|
|
614
|
+
roundTo(Math.sqrt(Math.max(value, 0)), 8),
|
|
615
|
+
0.01,
|
|
616
|
+
W17_W18_Ceiling
|
|
617
|
+
);
|
|
595
618
|
if (clip[17]) clip[17] = [clip[17][0], w17_w18_ceiling];
|
|
596
619
|
if (clip[18]) clip[18] = [clip[18][0], w17_w18_ceiling];
|
|
597
620
|
}
|
|
@@ -600,13 +623,13 @@
|
|
|
600
623
|
);
|
|
601
624
|
};
|
|
602
625
|
const checkParameters = (parameters) => {
|
|
603
|
-
const invalid = parameters.find(
|
|
604
|
-
(param) => !Number.isFinite(param) && !Number.isNaN(param)
|
|
605
|
-
);
|
|
626
|
+
const invalid = parameters.find((param) => !Number.isFinite(param));
|
|
606
627
|
if (invalid !== void 0) {
|
|
607
|
-
throw
|
|
628
|
+
throw new FSRSValidationError(
|
|
629
|
+
`Non-finite or NaN value in parameters ${parameters}`
|
|
630
|
+
);
|
|
608
631
|
} else if (![17, 19, 21].includes(parameters.length)) {
|
|
609
|
-
throw
|
|
632
|
+
throw new FSRSValidationError(
|
|
610
633
|
`Invalid parameter length: ${parameters.length}. Must be 17, 19 or 21 for FSRSv4, 5 and 6 respectively.`
|
|
611
634
|
);
|
|
612
635
|
}
|
|
@@ -736,7 +759,9 @@
|
|
|
736
759
|
*/
|
|
737
760
|
calculate_interval_modifier(request_retention) {
|
|
738
761
|
if (request_retention <= 0 || request_retention > 1) {
|
|
739
|
-
throw new
|
|
762
|
+
throw new FSRSValidationError(
|
|
763
|
+
"Requested retention rate should be in the range (0,1]"
|
|
764
|
+
);
|
|
740
765
|
}
|
|
741
766
|
const { decay, factor } = computeDecayFactor(this.param.w);
|
|
742
767
|
return roundTo((Math.pow(request_retention, 1 / decay) - 1) / factor, 8);
|
|
@@ -944,10 +969,10 @@
|
|
|
944
969
|
stability: 0
|
|
945
970
|
};
|
|
946
971
|
if (t < 0) {
|
|
947
|
-
throw new
|
|
972
|
+
throw new FSRSValidationError(`Invalid delta_t "${t}"`);
|
|
948
973
|
}
|
|
949
974
|
if (g < 0 || g > 4) {
|
|
950
|
-
throw new
|
|
975
|
+
throw new FSRSValidationError(`Invalid grade "${g}"`);
|
|
951
976
|
}
|
|
952
977
|
if (d === 0 && s === 0) {
|
|
953
978
|
return {
|
|
@@ -962,7 +987,7 @@
|
|
|
962
987
|
};
|
|
963
988
|
}
|
|
964
989
|
if (d < 1 || s < S_MIN) {
|
|
965
|
-
throw new
|
|
990
|
+
throw new FSRSValidationError(
|
|
966
991
|
`Invalid memory state { difficulty: ${d}, stability: ${s} }`
|
|
967
992
|
);
|
|
968
993
|
}
|
|
@@ -1367,7 +1392,9 @@
|
|
|
1367
1392
|
*/
|
|
1368
1393
|
handleManualRating(card, state, reviewed, elapsed_days, stability, difficulty, due) {
|
|
1369
1394
|
if (typeof state === "undefined") {
|
|
1370
|
-
throw new
|
|
1395
|
+
throw new FSRSValidationError(
|
|
1396
|
+
"reschedule: state is required for manual rating"
|
|
1397
|
+
);
|
|
1371
1398
|
}
|
|
1372
1399
|
let log;
|
|
1373
1400
|
let next_card;
|
|
@@ -1388,7 +1415,9 @@
|
|
|
1388
1415
|
next_card.last_review = reviewed;
|
|
1389
1416
|
} else {
|
|
1390
1417
|
if (typeof due === "undefined") {
|
|
1391
|
-
throw new
|
|
1418
|
+
throw new FSRSValidationError(
|
|
1419
|
+
"reschedule: due is required for manual rating"
|
|
1420
|
+
);
|
|
1392
1421
|
}
|
|
1393
1422
|
const scheduled_days = date_diff(due, reviewed, "days");
|
|
1394
1423
|
log = {
|
|
@@ -1497,6 +1526,9 @@
|
|
|
1497
1526
|
};
|
|
1498
1527
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
1499
1528
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1529
|
+
function applyAfterHandler(value, afterHandler) {
|
|
1530
|
+
return typeof afterHandler === "function" ? afterHandler(value) : value;
|
|
1531
|
+
}
|
|
1500
1532
|
class FSRS extends FSRSAlgorithm {
|
|
1501
1533
|
constructor(param) {
|
|
1502
1534
|
super(param);
|
|
@@ -1612,11 +1644,7 @@
|
|
|
1612
1644
|
repeat(card, now, afterHandler) {
|
|
1613
1645
|
const instance = this.getScheduler(card, now);
|
|
1614
1646
|
const recordLog = instance.preview();
|
|
1615
|
-
|
|
1616
|
-
return afterHandler(recordLog);
|
|
1617
|
-
} else {
|
|
1618
|
-
return recordLog;
|
|
1619
|
-
}
|
|
1647
|
+
return applyAfterHandler(recordLog, afterHandler);
|
|
1620
1648
|
}
|
|
1621
1649
|
/**
|
|
1622
1650
|
* Display the collection of cards and logs for the card scheduled at the current time, after applying a specific grade rating.
|
|
@@ -1676,14 +1704,10 @@
|
|
|
1676
1704
|
const instance = this.getScheduler(card, now);
|
|
1677
1705
|
const g = TypeConvert.rating(grade);
|
|
1678
1706
|
if (g === Rating.Manual) {
|
|
1679
|
-
throw new
|
|
1707
|
+
throw new FSRSValidationError("Cannot review a manual rating");
|
|
1680
1708
|
}
|
|
1681
1709
|
const recordLogItem = instance.review(g);
|
|
1682
|
-
|
|
1683
|
-
return afterHandler(recordLogItem);
|
|
1684
|
-
} else {
|
|
1685
|
-
return recordLogItem;
|
|
1686
|
-
}
|
|
1710
|
+
return applyAfterHandler(recordLogItem, afterHandler);
|
|
1687
1711
|
}
|
|
1688
1712
|
/**
|
|
1689
1713
|
* Get the retrievability of the card
|
|
@@ -1728,7 +1752,7 @@
|
|
|
1728
1752
|
const processedCard = TypeConvert.card(card);
|
|
1729
1753
|
const processedLog = TypeConvert.review_log(log);
|
|
1730
1754
|
if (processedLog.rating === Rating.Manual) {
|
|
1731
|
-
throw new
|
|
1755
|
+
throw new FSRSValidationError("Cannot rollback a manual rating");
|
|
1732
1756
|
}
|
|
1733
1757
|
let last_due;
|
|
1734
1758
|
let last_review;
|
|
@@ -1759,11 +1783,7 @@
|
|
|
1759
1783
|
state: processedLog.state,
|
|
1760
1784
|
last_review
|
|
1761
1785
|
});
|
|
1762
|
-
|
|
1763
|
-
return afterHandler(prevCard);
|
|
1764
|
-
} else {
|
|
1765
|
-
return prevCard;
|
|
1766
|
-
}
|
|
1786
|
+
return applyAfterHandler(prevCard, afterHandler);
|
|
1767
1787
|
}
|
|
1768
1788
|
/**
|
|
1769
1789
|
*
|
|
@@ -1845,11 +1865,7 @@
|
|
|
1845
1865
|
last_review: processedCard.last_review
|
|
1846
1866
|
});
|
|
1847
1867
|
const recordLogItem = { card: forget_card, log: forget_log };
|
|
1848
|
-
|
|
1849
|
-
return afterHandler(recordLogItem);
|
|
1850
|
-
} else {
|
|
1851
|
-
return recordLogItem;
|
|
1852
|
-
}
|
|
1868
|
+
return applyAfterHandler(recordLogItem, afterHandler);
|
|
1853
1869
|
}
|
|
1854
1870
|
/**
|
|
1855
1871
|
* Reschedules the current card and returns the rescheduled collections and reschedule item.
|
|
@@ -1916,15 +1932,9 @@
|
|
|
1916
1932
|
len ? collections[len - 1] : void 0,
|
|
1917
1933
|
updateMemoryState
|
|
1918
1934
|
);
|
|
1919
|
-
if (recordLogHandler && typeof recordLogHandler === "function") {
|
|
1920
|
-
return {
|
|
1921
|
-
collections: collections.map(recordLogHandler),
|
|
1922
|
-
reschedule_item: manual_item ? recordLogHandler(manual_item) : null
|
|
1923
|
-
};
|
|
1924
|
-
}
|
|
1925
1935
|
return {
|
|
1926
|
-
collections,
|
|
1927
|
-
reschedule_item: manual_item
|
|
1936
|
+
collections: typeof recordLogHandler === "function" ? collections.map(recordLogHandler) : collections,
|
|
1937
|
+
reschedule_item: manual_item ? applyAfterHandler(manual_item, recordLogHandler) : null
|
|
1928
1938
|
};
|
|
1929
1939
|
}
|
|
1930
1940
|
}
|