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/dist/index.cjs CHANGED
@@ -1,5 +1,20 @@
1
1
  'use strict';
2
2
 
3
+ class FSRSError extends Error {
4
+ constructor(message = "FSRS Error") {
5
+ super(message);
6
+ this.name = "FSRSError";
7
+ Error.captureStackTrace?.(this, FSRSError);
8
+ }
9
+ }
10
+ class FSRSValidationError extends FSRSError {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = "FSRSValidationError";
14
+ Error.captureStackTrace?.(this, FSRSValidationError);
15
+ }
16
+ }
17
+
3
18
  var State = /* @__PURE__ */ ((State2) => {
4
19
  State2[State2["New"] = 0] = "New";
5
20
  State2[State2["Learning"] = 1] = "Learning";
@@ -31,13 +46,13 @@ class TypeConvert {
31
46
  const restOfString = value.slice(1).toLowerCase();
32
47
  const ret = Rating[`${firstLetter}${restOfString}`];
33
48
  if (ret === void 0) {
34
- throw new Error(`Invalid rating:[${value}]`);
49
+ throw new FSRSValidationError(`Invalid rating:[${value}]`);
35
50
  }
36
51
  return ret;
37
52
  } else if (typeof value === "number") {
38
53
  return value;
39
54
  }
40
- throw new Error(`Invalid rating:[${value}]`);
55
+ throw new FSRSValidationError(`Invalid rating:[${value}]`);
41
56
  }
42
57
  static state(value) {
43
58
  if (typeof value === "string") {
@@ -45,13 +60,13 @@ class TypeConvert {
45
60
  const restOfString = value.slice(1).toLowerCase();
46
61
  const ret = State[`${firstLetter}${restOfString}`];
47
62
  if (ret === void 0) {
48
- throw new Error(`Invalid state:[${value}]`);
63
+ throw new FSRSValidationError(`Invalid state:[${value}]`);
49
64
  }
50
65
  return ret;
51
66
  } else if (typeof value === "number") {
52
67
  return value;
53
68
  }
54
- throw new Error(`Invalid state:[${value}]`);
69
+ throw new FSRSValidationError(`Invalid state:[${value}]`);
55
70
  }
56
71
  static time(value) {
57
72
  if (value instanceof Date) {
@@ -65,12 +80,12 @@ class TypeConvert {
65
80
  if (!Number.isNaN(timestamp)) {
66
81
  return new Date(timestamp);
67
82
  } else {
68
- throw new Error(`Invalid date:[${value}]`);
83
+ throw new FSRSValidationError(`Invalid date:[${value}]`);
69
84
  }
70
85
  } else if (typeof value === "number") {
71
86
  return new Date(value);
72
87
  }
73
- throw new Error(`Invalid date:[${value}]`);
88
+ throw new FSRSValidationError(`Invalid date:[${value}]`);
74
89
  }
75
90
  static review_log(log) {
76
91
  return {
@@ -106,7 +121,7 @@ function date_scheduler(now, t, isDay) {
106
121
  }
107
122
  function date_diff(now, pre, unit) {
108
123
  if (!now || !pre) {
109
- throw new Error("Invalid date");
124
+ throw new FSRSValidationError("Invalid date");
110
125
  }
111
126
  const diff = TypeConvert.time(now).getTime() - TypeConvert.time(pre).getTime();
112
127
  let r = 0;
@@ -232,7 +247,7 @@ const ConvertStepUnitToMinutes = (step) => {
232
247
  const unit = step.slice(-1);
233
248
  const value = parseInt(step.slice(0, -1), 10);
234
249
  if (Number.isNaN(value) || !Number.isFinite(value) || value < 0) {
235
- throw new Error(`Invalid step value: ${step}`);
250
+ throw new FSRSValidationError(`Invalid step value: ${step}`);
236
251
  }
237
252
  switch (unit) {
238
253
  case "m":
@@ -242,7 +257,9 @@ const ConvertStepUnitToMinutes = (step) => {
242
257
  case "d":
243
258
  return value * 1440;
244
259
  default:
245
- throw new Error(`Invalid step unit: ${step}, expected m/h/d`);
260
+ throw new FSRSValidationError(
261
+ `Invalid step unit: ${step}, expected m/h/d`
262
+ );
246
263
  }
247
264
  };
248
265
  const BasicLearningStepsStrategy = (params, state, cur_step) => {
@@ -339,8 +356,8 @@ class AbstractScheduler {
339
356
  this.init();
340
357
  }
341
358
  checkGrade(grade) {
342
- if (!Number.isFinite(grade) || grade < 0 || grade > 4) {
343
- throw new Error(`Invalid grade "${grade}",expected 1-4`);
359
+ if (!Number.isFinite(grade) || grade < 1 || grade > 4) {
360
+ throw new FSRSValidationError(`Invalid grade "${grade}",expected 1-4`);
344
361
  }
345
362
  }
346
363
  init() {
@@ -483,7 +500,7 @@ function alea(seed) {
483
500
  return prng;
484
501
  }
485
502
 
486
- const version="5.3.3";
503
+ const version="5.4.0";
487
504
 
488
505
  const default_request_retention = 0.9;
489
506
  const default_maximum_interval = 36500;
@@ -572,13 +589,13 @@ const clipParameters = (parameters, numRelearningSteps, enableShortTerm = defaul
572
589
  );
573
590
  };
574
591
  const checkParameters = (parameters) => {
575
- const invalid = parameters.find(
576
- (param) => !Number.isFinite(param) && !Number.isNaN(param)
577
- );
592
+ const invalid = parameters.find((param) => !Number.isFinite(param));
578
593
  if (invalid !== void 0) {
579
- throw Error(`Non-finite or NaN value in parameters ${parameters}`);
594
+ throw new FSRSValidationError(
595
+ `Non-finite or NaN value in parameters ${parameters}`
596
+ );
580
597
  } else if (![17, 19, 21].includes(parameters.length)) {
581
- throw Error(
598
+ throw new FSRSValidationError(
582
599
  `Invalid parameter length: ${parameters.length}. Must be 17, 19 or 21 for FSRSv4, 5 and 6 respectively.`
583
600
  );
584
601
  }
@@ -696,7 +713,9 @@ class FSRSAlgorithm {
696
713
  */
697
714
  calculate_interval_modifier(request_retention) {
698
715
  if (request_retention <= 0 || request_retention > 1) {
699
- throw new Error("Requested retention rate should be in the range (0,1]");
716
+ throw new FSRSValidationError(
717
+ "Requested retention rate should be in the range (0,1]"
718
+ );
700
719
  }
701
720
  const { decay, factor } = computeDecayFactor(this.param.w);
702
721
  return roundTo((Math.pow(request_retention, 1 / decay) - 1) / factor, 8);
@@ -912,10 +931,10 @@ class FSRSAlgorithm {
912
931
  stability: 0
913
932
  };
914
933
  if (t < 0) {
915
- throw new Error(`Invalid delta_t "${t}"`);
934
+ throw new FSRSValidationError(`Invalid delta_t "${t}"`);
916
935
  }
917
936
  if (g < 0 || g > 4) {
918
- throw new Error(`Invalid grade "${g}"`);
937
+ throw new FSRSValidationError(`Invalid grade "${g}"`);
919
938
  }
920
939
  if (d === 0 && s === 0) {
921
940
  return {
@@ -930,7 +949,7 @@ class FSRSAlgorithm {
930
949
  };
931
950
  }
932
951
  if (d < 1 || s < S_MIN) {
933
- throw new Error(
952
+ throw new FSRSValidationError(
934
953
  `Invalid memory state { difficulty: ${d}, stability: ${s} }`
935
954
  );
936
955
  }
@@ -1311,7 +1330,9 @@ class Reschedule {
1311
1330
  */
1312
1331
  handleManualRating(card, state, reviewed, elapsed_days, stability, difficulty, due) {
1313
1332
  if (typeof state === "undefined") {
1314
- throw new Error("reschedule: state is required for manual rating");
1333
+ throw new FSRSValidationError(
1334
+ "reschedule: state is required for manual rating"
1335
+ );
1315
1336
  }
1316
1337
  let log;
1317
1338
  let next_card;
@@ -1332,7 +1353,9 @@ class Reschedule {
1332
1353
  next_card.last_review = reviewed;
1333
1354
  } else {
1334
1355
  if (typeof due === "undefined") {
1335
- throw new Error("reschedule: due is required for manual rating");
1356
+ throw new FSRSValidationError(
1357
+ "reschedule: due is required for manual rating"
1358
+ );
1336
1359
  }
1337
1360
  const scheduled_days = date_diff(due, reviewed, "days");
1338
1361
  log = {
@@ -1422,6 +1445,9 @@ class Reschedule {
1422
1445
  }
1423
1446
  }
1424
1447
 
1448
+ function applyAfterHandler(value, afterHandler) {
1449
+ return typeof afterHandler === "function" ? afterHandler(value) : value;
1450
+ }
1425
1451
  class FSRS extends FSRSAlgorithm {
1426
1452
  strategyHandler = /* @__PURE__ */ new Map();
1427
1453
  Scheduler;
@@ -1537,11 +1563,7 @@ class FSRS extends FSRSAlgorithm {
1537
1563
  repeat(card, now, afterHandler) {
1538
1564
  const instance = this.getScheduler(card, now);
1539
1565
  const recordLog = instance.preview();
1540
- if (afterHandler && typeof afterHandler === "function") {
1541
- return afterHandler(recordLog);
1542
- } else {
1543
- return recordLog;
1544
- }
1566
+ return applyAfterHandler(recordLog, afterHandler);
1545
1567
  }
1546
1568
  /**
1547
1569
  * Display the collection of cards and logs for the card scheduled at the current time, after applying a specific grade rating.
@@ -1601,14 +1623,10 @@ class FSRS extends FSRSAlgorithm {
1601
1623
  const instance = this.getScheduler(card, now);
1602
1624
  const g = TypeConvert.rating(grade);
1603
1625
  if (g === Rating.Manual) {
1604
- throw new Error("Cannot review a manual rating");
1626
+ throw new FSRSValidationError("Cannot review a manual rating");
1605
1627
  }
1606
1628
  const recordLogItem = instance.review(g);
1607
- if (afterHandler && typeof afterHandler === "function") {
1608
- return afterHandler(recordLogItem);
1609
- } else {
1610
- return recordLogItem;
1611
- }
1629
+ return applyAfterHandler(recordLogItem, afterHandler);
1612
1630
  }
1613
1631
  /**
1614
1632
  * Get the retrievability of the card
@@ -1653,7 +1671,7 @@ class FSRS extends FSRSAlgorithm {
1653
1671
  const processedCard = TypeConvert.card(card);
1654
1672
  const processedLog = TypeConvert.review_log(log);
1655
1673
  if (processedLog.rating === Rating.Manual) {
1656
- throw new Error("Cannot rollback a manual rating");
1674
+ throw new FSRSValidationError("Cannot rollback a manual rating");
1657
1675
  }
1658
1676
  let last_due;
1659
1677
  let last_review;
@@ -1685,11 +1703,7 @@ class FSRS extends FSRSAlgorithm {
1685
1703
  state: processedLog.state,
1686
1704
  last_review
1687
1705
  };
1688
- if (afterHandler && typeof afterHandler === "function") {
1689
- return afterHandler(prevCard);
1690
- } else {
1691
- return prevCard;
1692
- }
1706
+ return applyAfterHandler(prevCard, afterHandler);
1693
1707
  }
1694
1708
  /**
1695
1709
  *
@@ -1772,11 +1786,7 @@ class FSRS extends FSRSAlgorithm {
1772
1786
  last_review: processedCard.last_review
1773
1787
  };
1774
1788
  const recordLogItem = { card: forget_card, log: forget_log };
1775
- if (afterHandler && typeof afterHandler === "function") {
1776
- return afterHandler(recordLogItem);
1777
- } else {
1778
- return recordLogItem;
1779
- }
1789
+ return applyAfterHandler(recordLogItem, afterHandler);
1780
1790
  }
1781
1791
  /**
1782
1792
  * Reschedules the current card and returns the rescheduled collections and reschedule item.
@@ -1843,15 +1853,9 @@ class FSRS extends FSRSAlgorithm {
1843
1853
  len ? collections[len - 1] : void 0,
1844
1854
  updateMemoryState
1845
1855
  );
1846
- if (recordLogHandler && typeof recordLogHandler === "function") {
1847
- return {
1848
- collections: collections.map(recordLogHandler),
1849
- reschedule_item: manual_item ? recordLogHandler(manual_item) : null
1850
- };
1851
- }
1852
1856
  return {
1853
- collections,
1854
- reschedule_item: manual_item
1857
+ collections: typeof recordLogHandler === "function" ? collections.map(recordLogHandler) : collections,
1858
+ reschedule_item: manual_item ? applyAfterHandler(manual_item, recordLogHandler) : null
1855
1859
  };
1856
1860
  }
1857
1861
  }