ts-fsrs 3.2.0 → 3.2.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/README.md CHANGED
@@ -1,13 +1,17 @@
1
+ [Introduction](./README.md) | [简体中文](./README_CN.md) |[はじめに](./README_JP.md)
2
+
3
+ ---
4
+
1
5
  # About The
2
6
  [![ts-fsrs npm version](https://img.shields.io/npm/v/ts-fsrs.svg)](https://www.npmjs.com/package/ts-fsrs)
3
- [![ts-fsrs beta npm version](https://img.shields.io/npm/v/ts-fsrs/beta.svg)](https://www.npmjs.com/package/ts-fsrs)
4
7
  [![Build and Publish](https://github.com/ishiko732/ts-fsrs/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/ishiko732/ts-fsrs/actions/workflows/npm-publish.yml)
5
8
  [![Deploy](https://github.com/ishiko732/ts-fsrs/actions/workflows/deploy.yml/badge.svg)](https://github.com/ishiko732/ts-fsrs/actions/workflows/deploy.yml)
6
9
 
7
- ts-fsrs is a TypeScript package used to implement the [Free Spaced Repetition Scheduler (FSRS) algorithm](https://github.com/open-spaced-repetition/free-spaced-repetition-scheduler). It helps
10
+ ts-fsrs is a [ES modules package](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) based on TypeScript, used to implement the [Free Spaced Repetition Scheduler (FSRS) algorithm](https://github.com/open-spaced-repetition/free-spaced-repetition-scheduler). It helps
8
11
  developers apply FSRS to their flashcard applications, there by improving the user learning experience.
9
12
 
10
13
  # Usage
14
+ You need to run on Node.js (>=16.0.0) , using the `"type":"module"` by default in `package.json`.
11
15
 
12
16
  ```
13
17
  npm install ts-fsrs
@@ -52,17 +56,20 @@ More refer:
52
56
  - [Docs - Github Pages](https://ishiko732.github.io/ts-fsrs/)
53
57
  - [Example.html - Github Pages](https://ishiko732.github.io/ts-fsrs/example)
54
58
  - [Browser](https://github.com/ishiko732/ts-fsrs/blob/master/example/example.html) (ts-fsrs package using CDN)
55
- - [Next.js+Prisma](https://github.com/ishiko732/ts-fsrs-demo)
59
+ - [ts-fsrs-demo - Next.js+Prisma](https://github.com/ishiko732/ts-fsrs-demo)
56
60
 
57
61
 
58
62
  # Basic Use
59
63
 
60
64
  ## 1. **Initialization**:
61
- To begin, create an empty card instance and set the current date:
65
+ To begin, create an empty card instance and set the current date(default: current time from system)):
62
66
 
63
67
  ```typescript
64
68
  import { Card, createEmptyCard } from "ts-fsrs";
65
69
  let card: Card = createEmptyCard();
70
+ // createEmptyCard(new Date('2022-2-1 10:00:00'));
71
+ // createEmptyCard(new Date(Date.UTC(2023, 9, 18, 14, 32, 3, 370)));
72
+ // createEmptyCard(new Date('2023-09-18T14:32:03.370Z'));
66
73
  ```
67
74
 
68
75
  ## 2. **Parameter Configuration**:
@@ -100,6 +107,21 @@ const good: RecordLogItem = scheduling_cards[Rating.Good];
100
107
  const newCard: Card = good.card;
101
108
  ```
102
109
 
110
+ Get the new state of card for each rating:
111
+ ```typescript
112
+ scheduling_cards[Rating.Again].card
113
+ scheduling_cards[Rating.Again].log
114
+
115
+ scheduling_cards[Rating.Hard].card
116
+ scheduling_cards[Rating.Hard].log
117
+
118
+ scheduling_cards[Rating.Good].card
119
+ scheduling_cards[Rating.Good].log
120
+
121
+ scheduling_cards[Rating.Easy].card
122
+ scheduling_cards[Rating.Easy].log
123
+ ```
124
+
103
125
  ## 5. **Understanding Card Attributes**:
104
126
  Each `Card` object consists of various attributes that determine its status, scheduling, and other metrics:
105
127
 
@@ -116,3 +138,20 @@ type Card = {
116
138
  last_review?: Date; // The most recent review date, if applicable
117
139
  };
118
140
  ```
141
+
142
+ ## 6. **Understanding Log Attributes**:
143
+ Each `ReviewLog` object contains various attributes that determine the review record information associated with the card, used for analysis, undoing the review, and [optimization (WIP)](https://github.com/open-spaced-repetition/fsrs-optimizer).
144
+
145
+ ```typescript
146
+ type ReviewLog = {
147
+ rating: Rating; // Rating of the review (Again, Hard, Good, Easy)
148
+ state: State; // State of the review (New, Learning, Review, Relearning)
149
+ due: Date; // Date of the last scheduling
150
+ stability: number; // Stability of the card before the review
151
+ difficulty: number; // Difficulty of the card before the review
152
+ elapsed_days: number; // Number of days elapsed since the last review
153
+ last_elapsed_days: number; // Number of days between the last two reviews
154
+ scheduled_days: number; // Number of days until the next review
155
+ review: Date; // Date of the review
156
+ }
157
+ ```
package/README_CN.md ADDED
@@ -0,0 +1,170 @@
1
+ [Introduction](./README.md) | [简体中文](./README_CN.md) |[はじめに](./README_JP.md)
2
+
3
+ ---
4
+
5
+ # 关于
6
+
7
+ [![ts-fsrs npm version](https://img.shields.io/npm/v/ts-fsrs.svg)](https://www.npmjs.com/package/ts-fsrs)
8
+ [![Build and Publish](https://github.com/ishiko732/ts-fsrs/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/ishiko732/ts-fsrs/actions/workflows/npm-publish.yml)
9
+ [![Deploy](https://github.com/ishiko732/ts-fsrs/actions/workflows/deploy.yml/badge.svg)](https://github.com/ishiko732/ts-fsrs/actions/workflows/deploy.yml)
10
+
11
+ ts-fsrs 是一个基于TypeScript开发的[ES modules包](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c)
12
+ ,用于实现[自由间隔重复调度器(FSRS)算法](https://github.com/open-spaced-repetition/free-spaced-repetition-scheduler/blob/main/README_CN.md)
13
+ 的工具。它可以帮助开发者将FSRS应用到他们的记忆卡应用中,从而提升用户的学习体验。
14
+
15
+ # 使用ts-fsrs
16
+
17
+ 你需要运行在 Node.js (>=16.0.0)上,并且在`package.json`默认使用 `"type":"module"`。
18
+
19
+ ```
20
+ npm install ts-fsrs
21
+ yarn install ts-fsrs
22
+ pnpm install ts-fsrs
23
+ ```
24
+
25
+ # 例子
26
+
27
+ ```typescript
28
+ import {createEmptyCard, formatDate, fsrs, generatorParameters, Rating, Grades} from 'ts-fsrs';
29
+
30
+ const params = generatorParameters({enable_fuzz: true});
31
+ const f = fsrs(params);
32
+ const card = createEmptyCard(new Date('2022-2-1 10:00:00'));// createEmptyCard();
33
+ const now = new Date('2022-2-2 10:00:00');// new Date();
34
+ const scheduling_cards = f.repeat(card, now);
35
+
36
+ // console.log(scheduling_cards);
37
+ Grades.forEach(grade => { // [Rating.Again, Rating.Hard, Rating.Good, Rating.Easy]
38
+ const {log, card} = scheduling_cards[grade];
39
+ console.group(`${Rating[grade]}`);
40
+ console.table({
41
+ [`card_${Rating[grade]}`]: {
42
+ ...card,
43
+ due: formatDate(card.due),
44
+ last_review: formatDate(card.last_review as Date),
45
+ },
46
+ });
47
+ console.table({
48
+ [`log_${Rating[grade]}`]: {
49
+ ...log,
50
+ review: formatDate(log.review),
51
+ },
52
+ });
53
+ console.groupEnd();
54
+ console.log('----------------------------------------------------------------');
55
+ });
56
+ ```
57
+
58
+ 更多的参考:
59
+
60
+ - [参考文档- Github Pages](https://ishiko732.github.io/ts-fsrs/)
61
+ - [参考调度 - Github Pages](https://ishiko732.github.io/ts-fsrs/example)
62
+ - [浏览器使用](https://github.com/ishiko732/ts-fsrs/blob/master/example/example.html) (使用CDN来访问ts-fsrs ESM包)
63
+ - [案例应用 - 基于Next.js+Prisma](https://github.com/ishiko732/ts-fsrs-demo)
64
+
65
+ # 基本使用方法
66
+
67
+ ## 1. **初始化**:
68
+
69
+ 首先,创建一个空的卡片实例并设置当前日期(默认为当前系统时间):
70
+
71
+ ```typescript
72
+ import {Card, createEmptyCard} from "ts-fsrs";
73
+
74
+ let card: Card = createEmptyCard();
75
+ // createEmptyCard(new Date('2022-2-1 10:00:00'));
76
+ // createEmptyCard(new Date(Date.UTC(2023, 9, 18, 14, 32, 3, 370)));
77
+ // createEmptyCard(new Date('2023-09-18T14:32:03.370Z'));
78
+ ```
79
+
80
+ ## 2. **FSRS参数配置**:
81
+
82
+ 该ts-fsrs库允许自定义SRS参数。使用`generatorParameters`来生成SRS算法的最终参数集。以下是设置最大间隔的示例:
83
+
84
+ ```typescript
85
+ import {Card, createEmptyCard, generatorParameters, FSRSParameters} from "ts-fsrs";
86
+
87
+ let card: Card = createEmptyCard();
88
+ const params: FSRSParameters = generatorParameters({maximum_interval: 1000});
89
+ ```
90
+
91
+ ## 3. **使用FSRS进行调度**:
92
+
93
+ 核心功能位于`fsrs`函数中。当调用`repeat`该函数时,它会根据不同的用户评级返回一个卡片集合的调度结果:
94
+
95
+ ```typescript
96
+ import {
97
+ Card,
98
+ createEmptyCard,
99
+ generatorParameters,
100
+ FSRSParameters,
101
+ FSRS,
102
+ RecordLog,
103
+ } from "ts-fsrs";
104
+
105
+ let card: Card = createEmptyCard();
106
+ const f: FSRS = new FSRS(); // or const f: FSRS = fsrs(params);
107
+ let scheduling_cards: RecordLog = f.repeat(card, new Date());
108
+ ```
109
+
110
+ ## 4. **检查调度卡片信息**:
111
+
112
+ 一旦你有了`scheduling_cards`对象,你可以根据用户评级来获取卡片。例如,要访问一个被安排在“`Good`”评级下的卡片:
113
+
114
+ ```typescript
115
+ const good: RecordLogItem = scheduling_cards[Rating.Good];
116
+ const newCard: Card = good.card;
117
+ ```
118
+
119
+ 当然,你可以获取每个评级下卡片的新状态和对应的历史记录:
120
+
121
+ ```typescript
122
+ scheduling_cards[Rating.Again].card
123
+ scheduling_cards[Rating.Again].log
124
+
125
+ scheduling_cards[Rating.Hard].card
126
+ scheduling_cards[Rating.Hard].log
127
+
128
+ scheduling_cards[Rating.Good].card
129
+ scheduling_cards[Rating.Good].log
130
+
131
+ scheduling_cards[Rating.Easy].card
132
+ scheduling_cards[Rating.Easy].log
133
+ ```
134
+
135
+ ## 5. **理解卡片属性**:
136
+
137
+ 每个`Card`对象都包含各种属性,这些属性决定了它的状态、调度和其他指标(DS):
138
+
139
+ ```typescript
140
+ type Card = {
141
+ due: Date; // 卡片下次复习的日期
142
+ stability: number; // 记忆稳定性
143
+ difficulty: number; // 卡片难度
144
+ elapsed_days: number; // 自上次复习以来的天数
145
+ scheduled_days: number; // 下次复习的间隔天数
146
+ reps: number; // 卡片被复习的总次数
147
+ lapses: number; // 卡片被遗忘或错误记忆的次数
148
+ state: State; // 卡片的当前状态(新卡片、学习中、复习中、重新学习中)
149
+ last_review?: Date; // 最近的复习日期(如果适用)
150
+ };
151
+ ```
152
+
153
+ ## 6. **理解复习记录属性**:
154
+
155
+ 每个`ReviewLog`
156
+ 对象都包含各种属性,这些属性决定了与之关联的卡片的复习记录信息,用于分析,回退本次复习,[优化(编写中)](https://github.com/open-spaced-repetition/fsrs-optimizer):
157
+
158
+ ```typescript
159
+ type ReviewLog = {
160
+ rating: Rating; // 复习的评级(手动变更,重来,困难,良好,容易)
161
+ state: State; // 复习的状态(新卡片、学习中、复习中、重新学习中)
162
+ due: Date; // 上次的调度日期
163
+ stability: number; // 复习前的记忆稳定性
164
+ difficulty: number; // 复习前的卡片难度
165
+ elapsed_days: number; // 自上次复习以来的天数
166
+ last_elapsed_days: number; // 上次复习的间隔天数
167
+ scheduled_days: number; // 下次复习的间隔天数
168
+ review: Date; // 复习的日期
169
+ }
170
+ ```
package/README_JP.md ADDED
@@ -0,0 +1,172 @@
1
+ [Introduction](./README.md) | [简体中文](./README_CN.md) |[はじめに](./README_JP.md)
2
+
3
+ ---
4
+
5
+ # について
6
+
7
+ [![ts-fsrs npm version](https://img.shields.io/npm/v/ts-fsrs.svg)](https://www.npmjs.com/package/ts-fsrs)
8
+ [![Build and Publish](https://github.com/ishiko732/ts-fsrs/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/ishiko732/ts-fsrs/actions/workflows/npm-publish.yml)
9
+ [![Deploy](https://github.com/ishiko732/ts-fsrs/actions/workflows/deploy.yml/badge.svg)](https://github.com/ishiko732/ts-fsrs/actions/workflows/deploy.yml)
10
+
11
+ ts-fsrsは[自由間隔重複スケジューラ(FSRS)アルゴリズム](https://github.com/open-spaced-repetition/free-spaced-repetition-scheduler)
12
+ を実装するための、TypeScriptで開発された[ESモジュールパッケージ](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c)
13
+ です。このツールは、開発者がFSRSアルゴリズムを彼らのフラッシュカードアプリケーションに適用し、ユーザーの学習体験を向上させるのに役立ちます。
14
+
15
+ # ts-fsrsの使用方法
16
+
17
+ Node.js (バージョン >= 16.0.0)を実行する環境を準備して、`package.json`で`"type":"module"`をデフォルトに設定してください。
18
+
19
+ ```
20
+ npm install ts-fsrs
21
+ yarn install ts-fsrs
22
+ pnpm install ts-fsrs
23
+ ```
24
+
25
+ # 例
26
+
27
+ ```typescript
28
+ import {createEmptyCard, formatDate, fsrs, generatorParameters, Rating, Grades} from 'ts-fsrs';
29
+
30
+ const params = generatorParameters({enable_fuzz: true});
31
+ const f = fsrs(params);
32
+ const card = createEmptyCard(new Date('2022-2-1 10:00:00'));// createEmptyCard();
33
+ const now = new Date('2022-2-2 10:00:00');// new Date();
34
+ const scheduling_cards = f.repeat(card, now);
35
+
36
+ // console.log(scheduling_cards);
37
+ Grades.forEach(grade => { // [Rating.Again, Rating.Hard, Rating.Good, Rating.Easy]
38
+ const {log, card} = scheduling_cards[grade];
39
+ console.group(`${Rating[grade]}`);
40
+ console.table({
41
+ [`card_${Rating[grade]}`]: {
42
+ ...card,
43
+ due: formatDate(card.due),
44
+ last_review: formatDate(card.last_review as Date),
45
+ },
46
+ });
47
+ console.table({
48
+ [`log_${Rating[grade]}`]: {
49
+ ...log,
50
+ review: formatDate(log.review),
51
+ },
52
+ });
53
+ console.groupEnd();
54
+ console.log('----------------------------------------------------------------');
55
+ });
56
+ ```
57
+
58
+ もっと:
59
+
60
+ - [参考資料- Github Pages](https://ishiko732.github.io/ts-fsrs/)
61
+ - [参考スケジューラ - Github Pages](https://ishiko732.github.io/ts-fsrs/example)
62
+ - [ブラウザで使い方](https://github.com/ishiko732/ts-fsrs/blob/master/example/example.html) (CDNを使用して ts-fsrs ESM
63
+ パッケージにアクセスする)
64
+ - [実際のケース - Next.js+Prismaを利用する](https://github.com/ishiko732/ts-fsrs-demo)
65
+
66
+ # 基本的な使い方
67
+
68
+ ## 1. **初期化**:
69
+
70
+ まずは、空ぽっいカードインスタンスを作成して、現在の日付を設定します(デフォルトはシステムの現在時刻):
71
+
72
+ ```typescript
73
+ import {Card, createEmptyCard} from "ts-fsrs";
74
+
75
+ let card: Card = createEmptyCard();
76
+ // createEmptyCard(new Date('2022-2-1 10:00:00'));
77
+ // createEmptyCard(new Date(Date.UTC(2023, 9, 18, 14, 32, 3, 370)));
78
+ // createEmptyCard(new Date('2023-09-18T14:32:03.370Z'));
79
+ ```
80
+
81
+ ## 2. **FSRSのパラメータ設定**:
82
+
83
+ このts-fsrsライブラリは、カスタムSRSパラメータを許可します。`generatorParameters`
84
+ を使用して、SRSアルゴリズムの最終パラメータセットを生成します。以下は、最大間隔を設定する例です:
85
+
86
+ ```typescript
87
+ import {Card, createEmptyCard, generatorParameters, FSRSParameters} from "ts-fsrs";
88
+
89
+ let card: Card = createEmptyCard();
90
+ const params: FSRSParameters = generatorParameters({maximum_interval: 1000});
91
+ ```
92
+
93
+ ## 3. **FSRSを使いしてスケジューリングする**:
94
+
95
+ 核心機能は「`fsrs`」関数にあります。この`repeat`関数を呼び出すと、異なるユーザー評価に基づいて、カードセットのスケジュール結果が返されます。
96
+
97
+ ```typescript
98
+ import {
99
+ Card,
100
+ createEmptyCard,
101
+ generatorParameters,
102
+ FSRSParameters,
103
+ FSRS,
104
+ RecordLog,
105
+ } from "ts-fsrs";
106
+
107
+ let card: Card = createEmptyCard();
108
+ const f: FSRS = new FSRS(); // or const f: FSRS = fsrs(params);
109
+ let scheduling_cards: RecordLog = f.repeat(card, new Date());
110
+ ```
111
+
112
+ ## 4. **スケジュールされたカードの取得**:
113
+
114
+ scheduling_cardsオブジェクトがあると、ユーザーの評価に基づいてカードを取得できます。例えば、`Good`評価でスケジュールされたカードにアクセスするには:
115
+
116
+ ```typescript
117
+ const good: RecordLogItem = scheduling_cards[Rating.Good];
118
+ const newCard: Card = good.card;
119
+ ```
120
+
121
+ もちろん、各評価に対応するカードの新しい状態と履歴を取得できます:
122
+
123
+ ```typescript
124
+ scheduling_cards[Rating.Again].card
125
+ scheduling_cards[Rating.Again].log
126
+
127
+ scheduling_cards[Rating.Hard].card
128
+ scheduling_cards[Rating.Hard].log
129
+
130
+ scheduling_cards[Rating.Good].card
131
+ scheduling_cards[Rating.Good].log
132
+
133
+ scheduling_cards[Rating.Easy].card
134
+ scheduling_cards[Rating.Easy].log
135
+ ```
136
+
137
+ ## 5. **カード属性の理解**:
138
+
139
+ それぞれの`Card`オブジェクトは、その状態、スケジュール、その他の指標を決定するさまざまな属性を含んでいます:
140
+
141
+ ```typescript
142
+ type Card = {
143
+ due: Date; // カードの次のレビュー日
144
+ stability: number; // 記憶の安定性
145
+ difficulty: number; // カードの難易度
146
+ elapsed_days: number; // 前回のレビューからの日数
147
+ scheduled_days: number; // 次のレビューの間隔日数
148
+ reps: number; // カードのレビュー回数
149
+ lapses: number; // カードが忘れられたか、間違って覚えられた回数
150
+ state: State; // カードの現在の状態(新しいカード、学習中、レビュー中、再学習中)
151
+ last_review?: Date; // 最近のレビュー日(適用される場合)
152
+ };
153
+ ```
154
+
155
+ ## 6. **レビュー履歴属性の理解**:
156
+
157
+ それぞれの`ReviewLog`
158
+ オブジェクトは、そのカードに関連するレビュー記録情報を決定するさまざまな属性を含んでいます。分析、今回のレビューをやり直す、[最適化(作成中)](https://github.com/open-spaced-repetition/fsrs-optimizer):
159
+
160
+ ```typescript
161
+ type ReviewLog = {
162
+ rating: Rating; // レビューの評価(手動変更、やり直し、難しい、良い、簡単)
163
+ state: State; // レビューの状態(新しいカード、学習中、レビュー中、再学習中)
164
+ due: Date; // レビューの次の日付
165
+ stability: number; // レビュー前の記憶の安定性
166
+ difficulty: number; // レビュー前のカードの難易度
167
+ elapsed_days: number; // 前回のレビューからの日数
168
+ last_elapsed_days: number; // 前回のレビューの間隔日数
169
+ scheduled_days: number; // 次のレビューの間隔日数
170
+ review: Date; // レビュー日
171
+ }
172
+ ```
package/dist/models.d.ts CHANGED
@@ -5,7 +5,7 @@ export declare enum State {
5
5
  Review = 2,
6
6
  Relearning = 3
7
7
  }
8
- export type RatingType = "Again" | "Hard" | "Good" | "Easy";
8
+ export type RatingType = "Manual" | "Again" | "Hard" | "Good" | "Easy";
9
9
  export declare enum Rating {
10
10
  Manual = 0,
11
11
  Again = 1,
@@ -1,2 +1,2 @@
1
- "use strict";var t,e,a=require("seedrandom");function s(t,e,a){return new Date(a?t.getTime()+24*e*60*60*1e3:t.getTime()+60*e*1e3)}function i(t,e,a){if(!t||!e)throw new Error("Invalid date");const s=t.getTime()-e.getTime();let i=0;switch(a){case"days":i=Math.floor(s/864e5);break;case"minutes":i=Math.floor(s/6e4)}return i}function r(t){const e=t.getFullYear(),a=t.getMonth()+1,s=t.getDate(),i=t.getHours(),r=t.getMinutes(),d=t.getSeconds();return`${e}-${n(a)}-${n(s)} ${n(i)}:${n(r)}:${n(d)}`}function n(t){return t<10?`0${t}`:`${t}`}exports.State=void 0,(t=exports.State||(exports.State={}))[t.New=0]="New",t[t.Learning=1]="Learning",t[t.Review=2]="Review",t[t.Relearning=3]="Relearning",exports.Rating=void 0,(e=exports.Rating||(exports.Rating={}))[e.Manual=0]="Manual",e[e.Again=1]="Again",e[e.Hard=2]="Hard",e[e.Good=3]="Good",e[e.Easy=4]="Easy",Date.prototype.scheduler=function(t,e){return s(this,t,e)},Date.prototype.diff=function(t,e){return i(this,t,e)},Date.prototype.format=function(){return r(this)},Date.prototype.dueFormat=function(t,e,a){return l(this,t,e,a)};const d=[60,60,24,31,12],o=["second","min","hour","day","month","year"];function l(t,e,a,s=o){t=h(t),e=h(e),s.length!==o.length&&(s=o);let i,r=t.getTime()-e.getTime();for(r/=1e3,i=0;i<d.length&&!(r<d[i]);i++)r/=d[i];return`${Math.floor(r)}${a?s[i]:""}`}function h(t){if("object"==typeof t&&t instanceof Date)return t;if("string"==typeof t){const e=Date.parse(t);if(isNaN(e))throw new Error(`Invalid date:[${t}]`);return new Date(e)}if("number"==typeof t)return new Date(t);throw new Error(`Invalid date:[${t}]`)}function p(t){if("string"==typeof t){const e=t.charAt(0).toUpperCase(),a=t.slice(1).toLowerCase(),s=exports.State[`${e}${a}`];if(void 0===s)throw new Error(`Invalid state:[${t}]`);return s}if("number"==typeof t)return t;throw new Error(`Invalid state:[${t}]`)}function u(t){if("string"==typeof t){const e=t.charAt(0).toUpperCase(),a=t.slice(1).toLowerCase(),s=exports.Rating[`${e}${a}`];if(void 0===s)throw new Error(`Invalid rating:[${t}]`);return s}if("number"==typeof t)return t;throw new Error(`Invalid rating:[${t}]`)}const y=[exports.Rating.Again,exports.Rating.Hard,exports.Rating.Good,exports.Rating.Easy];class c{again;hard;good;easy;last_review;last_elapsed_days;copy(t){return{...t}}constructor(t,e){this.last_review=t.last_review||t.due,this.last_elapsed_days=t.elapsed_days,t.elapsed_days=t.state===exports.State.New?0:e.diff(t.last_review,"days"),t.last_review=e,t.reps+=1,this.again=this.copy(t),this.hard=this.copy(t),this.good=this.copy(t),this.easy=this.copy(t)}update_state(t){return t===exports.State.New?(this.again.state=exports.State.Learning,this.hard.state=exports.State.Learning,this.good.state=exports.State.Learning,this.easy.state=exports.State.Review):t===exports.State.Learning||t===exports.State.Relearning?(this.again.state=t,this.hard.state=t,this.good.state=exports.State.Review,this.easy.state=exports.State.Review):t===exports.State.Review&&(this.again.state=exports.State.Relearning,this.hard.state=exports.State.Review,this.good.state=exports.State.Review,this.easy.state=exports.State.Review,this.again.lapses+=1),this}schedule(t,e,a,i){return this.again.scheduled_days=0,this.hard.scheduled_days=e,this.good.scheduled_days=a,this.easy.scheduled_days=i,this.again.due=s(t,5),this.hard.due=e>0?s(t,e,!0):s(t,10),this.good.due=s(t,a,!0),this.easy.due=s(t,i,!0),this}record_log(t,e){return{[exports.Rating.Again]:{card:this.again,log:{rating:exports.Rating.Again,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}},[exports.Rating.Hard]:{card:this.hard,log:{rating:exports.Rating.Hard,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}},[exports.Rating.Good]:{card:this.good,log:{rating:exports.Rating.Good,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}},[exports.Rating.Easy]:{card:this.easy,log:{rating:exports.Rating.Easy,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}}}}}const _=[.4,.6,2.4,5.8,4.93,.94,.86,.01,1.49,.14,.94,2.18,.05,.34,1.26,.29,2.61],f=t=>({request_retention:t?.request_retention||.9,maximum_interval:t?.maximum_interval||36500,w:t?.w||_,enable_fuzz:t?.enable_fuzz||false});class g{param;intervalModifier;seed;constructor(t){this.param=f(t),this.intervalModifier=9*(1/this.param.request_retention-1)}init_ds(t){t.again.difficulty=this.init_difficulty(exports.Rating.Again),t.again.stability=this.init_stability(exports.Rating.Again),t.hard.difficulty=this.init_difficulty(exports.Rating.Hard),t.hard.stability=this.init_stability(exports.Rating.Hard),t.good.difficulty=this.init_difficulty(exports.Rating.Good),t.good.stability=this.init_stability(exports.Rating.Good),t.easy.difficulty=this.init_difficulty(exports.Rating.Easy),t.easy.stability=this.init_stability(exports.Rating.Easy)}next_ds(t,e,a,s){t.again.difficulty=this.next_difficulty(e,exports.Rating.Again),t.again.stability=this.next_forget_stability(e,a,s),t.hard.difficulty=this.next_difficulty(e,exports.Rating.Hard),t.hard.stability=this.next_recall_stability(e,a,s,exports.Rating.Hard),t.good.difficulty=this.next_difficulty(e,exports.Rating.Good),t.good.stability=this.next_recall_stability(e,a,s,exports.Rating.Good),t.easy.difficulty=this.next_difficulty(e,exports.Rating.Easy),t.easy.stability=this.next_recall_stability(e,a,s,exports.Rating.Easy)}init_stability(t){return Math.max(this.param.w[t-1],.1)}init_difficulty(t){return Math.min(Math.max(this.param.w[4]-(t-3)*this.param.w[5],1),10)}apply_fuzz(t){if(!this.param.enable_fuzz||t<2.5)return t;const e=a(this.seed)();t=Math.round(t);const s=Math.max(2,Math.round(.95*t-1)),i=Math.round(1.05*t+1);return Math.floor(e*(i-s+1)+s)}next_interval(t){const e=this.apply_fuzz(t*this.intervalModifier);return Math.min(Math.max(Math.round(e),1),this.param.maximum_interval)}next_difficulty(t,e){const a=t-this.param.w[6]*(e-3);return this.constrain_difficulty(this.mean_reversion(this.param.w[4],a))}constrain_difficulty(t){return Math.min(Math.max(Number(t.toFixed(2)),1),10)}mean_reversion(t,e){return this.param.w[7]*t+(1-this.param.w[7])*e}next_recall_stability(t,e,a,s){const i=exports.Rating.Hard===s?this.param.w[15]:1,r=exports.Rating.Easy===s?this.param.w[16]:1;return e*(1+Math.exp(this.param.w[8])*(11-t)*Math.pow(e,-this.param.w[9])*(Math.exp((1-a)*this.param.w[10])-1)*i*r)}next_forget_stability(t,e,a){return this.param.w[11]*Math.pow(t,-this.param.w[12])*(Math.pow(e+1,this.param.w[13])-1)*Math.exp((1-a)*this.param.w[14])}current_retrievability(t,e){return Math.pow(1+t/(9*e),-1)}}class x extends g{constructor(t){super(t)}preProcessCard(t){return{...t,state:p(t.state),due:h(t.due),last_review:t.last_review?h(t.last_review):void 0}}preProcessDate(t){return h(t)}preProcessLog(t){return{...t,rating:u(t.rating),state:p(t.state),review:h(t.review)}}repeat=(t,e)=>{t=this.preProcessCard(t),e=this.preProcessDate(e);const a=new c(t,e).update_state(t.state);let s,i,r;switch(this.seed=String(e.getTime())+String(t.reps),t.state){case exports.State.New:this.init_ds(a),a.again.due=e.scheduler(1),a.hard.due=e.scheduler(5),a.good.due=e.scheduler(10),s=this.next_interval(a.easy.stability),a.easy.scheduled_days=s,a.easy.due=e.scheduler(s,!0);break;case exports.State.Learning:case exports.State.Relearning:r=0,i=this.next_interval(a.good.stability),s=Math.max(this.next_interval(a.easy.stability),i+1),a.schedule(e,r,i,s);break;case exports.State.Review:{const n=t.elapsed_days,d=t.difficulty,o=t.stability,l=this.current_retrievability(n,o);this.next_ds(a,d,o,l),r=this.next_interval(a.hard.stability),i=this.next_interval(a.good.stability),r=Math.min(r,i),i=Math.max(i,r+1),s=Math.max(this.next_interval(a.easy.stability),i+1),a.schedule(e,r,i,s);break}}return a.record_log(t,e)};get_retrievability=(t,e)=>{if(t=this.preProcessCard(t),e=this.preProcessDate(e),t.state!==exports.State.Review)return;const a=Math.max(e.diff(t.last_review,"days"),0);return(100*this.current_retrievability(a,t.stability)).toFixed(2)+"%"};rollback=(t,e)=>{if(t=this.preProcessCard(t),(e=this.preProcessLog(e)).rating===exports.Rating.Manual)throw new Error("Cannot rollback a manual rating");let a,s,i;switch(e.state){case exports.State.New:a=e.due,s=void 0,i=0;break;case exports.State.Learning:case exports.State.Relearning:case exports.State.Review:a=e.review,s=e.due,i=t.lapses-(e.rating===exports.Rating.Again&&e.state===exports.State.Review?1:0)}return{...t,due:a,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.last_elapsed_days,scheduled_days:e.scheduled_days,reps:Math.max(0,t.reps-1),lapses:Math.max(0,i),state:e.state,last_review:s}};forget=(t,e,a=!1)=>{t=this.preProcessCard(t),e=this.preProcessDate(e);const s=t.state===exports.State.New?0:e.diff(t.last_review,"days"),i={rating:exports.Rating.Manual,state:t.state,due:t.due,stability:t.stability,difficulty:t.difficulty,elapsed_days:0,last_elapsed_days:t.elapsed_days,scheduled_days:s,review:e};return{card:{...t,due:e,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:a?0:t.reps,lapses:a?0:t.lapses,state:exports.State.New,last_review:t.last_review},log:i}}}exports.FSRS=x,exports.FSRSVersion="3.2.0",exports.Grades=y,exports.SchedulingCard=c,exports.createEmptyCard=t=>({due:t?h(t):new Date,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:0,lapses:0,state:exports.State.New,last_review:void 0}),exports.date_diff=i,exports.date_scheduler=s,exports.default_enable_fuzz=false,exports.default_maximum_interval=36500,exports.default_request_retention=.9,exports.default_w=_,exports.fixDate=h,exports.fixRating=u,exports.fixState=p,exports.formatDate=r,exports.fsrs=t=>new x(t||{}),exports.generatorParameters=f,exports.show_diff_message=l;
1
+ "use strict";var t,e,a=require("seedrandom");function s(t,e,a){return new Date(a?t.getTime()+24*e*60*60*1e3:t.getTime()+60*e*1e3)}function i(t,e,a){if(!t||!e)throw new Error("Invalid date");const s=t.getTime()-e.getTime();let i=0;switch(a){case"days":i=Math.floor(s/864e5);break;case"minutes":i=Math.floor(s/6e4)}return i}function r(t){const e=t.getFullYear(),a=t.getMonth()+1,s=t.getDate(),i=t.getHours(),r=t.getMinutes(),d=t.getSeconds();return`${e}-${n(a)}-${n(s)} ${n(i)}:${n(r)}:${n(d)}`}function n(t){return t<10?`0${t}`:`${t}`}exports.State=void 0,(t=exports.State||(exports.State={}))[t.New=0]="New",t[t.Learning=1]="Learning",t[t.Review=2]="Review",t[t.Relearning=3]="Relearning",exports.Rating=void 0,(e=exports.Rating||(exports.Rating={}))[e.Manual=0]="Manual",e[e.Again=1]="Again",e[e.Hard=2]="Hard",e[e.Good=3]="Good",e[e.Easy=4]="Easy",Date.prototype.scheduler=function(t,e){return s(this,t,e)},Date.prototype.diff=function(t,e){return i(this,t,e)},Date.prototype.format=function(){return r(this)},Date.prototype.dueFormat=function(t,e,a){return l(this,t,e,a)};const d=[60,60,24,31,12],o=["second","min","hour","day","month","year"];function l(t,e,a,s=o){t=h(t),e=h(e),s.length!==o.length&&(s=o);let i,r=t.getTime()-e.getTime();for(r/=1e3,i=0;i<d.length&&!(r<d[i]);i++)r/=d[i];return`${Math.floor(r)}${a?s[i]:""}`}function h(t){if("object"==typeof t&&t instanceof Date)return t;if("string"==typeof t){const e=Date.parse(t);if(isNaN(e))throw new Error(`Invalid date:[${t}]`);return new Date(e)}if("number"==typeof t)return new Date(t);throw new Error(`Invalid date:[${t}]`)}function p(t){if("string"==typeof t){const e=t.charAt(0).toUpperCase(),a=t.slice(1).toLowerCase(),s=exports.State[`${e}${a}`];if(void 0===s)throw new Error(`Invalid state:[${t}]`);return s}if("number"==typeof t)return t;throw new Error(`Invalid state:[${t}]`)}function u(t){if("string"==typeof t){const e=t.charAt(0).toUpperCase(),a=t.slice(1).toLowerCase(),s=exports.Rating[`${e}${a}`];if(void 0===s)throw new Error(`Invalid rating:[${t}]`);return s}if("number"==typeof t)return t;throw new Error(`Invalid rating:[${t}]`)}const y=[exports.Rating.Again,exports.Rating.Hard,exports.Rating.Good,exports.Rating.Easy];class c{again;hard;good;easy;last_review;last_elapsed_days;copy(t){return{...t}}constructor(t,e){this.last_review=t.last_review||t.due,this.last_elapsed_days=t.elapsed_days,t.elapsed_days=t.state===exports.State.New?0:e.diff(t.last_review,"days"),t.last_review=e,t.reps+=1,this.again=this.copy(t),this.hard=this.copy(t),this.good=this.copy(t),this.easy=this.copy(t)}update_state(t){return t===exports.State.New?(this.again.state=exports.State.Learning,this.hard.state=exports.State.Learning,this.good.state=exports.State.Learning,this.easy.state=exports.State.Review):t===exports.State.Learning||t===exports.State.Relearning?(this.again.state=t,this.hard.state=t,this.good.state=exports.State.Review,this.easy.state=exports.State.Review):t===exports.State.Review&&(this.again.state=exports.State.Relearning,this.hard.state=exports.State.Review,this.good.state=exports.State.Review,this.easy.state=exports.State.Review,this.again.lapses+=1),this}schedule(t,e,a,i){return this.again.scheduled_days=0,this.hard.scheduled_days=e,this.good.scheduled_days=a,this.easy.scheduled_days=i,this.again.due=s(t,5),this.hard.due=e>0?s(t,e,!0):s(t,10),this.good.due=s(t,a,!0),this.easy.due=s(t,i,!0),this}record_log(t,e){return{[exports.Rating.Again]:{card:this.again,log:{rating:exports.Rating.Again,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}},[exports.Rating.Hard]:{card:this.hard,log:{rating:exports.Rating.Hard,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}},[exports.Rating.Good]:{card:this.good,log:{rating:exports.Rating.Good,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}},[exports.Rating.Easy]:{card:this.easy,log:{rating:exports.Rating.Easy,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}}}}}const _=[.4,.6,2.4,5.8,4.93,.94,.86,.01,1.49,.14,.94,2.18,.05,.34,1.26,.29,2.61],f=t=>({request_retention:t?.request_retention||.9,maximum_interval:t?.maximum_interval||36500,w:t?.w||_,enable_fuzz:t?.enable_fuzz||false});class g{param;intervalModifier;seed;constructor(t){this.param=f(t),this.intervalModifier=9*(1/this.param.request_retention-1)}init_ds(t){t.again.difficulty=this.init_difficulty(exports.Rating.Again),t.again.stability=this.init_stability(exports.Rating.Again),t.hard.difficulty=this.init_difficulty(exports.Rating.Hard),t.hard.stability=this.init_stability(exports.Rating.Hard),t.good.difficulty=this.init_difficulty(exports.Rating.Good),t.good.stability=this.init_stability(exports.Rating.Good),t.easy.difficulty=this.init_difficulty(exports.Rating.Easy),t.easy.stability=this.init_stability(exports.Rating.Easy)}next_ds(t,e,a,s){t.again.difficulty=this.next_difficulty(e,exports.Rating.Again),t.again.stability=this.next_forget_stability(e,a,s),t.hard.difficulty=this.next_difficulty(e,exports.Rating.Hard),t.hard.stability=this.next_recall_stability(e,a,s,exports.Rating.Hard),t.good.difficulty=this.next_difficulty(e,exports.Rating.Good),t.good.stability=this.next_recall_stability(e,a,s,exports.Rating.Good),t.easy.difficulty=this.next_difficulty(e,exports.Rating.Easy),t.easy.stability=this.next_recall_stability(e,a,s,exports.Rating.Easy)}init_stability(t){return Math.max(this.param.w[t-1],.1)}init_difficulty(t){return Math.min(Math.max(this.param.w[4]-(t-3)*this.param.w[5],1),10)}apply_fuzz(t){if(!this.param.enable_fuzz||t<2.5)return t;const e=a(this.seed)();t=Math.round(t);const s=Math.max(2,Math.round(.95*t-1)),i=Math.round(1.05*t+1);return Math.floor(e*(i-s+1)+s)}next_interval(t){const e=this.apply_fuzz(t*this.intervalModifier);return Math.min(Math.max(Math.round(e),1),this.param.maximum_interval)}next_difficulty(t,e){const a=t-this.param.w[6]*(e-3);return this.constrain_difficulty(this.mean_reversion(this.param.w[4],a))}constrain_difficulty(t){return Math.min(Math.max(Number(t.toFixed(2)),1),10)}mean_reversion(t,e){return this.param.w[7]*t+(1-this.param.w[7])*e}next_recall_stability(t,e,a,s){const i=exports.Rating.Hard===s?this.param.w[15]:1,r=exports.Rating.Easy===s?this.param.w[16]:1;return e*(1+Math.exp(this.param.w[8])*(11-t)*Math.pow(e,-this.param.w[9])*(Math.exp((1-a)*this.param.w[10])-1)*i*r)}next_forget_stability(t,e,a){return this.param.w[11]*Math.pow(t,-this.param.w[12])*(Math.pow(e+1,this.param.w[13])-1)*Math.exp((1-a)*this.param.w[14])}current_retrievability(t,e){return Math.pow(1+t/(9*e),-1)}}class x extends g{constructor(t){super(t)}preProcessCard(t){return{...t,state:p(t.state),due:h(t.due),last_review:t.last_review?h(t.last_review):void 0}}preProcessDate(t){return h(t)}preProcessLog(t){return{...t,rating:u(t.rating),state:p(t.state),review:h(t.review)}}repeat=(t,e)=>{t=this.preProcessCard(t),e=this.preProcessDate(e);const a=new c(t,e).update_state(t.state);let s,i,r;switch(this.seed=String(e.getTime())+String(t.reps),t.state){case exports.State.New:this.init_ds(a),a.again.due=e.scheduler(1),a.hard.due=e.scheduler(5),a.good.due=e.scheduler(10),s=this.next_interval(a.easy.stability),a.easy.scheduled_days=s,a.easy.due=e.scheduler(s,!0);break;case exports.State.Learning:case exports.State.Relearning:r=0,i=this.next_interval(a.good.stability),s=Math.max(this.next_interval(a.easy.stability),i+1),a.schedule(e,r,i,s);break;case exports.State.Review:{const n=t.elapsed_days,d=t.difficulty,o=t.stability,l=this.current_retrievability(n,o);this.next_ds(a,d,o,l),r=this.next_interval(a.hard.stability),i=this.next_interval(a.good.stability),r=Math.min(r,i),i=Math.max(i,r+1),s=Math.max(this.next_interval(a.easy.stability),i+1),a.schedule(e,r,i,s);break}}return a.record_log(t,e)};get_retrievability=(t,e)=>{if(t=this.preProcessCard(t),e=this.preProcessDate(e),t.state!==exports.State.Review)return;const a=Math.max(e.diff(t.last_review,"days"),0);return(100*this.current_retrievability(a,t.stability)).toFixed(2)+"%"};rollback=(t,e)=>{if(t=this.preProcessCard(t),(e=this.preProcessLog(e)).rating===exports.Rating.Manual)throw new Error("Cannot rollback a manual rating");let a,s,i;switch(e.state){case exports.State.New:a=e.due,s=void 0,i=0;break;case exports.State.Learning:case exports.State.Relearning:case exports.State.Review:a=e.review,s=e.due,i=t.lapses-(e.rating===exports.Rating.Again&&e.state===exports.State.Review?1:0)}return{...t,due:a,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.last_elapsed_days,scheduled_days:e.scheduled_days,reps:Math.max(0,t.reps-1),lapses:Math.max(0,i),state:e.state,last_review:s}};forget=(t,e,a=!1)=>{t=this.preProcessCard(t),e=this.preProcessDate(e);const s=t.state===exports.State.New?0:e.diff(t.last_review,"days"),i={rating:exports.Rating.Manual,state:t.state,due:t.due,stability:t.stability,difficulty:t.difficulty,elapsed_days:0,last_elapsed_days:t.elapsed_days,scheduled_days:s,review:e};return{card:{...t,due:e,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:a?0:t.reps,lapses:a?0:t.lapses,state:exports.State.New,last_review:t.last_review},log:i}}}exports.FSRS=x,exports.FSRSVersion="3.2.1",exports.Grades=y,exports.SchedulingCard=c,exports.createEmptyCard=t=>({due:t?h(t):new Date,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:0,lapses:0,state:exports.State.New,last_review:void 0}),exports.date_diff=i,exports.date_scheduler=s,exports.default_enable_fuzz=false,exports.default_maximum_interval=36500,exports.default_request_retention=.9,exports.default_w=_,exports.fixDate=h,exports.fixRating=u,exports.fixState=p,exports.formatDate=r,exports.fsrs=t=>new x(t||{}),exports.generatorParameters=f,exports.show_diff_message=l;
2
2
  //# sourceMappingURL=ts-fsrs.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ts-fsrs.cjs.js","sources":["../src/fsrs/models.ts","../src/fsrs/help.ts","../src/fsrs/scheduler.ts","../src/fsrs/default.ts","../src/fsrs/algorithm.ts","../src/fsrs/fsrs.ts"],"sourcesContent":["export type StateType = \"New\" | \"Learning\" | \"Review\" | \"Relearning\";\n\nexport enum State {\n New = 0,\n Learning = 1,\n Review = 2,\n Relearning = 3,\n}\n\nexport type RatingType = \"Again\" | \"Hard\" | \"Good\" | \"Easy\";\n\nexport enum Rating {\n Manual = 0,\n Again = 1,\n Hard = 2,\n Good = 3,\n Easy = 4,\n}\n\ntype ExcludeManual<T> = Exclude<T, Rating.Manual>;\n\nexport type Grade = ExcludeManual<Rating>;\n\nexport interface ReviewLog {\n rating: Rating;\n state: State;\n due: Date;\n stability: number;\n difficulty: number;\n elapsed_days: number;\n last_elapsed_days: number;\n scheduled_days: number;\n review: Date;\n}\nexport type RecordLogItem = {\n card: Card;\n log: ReviewLog;\n};\nexport type RecordLog = {\n [key in Grade]: RecordLogItem;\n};\n\nexport interface Card {\n due: Date; // Due date\n stability: number; // Stability\n difficulty: number; // Difficulty level\n elapsed_days: number; // Number of days elapsed\n scheduled_days: number; // Number of days scheduled\n reps: number; // Repetition count\n lapses: number; // Number of lapses or mistakes\n state: State; // Card's state (New, Learning, Review, Relearning)\n last_review?: Date; // Date of the last review (optional)\n}\n\nexport type CardInput = Card & { state: StateType | State };\nexport type DateInput = Date | number | string;\nexport type ReviewLogInput = ReviewLog & {\n rating: RatingType | Rating;\n state: StateType | State;\n};\n\nexport interface FSRSParameters {\n request_retention: number;\n maximum_interval: number;\n w: number[];\n enable_fuzz: boolean;\n}\n","import type { int, unit } from \"./type\";\nimport { Grade, Rating, State } from \"./models\";\n\ndeclare global {\n export interface Date {\n scheduler(t: int, isDay?: boolean): Date;\n\n diff(pre: Date, unit: unit): int;\n\n format(): string;\n\n dueFormat(last_review: Date, unit?: boolean, timeUnit?: string[]): string;\n }\n}\n\nDate.prototype.scheduler = function (t: int, isDay?: boolean): Date {\n return date_scheduler(this, t, isDay);\n};\n\n/**\n * 当前时间与之前的时间差值\n * @param pre 比当前时间还要之前\n * @param unit 单位: days | minutes\n */\nDate.prototype.diff = function (pre: Date, unit: unit): int {\n return date_diff(this, pre, unit) as int;\n};\n\nDate.prototype.format = function (): string {\n return formatDate(this);\n};\n\nDate.prototype.dueFormat = function (\n last_review: Date,\n unit?: boolean,\n timeUnit?: string[],\n) {\n return show_diff_message(this, last_review, unit, timeUnit);\n};\n\n/**\n * 计算日期和时间的偏移,并返回一个新的日期对象。\n * @param now 当前日期和时间\n * @param t 时间偏移量,当 isDay 为 true 时表示天数,为 false 时表示分钟\n * @param isDay (可选)是否按天数单位进行偏移,默认为 false,表示按分钟单位计算偏移\n * @returns 偏移后的日期和时间对象\n */\nexport function date_scheduler(now: Date, t: number, isDay?: boolean): Date {\n return new Date(\n isDay\n ? now.getTime() + t * 24 * 60 * 60 * 1000\n : now.getTime() + t * 60 * 1000,\n );\n}\n\nexport function date_diff(now: Date, pre: Date, unit: unit): number {\n if (!now || !pre) {\n throw new Error(\"Invalid date\");\n }\n const diff = now.getTime() - pre.getTime();\n let r = 0;\n switch (unit) {\n case \"days\":\n r = Math.floor(diff / (24 * 60 * 60 * 1000));\n break;\n case \"minutes\":\n r = Math.floor(diff / (60 * 1000));\n break;\n }\n return r;\n}\n\nexport function formatDate(date: Date): string {\n const year: number = date.getFullYear();\n const month: number = date.getMonth() + 1;\n const day: number = date.getDate();\n const hours: number = date.getHours();\n const minutes: number = date.getMinutes();\n const seconds: number = date.getSeconds();\n\n return `${year}-${padZero(month)}-${padZero(day)} ${padZero(hours)}:${padZero(\n minutes,\n )}:${padZero(seconds)}`;\n}\n\nfunction padZero(num: number): string {\n return num < 10 ? `0${num}` : `${num}`;\n}\n\nconst TIMEUNIT = [60, 60, 24, 31, 12];\nconst TIMEUNITFORMAT = [\"second\", \"min\", \"hour\", \"day\", \"month\", \"year\"];\n\nexport function show_diff_message(\n due: Date,\n last_review: Date,\n unit?: boolean,\n timeUnit: string[] = TIMEUNITFORMAT,\n): string {\n due = fixDate(due);\n last_review = fixDate(last_review);\n if (timeUnit.length !== TIMEUNITFORMAT.length) {\n timeUnit = TIMEUNITFORMAT;\n }\n let diff = due.getTime() - last_review.getTime();\n let i;\n diff /= 1000;\n for (i = 0; i < TIMEUNIT.length; i++) {\n if (diff < TIMEUNIT[i]) {\n break;\n } else {\n diff /= TIMEUNIT[i];\n }\n }\n return `${Math.floor(diff)}${unit ? timeUnit[i] : \"\"}`;\n}\n\nexport function fixDate(value: unknown) {\n if (typeof value === \"object\" && value instanceof Date) {\n return value;\n } else if (typeof value === \"string\") {\n const timestamp = Date.parse(value);\n if (!isNaN(timestamp)) {\n return new Date(timestamp);\n } else {\n throw new Error(`Invalid date:[${value}]`);\n }\n } else if (typeof value === \"number\") {\n return new Date(value);\n }\n throw new Error(`Invalid date:[${value}]`);\n}\n\nexport function fixState(value: unknown): State {\n if (typeof value === \"string\") {\n const firstLetter = value.charAt(0).toUpperCase();\n const restOfString = value.slice(1).toLowerCase();\n const ret = State[`${firstLetter}${restOfString}` as keyof typeof State];\n if (ret === undefined) {\n throw new Error(`Invalid state:[${value}]`);\n }\n return ret;\n } else if (typeof value === \"number\") {\n return value as State;\n }\n throw new Error(`Invalid state:[${value}]`);\n}\n\nexport function fixRating(value: unknown): Rating {\n if (typeof value === \"string\") {\n const firstLetter = value.charAt(0).toUpperCase();\n const restOfString = value.slice(1).toLowerCase();\n const ret = Rating[`${firstLetter}${restOfString}` as keyof typeof Rating];\n if (ret === undefined) {\n throw new Error(`Invalid rating:[${value}]`);\n }\n return ret;\n } else if (typeof value === \"number\") {\n return value as Rating;\n }\n throw new Error(`Invalid rating:[${value}]`);\n}\n\nexport const Grades: Grade[] = [\n Rating.Again,\n Rating.Hard,\n Rating.Good,\n Rating.Easy,\n];\n","import { Card, Rating, RecordLog, State } from \"./models\";\nimport { date_scheduler } from \"./help\";\n\nexport class SchedulingCard {\n again: Card;\n hard: Card;\n good: Card;\n easy: Card;\n last_review: Date;\n last_elapsed_days: number;\n\n private copy(card: Card): Card {\n return {\n ...card,\n };\n }\n\n constructor(card: Card, now: Date) {\n this.last_review = card.last_review || card.due;\n this.last_elapsed_days = card.elapsed_days;\n card.elapsed_days =\n card.state === State.New ? 0 : now.diff(card.last_review as Date, \"days\"); //相距时间\n card.last_review = now; // 上次复习时间\n card.reps += 1;\n this.again = this.copy(card);\n this.hard = this.copy(card);\n this.good = this.copy(card);\n this.easy = this.copy(card);\n }\n\n update_state(state: State) {\n if (state === State.New) {\n this.again.state = State.Learning;\n this.hard.state = State.Learning;\n this.good.state = State.Learning;\n this.easy.state = State.Review;\n } else if (state === State.Learning || state === State.Relearning) {\n this.again.state = state;\n this.hard.state = state;\n this.good.state = State.Review;\n this.easy.state = State.Review;\n } else if (state === State.Review) {\n this.again.state = State.Relearning;\n this.hard.state = State.Review;\n this.good.state = State.Review;\n this.easy.state = State.Review;\n this.again.lapses += 1;\n }\n return this;\n }\n\n schedule(\n now: Date,\n hard_interval: number,\n good_interval: number,\n easy_interval: number,\n ): SchedulingCard {\n this.again.scheduled_days = 0;\n this.hard.scheduled_days = hard_interval;\n this.good.scheduled_days = good_interval;\n this.easy.scheduled_days = easy_interval;\n this.again.due = date_scheduler(now, 5);\n this.hard.due =\n hard_interval > 0\n ? date_scheduler(now, hard_interval, true)\n : date_scheduler(now, 10);\n this.good.due = date_scheduler(now, good_interval, true);\n this.easy.due = date_scheduler(now, easy_interval, true);\n return this;\n }\n\n record_log(card: Card, now: Date): RecordLog {\n return {\n [Rating.Again]: {\n card: this.again,\n log: {\n rating: Rating.Again,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n [Rating.Hard]: {\n card: this.hard,\n log: {\n rating: Rating.Hard,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n [Rating.Good]: {\n card: this.good,\n log: {\n rating: Rating.Good,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n [Rating.Easy]: {\n card: this.easy,\n log: {\n rating: Rating.Easy,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n };\n }\n}\n","import { Card, DateInput, FSRSParameters, State } from \"./models\";\nimport { fixDate } from \"./help\";\n\nexport const default_request_retention = 0.9;\nexport const default_maximum_interval = 36500;\nexport const default_w = [\n 0.4, 0.6, 2.4, 5.8, 4.93, 0.94, 0.86, 0.01, 1.49, 0.14, 0.94, 2.18, 0.05,\n 0.34, 1.26, 0.29, 2.61,\n];\nexport const default_enable_fuzz = false;\n\nexport const FSRSVersion: string = \"3.2.0\";\n\nexport const generatorParameters = (\n props?: Partial<FSRSParameters>,\n): FSRSParameters => {\n return {\n request_retention: props?.request_retention || default_request_retention,\n maximum_interval: props?.maximum_interval || default_maximum_interval,\n w: props?.w || default_w,\n enable_fuzz: props?.enable_fuzz || default_enable_fuzz,\n };\n};\n\nexport const createEmptyCard = (now?: DateInput): Card => {\n return {\n due: now ? fixDate(now) : new Date(),\n stability: 0,\n difficulty: 0,\n elapsed_days: 0,\n scheduled_days: 0,\n reps: 0,\n lapses: 0,\n state: State.New,\n last_review: undefined,\n };\n};\n","import pseudorandom from \"seedrandom\";\nimport { generatorParameters } from \"./default\";\nimport { SchedulingCard } from \"./scheduler\";\nimport { FSRSParameters, Grade, Rating } from \"./models\";\nimport type { int } from \"./type\";\n\n// Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-v4\nexport class FSRSAlgorithm {\n protected param: FSRSParameters;\n private readonly intervalModifier: number;\n protected seed?: string;\n\n constructor(param: Partial<FSRSParameters>) {\n this.param = generatorParameters(param);\n // Ref: https://github.com/open-spaced-repetition/py-fsrs/blob/ecd68e453611eb808c7367c7a5312d7cadeedf5c/src/fsrs/fsrs.py#L79\n // The formula used is : I(r,s)=9 \\cdot s \\cdot (\\frac{1}{r}-1)\n this.intervalModifier = 9 * (1 / this.param.request_retention - 1);\n }\n\n init_ds(s: SchedulingCard): void {\n s.again.difficulty = this.init_difficulty(Rating.Again);\n s.again.stability = this.init_stability(Rating.Again);\n s.hard.difficulty = this.init_difficulty(Rating.Hard);\n s.hard.stability = this.init_stability(Rating.Hard);\n s.good.difficulty = this.init_difficulty(Rating.Good);\n s.good.stability = this.init_stability(Rating.Good);\n s.easy.difficulty = this.init_difficulty(Rating.Easy);\n s.easy.stability = this.init_stability(Rating.Easy);\n }\n\n /**\n * Updates the difficulty and stability values of the scheduling card based on the last difficulty,\n * last stability, and the current retrievability.\n * @param {SchedulingCard} s scheduling Card\n * @param {number} last_d Difficulty\n * @param {number} last_s Stability\n * @param retrievability Retrievability\n */\n next_ds(\n s: SchedulingCard,\n last_d: number,\n last_s: number,\n retrievability: number,\n ): void {\n s.again.difficulty = this.next_difficulty(last_d, Rating.Again);\n s.again.stability = this.next_forget_stability(\n last_d,\n last_s,\n retrievability,\n );\n s.hard.difficulty = this.next_difficulty(last_d, Rating.Hard);\n s.hard.stability = this.next_recall_stability(\n last_d,\n last_s,\n retrievability,\n Rating.Hard,\n );\n s.good.difficulty = this.next_difficulty(last_d, Rating.Good);\n s.good.stability = this.next_recall_stability(\n last_d,\n last_s,\n retrievability,\n Rating.Good,\n );\n s.easy.difficulty = this.next_difficulty(last_d, Rating.Easy);\n s.easy.stability = this.next_recall_stability(\n last_d,\n last_s,\n retrievability,\n Rating.Easy,\n );\n }\n\n /**\n * The formula used is :\n * S_0(G) = w_{G-1}\n * \\max \\{S_0,0.1\\}\n * @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]\n * @return Stability (interval when R=90%)\n */\n init_stability(g: Grade): number {\n return Math.max(this.param.w[g - 1], 0.1);\n }\n\n /**\n * The formula used is :\n * $$D_0(G) = w_4 - (G-3) \\cdot w_5$$\n * $$\\min \\{\\max \\{D_0(G),1\\},10\\}$$\n * where the D_0(3)=w_4 when the first rating is good.\n * @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]\n * @return {number} Difficulty D \\in [1,10]\n */\n init_difficulty(g: Grade): number {\n return Math.min(\n Math.max(this.param.w[4] - (g - 3) * this.param.w[5], 1),\n 10,\n );\n }\n\n /**\n * If fuzzing is disabled or ivl is less than 2.5, it returns the original interval.\n * @param {number} ivl - The interval to be fuzzed.\n * @return {number} - The fuzzed interval.\n **/\n apply_fuzz(ivl: number): number {\n if (!this.param.enable_fuzz || ivl < 2.5) return ivl;\n const generator = pseudorandom(this.seed);\n const fuzz_factor = generator();\n ivl = Math.round(ivl);\n const min_ivl = Math.max(2, Math.round(ivl * 0.95 - 1));\n const max_ivl = Math.round(ivl * 1.05 + 1);\n return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl);\n }\n\n /**\n * Ref:\n * constructor(param: Partial<FSRSParameters>)\n * this.intervalModifier = 9 * (1 / this.param.request_retention - 1);\n * @param {number} s - Stability (interval when R=90%)\n */\n next_interval(s: number): int {\n const newInterval = this.apply_fuzz(s * this.intervalModifier);\n return Math.min(\n Math.max(Math.round(newInterval), 1),\n this.param.maximum_interval,\n ) as int;\n }\n\n /**\n * The formula used is :\n * $$next_d = D - w_6 \\cdot (R - 2)$$\n * $$D^\\prime(D,R) = w_5 \\cdot D_0(2) +(1 - w_5) \\cdot next_d$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]\n * @return {number} next_D\n */\n next_difficulty(d: number, g: Grade): number {\n const next_d = d - this.param.w[6] * (g - 3);\n return this.constrain_difficulty(\n this.mean_reversion(this.param.w[4], next_d),\n );\n }\n\n /**\n * The formula used is :\n * $$\\min \\{\\max \\{D_0,1\\},10\\}$$\n * @param {number} difficulty D \\in [1,10]\n */\n constrain_difficulty(difficulty: number): number {\n return Math.min(Math.max(Number(difficulty.toFixed(2)), 1), 10);\n }\n\n /**\n * The formula used is :\n * $$w_7 \\cdot init +(1 - w_7) \\cdot current$$\n * @param {number} init $$w_2 : D_0(3) = w_2 + (R-2) \\cdot w_3= w_2$$\n * @param {number} current $$D - w_6 \\cdot (R - 2)$$\n * @return {number} difficulty\n */\n mean_reversion(init: number, current: number): number {\n return this.param.w[7] * init + (1 - this.param.w[7]) * current;\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_r(D,S,R,G) = S\\cdot(e^{w_8}\\cdot (11-D)\\cdot S^{-w_9}\\cdot(e^{w_10\\cdot(1-R)}-1)\\cdot w_15(if G=2) \\cdot w_16(if G=4)+1)$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @param {Grade} g Grade (Rating[0.again,1.hard,2.good,3.easy])\n * @return {number} S^\\prime_r new stability after recall\n */\n next_recall_stability(d: number, s: number, r: number, g: Grade): number {\n const hard_penalty = Rating.Hard === g ? this.param.w[15] : 1;\n const easy_bound = Rating.Easy === g ? this.param.w[16] : 1;\n return (\n s *\n (1 +\n Math.exp(this.param.w[8]) *\n (11 - d) *\n Math.pow(s, -this.param.w[9]) *\n (Math.exp((1 - r) * this.param.w[10]) - 1) *\n hard_penalty *\n easy_bound)\n );\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_f(D,S,R) = w_11\\cdot D^{-w_{12}}\\cdot ((S+1)^{w_{13}}-1) \\cdot e^{w_{14}\\cdot(1-R)}.$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @return {number} S^\\prime_f new stability after forgetting\n */\n next_forget_stability(d: number, s: number, r: number): number {\n return (\n this.param.w[11] *\n Math.pow(d, -this.param.w[12]) *\n (Math.pow(s + 1, this.param.w[13]) - 1) *\n Math.exp((1 - r) * this.param.w[14])\n );\n }\n\n /**\n * The formula used is :\n * $$R(t,S) = (1 + \\frac{t}{9 \\cdot S})^{-1},$$\n * @param {number} t t days since the last review\n * @param {number} s Stability (interval when R=90%)\n * @return {number} r Retrievability (probability of recall)\n */\n current_retrievability(t: number, s: number): number {\n return Math.pow(1 + t / (9 * s), -1);\n }\n}\n","import { SchedulingCard } from \"./scheduler\";\nimport { fixDate, fixRating, fixState } from \"./help\";\nimport {\n Card,\n CardInput,\n DateInput,\n FSRSParameters,\n Rating,\n RecordLog,\n RecordLogItem,\n ReviewLog,\n ReviewLogInput,\n State,\n} from \"./models\";\nimport type { int } from \"./type\";\nimport { FSRSAlgorithm } from \"./algorithm\";\n\nexport class FSRS extends FSRSAlgorithm {\n constructor(param: Partial<FSRSParameters>) {\n super(param);\n }\n\n private preProcessCard(_card: CardInput): Card {\n return {\n ..._card,\n state: fixState(_card.state),\n due: fixDate(_card.due),\n last_review: _card.last_review ? fixDate(_card.last_review) : undefined,\n };\n }\n\n private preProcessDate(_date: DateInput): Date {\n return fixDate(_date);\n }\n\n private preProcessLog(_log: ReviewLogInput): ReviewLog {\n return {\n ..._log,\n rating: fixRating(_log.rating),\n state: fixState(_log.state),\n review: fixDate(_log.review),\n };\n }\n\n repeat = (card: CardInput, now: DateInput): RecordLog => {\n card = this.preProcessCard(card);\n now = this.preProcessDate(now);\n const s = new SchedulingCard(card, now).update_state(card.state);\n this.seed = String(now.getTime()) + String(card.reps);\n let easy_interval, good_interval, hard_interval;\n switch (card.state) {\n case State.New:\n this.init_ds(s);\n s.again.due = now.scheduler(1 as int);\n s.hard.due = now.scheduler(5 as int);\n s.good.due = now.scheduler(10 as int);\n easy_interval = this.next_interval(s.easy.stability);\n s.easy.scheduled_days = easy_interval;\n s.easy.due = now.scheduler(easy_interval, true);\n break;\n case State.Learning:\n case State.Relearning:\n hard_interval = 0;\n good_interval = this.next_interval(s.good.stability);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n case State.Review: {\n const interval = card.elapsed_days;\n const last_d = card.difficulty;\n const last_s = card.stability;\n const retrievability = this.current_retrievability(interval, last_s);\n this.next_ds(s, last_d, last_s, retrievability);\n hard_interval = this.next_interval(s.hard.stability);\n good_interval = this.next_interval(s.good.stability);\n hard_interval = Math.min(hard_interval, good_interval);\n good_interval = Math.max(good_interval, hard_interval + 1);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n }\n }\n return s.record_log(card, now);\n };\n\n get_retrievability = (card: Card, now: Date): undefined | string => {\n card = this.preProcessCard(card);\n now = this.preProcessDate(now);\n if (card.state !== State.Review) {\n return undefined;\n }\n const t = Math.max(now.diff(card.last_review as Date, \"days\"), 0);\n return (\n (this.current_retrievability(t, card.stability) * 100).toFixed(2) + \"%\"\n );\n };\n\n rollback = (card: CardInput, log: ReviewLogInput): Card => {\n card = this.preProcessCard(card);\n log = this.preProcessLog(log);\n if (log.rating === Rating.Manual) {\n throw new Error(\"Cannot rollback a manual rating\");\n }\n let last_due, last_review, last_lapses;\n switch (log.state) {\n case State.New:\n last_due = log.due;\n last_review = undefined;\n last_lapses = 0;\n break;\n case State.Learning:\n case State.Relearning:\n case State.Review:\n last_due = log.review;\n last_review = log.due;\n last_lapses =\n card.lapses -\n (log.rating === Rating.Again && log.state === State.Review ? 1 : 0);\n break;\n }\n\n return {\n ...card,\n due: last_due,\n stability: log.stability,\n difficulty: log.difficulty,\n elapsed_days: log.last_elapsed_days,\n scheduled_days: log.scheduled_days,\n reps: Math.max(0, card.reps - 1),\n lapses: Math.max(0, last_lapses),\n state: log.state,\n last_review: last_review,\n };\n };\n\n forget = (\n card: CardInput,\n now: DateInput,\n reset_count: boolean = false,\n ): RecordLogItem => {\n card = this.preProcessCard(card);\n now = this.preProcessDate(now);\n const scheduled_days =\n card.state === State.New ? 0 : now.diff(card.last_review as Date, \"days\");\n const forget_log: ReviewLog = {\n rating: Rating.Manual,\n state: card.state,\n due: card.due,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: 0,\n last_elapsed_days: card.elapsed_days,\n scheduled_days: scheduled_days,\n review: now,\n };\n const forget_card: Card = {\n ...card,\n due: now,\n stability: 0,\n difficulty: 0,\n elapsed_days: 0,\n scheduled_days: 0,\n reps: reset_count ? 0 : card.reps,\n lapses: reset_count ? 0 : card.lapses,\n state: State.New,\n last_review: card.last_review,\n };\n return { card: forget_card, log: forget_log };\n };\n}\n\nexport const fsrs = (params?: Partial<FSRSParameters>) => {\n return new FSRS(params || {});\n};\n"],"names":["State","Rating","date_scheduler","now","t","isDay","Date","getTime","date_diff","pre","unit","Error","diff","r","Math","floor","formatDate","date","year","getFullYear","month","getMonth","day","getDate","hours","getHours","minutes","getMinutes","seconds","getSeconds","padZero","num","prototype","scheduler","this","format","dueFormat","last_review","timeUnit","show_diff_message","TIMEUNIT","TIMEUNITFORMAT","due","fixDate","length","i","value","timestamp","parse","isNaN","fixState","firstLetter","charAt","toUpperCase","restOfString","slice","toLowerCase","ret","undefined","fixRating","Grades","Again","Hard","Good","Easy","SchedulingCard","again","hard","good","easy","last_elapsed_days","copy","card","constructor","elapsed_days","state","New","reps","update_state","Learning","Review","Relearning","lapses","schedule","hard_interval","good_interval","easy_interval","scheduled_days","record_log","log","rating","stability","difficulty","review","default_w","generatorParameters","props","request_retention","maximum_interval","w","enable_fuzz","FSRSAlgorithm","param","intervalModifier","seed","init_ds","s","init_difficulty","init_stability","next_ds","last_d","last_s","retrievability","next_difficulty","next_forget_stability","next_recall_stability","g","max","min","apply_fuzz","ivl","fuzz_factor","pseudorandom","generator","round","min_ivl","max_ivl","next_interval","newInterval","d","next_d","constrain_difficulty","mean_reversion","Number","toFixed","init","current","hard_penalty","easy_bound","exp","pow","current_retrievability","FSRS","super","preProcessCard","_card","preProcessDate","_date","preProcessLog","_log","repeat","String","interval","get_retrievability","rollback","Manual","last_due","last_lapses","forget","reset_count","forget_log","params"],"mappings":"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"}
1
+ {"version":3,"file":"ts-fsrs.cjs.js","sources":["../src/fsrs/models.ts","../src/fsrs/help.ts","../src/fsrs/scheduler.ts","../src/fsrs/default.ts","../src/fsrs/algorithm.ts","../src/fsrs/fsrs.ts"],"sourcesContent":["export type StateType = \"New\" | \"Learning\" | \"Review\" | \"Relearning\";\n\nexport enum State {\n New = 0,\n Learning = 1,\n Review = 2,\n Relearning = 3,\n}\n\nexport type RatingType = \"Manual\" | \"Again\" | \"Hard\" | \"Good\" | \"Easy\";\n\nexport enum Rating {\n Manual = 0,\n Again = 1,\n Hard = 2,\n Good = 3,\n Easy = 4,\n}\n\ntype ExcludeManual<T> = Exclude<T, Rating.Manual>;\n\nexport type Grade = ExcludeManual<Rating>;\n\nexport interface ReviewLog {\n rating: Rating; // Rating of the review (Again, Hard, Good, Easy)\n state: State; // State of the review (New, Learning, Review, Relearning)\n due: Date; // Date of the last scheduling\n stability: number; // Memory stability during the review\n difficulty: number; // Difficulty of the card during the review\n elapsed_days: number; // Number of days elapsed since the last review\n last_elapsed_days: number; // Number of days between the last two reviews\n scheduled_days: number; // Number of days until the next review\n review: Date; // Date of the review\n}\nexport type RecordLogItem = {\n card: Card;\n log: ReviewLog;\n};\nexport type RecordLog = {\n [key in Grade]: RecordLogItem;\n};\n\nexport interface Card {\n due: Date; // Due date\n stability: number; // Stability\n difficulty: number; // Difficulty level\n elapsed_days: number; // Number of days elapsed\n scheduled_days: number; // Number of days scheduled\n reps: number; // Repetition count\n lapses: number; // Number of lapses or mistakes\n state: State; // Card's state (New, Learning, Review, Relearning)\n last_review?: Date; // Date of the last review (optional)\n}\n\nexport type CardInput = Card & { state: StateType | State };\nexport type DateInput = Date | number | string;\nexport type ReviewLogInput = ReviewLog & {\n rating: RatingType | Rating;\n state: StateType | State;\n};\n\nexport interface FSRSParameters {\n request_retention: number;\n maximum_interval: number;\n w: number[];\n enable_fuzz: boolean;\n}\n","import type { int, unit } from \"./type\";\nimport { Grade, Rating, State } from \"./models\";\n\ndeclare global {\n export interface Date {\n scheduler(t: int, isDay?: boolean): Date;\n\n diff(pre: Date, unit: unit): int;\n\n format(): string;\n\n dueFormat(last_review: Date, unit?: boolean, timeUnit?: string[]): string;\n }\n}\n\nDate.prototype.scheduler = function (t: int, isDay?: boolean): Date {\n return date_scheduler(this, t, isDay);\n};\n\n/**\n * 当前时间与之前的时间差值\n * @param pre 比当前时间还要之前\n * @param unit 单位: days | minutes\n */\nDate.prototype.diff = function (pre: Date, unit: unit): int {\n return date_diff(this, pre, unit) as int;\n};\n\nDate.prototype.format = function (): string {\n return formatDate(this);\n};\n\nDate.prototype.dueFormat = function (\n last_review: Date,\n unit?: boolean,\n timeUnit?: string[],\n) {\n return show_diff_message(this, last_review, unit, timeUnit);\n};\n\n/**\n * 计算日期和时间的偏移,并返回一个新的日期对象。\n * @param now 当前日期和时间\n * @param t 时间偏移量,当 isDay 为 true 时表示天数,为 false 时表示分钟\n * @param isDay (可选)是否按天数单位进行偏移,默认为 false,表示按分钟单位计算偏移\n * @returns 偏移后的日期和时间对象\n */\nexport function date_scheduler(now: Date, t: number, isDay?: boolean): Date {\n return new Date(\n isDay\n ? now.getTime() + t * 24 * 60 * 60 * 1000\n : now.getTime() + t * 60 * 1000,\n );\n}\n\nexport function date_diff(now: Date, pre: Date, unit: unit): number {\n if (!now || !pre) {\n throw new Error(\"Invalid date\");\n }\n const diff = now.getTime() - pre.getTime();\n let r = 0;\n switch (unit) {\n case \"days\":\n r = Math.floor(diff / (24 * 60 * 60 * 1000));\n break;\n case \"minutes\":\n r = Math.floor(diff / (60 * 1000));\n break;\n }\n return r;\n}\n\nexport function formatDate(date: Date): string {\n const year: number = date.getFullYear();\n const month: number = date.getMonth() + 1;\n const day: number = date.getDate();\n const hours: number = date.getHours();\n const minutes: number = date.getMinutes();\n const seconds: number = date.getSeconds();\n\n return `${year}-${padZero(month)}-${padZero(day)} ${padZero(hours)}:${padZero(\n minutes,\n )}:${padZero(seconds)}`;\n}\n\nfunction padZero(num: number): string {\n return num < 10 ? `0${num}` : `${num}`;\n}\n\nconst TIMEUNIT = [60, 60, 24, 31, 12];\nconst TIMEUNITFORMAT = [\"second\", \"min\", \"hour\", \"day\", \"month\", \"year\"];\n\nexport function show_diff_message(\n due: Date,\n last_review: Date,\n unit?: boolean,\n timeUnit: string[] = TIMEUNITFORMAT,\n): string {\n due = fixDate(due);\n last_review = fixDate(last_review);\n if (timeUnit.length !== TIMEUNITFORMAT.length) {\n timeUnit = TIMEUNITFORMAT;\n }\n let diff = due.getTime() - last_review.getTime();\n let i;\n diff /= 1000;\n for (i = 0; i < TIMEUNIT.length; i++) {\n if (diff < TIMEUNIT[i]) {\n break;\n } else {\n diff /= TIMEUNIT[i];\n }\n }\n return `${Math.floor(diff)}${unit ? timeUnit[i] : \"\"}`;\n}\n\nexport function fixDate(value: unknown) {\n if (typeof value === \"object\" && value instanceof Date) {\n return value;\n } else if (typeof value === \"string\") {\n const timestamp = Date.parse(value);\n if (!isNaN(timestamp)) {\n return new Date(timestamp);\n } else {\n throw new Error(`Invalid date:[${value}]`);\n }\n } else if (typeof value === \"number\") {\n return new Date(value);\n }\n throw new Error(`Invalid date:[${value}]`);\n}\n\nexport function fixState(value: unknown): State {\n if (typeof value === \"string\") {\n const firstLetter = value.charAt(0).toUpperCase();\n const restOfString = value.slice(1).toLowerCase();\n const ret = State[`${firstLetter}${restOfString}` as keyof typeof State];\n if (ret === undefined) {\n throw new Error(`Invalid state:[${value}]`);\n }\n return ret;\n } else if (typeof value === \"number\") {\n return value as State;\n }\n throw new Error(`Invalid state:[${value}]`);\n}\n\nexport function fixRating(value: unknown): Rating {\n if (typeof value === \"string\") {\n const firstLetter = value.charAt(0).toUpperCase();\n const restOfString = value.slice(1).toLowerCase();\n const ret = Rating[`${firstLetter}${restOfString}` as keyof typeof Rating];\n if (ret === undefined) {\n throw new Error(`Invalid rating:[${value}]`);\n }\n return ret;\n } else if (typeof value === \"number\") {\n return value as Rating;\n }\n throw new Error(`Invalid rating:[${value}]`);\n}\n\nexport const Grades: Grade[] = [\n Rating.Again,\n Rating.Hard,\n Rating.Good,\n Rating.Easy,\n];\n","import { Card, Rating, RecordLog, State } from \"./models\";\nimport { date_scheduler } from \"./help\";\n\nexport class SchedulingCard {\n again: Card;\n hard: Card;\n good: Card;\n easy: Card;\n last_review: Date;\n last_elapsed_days: number;\n\n private copy(card: Card): Card {\n return {\n ...card,\n };\n }\n\n constructor(card: Card, now: Date) {\n this.last_review = card.last_review || card.due;\n this.last_elapsed_days = card.elapsed_days;\n card.elapsed_days =\n card.state === State.New ? 0 : now.diff(card.last_review as Date, \"days\"); //相距时间\n card.last_review = now; // 上次复习时间\n card.reps += 1;\n this.again = this.copy(card);\n this.hard = this.copy(card);\n this.good = this.copy(card);\n this.easy = this.copy(card);\n }\n\n update_state(state: State) {\n if (state === State.New) {\n this.again.state = State.Learning;\n this.hard.state = State.Learning;\n this.good.state = State.Learning;\n this.easy.state = State.Review;\n } else if (state === State.Learning || state === State.Relearning) {\n this.again.state = state;\n this.hard.state = state;\n this.good.state = State.Review;\n this.easy.state = State.Review;\n } else if (state === State.Review) {\n this.again.state = State.Relearning;\n this.hard.state = State.Review;\n this.good.state = State.Review;\n this.easy.state = State.Review;\n this.again.lapses += 1;\n }\n return this;\n }\n\n schedule(\n now: Date,\n hard_interval: number,\n good_interval: number,\n easy_interval: number,\n ): SchedulingCard {\n this.again.scheduled_days = 0;\n this.hard.scheduled_days = hard_interval;\n this.good.scheduled_days = good_interval;\n this.easy.scheduled_days = easy_interval;\n this.again.due = date_scheduler(now, 5);\n this.hard.due =\n hard_interval > 0\n ? date_scheduler(now, hard_interval, true)\n : date_scheduler(now, 10);\n this.good.due = date_scheduler(now, good_interval, true);\n this.easy.due = date_scheduler(now, easy_interval, true);\n return this;\n }\n\n record_log(card: Card, now: Date): RecordLog {\n return {\n [Rating.Again]: {\n card: this.again,\n log: {\n rating: Rating.Again,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n [Rating.Hard]: {\n card: this.hard,\n log: {\n rating: Rating.Hard,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n [Rating.Good]: {\n card: this.good,\n log: {\n rating: Rating.Good,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n [Rating.Easy]: {\n card: this.easy,\n log: {\n rating: Rating.Easy,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n };\n }\n}\n","import { Card, DateInput, FSRSParameters, State } from \"./models\";\nimport { fixDate } from \"./help\";\n\nexport const default_request_retention = 0.9;\nexport const default_maximum_interval = 36500;\nexport const default_w = [\n 0.4, 0.6, 2.4, 5.8, 4.93, 0.94, 0.86, 0.01, 1.49, 0.14, 0.94, 2.18, 0.05,\n 0.34, 1.26, 0.29, 2.61,\n];\nexport const default_enable_fuzz = false;\n\nexport const FSRSVersion: string = \"3.2.1\";\n\nexport const generatorParameters = (\n props?: Partial<FSRSParameters>,\n): FSRSParameters => {\n return {\n request_retention: props?.request_retention || default_request_retention,\n maximum_interval: props?.maximum_interval || default_maximum_interval,\n w: props?.w || default_w,\n enable_fuzz: props?.enable_fuzz || default_enable_fuzz,\n };\n};\n\nexport const createEmptyCard = (now?: DateInput): Card => {\n return {\n due: now ? fixDate(now) : new Date(),\n stability: 0,\n difficulty: 0,\n elapsed_days: 0,\n scheduled_days: 0,\n reps: 0,\n lapses: 0,\n state: State.New,\n last_review: undefined,\n };\n};\n","import pseudorandom from \"seedrandom\";\nimport { generatorParameters } from \"./default\";\nimport { SchedulingCard } from \"./scheduler\";\nimport { FSRSParameters, Grade, Rating } from \"./models\";\nimport type { int } from \"./type\";\n\n// Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-v4\nexport class FSRSAlgorithm {\n protected param: FSRSParameters;\n private readonly intervalModifier: number;\n protected seed?: string;\n\n constructor(param: Partial<FSRSParameters>) {\n this.param = generatorParameters(param);\n // Ref: https://github.com/open-spaced-repetition/py-fsrs/blob/ecd68e453611eb808c7367c7a5312d7cadeedf5c/src/fsrs/fsrs.py#L79\n // The formula used is : I(r,s)=9 \\cdot s \\cdot (\\frac{1}{r}-1)\n this.intervalModifier = 9 * (1 / this.param.request_retention - 1);\n }\n\n init_ds(s: SchedulingCard): void {\n s.again.difficulty = this.init_difficulty(Rating.Again);\n s.again.stability = this.init_stability(Rating.Again);\n s.hard.difficulty = this.init_difficulty(Rating.Hard);\n s.hard.stability = this.init_stability(Rating.Hard);\n s.good.difficulty = this.init_difficulty(Rating.Good);\n s.good.stability = this.init_stability(Rating.Good);\n s.easy.difficulty = this.init_difficulty(Rating.Easy);\n s.easy.stability = this.init_stability(Rating.Easy);\n }\n\n /**\n * Updates the difficulty and stability values of the scheduling card based on the last difficulty,\n * last stability, and the current retrievability.\n * @param {SchedulingCard} s scheduling Card\n * @param {number} last_d Difficulty\n * @param {number} last_s Stability\n * @param retrievability Retrievability\n */\n next_ds(\n s: SchedulingCard,\n last_d: number,\n last_s: number,\n retrievability: number,\n ): void {\n s.again.difficulty = this.next_difficulty(last_d, Rating.Again);\n s.again.stability = this.next_forget_stability(\n last_d,\n last_s,\n retrievability,\n );\n s.hard.difficulty = this.next_difficulty(last_d, Rating.Hard);\n s.hard.stability = this.next_recall_stability(\n last_d,\n last_s,\n retrievability,\n Rating.Hard,\n );\n s.good.difficulty = this.next_difficulty(last_d, Rating.Good);\n s.good.stability = this.next_recall_stability(\n last_d,\n last_s,\n retrievability,\n Rating.Good,\n );\n s.easy.difficulty = this.next_difficulty(last_d, Rating.Easy);\n s.easy.stability = this.next_recall_stability(\n last_d,\n last_s,\n retrievability,\n Rating.Easy,\n );\n }\n\n /**\n * The formula used is :\n * S_0(G) = w_{G-1}\n * \\max \\{S_0,0.1\\}\n * @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]\n * @return Stability (interval when R=90%)\n */\n init_stability(g: Grade): number {\n return Math.max(this.param.w[g - 1], 0.1);\n }\n\n /**\n * The formula used is :\n * $$D_0(G) = w_4 - (G-3) \\cdot w_5$$\n * $$\\min \\{\\max \\{D_0(G),1\\},10\\}$$\n * where the D_0(3)=w_4 when the first rating is good.\n * @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]\n * @return {number} Difficulty D \\in [1,10]\n */\n init_difficulty(g: Grade): number {\n return Math.min(\n Math.max(this.param.w[4] - (g - 3) * this.param.w[5], 1),\n 10,\n );\n }\n\n /**\n * If fuzzing is disabled or ivl is less than 2.5, it returns the original interval.\n * @param {number} ivl - The interval to be fuzzed.\n * @return {number} - The fuzzed interval.\n **/\n apply_fuzz(ivl: number): number {\n if (!this.param.enable_fuzz || ivl < 2.5) return ivl;\n const generator = pseudorandom(this.seed);\n const fuzz_factor = generator();\n ivl = Math.round(ivl);\n const min_ivl = Math.max(2, Math.round(ivl * 0.95 - 1));\n const max_ivl = Math.round(ivl * 1.05 + 1);\n return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl);\n }\n\n /**\n * Ref:\n * constructor(param: Partial<FSRSParameters>)\n * this.intervalModifier = 9 * (1 / this.param.request_retention - 1);\n * @param {number} s - Stability (interval when R=90%)\n */\n next_interval(s: number): int {\n const newInterval = this.apply_fuzz(s * this.intervalModifier);\n return Math.min(\n Math.max(Math.round(newInterval), 1),\n this.param.maximum_interval,\n ) as int;\n }\n\n /**\n * The formula used is :\n * $$next_d = D - w_6 \\cdot (R - 2)$$\n * $$D^\\prime(D,R) = w_5 \\cdot D_0(2) +(1 - w_5) \\cdot next_d$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]\n * @return {number} next_D\n */\n next_difficulty(d: number, g: Grade): number {\n const next_d = d - this.param.w[6] * (g - 3);\n return this.constrain_difficulty(\n this.mean_reversion(this.param.w[4], next_d),\n );\n }\n\n /**\n * The formula used is :\n * $$\\min \\{\\max \\{D_0,1\\},10\\}$$\n * @param {number} difficulty D \\in [1,10]\n */\n constrain_difficulty(difficulty: number): number {\n return Math.min(Math.max(Number(difficulty.toFixed(2)), 1), 10);\n }\n\n /**\n * The formula used is :\n * $$w_7 \\cdot init +(1 - w_7) \\cdot current$$\n * @param {number} init $$w_2 : D_0(3) = w_2 + (R-2) \\cdot w_3= w_2$$\n * @param {number} current $$D - w_6 \\cdot (R - 2)$$\n * @return {number} difficulty\n */\n mean_reversion(init: number, current: number): number {\n return this.param.w[7] * init + (1 - this.param.w[7]) * current;\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_r(D,S,R,G) = S\\cdot(e^{w_8}\\cdot (11-D)\\cdot S^{-w_9}\\cdot(e^{w_10\\cdot(1-R)}-1)\\cdot w_15(if G=2) \\cdot w_16(if G=4)+1)$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @param {Grade} g Grade (Rating[0.again,1.hard,2.good,3.easy])\n * @return {number} S^\\prime_r new stability after recall\n */\n next_recall_stability(d: number, s: number, r: number, g: Grade): number {\n const hard_penalty = Rating.Hard === g ? this.param.w[15] : 1;\n const easy_bound = Rating.Easy === g ? this.param.w[16] : 1;\n return (\n s *\n (1 +\n Math.exp(this.param.w[8]) *\n (11 - d) *\n Math.pow(s, -this.param.w[9]) *\n (Math.exp((1 - r) * this.param.w[10]) - 1) *\n hard_penalty *\n easy_bound)\n );\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_f(D,S,R) = w_11\\cdot D^{-w_{12}}\\cdot ((S+1)^{w_{13}}-1) \\cdot e^{w_{14}\\cdot(1-R)}.$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @return {number} S^\\prime_f new stability after forgetting\n */\n next_forget_stability(d: number, s: number, r: number): number {\n return (\n this.param.w[11] *\n Math.pow(d, -this.param.w[12]) *\n (Math.pow(s + 1, this.param.w[13]) - 1) *\n Math.exp((1 - r) * this.param.w[14])\n );\n }\n\n /**\n * The formula used is :\n * $$R(t,S) = (1 + \\frac{t}{9 \\cdot S})^{-1},$$\n * @param {number} t t days since the last review\n * @param {number} s Stability (interval when R=90%)\n * @return {number} r Retrievability (probability of recall)\n */\n current_retrievability(t: number, s: number): number {\n return Math.pow(1 + t / (9 * s), -1);\n }\n}\n","import { SchedulingCard } from \"./scheduler\";\nimport { fixDate, fixRating, fixState } from \"./help\";\nimport {\n Card,\n CardInput,\n DateInput,\n FSRSParameters,\n Rating,\n RecordLog,\n RecordLogItem,\n ReviewLog,\n ReviewLogInput,\n State,\n} from \"./models\";\nimport type { int } from \"./type\";\nimport { FSRSAlgorithm } from \"./algorithm\";\n\nexport class FSRS extends FSRSAlgorithm {\n constructor(param: Partial<FSRSParameters>) {\n super(param);\n }\n\n private preProcessCard(_card: CardInput): Card {\n return {\n ..._card,\n state: fixState(_card.state),\n due: fixDate(_card.due),\n last_review: _card.last_review ? fixDate(_card.last_review) : undefined,\n };\n }\n\n private preProcessDate(_date: DateInput): Date {\n return fixDate(_date);\n }\n\n private preProcessLog(_log: ReviewLogInput): ReviewLog {\n return {\n ..._log,\n rating: fixRating(_log.rating),\n state: fixState(_log.state),\n review: fixDate(_log.review),\n };\n }\n\n repeat = (card: CardInput, now: DateInput): RecordLog => {\n card = this.preProcessCard(card);\n now = this.preProcessDate(now);\n const s = new SchedulingCard(card, now).update_state(card.state);\n this.seed = String(now.getTime()) + String(card.reps);\n let easy_interval, good_interval, hard_interval;\n switch (card.state) {\n case State.New:\n this.init_ds(s);\n s.again.due = now.scheduler(1 as int);\n s.hard.due = now.scheduler(5 as int);\n s.good.due = now.scheduler(10 as int);\n easy_interval = this.next_interval(s.easy.stability);\n s.easy.scheduled_days = easy_interval;\n s.easy.due = now.scheduler(easy_interval, true);\n break;\n case State.Learning:\n case State.Relearning:\n hard_interval = 0;\n good_interval = this.next_interval(s.good.stability);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n case State.Review: {\n const interval = card.elapsed_days;\n const last_d = card.difficulty;\n const last_s = card.stability;\n const retrievability = this.current_retrievability(interval, last_s);\n this.next_ds(s, last_d, last_s, retrievability);\n hard_interval = this.next_interval(s.hard.stability);\n good_interval = this.next_interval(s.good.stability);\n hard_interval = Math.min(hard_interval, good_interval);\n good_interval = Math.max(good_interval, hard_interval + 1);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n }\n }\n return s.record_log(card, now);\n };\n\n get_retrievability = (card: Card, now: Date): undefined | string => {\n card = this.preProcessCard(card);\n now = this.preProcessDate(now);\n if (card.state !== State.Review) {\n return undefined;\n }\n const t = Math.max(now.diff(card.last_review as Date, \"days\"), 0);\n return (\n (this.current_retrievability(t, card.stability) * 100).toFixed(2) + \"%\"\n );\n };\n\n rollback = (card: CardInput, log: ReviewLogInput): Card => {\n card = this.preProcessCard(card);\n log = this.preProcessLog(log);\n if (log.rating === Rating.Manual) {\n throw new Error(\"Cannot rollback a manual rating\");\n }\n let last_due, last_review, last_lapses;\n switch (log.state) {\n case State.New:\n last_due = log.due;\n last_review = undefined;\n last_lapses = 0;\n break;\n case State.Learning:\n case State.Relearning:\n case State.Review:\n last_due = log.review;\n last_review = log.due;\n last_lapses =\n card.lapses -\n (log.rating === Rating.Again && log.state === State.Review ? 1 : 0);\n break;\n }\n\n return {\n ...card,\n due: last_due,\n stability: log.stability,\n difficulty: log.difficulty,\n elapsed_days: log.last_elapsed_days,\n scheduled_days: log.scheduled_days,\n reps: Math.max(0, card.reps - 1),\n lapses: Math.max(0, last_lapses),\n state: log.state,\n last_review: last_review,\n };\n };\n\n forget = (\n card: CardInput,\n now: DateInput,\n reset_count: boolean = false,\n ): RecordLogItem => {\n card = this.preProcessCard(card);\n now = this.preProcessDate(now);\n const scheduled_days =\n card.state === State.New ? 0 : now.diff(card.last_review as Date, \"days\");\n const forget_log: ReviewLog = {\n rating: Rating.Manual,\n state: card.state,\n due: card.due,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: 0,\n last_elapsed_days: card.elapsed_days,\n scheduled_days: scheduled_days,\n review: now,\n };\n const forget_card: Card = {\n ...card,\n due: now,\n stability: 0,\n difficulty: 0,\n elapsed_days: 0,\n scheduled_days: 0,\n reps: reset_count ? 0 : card.reps,\n lapses: reset_count ? 0 : card.lapses,\n state: State.New,\n last_review: card.last_review,\n };\n return { card: forget_card, log: forget_log };\n };\n}\n\nexport const fsrs = (params?: Partial<FSRSParameters>) => {\n return new FSRS(params || {});\n};\n"],"names":["State","Rating","date_scheduler","now","t","isDay","Date","getTime","date_diff","pre","unit","Error","diff","r","Math","floor","formatDate","date","year","getFullYear","month","getMonth","day","getDate","hours","getHours","minutes","getMinutes","seconds","getSeconds","padZero","num","prototype","scheduler","this","format","dueFormat","last_review","timeUnit","show_diff_message","TIMEUNIT","TIMEUNITFORMAT","due","fixDate","length","i","value","timestamp","parse","isNaN","fixState","firstLetter","charAt","toUpperCase","restOfString","slice","toLowerCase","ret","undefined","fixRating","Grades","Again","Hard","Good","Easy","SchedulingCard","again","hard","good","easy","last_elapsed_days","copy","card","constructor","elapsed_days","state","New","reps","update_state","Learning","Review","Relearning","lapses","schedule","hard_interval","good_interval","easy_interval","scheduled_days","record_log","log","rating","stability","difficulty","review","default_w","generatorParameters","props","request_retention","maximum_interval","w","enable_fuzz","FSRSAlgorithm","param","intervalModifier","seed","init_ds","s","init_difficulty","init_stability","next_ds","last_d","last_s","retrievability","next_difficulty","next_forget_stability","next_recall_stability","g","max","min","apply_fuzz","ivl","fuzz_factor","pseudorandom","generator","round","min_ivl","max_ivl","next_interval","newInterval","d","next_d","constrain_difficulty","mean_reversion","Number","toFixed","init","current","hard_penalty","easy_bound","exp","pow","current_retrievability","FSRS","super","preProcessCard","_card","preProcessDate","_date","preProcessLog","_log","repeat","String","interval","get_retrievability","rollback","Manual","last_due","last_lapses","forget","reset_count","forget_log","params"],"mappings":"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"}
package/dist/ts-fsrs.js CHANGED
@@ -1,2 +1,2 @@
1
- import t from"seedrandom";var e,a;function i(t,e,a){return new Date(a?t.getTime()+24*e*60*60*1e3:t.getTime()+60*e*1e3)}function s(t,e,a){if(!t||!e)throw new Error("Invalid date");const i=t.getTime()-e.getTime();let s=0;switch(a){case"days":s=Math.floor(i/864e5);break;case"minutes":s=Math.floor(i/6e4)}return s}function r(t){const e=t.getFullYear(),a=t.getMonth()+1,i=t.getDate(),s=t.getHours(),r=t.getMinutes(),n=t.getSeconds();return`${e}-${d(a)}-${d(i)} ${d(s)}:${d(r)}:${d(n)}`}function d(t){return t<10?`0${t}`:`${t}`}!function(t){t[t.New=0]="New",t[t.Learning=1]="Learning",t[t.Review=2]="Review",t[t.Relearning=3]="Relearning"}(e||(e={})),function(t){t[t.Manual=0]="Manual",t[t.Again=1]="Again",t[t.Hard=2]="Hard",t[t.Good=3]="Good",t[t.Easy=4]="Easy"}(a||(a={})),Date.prototype.scheduler=function(t,e){return i(this,t,e)},Date.prototype.diff=function(t,e){return s(this,t,e)},Date.prototype.format=function(){return r(this)},Date.prototype.dueFormat=function(t,e,a){return o(this,t,e,a)};const n=[60,60,24,31,12],l=["second","min","hour","day","month","year"];function o(t,e,a,i=l){t=h(t),e=h(e),i.length!==l.length&&(i=l);let s,r=t.getTime()-e.getTime();for(r/=1e3,s=0;s<n.length&&!(r<n[s]);s++)r/=n[s];return`${Math.floor(r)}${a?i[s]:""}`}function h(t){if("object"==typeof t&&t instanceof Date)return t;if("string"==typeof t){const e=Date.parse(t);if(isNaN(e))throw new Error(`Invalid date:[${t}]`);return new Date(e)}if("number"==typeof t)return new Date(t);throw new Error(`Invalid date:[${t}]`)}function y(t){if("string"==typeof t){const a=t.charAt(0).toUpperCase(),i=t.slice(1).toLowerCase(),s=e[`${a}${i}`];if(void 0===s)throw new Error(`Invalid state:[${t}]`);return s}if("number"==typeof t)return t;throw new Error(`Invalid state:[${t}]`)}function u(t){if("string"==typeof t){const e=t.charAt(0).toUpperCase(),i=t.slice(1).toLowerCase(),s=a[`${e}${i}`];if(void 0===s)throw new Error(`Invalid rating:[${t}]`);return s}if("number"==typeof t)return t;throw new Error(`Invalid rating:[${t}]`)}const c=[a.Again,a.Hard,a.Good,a.Easy];class _{again;hard;good;easy;last_review;last_elapsed_days;copy(t){return{...t}}constructor(t,a){this.last_review=t.last_review||t.due,this.last_elapsed_days=t.elapsed_days,t.elapsed_days=t.state===e.New?0:a.diff(t.last_review,"days"),t.last_review=a,t.reps+=1,this.again=this.copy(t),this.hard=this.copy(t),this.good=this.copy(t),this.easy=this.copy(t)}update_state(t){return t===e.New?(this.again.state=e.Learning,this.hard.state=e.Learning,this.good.state=e.Learning,this.easy.state=e.Review):t===e.Learning||t===e.Relearning?(this.again.state=t,this.hard.state=t,this.good.state=e.Review,this.easy.state=e.Review):t===e.Review&&(this.again.state=e.Relearning,this.hard.state=e.Review,this.good.state=e.Review,this.easy.state=e.Review,this.again.lapses+=1),this}schedule(t,e,a,s){return this.again.scheduled_days=0,this.hard.scheduled_days=e,this.good.scheduled_days=a,this.easy.scheduled_days=s,this.again.due=i(t,5),this.hard.due=e>0?i(t,e,!0):i(t,10),this.good.due=i(t,a,!0),this.easy.due=i(t,s,!0),this}record_log(t,e){return{[a.Again]:{card:this.again,log:{rating:a.Again,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}},[a.Hard]:{card:this.hard,log:{rating:a.Hard,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}},[a.Good]:{card:this.good,log:{rating:a.Good,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}},[a.Easy]:{card:this.easy,log:{rating:a.Easy,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}}}}}const f=.9,p=36500,g=[.4,.6,2.4,5.8,4.93,.94,.86,.01,1.49,.14,.94,2.18,.05,.34,1.26,.29,2.61],w=!1,v="3.2.0",m=t=>({request_retention:t?.request_retention||.9,maximum_interval:t?.maximum_interval||36500,w:t?.w||g,enable_fuzz:t?.enable_fuzz||false}),b=t=>({due:t?h(t):new Date,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:0,lapses:0,state:e.New,last_review:void 0});class x{param;intervalModifier;seed;constructor(t){this.param=m(t),this.intervalModifier=9*(1/this.param.request_retention-1)}init_ds(t){t.again.difficulty=this.init_difficulty(a.Again),t.again.stability=this.init_stability(a.Again),t.hard.difficulty=this.init_difficulty(a.Hard),t.hard.stability=this.init_stability(a.Hard),t.good.difficulty=this.init_difficulty(a.Good),t.good.stability=this.init_stability(a.Good),t.easy.difficulty=this.init_difficulty(a.Easy),t.easy.stability=this.init_stability(a.Easy)}next_ds(t,e,i,s){t.again.difficulty=this.next_difficulty(e,a.Again),t.again.stability=this.next_forget_stability(e,i,s),t.hard.difficulty=this.next_difficulty(e,a.Hard),t.hard.stability=this.next_recall_stability(e,i,s,a.Hard),t.good.difficulty=this.next_difficulty(e,a.Good),t.good.stability=this.next_recall_stability(e,i,s,a.Good),t.easy.difficulty=this.next_difficulty(e,a.Easy),t.easy.stability=this.next_recall_stability(e,i,s,a.Easy)}init_stability(t){return Math.max(this.param.w[t-1],.1)}init_difficulty(t){return Math.min(Math.max(this.param.w[4]-(t-3)*this.param.w[5],1),10)}apply_fuzz(e){if(!this.param.enable_fuzz||e<2.5)return e;const a=t(this.seed)();e=Math.round(e);const i=Math.max(2,Math.round(.95*e-1)),s=Math.round(1.05*e+1);return Math.floor(a*(s-i+1)+i)}next_interval(t){const e=this.apply_fuzz(t*this.intervalModifier);return Math.min(Math.max(Math.round(e),1),this.param.maximum_interval)}next_difficulty(t,e){const a=t-this.param.w[6]*(e-3);return this.constrain_difficulty(this.mean_reversion(this.param.w[4],a))}constrain_difficulty(t){return Math.min(Math.max(Number(t.toFixed(2)),1),10)}mean_reversion(t,e){return this.param.w[7]*t+(1-this.param.w[7])*e}next_recall_stability(t,e,i,s){const r=a.Hard===s?this.param.w[15]:1,d=a.Easy===s?this.param.w[16]:1;return e*(1+Math.exp(this.param.w[8])*(11-t)*Math.pow(e,-this.param.w[9])*(Math.exp((1-i)*this.param.w[10])-1)*r*d)}next_forget_stability(t,e,a){return this.param.w[11]*Math.pow(t,-this.param.w[12])*(Math.pow(e+1,this.param.w[13])-1)*Math.exp((1-a)*this.param.w[14])}current_retrievability(t,e){return Math.pow(1+t/(9*e),-1)}}class M extends x{constructor(t){super(t)}preProcessCard(t){return{...t,state:y(t.state),due:h(t.due),last_review:t.last_review?h(t.last_review):void 0}}preProcessDate(t){return h(t)}preProcessLog(t){return{...t,rating:u(t.rating),state:y(t.state),review:h(t.review)}}repeat=(t,a)=>{t=this.preProcessCard(t),a=this.preProcessDate(a);const i=new _(t,a).update_state(t.state);let s,r,d;switch(this.seed=String(a.getTime())+String(t.reps),t.state){case e.New:this.init_ds(i),i.again.due=a.scheduler(1),i.hard.due=a.scheduler(5),i.good.due=a.scheduler(10),s=this.next_interval(i.easy.stability),i.easy.scheduled_days=s,i.easy.due=a.scheduler(s,!0);break;case e.Learning:case e.Relearning:d=0,r=this.next_interval(i.good.stability),s=Math.max(this.next_interval(i.easy.stability),r+1),i.schedule(a,d,r,s);break;case e.Review:{const e=t.elapsed_days,n=t.difficulty,l=t.stability,o=this.current_retrievability(e,l);this.next_ds(i,n,l,o),d=this.next_interval(i.hard.stability),r=this.next_interval(i.good.stability),d=Math.min(d,r),r=Math.max(r,d+1),s=Math.max(this.next_interval(i.easy.stability),r+1),i.schedule(a,d,r,s);break}}return i.record_log(t,a)};get_retrievability=(t,a)=>{if(t=this.preProcessCard(t),a=this.preProcessDate(a),t.state!==e.Review)return;const i=Math.max(a.diff(t.last_review,"days"),0);return(100*this.current_retrievability(i,t.stability)).toFixed(2)+"%"};rollback=(t,i)=>{if(t=this.preProcessCard(t),(i=this.preProcessLog(i)).rating===a.Manual)throw new Error("Cannot rollback a manual rating");let s,r,d;switch(i.state){case e.New:s=i.due,r=void 0,d=0;break;case e.Learning:case e.Relearning:case e.Review:s=i.review,r=i.due,d=t.lapses-(i.rating===a.Again&&i.state===e.Review?1:0)}return{...t,due:s,stability:i.stability,difficulty:i.difficulty,elapsed_days:i.last_elapsed_days,scheduled_days:i.scheduled_days,reps:Math.max(0,t.reps-1),lapses:Math.max(0,d),state:i.state,last_review:r}};forget=(t,i,s=!1)=>{t=this.preProcessCard(t),i=this.preProcessDate(i);const r=t.state===e.New?0:i.diff(t.last_review,"days"),d={rating:a.Manual,state:t.state,due:t.due,stability:t.stability,difficulty:t.difficulty,elapsed_days:0,last_elapsed_days:t.elapsed_days,scheduled_days:r,review:i};return{card:{...t,due:i,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:s?0:t.reps,lapses:s?0:t.lapses,state:e.New,last_review:t.last_review},log:d}}}const $=t=>new M(t||{});export{M as FSRS,v as FSRSVersion,c as Grades,a as Rating,_ as SchedulingCard,e as State,b as createEmptyCard,s as date_diff,i as date_scheduler,w as default_enable_fuzz,p as default_maximum_interval,f as default_request_retention,g as default_w,h as fixDate,u as fixRating,y as fixState,r as formatDate,$ as fsrs,m as generatorParameters,o as show_diff_message};
1
+ import t from"seedrandom";var e,a;function i(t,e,a){return new Date(a?t.getTime()+24*e*60*60*1e3:t.getTime()+60*e*1e3)}function s(t,e,a){if(!t||!e)throw new Error("Invalid date");const i=t.getTime()-e.getTime();let s=0;switch(a){case"days":s=Math.floor(i/864e5);break;case"minutes":s=Math.floor(i/6e4)}return s}function r(t){const e=t.getFullYear(),a=t.getMonth()+1,i=t.getDate(),s=t.getHours(),r=t.getMinutes(),n=t.getSeconds();return`${e}-${d(a)}-${d(i)} ${d(s)}:${d(r)}:${d(n)}`}function d(t){return t<10?`0${t}`:`${t}`}!function(t){t[t.New=0]="New",t[t.Learning=1]="Learning",t[t.Review=2]="Review",t[t.Relearning=3]="Relearning"}(e||(e={})),function(t){t[t.Manual=0]="Manual",t[t.Again=1]="Again",t[t.Hard=2]="Hard",t[t.Good=3]="Good",t[t.Easy=4]="Easy"}(a||(a={})),Date.prototype.scheduler=function(t,e){return i(this,t,e)},Date.prototype.diff=function(t,e){return s(this,t,e)},Date.prototype.format=function(){return r(this)},Date.prototype.dueFormat=function(t,e,a){return o(this,t,e,a)};const n=[60,60,24,31,12],l=["second","min","hour","day","month","year"];function o(t,e,a,i=l){t=h(t),e=h(e),i.length!==l.length&&(i=l);let s,r=t.getTime()-e.getTime();for(r/=1e3,s=0;s<n.length&&!(r<n[s]);s++)r/=n[s];return`${Math.floor(r)}${a?i[s]:""}`}function h(t){if("object"==typeof t&&t instanceof Date)return t;if("string"==typeof t){const e=Date.parse(t);if(isNaN(e))throw new Error(`Invalid date:[${t}]`);return new Date(e)}if("number"==typeof t)return new Date(t);throw new Error(`Invalid date:[${t}]`)}function y(t){if("string"==typeof t){const a=t.charAt(0).toUpperCase(),i=t.slice(1).toLowerCase(),s=e[`${a}${i}`];if(void 0===s)throw new Error(`Invalid state:[${t}]`);return s}if("number"==typeof t)return t;throw new Error(`Invalid state:[${t}]`)}function u(t){if("string"==typeof t){const e=t.charAt(0).toUpperCase(),i=t.slice(1).toLowerCase(),s=a[`${e}${i}`];if(void 0===s)throw new Error(`Invalid rating:[${t}]`);return s}if("number"==typeof t)return t;throw new Error(`Invalid rating:[${t}]`)}const c=[a.Again,a.Hard,a.Good,a.Easy];class _{again;hard;good;easy;last_review;last_elapsed_days;copy(t){return{...t}}constructor(t,a){this.last_review=t.last_review||t.due,this.last_elapsed_days=t.elapsed_days,t.elapsed_days=t.state===e.New?0:a.diff(t.last_review,"days"),t.last_review=a,t.reps+=1,this.again=this.copy(t),this.hard=this.copy(t),this.good=this.copy(t),this.easy=this.copy(t)}update_state(t){return t===e.New?(this.again.state=e.Learning,this.hard.state=e.Learning,this.good.state=e.Learning,this.easy.state=e.Review):t===e.Learning||t===e.Relearning?(this.again.state=t,this.hard.state=t,this.good.state=e.Review,this.easy.state=e.Review):t===e.Review&&(this.again.state=e.Relearning,this.hard.state=e.Review,this.good.state=e.Review,this.easy.state=e.Review,this.again.lapses+=1),this}schedule(t,e,a,s){return this.again.scheduled_days=0,this.hard.scheduled_days=e,this.good.scheduled_days=a,this.easy.scheduled_days=s,this.again.due=i(t,5),this.hard.due=e>0?i(t,e,!0):i(t,10),this.good.due=i(t,a,!0),this.easy.due=i(t,s,!0),this}record_log(t,e){return{[a.Again]:{card:this.again,log:{rating:a.Again,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}},[a.Hard]:{card:this.hard,log:{rating:a.Hard,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}},[a.Good]:{card:this.good,log:{rating:a.Good,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}},[a.Easy]:{card:this.easy,log:{rating:a.Easy,state:t.state,due:this.last_review,stability:t.stability,difficulty:t.difficulty,elapsed_days:t.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:t.scheduled_days,review:e}}}}}const f=.9,p=36500,g=[.4,.6,2.4,5.8,4.93,.94,.86,.01,1.49,.14,.94,2.18,.05,.34,1.26,.29,2.61],w=!1,v="3.2.1",m=t=>({request_retention:t?.request_retention||.9,maximum_interval:t?.maximum_interval||36500,w:t?.w||g,enable_fuzz:t?.enable_fuzz||false}),b=t=>({due:t?h(t):new Date,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:0,lapses:0,state:e.New,last_review:void 0});class x{param;intervalModifier;seed;constructor(t){this.param=m(t),this.intervalModifier=9*(1/this.param.request_retention-1)}init_ds(t){t.again.difficulty=this.init_difficulty(a.Again),t.again.stability=this.init_stability(a.Again),t.hard.difficulty=this.init_difficulty(a.Hard),t.hard.stability=this.init_stability(a.Hard),t.good.difficulty=this.init_difficulty(a.Good),t.good.stability=this.init_stability(a.Good),t.easy.difficulty=this.init_difficulty(a.Easy),t.easy.stability=this.init_stability(a.Easy)}next_ds(t,e,i,s){t.again.difficulty=this.next_difficulty(e,a.Again),t.again.stability=this.next_forget_stability(e,i,s),t.hard.difficulty=this.next_difficulty(e,a.Hard),t.hard.stability=this.next_recall_stability(e,i,s,a.Hard),t.good.difficulty=this.next_difficulty(e,a.Good),t.good.stability=this.next_recall_stability(e,i,s,a.Good),t.easy.difficulty=this.next_difficulty(e,a.Easy),t.easy.stability=this.next_recall_stability(e,i,s,a.Easy)}init_stability(t){return Math.max(this.param.w[t-1],.1)}init_difficulty(t){return Math.min(Math.max(this.param.w[4]-(t-3)*this.param.w[5],1),10)}apply_fuzz(e){if(!this.param.enable_fuzz||e<2.5)return e;const a=t(this.seed)();e=Math.round(e);const i=Math.max(2,Math.round(.95*e-1)),s=Math.round(1.05*e+1);return Math.floor(a*(s-i+1)+i)}next_interval(t){const e=this.apply_fuzz(t*this.intervalModifier);return Math.min(Math.max(Math.round(e),1),this.param.maximum_interval)}next_difficulty(t,e){const a=t-this.param.w[6]*(e-3);return this.constrain_difficulty(this.mean_reversion(this.param.w[4],a))}constrain_difficulty(t){return Math.min(Math.max(Number(t.toFixed(2)),1),10)}mean_reversion(t,e){return this.param.w[7]*t+(1-this.param.w[7])*e}next_recall_stability(t,e,i,s){const r=a.Hard===s?this.param.w[15]:1,d=a.Easy===s?this.param.w[16]:1;return e*(1+Math.exp(this.param.w[8])*(11-t)*Math.pow(e,-this.param.w[9])*(Math.exp((1-i)*this.param.w[10])-1)*r*d)}next_forget_stability(t,e,a){return this.param.w[11]*Math.pow(t,-this.param.w[12])*(Math.pow(e+1,this.param.w[13])-1)*Math.exp((1-a)*this.param.w[14])}current_retrievability(t,e){return Math.pow(1+t/(9*e),-1)}}class M extends x{constructor(t){super(t)}preProcessCard(t){return{...t,state:y(t.state),due:h(t.due),last_review:t.last_review?h(t.last_review):void 0}}preProcessDate(t){return h(t)}preProcessLog(t){return{...t,rating:u(t.rating),state:y(t.state),review:h(t.review)}}repeat=(t,a)=>{t=this.preProcessCard(t),a=this.preProcessDate(a);const i=new _(t,a).update_state(t.state);let s,r,d;switch(this.seed=String(a.getTime())+String(t.reps),t.state){case e.New:this.init_ds(i),i.again.due=a.scheduler(1),i.hard.due=a.scheduler(5),i.good.due=a.scheduler(10),s=this.next_interval(i.easy.stability),i.easy.scheduled_days=s,i.easy.due=a.scheduler(s,!0);break;case e.Learning:case e.Relearning:d=0,r=this.next_interval(i.good.stability),s=Math.max(this.next_interval(i.easy.stability),r+1),i.schedule(a,d,r,s);break;case e.Review:{const e=t.elapsed_days,n=t.difficulty,l=t.stability,o=this.current_retrievability(e,l);this.next_ds(i,n,l,o),d=this.next_interval(i.hard.stability),r=this.next_interval(i.good.stability),d=Math.min(d,r),r=Math.max(r,d+1),s=Math.max(this.next_interval(i.easy.stability),r+1),i.schedule(a,d,r,s);break}}return i.record_log(t,a)};get_retrievability=(t,a)=>{if(t=this.preProcessCard(t),a=this.preProcessDate(a),t.state!==e.Review)return;const i=Math.max(a.diff(t.last_review,"days"),0);return(100*this.current_retrievability(i,t.stability)).toFixed(2)+"%"};rollback=(t,i)=>{if(t=this.preProcessCard(t),(i=this.preProcessLog(i)).rating===a.Manual)throw new Error("Cannot rollback a manual rating");let s,r,d;switch(i.state){case e.New:s=i.due,r=void 0,d=0;break;case e.Learning:case e.Relearning:case e.Review:s=i.review,r=i.due,d=t.lapses-(i.rating===a.Again&&i.state===e.Review?1:0)}return{...t,due:s,stability:i.stability,difficulty:i.difficulty,elapsed_days:i.last_elapsed_days,scheduled_days:i.scheduled_days,reps:Math.max(0,t.reps-1),lapses:Math.max(0,d),state:i.state,last_review:r}};forget=(t,i,s=!1)=>{t=this.preProcessCard(t),i=this.preProcessDate(i);const r=t.state===e.New?0:i.diff(t.last_review,"days"),d={rating:a.Manual,state:t.state,due:t.due,stability:t.stability,difficulty:t.difficulty,elapsed_days:0,last_elapsed_days:t.elapsed_days,scheduled_days:r,review:i};return{card:{...t,due:i,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:s?0:t.reps,lapses:s?0:t.lapses,state:e.New,last_review:t.last_review},log:d}}}const $=t=>new M(t||{});export{M as FSRS,v as FSRSVersion,c as Grades,a as Rating,_ as SchedulingCard,e as State,b as createEmptyCard,s as date_diff,i as date_scheduler,w as default_enable_fuzz,p as default_maximum_interval,f as default_request_retention,g as default_w,h as fixDate,u as fixRating,y as fixState,r as formatDate,$ as fsrs,m as generatorParameters,o as show_diff_message};
2
2
  //# sourceMappingURL=ts-fsrs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ts-fsrs.js","sources":["../src/fsrs/models.ts","../src/fsrs/help.ts","../src/fsrs/scheduler.ts","../src/fsrs/default.ts","../src/fsrs/algorithm.ts","../src/fsrs/fsrs.ts"],"sourcesContent":["export type StateType = \"New\" | \"Learning\" | \"Review\" | \"Relearning\";\n\nexport enum State {\n New = 0,\n Learning = 1,\n Review = 2,\n Relearning = 3,\n}\n\nexport type RatingType = \"Again\" | \"Hard\" | \"Good\" | \"Easy\";\n\nexport enum Rating {\n Manual = 0,\n Again = 1,\n Hard = 2,\n Good = 3,\n Easy = 4,\n}\n\ntype ExcludeManual<T> = Exclude<T, Rating.Manual>;\n\nexport type Grade = ExcludeManual<Rating>;\n\nexport interface ReviewLog {\n rating: Rating;\n state: State;\n due: Date;\n stability: number;\n difficulty: number;\n elapsed_days: number;\n last_elapsed_days: number;\n scheduled_days: number;\n review: Date;\n}\nexport type RecordLogItem = {\n card: Card;\n log: ReviewLog;\n};\nexport type RecordLog = {\n [key in Grade]: RecordLogItem;\n};\n\nexport interface Card {\n due: Date; // Due date\n stability: number; // Stability\n difficulty: number; // Difficulty level\n elapsed_days: number; // Number of days elapsed\n scheduled_days: number; // Number of days scheduled\n reps: number; // Repetition count\n lapses: number; // Number of lapses or mistakes\n state: State; // Card's state (New, Learning, Review, Relearning)\n last_review?: Date; // Date of the last review (optional)\n}\n\nexport type CardInput = Card & { state: StateType | State };\nexport type DateInput = Date | number | string;\nexport type ReviewLogInput = ReviewLog & {\n rating: RatingType | Rating;\n state: StateType | State;\n};\n\nexport interface FSRSParameters {\n request_retention: number;\n maximum_interval: number;\n w: number[];\n enable_fuzz: boolean;\n}\n","import type { int, unit } from \"./type\";\nimport { Grade, Rating, State } from \"./models\";\n\ndeclare global {\n export interface Date {\n scheduler(t: int, isDay?: boolean): Date;\n\n diff(pre: Date, unit: unit): int;\n\n format(): string;\n\n dueFormat(last_review: Date, unit?: boolean, timeUnit?: string[]): string;\n }\n}\n\nDate.prototype.scheduler = function (t: int, isDay?: boolean): Date {\n return date_scheduler(this, t, isDay);\n};\n\n/**\n * 当前时间与之前的时间差值\n * @param pre 比当前时间还要之前\n * @param unit 单位: days | minutes\n */\nDate.prototype.diff = function (pre: Date, unit: unit): int {\n return date_diff(this, pre, unit) as int;\n};\n\nDate.prototype.format = function (): string {\n return formatDate(this);\n};\n\nDate.prototype.dueFormat = function (\n last_review: Date,\n unit?: boolean,\n timeUnit?: string[],\n) {\n return show_diff_message(this, last_review, unit, timeUnit);\n};\n\n/**\n * 计算日期和时间的偏移,并返回一个新的日期对象。\n * @param now 当前日期和时间\n * @param t 时间偏移量,当 isDay 为 true 时表示天数,为 false 时表示分钟\n * @param isDay (可选)是否按天数单位进行偏移,默认为 false,表示按分钟单位计算偏移\n * @returns 偏移后的日期和时间对象\n */\nexport function date_scheduler(now: Date, t: number, isDay?: boolean): Date {\n return new Date(\n isDay\n ? now.getTime() + t * 24 * 60 * 60 * 1000\n : now.getTime() + t * 60 * 1000,\n );\n}\n\nexport function date_diff(now: Date, pre: Date, unit: unit): number {\n if (!now || !pre) {\n throw new Error(\"Invalid date\");\n }\n const diff = now.getTime() - pre.getTime();\n let r = 0;\n switch (unit) {\n case \"days\":\n r = Math.floor(diff / (24 * 60 * 60 * 1000));\n break;\n case \"minutes\":\n r = Math.floor(diff / (60 * 1000));\n break;\n }\n return r;\n}\n\nexport function formatDate(date: Date): string {\n const year: number = date.getFullYear();\n const month: number = date.getMonth() + 1;\n const day: number = date.getDate();\n const hours: number = date.getHours();\n const minutes: number = date.getMinutes();\n const seconds: number = date.getSeconds();\n\n return `${year}-${padZero(month)}-${padZero(day)} ${padZero(hours)}:${padZero(\n minutes,\n )}:${padZero(seconds)}`;\n}\n\nfunction padZero(num: number): string {\n return num < 10 ? `0${num}` : `${num}`;\n}\n\nconst TIMEUNIT = [60, 60, 24, 31, 12];\nconst TIMEUNITFORMAT = [\"second\", \"min\", \"hour\", \"day\", \"month\", \"year\"];\n\nexport function show_diff_message(\n due: Date,\n last_review: Date,\n unit?: boolean,\n timeUnit: string[] = TIMEUNITFORMAT,\n): string {\n due = fixDate(due);\n last_review = fixDate(last_review);\n if (timeUnit.length !== TIMEUNITFORMAT.length) {\n timeUnit = TIMEUNITFORMAT;\n }\n let diff = due.getTime() - last_review.getTime();\n let i;\n diff /= 1000;\n for (i = 0; i < TIMEUNIT.length; i++) {\n if (diff < TIMEUNIT[i]) {\n break;\n } else {\n diff /= TIMEUNIT[i];\n }\n }\n return `${Math.floor(diff)}${unit ? timeUnit[i] : \"\"}`;\n}\n\nexport function fixDate(value: unknown) {\n if (typeof value === \"object\" && value instanceof Date) {\n return value;\n } else if (typeof value === \"string\") {\n const timestamp = Date.parse(value);\n if (!isNaN(timestamp)) {\n return new Date(timestamp);\n } else {\n throw new Error(`Invalid date:[${value}]`);\n }\n } else if (typeof value === \"number\") {\n return new Date(value);\n }\n throw new Error(`Invalid date:[${value}]`);\n}\n\nexport function fixState(value: unknown): State {\n if (typeof value === \"string\") {\n const firstLetter = value.charAt(0).toUpperCase();\n const restOfString = value.slice(1).toLowerCase();\n const ret = State[`${firstLetter}${restOfString}` as keyof typeof State];\n if (ret === undefined) {\n throw new Error(`Invalid state:[${value}]`);\n }\n return ret;\n } else if (typeof value === \"number\") {\n return value as State;\n }\n throw new Error(`Invalid state:[${value}]`);\n}\n\nexport function fixRating(value: unknown): Rating {\n if (typeof value === \"string\") {\n const firstLetter = value.charAt(0).toUpperCase();\n const restOfString = value.slice(1).toLowerCase();\n const ret = Rating[`${firstLetter}${restOfString}` as keyof typeof Rating];\n if (ret === undefined) {\n throw new Error(`Invalid rating:[${value}]`);\n }\n return ret;\n } else if (typeof value === \"number\") {\n return value as Rating;\n }\n throw new Error(`Invalid rating:[${value}]`);\n}\n\nexport const Grades: Grade[] = [\n Rating.Again,\n Rating.Hard,\n Rating.Good,\n Rating.Easy,\n];\n","import { Card, Rating, RecordLog, State } from \"./models\";\nimport { date_scheduler } from \"./help\";\n\nexport class SchedulingCard {\n again: Card;\n hard: Card;\n good: Card;\n easy: Card;\n last_review: Date;\n last_elapsed_days: number;\n\n private copy(card: Card): Card {\n return {\n ...card,\n };\n }\n\n constructor(card: Card, now: Date) {\n this.last_review = card.last_review || card.due;\n this.last_elapsed_days = card.elapsed_days;\n card.elapsed_days =\n card.state === State.New ? 0 : now.diff(card.last_review as Date, \"days\"); //相距时间\n card.last_review = now; // 上次复习时间\n card.reps += 1;\n this.again = this.copy(card);\n this.hard = this.copy(card);\n this.good = this.copy(card);\n this.easy = this.copy(card);\n }\n\n update_state(state: State) {\n if (state === State.New) {\n this.again.state = State.Learning;\n this.hard.state = State.Learning;\n this.good.state = State.Learning;\n this.easy.state = State.Review;\n } else if (state === State.Learning || state === State.Relearning) {\n this.again.state = state;\n this.hard.state = state;\n this.good.state = State.Review;\n this.easy.state = State.Review;\n } else if (state === State.Review) {\n this.again.state = State.Relearning;\n this.hard.state = State.Review;\n this.good.state = State.Review;\n this.easy.state = State.Review;\n this.again.lapses += 1;\n }\n return this;\n }\n\n schedule(\n now: Date,\n hard_interval: number,\n good_interval: number,\n easy_interval: number,\n ): SchedulingCard {\n this.again.scheduled_days = 0;\n this.hard.scheduled_days = hard_interval;\n this.good.scheduled_days = good_interval;\n this.easy.scheduled_days = easy_interval;\n this.again.due = date_scheduler(now, 5);\n this.hard.due =\n hard_interval > 0\n ? date_scheduler(now, hard_interval, true)\n : date_scheduler(now, 10);\n this.good.due = date_scheduler(now, good_interval, true);\n this.easy.due = date_scheduler(now, easy_interval, true);\n return this;\n }\n\n record_log(card: Card, now: Date): RecordLog {\n return {\n [Rating.Again]: {\n card: this.again,\n log: {\n rating: Rating.Again,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n [Rating.Hard]: {\n card: this.hard,\n log: {\n rating: Rating.Hard,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n [Rating.Good]: {\n card: this.good,\n log: {\n rating: Rating.Good,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n [Rating.Easy]: {\n card: this.easy,\n log: {\n rating: Rating.Easy,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n };\n }\n}\n","import { Card, DateInput, FSRSParameters, State } from \"./models\";\nimport { fixDate } from \"./help\";\n\nexport const default_request_retention = 0.9;\nexport const default_maximum_interval = 36500;\nexport const default_w = [\n 0.4, 0.6, 2.4, 5.8, 4.93, 0.94, 0.86, 0.01, 1.49, 0.14, 0.94, 2.18, 0.05,\n 0.34, 1.26, 0.29, 2.61,\n];\nexport const default_enable_fuzz = false;\n\nexport const FSRSVersion: string = \"3.2.0\";\n\nexport const generatorParameters = (\n props?: Partial<FSRSParameters>,\n): FSRSParameters => {\n return {\n request_retention: props?.request_retention || default_request_retention,\n maximum_interval: props?.maximum_interval || default_maximum_interval,\n w: props?.w || default_w,\n enable_fuzz: props?.enable_fuzz || default_enable_fuzz,\n };\n};\n\nexport const createEmptyCard = (now?: DateInput): Card => {\n return {\n due: now ? fixDate(now) : new Date(),\n stability: 0,\n difficulty: 0,\n elapsed_days: 0,\n scheduled_days: 0,\n reps: 0,\n lapses: 0,\n state: State.New,\n last_review: undefined,\n };\n};\n","import pseudorandom from \"seedrandom\";\nimport { generatorParameters } from \"./default\";\nimport { SchedulingCard } from \"./scheduler\";\nimport { FSRSParameters, Grade, Rating } from \"./models\";\nimport type { int } from \"./type\";\n\n// Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-v4\nexport class FSRSAlgorithm {\n protected param: FSRSParameters;\n private readonly intervalModifier: number;\n protected seed?: string;\n\n constructor(param: Partial<FSRSParameters>) {\n this.param = generatorParameters(param);\n // Ref: https://github.com/open-spaced-repetition/py-fsrs/blob/ecd68e453611eb808c7367c7a5312d7cadeedf5c/src/fsrs/fsrs.py#L79\n // The formula used is : I(r,s)=9 \\cdot s \\cdot (\\frac{1}{r}-1)\n this.intervalModifier = 9 * (1 / this.param.request_retention - 1);\n }\n\n init_ds(s: SchedulingCard): void {\n s.again.difficulty = this.init_difficulty(Rating.Again);\n s.again.stability = this.init_stability(Rating.Again);\n s.hard.difficulty = this.init_difficulty(Rating.Hard);\n s.hard.stability = this.init_stability(Rating.Hard);\n s.good.difficulty = this.init_difficulty(Rating.Good);\n s.good.stability = this.init_stability(Rating.Good);\n s.easy.difficulty = this.init_difficulty(Rating.Easy);\n s.easy.stability = this.init_stability(Rating.Easy);\n }\n\n /**\n * Updates the difficulty and stability values of the scheduling card based on the last difficulty,\n * last stability, and the current retrievability.\n * @param {SchedulingCard} s scheduling Card\n * @param {number} last_d Difficulty\n * @param {number} last_s Stability\n * @param retrievability Retrievability\n */\n next_ds(\n s: SchedulingCard,\n last_d: number,\n last_s: number,\n retrievability: number,\n ): void {\n s.again.difficulty = this.next_difficulty(last_d, Rating.Again);\n s.again.stability = this.next_forget_stability(\n last_d,\n last_s,\n retrievability,\n );\n s.hard.difficulty = this.next_difficulty(last_d, Rating.Hard);\n s.hard.stability = this.next_recall_stability(\n last_d,\n last_s,\n retrievability,\n Rating.Hard,\n );\n s.good.difficulty = this.next_difficulty(last_d, Rating.Good);\n s.good.stability = this.next_recall_stability(\n last_d,\n last_s,\n retrievability,\n Rating.Good,\n );\n s.easy.difficulty = this.next_difficulty(last_d, Rating.Easy);\n s.easy.stability = this.next_recall_stability(\n last_d,\n last_s,\n retrievability,\n Rating.Easy,\n );\n }\n\n /**\n * The formula used is :\n * S_0(G) = w_{G-1}\n * \\max \\{S_0,0.1\\}\n * @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]\n * @return Stability (interval when R=90%)\n */\n init_stability(g: Grade): number {\n return Math.max(this.param.w[g - 1], 0.1);\n }\n\n /**\n * The formula used is :\n * $$D_0(G) = w_4 - (G-3) \\cdot w_5$$\n * $$\\min \\{\\max \\{D_0(G),1\\},10\\}$$\n * where the D_0(3)=w_4 when the first rating is good.\n * @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]\n * @return {number} Difficulty D \\in [1,10]\n */\n init_difficulty(g: Grade): number {\n return Math.min(\n Math.max(this.param.w[4] - (g - 3) * this.param.w[5], 1),\n 10,\n );\n }\n\n /**\n * If fuzzing is disabled or ivl is less than 2.5, it returns the original interval.\n * @param {number} ivl - The interval to be fuzzed.\n * @return {number} - The fuzzed interval.\n **/\n apply_fuzz(ivl: number): number {\n if (!this.param.enable_fuzz || ivl < 2.5) return ivl;\n const generator = pseudorandom(this.seed);\n const fuzz_factor = generator();\n ivl = Math.round(ivl);\n const min_ivl = Math.max(2, Math.round(ivl * 0.95 - 1));\n const max_ivl = Math.round(ivl * 1.05 + 1);\n return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl);\n }\n\n /**\n * Ref:\n * constructor(param: Partial<FSRSParameters>)\n * this.intervalModifier = 9 * (1 / this.param.request_retention - 1);\n * @param {number} s - Stability (interval when R=90%)\n */\n next_interval(s: number): int {\n const newInterval = this.apply_fuzz(s * this.intervalModifier);\n return Math.min(\n Math.max(Math.round(newInterval), 1),\n this.param.maximum_interval,\n ) as int;\n }\n\n /**\n * The formula used is :\n * $$next_d = D - w_6 \\cdot (R - 2)$$\n * $$D^\\prime(D,R) = w_5 \\cdot D_0(2) +(1 - w_5) \\cdot next_d$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]\n * @return {number} next_D\n */\n next_difficulty(d: number, g: Grade): number {\n const next_d = d - this.param.w[6] * (g - 3);\n return this.constrain_difficulty(\n this.mean_reversion(this.param.w[4], next_d),\n );\n }\n\n /**\n * The formula used is :\n * $$\\min \\{\\max \\{D_0,1\\},10\\}$$\n * @param {number} difficulty D \\in [1,10]\n */\n constrain_difficulty(difficulty: number): number {\n return Math.min(Math.max(Number(difficulty.toFixed(2)), 1), 10);\n }\n\n /**\n * The formula used is :\n * $$w_7 \\cdot init +(1 - w_7) \\cdot current$$\n * @param {number} init $$w_2 : D_0(3) = w_2 + (R-2) \\cdot w_3= w_2$$\n * @param {number} current $$D - w_6 \\cdot (R - 2)$$\n * @return {number} difficulty\n */\n mean_reversion(init: number, current: number): number {\n return this.param.w[7] * init + (1 - this.param.w[7]) * current;\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_r(D,S,R,G) = S\\cdot(e^{w_8}\\cdot (11-D)\\cdot S^{-w_9}\\cdot(e^{w_10\\cdot(1-R)}-1)\\cdot w_15(if G=2) \\cdot w_16(if G=4)+1)$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @param {Grade} g Grade (Rating[0.again,1.hard,2.good,3.easy])\n * @return {number} S^\\prime_r new stability after recall\n */\n next_recall_stability(d: number, s: number, r: number, g: Grade): number {\n const hard_penalty = Rating.Hard === g ? this.param.w[15] : 1;\n const easy_bound = Rating.Easy === g ? this.param.w[16] : 1;\n return (\n s *\n (1 +\n Math.exp(this.param.w[8]) *\n (11 - d) *\n Math.pow(s, -this.param.w[9]) *\n (Math.exp((1 - r) * this.param.w[10]) - 1) *\n hard_penalty *\n easy_bound)\n );\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_f(D,S,R) = w_11\\cdot D^{-w_{12}}\\cdot ((S+1)^{w_{13}}-1) \\cdot e^{w_{14}\\cdot(1-R)}.$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @return {number} S^\\prime_f new stability after forgetting\n */\n next_forget_stability(d: number, s: number, r: number): number {\n return (\n this.param.w[11] *\n Math.pow(d, -this.param.w[12]) *\n (Math.pow(s + 1, this.param.w[13]) - 1) *\n Math.exp((1 - r) * this.param.w[14])\n );\n }\n\n /**\n * The formula used is :\n * $$R(t,S) = (1 + \\frac{t}{9 \\cdot S})^{-1},$$\n * @param {number} t t days since the last review\n * @param {number} s Stability (interval when R=90%)\n * @return {number} r Retrievability (probability of recall)\n */\n current_retrievability(t: number, s: number): number {\n return Math.pow(1 + t / (9 * s), -1);\n }\n}\n","import { SchedulingCard } from \"./scheduler\";\nimport { fixDate, fixRating, fixState } from \"./help\";\nimport {\n Card,\n CardInput,\n DateInput,\n FSRSParameters,\n Rating,\n RecordLog,\n RecordLogItem,\n ReviewLog,\n ReviewLogInput,\n State,\n} from \"./models\";\nimport type { int } from \"./type\";\nimport { FSRSAlgorithm } from \"./algorithm\";\n\nexport class FSRS extends FSRSAlgorithm {\n constructor(param: Partial<FSRSParameters>) {\n super(param);\n }\n\n private preProcessCard(_card: CardInput): Card {\n return {\n ..._card,\n state: fixState(_card.state),\n due: fixDate(_card.due),\n last_review: _card.last_review ? fixDate(_card.last_review) : undefined,\n };\n }\n\n private preProcessDate(_date: DateInput): Date {\n return fixDate(_date);\n }\n\n private preProcessLog(_log: ReviewLogInput): ReviewLog {\n return {\n ..._log,\n rating: fixRating(_log.rating),\n state: fixState(_log.state),\n review: fixDate(_log.review),\n };\n }\n\n repeat = (card: CardInput, now: DateInput): RecordLog => {\n card = this.preProcessCard(card);\n now = this.preProcessDate(now);\n const s = new SchedulingCard(card, now).update_state(card.state);\n this.seed = String(now.getTime()) + String(card.reps);\n let easy_interval, good_interval, hard_interval;\n switch (card.state) {\n case State.New:\n this.init_ds(s);\n s.again.due = now.scheduler(1 as int);\n s.hard.due = now.scheduler(5 as int);\n s.good.due = now.scheduler(10 as int);\n easy_interval = this.next_interval(s.easy.stability);\n s.easy.scheduled_days = easy_interval;\n s.easy.due = now.scheduler(easy_interval, true);\n break;\n case State.Learning:\n case State.Relearning:\n hard_interval = 0;\n good_interval = this.next_interval(s.good.stability);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n case State.Review: {\n const interval = card.elapsed_days;\n const last_d = card.difficulty;\n const last_s = card.stability;\n const retrievability = this.current_retrievability(interval, last_s);\n this.next_ds(s, last_d, last_s, retrievability);\n hard_interval = this.next_interval(s.hard.stability);\n good_interval = this.next_interval(s.good.stability);\n hard_interval = Math.min(hard_interval, good_interval);\n good_interval = Math.max(good_interval, hard_interval + 1);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n }\n }\n return s.record_log(card, now);\n };\n\n get_retrievability = (card: Card, now: Date): undefined | string => {\n card = this.preProcessCard(card);\n now = this.preProcessDate(now);\n if (card.state !== State.Review) {\n return undefined;\n }\n const t = Math.max(now.diff(card.last_review as Date, \"days\"), 0);\n return (\n (this.current_retrievability(t, card.stability) * 100).toFixed(2) + \"%\"\n );\n };\n\n rollback = (card: CardInput, log: ReviewLogInput): Card => {\n card = this.preProcessCard(card);\n log = this.preProcessLog(log);\n if (log.rating === Rating.Manual) {\n throw new Error(\"Cannot rollback a manual rating\");\n }\n let last_due, last_review, last_lapses;\n switch (log.state) {\n case State.New:\n last_due = log.due;\n last_review = undefined;\n last_lapses = 0;\n break;\n case State.Learning:\n case State.Relearning:\n case State.Review:\n last_due = log.review;\n last_review = log.due;\n last_lapses =\n card.lapses -\n (log.rating === Rating.Again && log.state === State.Review ? 1 : 0);\n break;\n }\n\n return {\n ...card,\n due: last_due,\n stability: log.stability,\n difficulty: log.difficulty,\n elapsed_days: log.last_elapsed_days,\n scheduled_days: log.scheduled_days,\n reps: Math.max(0, card.reps - 1),\n lapses: Math.max(0, last_lapses),\n state: log.state,\n last_review: last_review,\n };\n };\n\n forget = (\n card: CardInput,\n now: DateInput,\n reset_count: boolean = false,\n ): RecordLogItem => {\n card = this.preProcessCard(card);\n now = this.preProcessDate(now);\n const scheduled_days =\n card.state === State.New ? 0 : now.diff(card.last_review as Date, \"days\");\n const forget_log: ReviewLog = {\n rating: Rating.Manual,\n state: card.state,\n due: card.due,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: 0,\n last_elapsed_days: card.elapsed_days,\n scheduled_days: scheduled_days,\n review: now,\n };\n const forget_card: Card = {\n ...card,\n due: now,\n stability: 0,\n difficulty: 0,\n elapsed_days: 0,\n scheduled_days: 0,\n reps: reset_count ? 0 : card.reps,\n lapses: reset_count ? 0 : card.lapses,\n state: State.New,\n last_review: card.last_review,\n };\n return { card: forget_card, log: forget_log };\n };\n}\n\nexport const fsrs = (params?: Partial<FSRSParameters>) => {\n return new FSRS(params || {});\n};\n"],"names":["State","Rating","date_scheduler","now","t","isDay","Date","getTime","date_diff","pre","unit","Error","diff","r","Math","floor","formatDate","date","year","getFullYear","month","getMonth","day","getDate","hours","getHours","minutes","getMinutes","seconds","getSeconds","padZero","num","prototype","scheduler","this","format","dueFormat","last_review","timeUnit","show_diff_message","TIMEUNIT","TIMEUNITFORMAT","due","fixDate","length","i","value","timestamp","parse","isNaN","fixState","firstLetter","charAt","toUpperCase","restOfString","slice","toLowerCase","ret","undefined","fixRating","Grades","Again","Hard","Good","Easy","SchedulingCard","again","hard","good","easy","last_elapsed_days","copy","card","constructor","elapsed_days","state","New","reps","update_state","Learning","Review","Relearning","lapses","schedule","hard_interval","good_interval","easy_interval","scheduled_days","record_log","log","rating","stability","difficulty","review","default_request_retention","default_maximum_interval","default_w","default_enable_fuzz","FSRSVersion","generatorParameters","props","request_retention","maximum_interval","w","enable_fuzz","createEmptyCard","FSRSAlgorithm","param","intervalModifier","seed","init_ds","s","init_difficulty","init_stability","next_ds","last_d","last_s","retrievability","next_difficulty","next_forget_stability","next_recall_stability","g","max","min","apply_fuzz","ivl","fuzz_factor","pseudorandom","generator","round","min_ivl","max_ivl","next_interval","newInterval","d","next_d","constrain_difficulty","mean_reversion","Number","toFixed","init","current","hard_penalty","easy_bound","exp","pow","current_retrievability","FSRS","super","preProcessCard","_card","preProcessDate","_date","preProcessLog","_log","repeat","String","interval","get_retrievability","rollback","Manual","last_due","last_lapses","forget","reset_count","forget_log","fsrs","params"],"mappings":"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"}
1
+ {"version":3,"file":"ts-fsrs.js","sources":["../src/fsrs/models.ts","../src/fsrs/help.ts","../src/fsrs/scheduler.ts","../src/fsrs/default.ts","../src/fsrs/algorithm.ts","../src/fsrs/fsrs.ts"],"sourcesContent":["export type StateType = \"New\" | \"Learning\" | \"Review\" | \"Relearning\";\n\nexport enum State {\n New = 0,\n Learning = 1,\n Review = 2,\n Relearning = 3,\n}\n\nexport type RatingType = \"Manual\" | \"Again\" | \"Hard\" | \"Good\" | \"Easy\";\n\nexport enum Rating {\n Manual = 0,\n Again = 1,\n Hard = 2,\n Good = 3,\n Easy = 4,\n}\n\ntype ExcludeManual<T> = Exclude<T, Rating.Manual>;\n\nexport type Grade = ExcludeManual<Rating>;\n\nexport interface ReviewLog {\n rating: Rating; // Rating of the review (Again, Hard, Good, Easy)\n state: State; // State of the review (New, Learning, Review, Relearning)\n due: Date; // Date of the last scheduling\n stability: number; // Memory stability during the review\n difficulty: number; // Difficulty of the card during the review\n elapsed_days: number; // Number of days elapsed since the last review\n last_elapsed_days: number; // Number of days between the last two reviews\n scheduled_days: number; // Number of days until the next review\n review: Date; // Date of the review\n}\nexport type RecordLogItem = {\n card: Card;\n log: ReviewLog;\n};\nexport type RecordLog = {\n [key in Grade]: RecordLogItem;\n};\n\nexport interface Card {\n due: Date; // Due date\n stability: number; // Stability\n difficulty: number; // Difficulty level\n elapsed_days: number; // Number of days elapsed\n scheduled_days: number; // Number of days scheduled\n reps: number; // Repetition count\n lapses: number; // Number of lapses or mistakes\n state: State; // Card's state (New, Learning, Review, Relearning)\n last_review?: Date; // Date of the last review (optional)\n}\n\nexport type CardInput = Card & { state: StateType | State };\nexport type DateInput = Date | number | string;\nexport type ReviewLogInput = ReviewLog & {\n rating: RatingType | Rating;\n state: StateType | State;\n};\n\nexport interface FSRSParameters {\n request_retention: number;\n maximum_interval: number;\n w: number[];\n enable_fuzz: boolean;\n}\n","import type { int, unit } from \"./type\";\nimport { Grade, Rating, State } from \"./models\";\n\ndeclare global {\n export interface Date {\n scheduler(t: int, isDay?: boolean): Date;\n\n diff(pre: Date, unit: unit): int;\n\n format(): string;\n\n dueFormat(last_review: Date, unit?: boolean, timeUnit?: string[]): string;\n }\n}\n\nDate.prototype.scheduler = function (t: int, isDay?: boolean): Date {\n return date_scheduler(this, t, isDay);\n};\n\n/**\n * 当前时间与之前的时间差值\n * @param pre 比当前时间还要之前\n * @param unit 单位: days | minutes\n */\nDate.prototype.diff = function (pre: Date, unit: unit): int {\n return date_diff(this, pre, unit) as int;\n};\n\nDate.prototype.format = function (): string {\n return formatDate(this);\n};\n\nDate.prototype.dueFormat = function (\n last_review: Date,\n unit?: boolean,\n timeUnit?: string[],\n) {\n return show_diff_message(this, last_review, unit, timeUnit);\n};\n\n/**\n * 计算日期和时间的偏移,并返回一个新的日期对象。\n * @param now 当前日期和时间\n * @param t 时间偏移量,当 isDay 为 true 时表示天数,为 false 时表示分钟\n * @param isDay (可选)是否按天数单位进行偏移,默认为 false,表示按分钟单位计算偏移\n * @returns 偏移后的日期和时间对象\n */\nexport function date_scheduler(now: Date, t: number, isDay?: boolean): Date {\n return new Date(\n isDay\n ? now.getTime() + t * 24 * 60 * 60 * 1000\n : now.getTime() + t * 60 * 1000,\n );\n}\n\nexport function date_diff(now: Date, pre: Date, unit: unit): number {\n if (!now || !pre) {\n throw new Error(\"Invalid date\");\n }\n const diff = now.getTime() - pre.getTime();\n let r = 0;\n switch (unit) {\n case \"days\":\n r = Math.floor(diff / (24 * 60 * 60 * 1000));\n break;\n case \"minutes\":\n r = Math.floor(diff / (60 * 1000));\n break;\n }\n return r;\n}\n\nexport function formatDate(date: Date): string {\n const year: number = date.getFullYear();\n const month: number = date.getMonth() + 1;\n const day: number = date.getDate();\n const hours: number = date.getHours();\n const minutes: number = date.getMinutes();\n const seconds: number = date.getSeconds();\n\n return `${year}-${padZero(month)}-${padZero(day)} ${padZero(hours)}:${padZero(\n minutes,\n )}:${padZero(seconds)}`;\n}\n\nfunction padZero(num: number): string {\n return num < 10 ? `0${num}` : `${num}`;\n}\n\nconst TIMEUNIT = [60, 60, 24, 31, 12];\nconst TIMEUNITFORMAT = [\"second\", \"min\", \"hour\", \"day\", \"month\", \"year\"];\n\nexport function show_diff_message(\n due: Date,\n last_review: Date,\n unit?: boolean,\n timeUnit: string[] = TIMEUNITFORMAT,\n): string {\n due = fixDate(due);\n last_review = fixDate(last_review);\n if (timeUnit.length !== TIMEUNITFORMAT.length) {\n timeUnit = TIMEUNITFORMAT;\n }\n let diff = due.getTime() - last_review.getTime();\n let i;\n diff /= 1000;\n for (i = 0; i < TIMEUNIT.length; i++) {\n if (diff < TIMEUNIT[i]) {\n break;\n } else {\n diff /= TIMEUNIT[i];\n }\n }\n return `${Math.floor(diff)}${unit ? timeUnit[i] : \"\"}`;\n}\n\nexport function fixDate(value: unknown) {\n if (typeof value === \"object\" && value instanceof Date) {\n return value;\n } else if (typeof value === \"string\") {\n const timestamp = Date.parse(value);\n if (!isNaN(timestamp)) {\n return new Date(timestamp);\n } else {\n throw new Error(`Invalid date:[${value}]`);\n }\n } else if (typeof value === \"number\") {\n return new Date(value);\n }\n throw new Error(`Invalid date:[${value}]`);\n}\n\nexport function fixState(value: unknown): State {\n if (typeof value === \"string\") {\n const firstLetter = value.charAt(0).toUpperCase();\n const restOfString = value.slice(1).toLowerCase();\n const ret = State[`${firstLetter}${restOfString}` as keyof typeof State];\n if (ret === undefined) {\n throw new Error(`Invalid state:[${value}]`);\n }\n return ret;\n } else if (typeof value === \"number\") {\n return value as State;\n }\n throw new Error(`Invalid state:[${value}]`);\n}\n\nexport function fixRating(value: unknown): Rating {\n if (typeof value === \"string\") {\n const firstLetter = value.charAt(0).toUpperCase();\n const restOfString = value.slice(1).toLowerCase();\n const ret = Rating[`${firstLetter}${restOfString}` as keyof typeof Rating];\n if (ret === undefined) {\n throw new Error(`Invalid rating:[${value}]`);\n }\n return ret;\n } else if (typeof value === \"number\") {\n return value as Rating;\n }\n throw new Error(`Invalid rating:[${value}]`);\n}\n\nexport const Grades: Grade[] = [\n Rating.Again,\n Rating.Hard,\n Rating.Good,\n Rating.Easy,\n];\n","import { Card, Rating, RecordLog, State } from \"./models\";\nimport { date_scheduler } from \"./help\";\n\nexport class SchedulingCard {\n again: Card;\n hard: Card;\n good: Card;\n easy: Card;\n last_review: Date;\n last_elapsed_days: number;\n\n private copy(card: Card): Card {\n return {\n ...card,\n };\n }\n\n constructor(card: Card, now: Date) {\n this.last_review = card.last_review || card.due;\n this.last_elapsed_days = card.elapsed_days;\n card.elapsed_days =\n card.state === State.New ? 0 : now.diff(card.last_review as Date, \"days\"); //相距时间\n card.last_review = now; // 上次复习时间\n card.reps += 1;\n this.again = this.copy(card);\n this.hard = this.copy(card);\n this.good = this.copy(card);\n this.easy = this.copy(card);\n }\n\n update_state(state: State) {\n if (state === State.New) {\n this.again.state = State.Learning;\n this.hard.state = State.Learning;\n this.good.state = State.Learning;\n this.easy.state = State.Review;\n } else if (state === State.Learning || state === State.Relearning) {\n this.again.state = state;\n this.hard.state = state;\n this.good.state = State.Review;\n this.easy.state = State.Review;\n } else if (state === State.Review) {\n this.again.state = State.Relearning;\n this.hard.state = State.Review;\n this.good.state = State.Review;\n this.easy.state = State.Review;\n this.again.lapses += 1;\n }\n return this;\n }\n\n schedule(\n now: Date,\n hard_interval: number,\n good_interval: number,\n easy_interval: number,\n ): SchedulingCard {\n this.again.scheduled_days = 0;\n this.hard.scheduled_days = hard_interval;\n this.good.scheduled_days = good_interval;\n this.easy.scheduled_days = easy_interval;\n this.again.due = date_scheduler(now, 5);\n this.hard.due =\n hard_interval > 0\n ? date_scheduler(now, hard_interval, true)\n : date_scheduler(now, 10);\n this.good.due = date_scheduler(now, good_interval, true);\n this.easy.due = date_scheduler(now, easy_interval, true);\n return this;\n }\n\n record_log(card: Card, now: Date): RecordLog {\n return {\n [Rating.Again]: {\n card: this.again,\n log: {\n rating: Rating.Again,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n [Rating.Hard]: {\n card: this.hard,\n log: {\n rating: Rating.Hard,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n [Rating.Good]: {\n card: this.good,\n log: {\n rating: Rating.Good,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n [Rating.Easy]: {\n card: this.easy,\n log: {\n rating: Rating.Easy,\n state: card.state,\n due: this.last_review,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: card.elapsed_days,\n last_elapsed_days: this.last_elapsed_days,\n scheduled_days: card.scheduled_days,\n review: now,\n },\n },\n };\n }\n}\n","import { Card, DateInput, FSRSParameters, State } from \"./models\";\nimport { fixDate } from \"./help\";\n\nexport const default_request_retention = 0.9;\nexport const default_maximum_interval = 36500;\nexport const default_w = [\n 0.4, 0.6, 2.4, 5.8, 4.93, 0.94, 0.86, 0.01, 1.49, 0.14, 0.94, 2.18, 0.05,\n 0.34, 1.26, 0.29, 2.61,\n];\nexport const default_enable_fuzz = false;\n\nexport const FSRSVersion: string = \"3.2.1\";\n\nexport const generatorParameters = (\n props?: Partial<FSRSParameters>,\n): FSRSParameters => {\n return {\n request_retention: props?.request_retention || default_request_retention,\n maximum_interval: props?.maximum_interval || default_maximum_interval,\n w: props?.w || default_w,\n enable_fuzz: props?.enable_fuzz || default_enable_fuzz,\n };\n};\n\nexport const createEmptyCard = (now?: DateInput): Card => {\n return {\n due: now ? fixDate(now) : new Date(),\n stability: 0,\n difficulty: 0,\n elapsed_days: 0,\n scheduled_days: 0,\n reps: 0,\n lapses: 0,\n state: State.New,\n last_review: undefined,\n };\n};\n","import pseudorandom from \"seedrandom\";\nimport { generatorParameters } from \"./default\";\nimport { SchedulingCard } from \"./scheduler\";\nimport { FSRSParameters, Grade, Rating } from \"./models\";\nimport type { int } from \"./type\";\n\n// Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-v4\nexport class FSRSAlgorithm {\n protected param: FSRSParameters;\n private readonly intervalModifier: number;\n protected seed?: string;\n\n constructor(param: Partial<FSRSParameters>) {\n this.param = generatorParameters(param);\n // Ref: https://github.com/open-spaced-repetition/py-fsrs/blob/ecd68e453611eb808c7367c7a5312d7cadeedf5c/src/fsrs/fsrs.py#L79\n // The formula used is : I(r,s)=9 \\cdot s \\cdot (\\frac{1}{r}-1)\n this.intervalModifier = 9 * (1 / this.param.request_retention - 1);\n }\n\n init_ds(s: SchedulingCard): void {\n s.again.difficulty = this.init_difficulty(Rating.Again);\n s.again.stability = this.init_stability(Rating.Again);\n s.hard.difficulty = this.init_difficulty(Rating.Hard);\n s.hard.stability = this.init_stability(Rating.Hard);\n s.good.difficulty = this.init_difficulty(Rating.Good);\n s.good.stability = this.init_stability(Rating.Good);\n s.easy.difficulty = this.init_difficulty(Rating.Easy);\n s.easy.stability = this.init_stability(Rating.Easy);\n }\n\n /**\n * Updates the difficulty and stability values of the scheduling card based on the last difficulty,\n * last stability, and the current retrievability.\n * @param {SchedulingCard} s scheduling Card\n * @param {number} last_d Difficulty\n * @param {number} last_s Stability\n * @param retrievability Retrievability\n */\n next_ds(\n s: SchedulingCard,\n last_d: number,\n last_s: number,\n retrievability: number,\n ): void {\n s.again.difficulty = this.next_difficulty(last_d, Rating.Again);\n s.again.stability = this.next_forget_stability(\n last_d,\n last_s,\n retrievability,\n );\n s.hard.difficulty = this.next_difficulty(last_d, Rating.Hard);\n s.hard.stability = this.next_recall_stability(\n last_d,\n last_s,\n retrievability,\n Rating.Hard,\n );\n s.good.difficulty = this.next_difficulty(last_d, Rating.Good);\n s.good.stability = this.next_recall_stability(\n last_d,\n last_s,\n retrievability,\n Rating.Good,\n );\n s.easy.difficulty = this.next_difficulty(last_d, Rating.Easy);\n s.easy.stability = this.next_recall_stability(\n last_d,\n last_s,\n retrievability,\n Rating.Easy,\n );\n }\n\n /**\n * The formula used is :\n * S_0(G) = w_{G-1}\n * \\max \\{S_0,0.1\\}\n * @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]\n * @return Stability (interval when R=90%)\n */\n init_stability(g: Grade): number {\n return Math.max(this.param.w[g - 1], 0.1);\n }\n\n /**\n * The formula used is :\n * $$D_0(G) = w_4 - (G-3) \\cdot w_5$$\n * $$\\min \\{\\max \\{D_0(G),1\\},10\\}$$\n * where the D_0(3)=w_4 when the first rating is good.\n * @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]\n * @return {number} Difficulty D \\in [1,10]\n */\n init_difficulty(g: Grade): number {\n return Math.min(\n Math.max(this.param.w[4] - (g - 3) * this.param.w[5], 1),\n 10,\n );\n }\n\n /**\n * If fuzzing is disabled or ivl is less than 2.5, it returns the original interval.\n * @param {number} ivl - The interval to be fuzzed.\n * @return {number} - The fuzzed interval.\n **/\n apply_fuzz(ivl: number): number {\n if (!this.param.enable_fuzz || ivl < 2.5) return ivl;\n const generator = pseudorandom(this.seed);\n const fuzz_factor = generator();\n ivl = Math.round(ivl);\n const min_ivl = Math.max(2, Math.round(ivl * 0.95 - 1));\n const max_ivl = Math.round(ivl * 1.05 + 1);\n return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl);\n }\n\n /**\n * Ref:\n * constructor(param: Partial<FSRSParameters>)\n * this.intervalModifier = 9 * (1 / this.param.request_retention - 1);\n * @param {number} s - Stability (interval when R=90%)\n */\n next_interval(s: number): int {\n const newInterval = this.apply_fuzz(s * this.intervalModifier);\n return Math.min(\n Math.max(Math.round(newInterval), 1),\n this.param.maximum_interval,\n ) as int;\n }\n\n /**\n * The formula used is :\n * $$next_d = D - w_6 \\cdot (R - 2)$$\n * $$D^\\prime(D,R) = w_5 \\cdot D_0(2) +(1 - w_5) \\cdot next_d$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]\n * @return {number} next_D\n */\n next_difficulty(d: number, g: Grade): number {\n const next_d = d - this.param.w[6] * (g - 3);\n return this.constrain_difficulty(\n this.mean_reversion(this.param.w[4], next_d),\n );\n }\n\n /**\n * The formula used is :\n * $$\\min \\{\\max \\{D_0,1\\},10\\}$$\n * @param {number} difficulty D \\in [1,10]\n */\n constrain_difficulty(difficulty: number): number {\n return Math.min(Math.max(Number(difficulty.toFixed(2)), 1), 10);\n }\n\n /**\n * The formula used is :\n * $$w_7 \\cdot init +(1 - w_7) \\cdot current$$\n * @param {number} init $$w_2 : D_0(3) = w_2 + (R-2) \\cdot w_3= w_2$$\n * @param {number} current $$D - w_6 \\cdot (R - 2)$$\n * @return {number} difficulty\n */\n mean_reversion(init: number, current: number): number {\n return this.param.w[7] * init + (1 - this.param.w[7]) * current;\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_r(D,S,R,G) = S\\cdot(e^{w_8}\\cdot (11-D)\\cdot S^{-w_9}\\cdot(e^{w_10\\cdot(1-R)}-1)\\cdot w_15(if G=2) \\cdot w_16(if G=4)+1)$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @param {Grade} g Grade (Rating[0.again,1.hard,2.good,3.easy])\n * @return {number} S^\\prime_r new stability after recall\n */\n next_recall_stability(d: number, s: number, r: number, g: Grade): number {\n const hard_penalty = Rating.Hard === g ? this.param.w[15] : 1;\n const easy_bound = Rating.Easy === g ? this.param.w[16] : 1;\n return (\n s *\n (1 +\n Math.exp(this.param.w[8]) *\n (11 - d) *\n Math.pow(s, -this.param.w[9]) *\n (Math.exp((1 - r) * this.param.w[10]) - 1) *\n hard_penalty *\n easy_bound)\n );\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_f(D,S,R) = w_11\\cdot D^{-w_{12}}\\cdot ((S+1)^{w_{13}}-1) \\cdot e^{w_{14}\\cdot(1-R)}.$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @return {number} S^\\prime_f new stability after forgetting\n */\n next_forget_stability(d: number, s: number, r: number): number {\n return (\n this.param.w[11] *\n Math.pow(d, -this.param.w[12]) *\n (Math.pow(s + 1, this.param.w[13]) - 1) *\n Math.exp((1 - r) * this.param.w[14])\n );\n }\n\n /**\n * The formula used is :\n * $$R(t,S) = (1 + \\frac{t}{9 \\cdot S})^{-1},$$\n * @param {number} t t days since the last review\n * @param {number} s Stability (interval when R=90%)\n * @return {number} r Retrievability (probability of recall)\n */\n current_retrievability(t: number, s: number): number {\n return Math.pow(1 + t / (9 * s), -1);\n }\n}\n","import { SchedulingCard } from \"./scheduler\";\nimport { fixDate, fixRating, fixState } from \"./help\";\nimport {\n Card,\n CardInput,\n DateInput,\n FSRSParameters,\n Rating,\n RecordLog,\n RecordLogItem,\n ReviewLog,\n ReviewLogInput,\n State,\n} from \"./models\";\nimport type { int } from \"./type\";\nimport { FSRSAlgorithm } from \"./algorithm\";\n\nexport class FSRS extends FSRSAlgorithm {\n constructor(param: Partial<FSRSParameters>) {\n super(param);\n }\n\n private preProcessCard(_card: CardInput): Card {\n return {\n ..._card,\n state: fixState(_card.state),\n due: fixDate(_card.due),\n last_review: _card.last_review ? fixDate(_card.last_review) : undefined,\n };\n }\n\n private preProcessDate(_date: DateInput): Date {\n return fixDate(_date);\n }\n\n private preProcessLog(_log: ReviewLogInput): ReviewLog {\n return {\n ..._log,\n rating: fixRating(_log.rating),\n state: fixState(_log.state),\n review: fixDate(_log.review),\n };\n }\n\n repeat = (card: CardInput, now: DateInput): RecordLog => {\n card = this.preProcessCard(card);\n now = this.preProcessDate(now);\n const s = new SchedulingCard(card, now).update_state(card.state);\n this.seed = String(now.getTime()) + String(card.reps);\n let easy_interval, good_interval, hard_interval;\n switch (card.state) {\n case State.New:\n this.init_ds(s);\n s.again.due = now.scheduler(1 as int);\n s.hard.due = now.scheduler(5 as int);\n s.good.due = now.scheduler(10 as int);\n easy_interval = this.next_interval(s.easy.stability);\n s.easy.scheduled_days = easy_interval;\n s.easy.due = now.scheduler(easy_interval, true);\n break;\n case State.Learning:\n case State.Relearning:\n hard_interval = 0;\n good_interval = this.next_interval(s.good.stability);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n case State.Review: {\n const interval = card.elapsed_days;\n const last_d = card.difficulty;\n const last_s = card.stability;\n const retrievability = this.current_retrievability(interval, last_s);\n this.next_ds(s, last_d, last_s, retrievability);\n hard_interval = this.next_interval(s.hard.stability);\n good_interval = this.next_interval(s.good.stability);\n hard_interval = Math.min(hard_interval, good_interval);\n good_interval = Math.max(good_interval, hard_interval + 1);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n }\n }\n return s.record_log(card, now);\n };\n\n get_retrievability = (card: Card, now: Date): undefined | string => {\n card = this.preProcessCard(card);\n now = this.preProcessDate(now);\n if (card.state !== State.Review) {\n return undefined;\n }\n const t = Math.max(now.diff(card.last_review as Date, \"days\"), 0);\n return (\n (this.current_retrievability(t, card.stability) * 100).toFixed(2) + \"%\"\n );\n };\n\n rollback = (card: CardInput, log: ReviewLogInput): Card => {\n card = this.preProcessCard(card);\n log = this.preProcessLog(log);\n if (log.rating === Rating.Manual) {\n throw new Error(\"Cannot rollback a manual rating\");\n }\n let last_due, last_review, last_lapses;\n switch (log.state) {\n case State.New:\n last_due = log.due;\n last_review = undefined;\n last_lapses = 0;\n break;\n case State.Learning:\n case State.Relearning:\n case State.Review:\n last_due = log.review;\n last_review = log.due;\n last_lapses =\n card.lapses -\n (log.rating === Rating.Again && log.state === State.Review ? 1 : 0);\n break;\n }\n\n return {\n ...card,\n due: last_due,\n stability: log.stability,\n difficulty: log.difficulty,\n elapsed_days: log.last_elapsed_days,\n scheduled_days: log.scheduled_days,\n reps: Math.max(0, card.reps - 1),\n lapses: Math.max(0, last_lapses),\n state: log.state,\n last_review: last_review,\n };\n };\n\n forget = (\n card: CardInput,\n now: DateInput,\n reset_count: boolean = false,\n ): RecordLogItem => {\n card = this.preProcessCard(card);\n now = this.preProcessDate(now);\n const scheduled_days =\n card.state === State.New ? 0 : now.diff(card.last_review as Date, \"days\");\n const forget_log: ReviewLog = {\n rating: Rating.Manual,\n state: card.state,\n due: card.due,\n stability: card.stability,\n difficulty: card.difficulty,\n elapsed_days: 0,\n last_elapsed_days: card.elapsed_days,\n scheduled_days: scheduled_days,\n review: now,\n };\n const forget_card: Card = {\n ...card,\n due: now,\n stability: 0,\n difficulty: 0,\n elapsed_days: 0,\n scheduled_days: 0,\n reps: reset_count ? 0 : card.reps,\n lapses: reset_count ? 0 : card.lapses,\n state: State.New,\n last_review: card.last_review,\n };\n return { card: forget_card, log: forget_log };\n };\n}\n\nexport const fsrs = (params?: Partial<FSRSParameters>) => {\n return new FSRS(params || {});\n};\n"],"names":["State","Rating","date_scheduler","now","t","isDay","Date","getTime","date_diff","pre","unit","Error","diff","r","Math","floor","formatDate","date","year","getFullYear","month","getMonth","day","getDate","hours","getHours","minutes","getMinutes","seconds","getSeconds","padZero","num","prototype","scheduler","this","format","dueFormat","last_review","timeUnit","show_diff_message","TIMEUNIT","TIMEUNITFORMAT","due","fixDate","length","i","value","timestamp","parse","isNaN","fixState","firstLetter","charAt","toUpperCase","restOfString","slice","toLowerCase","ret","undefined","fixRating","Grades","Again","Hard","Good","Easy","SchedulingCard","again","hard","good","easy","last_elapsed_days","copy","card","constructor","elapsed_days","state","New","reps","update_state","Learning","Review","Relearning","lapses","schedule","hard_interval","good_interval","easy_interval","scheduled_days","record_log","log","rating","stability","difficulty","review","default_request_retention","default_maximum_interval","default_w","default_enable_fuzz","FSRSVersion","generatorParameters","props","request_retention","maximum_interval","w","enable_fuzz","createEmptyCard","FSRSAlgorithm","param","intervalModifier","seed","init_ds","s","init_difficulty","init_stability","next_ds","last_d","last_s","retrievability","next_difficulty","next_forget_stability","next_recall_stability","g","max","min","apply_fuzz","ivl","fuzz_factor","pseudorandom","generator","round","min_ivl","max_ivl","next_interval","newInterval","d","next_d","constrain_difficulty","mean_reversion","Number","toFixed","init","current","hard_penalty","easy_bound","exp","pow","current_retrievability","FSRS","super","preProcessCard","_card","preProcessDate","_date","preProcessLog","_log","repeat","String","interval","get_retrievability","rollback","Manual","last_due","last_lapses","forget","reset_count","forget_log","fsrs","params"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-fsrs",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "description": "ts-fsrs is a TypeScript package used to implement the Free Spaced Repetition Scheduler (FSRS) algorithm. It helps developers apply FSRS to their flashcard applications, thereby improving the user learning experience.",
5
5
  "main": "dist/ts-fsrs.js",
6
6
  "module": "dist/ts-fsrs.mjs",
@@ -17,11 +17,9 @@
17
17
  },
18
18
  "devDependencies": {
19
19
  "@babel/core": "^7.23.3",
20
- "@babel/preset-env": "^7.23.3",
21
20
  "@rollup/plugin-alias": "^5.0.1",
22
21
  "@rollup/plugin-babel": "^6.0.4",
23
22
  "@rollup/plugin-commonjs": "^25.0.7",
24
- "@rollup/plugin-json": "^6.0.1",
25
23
  "@rollup/plugin-terser": "^0.4.4",
26
24
  "@types/jest": "^29.5.8",
27
25
  "@types/node": "^20.9.1",
@@ -61,6 +59,8 @@
61
59
  "files": [
62
60
  "dist",
63
61
  "README.md",
62
+ "README_CN.md",
63
+ "README_JP.md",
64
64
  "LICENSE"
65
65
  ],
66
66
  "repository": {
@@ -70,5 +70,8 @@
70
70
  "bugs": {
71
71
  "url": "https://github.com/ishiko732/ts-fsrs/issues"
72
72
  },
73
- "homepage": "https://github.com/ishiko732/ts-fsrs#readme"
73
+ "homepage": "https://github.com/ishiko732/ts-fsrs#readme",
74
+ "engines": {
75
+ "node": ">=16.0.0"
76
+ }
74
77
  }