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/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 Error(`Invalid rating:[${value}]`);
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 Error(`Invalid rating:[${value}]`);
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 Error(`Invalid state:[${value}]`);
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 Error(`Invalid state:[${value}]`);
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 Error(`Invalid date:[${value}]`);
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 Error(`Invalid date:[${value}]`);
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 Error("Invalid date");
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 Error(`Invalid step value: ${step}`);
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 Error(`Invalid step unit: ${step}, expected m/h/d`);
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 < 0 || grade > 4) {
368
- throw new Error(`Invalid grade "${grade}",expected 1-4`);
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.3.3";
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(roundTo(value, 8), 0.01, W17_W18_Ceiling);
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 Error(`Non-finite or NaN value in parameters ${parameters}`);
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 Error(
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 Error("Requested retention rate should be in the range (0,1]");
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 Error(`Invalid delta_t "${t}"`);
972
+ throw new FSRSValidationError(`Invalid delta_t "${t}"`);
948
973
  }
949
974
  if (g < 0 || g > 4) {
950
- throw new Error(`Invalid grade "${g}"`);
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 Error(
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 Error("reschedule: state is required for manual rating");
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 Error("reschedule: due is required for manual rating");
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
- if (afterHandler && typeof afterHandler === "function") {
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 Error("Cannot review a manual rating");
1707
+ throw new FSRSValidationError("Cannot review a manual rating");
1680
1708
  }
1681
1709
  const recordLogItem = instance.review(g);
1682
- if (afterHandler && typeof afterHandler === "function") {
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 Error("Cannot rollback a manual rating");
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
- if (afterHandler && typeof afterHandler === "function") {
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
- if (afterHandler && typeof afterHandler === "function") {
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
  }