ts-fsrs 5.3.3 → 5.4.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/CHANGELOG.md +475 -0
- package/dist/index.cjs +57 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +57 -53
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +59 -53
- 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.0";
|
|
515
534
|
|
|
516
535
|
const default_request_retention = 0.9;
|
|
517
536
|
const default_maximum_interval = 36500;
|
|
@@ -600,13 +619,13 @@
|
|
|
600
619
|
);
|
|
601
620
|
};
|
|
602
621
|
const checkParameters = (parameters) => {
|
|
603
|
-
const invalid = parameters.find(
|
|
604
|
-
(param) => !Number.isFinite(param) && !Number.isNaN(param)
|
|
605
|
-
);
|
|
622
|
+
const invalid = parameters.find((param) => !Number.isFinite(param));
|
|
606
623
|
if (invalid !== void 0) {
|
|
607
|
-
throw
|
|
624
|
+
throw new FSRSValidationError(
|
|
625
|
+
`Non-finite or NaN value in parameters ${parameters}`
|
|
626
|
+
);
|
|
608
627
|
} else if (![17, 19, 21].includes(parameters.length)) {
|
|
609
|
-
throw
|
|
628
|
+
throw new FSRSValidationError(
|
|
610
629
|
`Invalid parameter length: ${parameters.length}. Must be 17, 19 or 21 for FSRSv4, 5 and 6 respectively.`
|
|
611
630
|
);
|
|
612
631
|
}
|
|
@@ -736,7 +755,9 @@
|
|
|
736
755
|
*/
|
|
737
756
|
calculate_interval_modifier(request_retention) {
|
|
738
757
|
if (request_retention <= 0 || request_retention > 1) {
|
|
739
|
-
throw new
|
|
758
|
+
throw new FSRSValidationError(
|
|
759
|
+
"Requested retention rate should be in the range (0,1]"
|
|
760
|
+
);
|
|
740
761
|
}
|
|
741
762
|
const { decay, factor } = computeDecayFactor(this.param.w);
|
|
742
763
|
return roundTo((Math.pow(request_retention, 1 / decay) - 1) / factor, 8);
|
|
@@ -944,10 +965,10 @@
|
|
|
944
965
|
stability: 0
|
|
945
966
|
};
|
|
946
967
|
if (t < 0) {
|
|
947
|
-
throw new
|
|
968
|
+
throw new FSRSValidationError(`Invalid delta_t "${t}"`);
|
|
948
969
|
}
|
|
949
970
|
if (g < 0 || g > 4) {
|
|
950
|
-
throw new
|
|
971
|
+
throw new FSRSValidationError(`Invalid grade "${g}"`);
|
|
951
972
|
}
|
|
952
973
|
if (d === 0 && s === 0) {
|
|
953
974
|
return {
|
|
@@ -962,7 +983,7 @@
|
|
|
962
983
|
};
|
|
963
984
|
}
|
|
964
985
|
if (d < 1 || s < S_MIN) {
|
|
965
|
-
throw new
|
|
986
|
+
throw new FSRSValidationError(
|
|
966
987
|
`Invalid memory state { difficulty: ${d}, stability: ${s} }`
|
|
967
988
|
);
|
|
968
989
|
}
|
|
@@ -1367,7 +1388,9 @@
|
|
|
1367
1388
|
*/
|
|
1368
1389
|
handleManualRating(card, state, reviewed, elapsed_days, stability, difficulty, due) {
|
|
1369
1390
|
if (typeof state === "undefined") {
|
|
1370
|
-
throw new
|
|
1391
|
+
throw new FSRSValidationError(
|
|
1392
|
+
"reschedule: state is required for manual rating"
|
|
1393
|
+
);
|
|
1371
1394
|
}
|
|
1372
1395
|
let log;
|
|
1373
1396
|
let next_card;
|
|
@@ -1388,7 +1411,9 @@
|
|
|
1388
1411
|
next_card.last_review = reviewed;
|
|
1389
1412
|
} else {
|
|
1390
1413
|
if (typeof due === "undefined") {
|
|
1391
|
-
throw new
|
|
1414
|
+
throw new FSRSValidationError(
|
|
1415
|
+
"reschedule: due is required for manual rating"
|
|
1416
|
+
);
|
|
1392
1417
|
}
|
|
1393
1418
|
const scheduled_days = date_diff(due, reviewed, "days");
|
|
1394
1419
|
log = {
|
|
@@ -1497,6 +1522,9 @@
|
|
|
1497
1522
|
};
|
|
1498
1523
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
1499
1524
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1525
|
+
function applyAfterHandler(value, afterHandler) {
|
|
1526
|
+
return typeof afterHandler === "function" ? afterHandler(value) : value;
|
|
1527
|
+
}
|
|
1500
1528
|
class FSRS extends FSRSAlgorithm {
|
|
1501
1529
|
constructor(param) {
|
|
1502
1530
|
super(param);
|
|
@@ -1612,11 +1640,7 @@
|
|
|
1612
1640
|
repeat(card, now, afterHandler) {
|
|
1613
1641
|
const instance = this.getScheduler(card, now);
|
|
1614
1642
|
const recordLog = instance.preview();
|
|
1615
|
-
|
|
1616
|
-
return afterHandler(recordLog);
|
|
1617
|
-
} else {
|
|
1618
|
-
return recordLog;
|
|
1619
|
-
}
|
|
1643
|
+
return applyAfterHandler(recordLog, afterHandler);
|
|
1620
1644
|
}
|
|
1621
1645
|
/**
|
|
1622
1646
|
* Display the collection of cards and logs for the card scheduled at the current time, after applying a specific grade rating.
|
|
@@ -1676,14 +1700,10 @@
|
|
|
1676
1700
|
const instance = this.getScheduler(card, now);
|
|
1677
1701
|
const g = TypeConvert.rating(grade);
|
|
1678
1702
|
if (g === Rating.Manual) {
|
|
1679
|
-
throw new
|
|
1703
|
+
throw new FSRSValidationError("Cannot review a manual rating");
|
|
1680
1704
|
}
|
|
1681
1705
|
const recordLogItem = instance.review(g);
|
|
1682
|
-
|
|
1683
|
-
return afterHandler(recordLogItem);
|
|
1684
|
-
} else {
|
|
1685
|
-
return recordLogItem;
|
|
1686
|
-
}
|
|
1706
|
+
return applyAfterHandler(recordLogItem, afterHandler);
|
|
1687
1707
|
}
|
|
1688
1708
|
/**
|
|
1689
1709
|
* Get the retrievability of the card
|
|
@@ -1728,7 +1748,7 @@
|
|
|
1728
1748
|
const processedCard = TypeConvert.card(card);
|
|
1729
1749
|
const processedLog = TypeConvert.review_log(log);
|
|
1730
1750
|
if (processedLog.rating === Rating.Manual) {
|
|
1731
|
-
throw new
|
|
1751
|
+
throw new FSRSValidationError("Cannot rollback a manual rating");
|
|
1732
1752
|
}
|
|
1733
1753
|
let last_due;
|
|
1734
1754
|
let last_review;
|
|
@@ -1759,11 +1779,7 @@
|
|
|
1759
1779
|
state: processedLog.state,
|
|
1760
1780
|
last_review
|
|
1761
1781
|
});
|
|
1762
|
-
|
|
1763
|
-
return afterHandler(prevCard);
|
|
1764
|
-
} else {
|
|
1765
|
-
return prevCard;
|
|
1766
|
-
}
|
|
1782
|
+
return applyAfterHandler(prevCard, afterHandler);
|
|
1767
1783
|
}
|
|
1768
1784
|
/**
|
|
1769
1785
|
*
|
|
@@ -1845,11 +1861,7 @@
|
|
|
1845
1861
|
last_review: processedCard.last_review
|
|
1846
1862
|
});
|
|
1847
1863
|
const recordLogItem = { card: forget_card, log: forget_log };
|
|
1848
|
-
|
|
1849
|
-
return afterHandler(recordLogItem);
|
|
1850
|
-
} else {
|
|
1851
|
-
return recordLogItem;
|
|
1852
|
-
}
|
|
1864
|
+
return applyAfterHandler(recordLogItem, afterHandler);
|
|
1853
1865
|
}
|
|
1854
1866
|
/**
|
|
1855
1867
|
* Reschedules the current card and returns the rescheduled collections and reschedule item.
|
|
@@ -1916,15 +1928,9 @@
|
|
|
1916
1928
|
len ? collections[len - 1] : void 0,
|
|
1917
1929
|
updateMemoryState
|
|
1918
1930
|
);
|
|
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
1931
|
return {
|
|
1926
|
-
collections,
|
|
1927
|
-
reschedule_item: manual_item
|
|
1932
|
+
collections: typeof recordLogHandler === "function" ? collections.map(recordLogHandler) : collections,
|
|
1933
|
+
reschedule_item: manual_item ? applyAfterHandler(manual_item, recordLogHandler) : null
|
|
1928
1934
|
};
|
|
1929
1935
|
}
|
|
1930
1936
|
}
|