ts-fsrs 5.3.2 → 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/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.2";
533
+ const version="5.4.0";
515
534
 
516
535
  const default_request_retention = 0.9;
517
536
  const default_maximum_interval = 36500;
@@ -582,27 +601,31 @@
582
601
  ];
583
602
 
584
603
  const clipParameters = (parameters, numRelearningSteps, enableShortTerm = default_enable_short_term) => {
585
- let w17_w18_ceiling = W17_W18_Ceiling;
586
- if (Math.max(0, numRelearningSteps) > 1) {
587
- const value = -(Math.log(parameters[11]) + Math.log(Math.pow(2, parameters[13]) - 1) + parameters[14] * 0.3) / numRelearningSteps;
588
- w17_w18_ceiling = clamp(+value.toFixed(8), 0.01, 2);
589
- }
590
- const clip = CLAMP_PARAMETERS(w17_w18_ceiling, enableShortTerm).slice(
604
+ const clip = CLAMP_PARAMETERS(W17_W18_Ceiling, enableShortTerm).slice(
591
605
  0,
592
606
  parameters.length
593
607
  );
608
+ if (Math.max(0, numRelearningSteps) > 1) {
609
+ const w11 = clamp(parameters[11] || 0, clip[11][0], clip[11][1]);
610
+ const w13 = clamp(parameters[13] || 0, clip[13][0], clip[13][1]);
611
+ const w14 = clamp(parameters[14] || 0, clip[14][0], clip[14][1]);
612
+ const value = -(Math.log(w11) + Math.log(Math.pow(2, w13) - 1) + w14 * 0.3) / numRelearningSteps;
613
+ const w17_w18_ceiling = clamp(roundTo(value, 8), 0.01, W17_W18_Ceiling);
614
+ if (clip[17]) clip[17] = [clip[17][0], w17_w18_ceiling];
615
+ if (clip[18]) clip[18] = [clip[18][0], w17_w18_ceiling];
616
+ }
594
617
  return clip.map(
595
618
  ([min, max], index) => clamp(parameters[index] || 0, min, max)
596
619
  );
597
620
  };
598
621
  const checkParameters = (parameters) => {
599
- const invalid = parameters.find(
600
- (param) => !Number.isFinite(param) && !Number.isNaN(param)
601
- );
622
+ const invalid = parameters.find((param) => !Number.isFinite(param));
602
623
  if (invalid !== void 0) {
603
- throw Error(`Non-finite or NaN value in parameters ${parameters}`);
624
+ throw new FSRSValidationError(
625
+ `Non-finite or NaN value in parameters ${parameters}`
626
+ );
604
627
  } else if (![17, 19, 21].includes(parameters.length)) {
605
- throw Error(
628
+ throw new FSRSValidationError(
606
629
  `Invalid parameter length: ${parameters.length}. Must be 17, 19 or 21 for FSRSv4, 5 and 6 respectively.`
607
630
  );
608
631
  }
@@ -732,7 +755,9 @@
732
755
  */
733
756
  calculate_interval_modifier(request_retention) {
734
757
  if (request_retention <= 0 || request_retention > 1) {
735
- throw new Error("Requested retention rate should be in the range (0,1]");
758
+ throw new FSRSValidationError(
759
+ "Requested retention rate should be in the range (0,1]"
760
+ );
736
761
  }
737
762
  const { decay, factor } = computeDecayFactor(this.param.w);
738
763
  return roundTo((Math.pow(request_retention, 1 / decay) - 1) / factor, 8);
@@ -940,10 +965,10 @@
940
965
  stability: 0
941
966
  };
942
967
  if (t < 0) {
943
- throw new Error(`Invalid delta_t "${t}"`);
968
+ throw new FSRSValidationError(`Invalid delta_t "${t}"`);
944
969
  }
945
970
  if (g < 0 || g > 4) {
946
- throw new Error(`Invalid grade "${g}"`);
971
+ throw new FSRSValidationError(`Invalid grade "${g}"`);
947
972
  }
948
973
  if (d === 0 && s === 0) {
949
974
  return {
@@ -958,7 +983,7 @@
958
983
  };
959
984
  }
960
985
  if (d < 1 || s < S_MIN) {
961
- throw new Error(
986
+ throw new FSRSValidationError(
962
987
  `Invalid memory state { difficulty: ${d}, stability: ${s} }`
963
988
  );
964
989
  }
@@ -1363,7 +1388,9 @@
1363
1388
  */
1364
1389
  handleManualRating(card, state, reviewed, elapsed_days, stability, difficulty, due) {
1365
1390
  if (typeof state === "undefined") {
1366
- throw new Error("reschedule: state is required for manual rating");
1391
+ throw new FSRSValidationError(
1392
+ "reschedule: state is required for manual rating"
1393
+ );
1367
1394
  }
1368
1395
  let log;
1369
1396
  let next_card;
@@ -1384,7 +1411,9 @@
1384
1411
  next_card.last_review = reviewed;
1385
1412
  } else {
1386
1413
  if (typeof due === "undefined") {
1387
- throw new Error("reschedule: due is required for manual rating");
1414
+ throw new FSRSValidationError(
1415
+ "reschedule: due is required for manual rating"
1416
+ );
1388
1417
  }
1389
1418
  const scheduled_days = date_diff(due, reviewed, "days");
1390
1419
  log = {
@@ -1493,6 +1522,9 @@
1493
1522
  };
1494
1523
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
1495
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
+ }
1496
1528
  class FSRS extends FSRSAlgorithm {
1497
1529
  constructor(param) {
1498
1530
  super(param);
@@ -1608,11 +1640,7 @@
1608
1640
  repeat(card, now, afterHandler) {
1609
1641
  const instance = this.getScheduler(card, now);
1610
1642
  const recordLog = instance.preview();
1611
- if (afterHandler && typeof afterHandler === "function") {
1612
- return afterHandler(recordLog);
1613
- } else {
1614
- return recordLog;
1615
- }
1643
+ return applyAfterHandler(recordLog, afterHandler);
1616
1644
  }
1617
1645
  /**
1618
1646
  * Display the collection of cards and logs for the card scheduled at the current time, after applying a specific grade rating.
@@ -1672,14 +1700,10 @@
1672
1700
  const instance = this.getScheduler(card, now);
1673
1701
  const g = TypeConvert.rating(grade);
1674
1702
  if (g === Rating.Manual) {
1675
- throw new Error("Cannot review a manual rating");
1703
+ throw new FSRSValidationError("Cannot review a manual rating");
1676
1704
  }
1677
1705
  const recordLogItem = instance.review(g);
1678
- if (afterHandler && typeof afterHandler === "function") {
1679
- return afterHandler(recordLogItem);
1680
- } else {
1681
- return recordLogItem;
1682
- }
1706
+ return applyAfterHandler(recordLogItem, afterHandler);
1683
1707
  }
1684
1708
  /**
1685
1709
  * Get the retrievability of the card
@@ -1724,7 +1748,7 @@
1724
1748
  const processedCard = TypeConvert.card(card);
1725
1749
  const processedLog = TypeConvert.review_log(log);
1726
1750
  if (processedLog.rating === Rating.Manual) {
1727
- throw new Error("Cannot rollback a manual rating");
1751
+ throw new FSRSValidationError("Cannot rollback a manual rating");
1728
1752
  }
1729
1753
  let last_due;
1730
1754
  let last_review;
@@ -1755,11 +1779,7 @@
1755
1779
  state: processedLog.state,
1756
1780
  last_review
1757
1781
  });
1758
- if (afterHandler && typeof afterHandler === "function") {
1759
- return afterHandler(prevCard);
1760
- } else {
1761
- return prevCard;
1762
- }
1782
+ return applyAfterHandler(prevCard, afterHandler);
1763
1783
  }
1764
1784
  /**
1765
1785
  *
@@ -1841,11 +1861,7 @@
1841
1861
  last_review: processedCard.last_review
1842
1862
  });
1843
1863
  const recordLogItem = { card: forget_card, log: forget_log };
1844
- if (afterHandler && typeof afterHandler === "function") {
1845
- return afterHandler(recordLogItem);
1846
- } else {
1847
- return recordLogItem;
1848
- }
1864
+ return applyAfterHandler(recordLogItem, afterHandler);
1849
1865
  }
1850
1866
  /**
1851
1867
  * Reschedules the current card and returns the rescheduled collections and reschedule item.
@@ -1912,15 +1928,9 @@
1912
1928
  len ? collections[len - 1] : void 0,
1913
1929
  updateMemoryState
1914
1930
  );
1915
- if (recordLogHandler && typeof recordLogHandler === "function") {
1916
- return {
1917
- collections: collections.map(recordLogHandler),
1918
- reschedule_item: manual_item ? recordLogHandler(manual_item) : null
1919
- };
1920
- }
1921
1931
  return {
1922
- collections,
1923
- reschedule_item: manual_item
1932
+ collections: typeof recordLogHandler === "function" ? collections.map(recordLogHandler) : collections,
1933
+ reschedule_item: manual_item ? applyAfterHandler(manual_item, recordLogHandler) : null
1924
1934
  };
1925
1935
  }
1926
1936
  }