ts-fsrs 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 ishiko
3
+ Copyright (c) 2024 Open Spaced Repetition
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -5,8 +5,8 @@
5
5
  # About The
6
6
  [![ts-fsrs npm version](https://img.shields.io/npm/v/ts-fsrs.svg)](https://www.npmjs.com/package/ts-fsrs)
7
7
  [![Downloads](https://img.shields.io/npm/dm/ts-fsrs)](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)
8
+ [![Build and Publish](https://github.com/open-spaced-repetition/ts-fsrs/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/open-spaced-repetition/ts-fsrs/actions/workflows/npm-publish.yml)
9
+ [![Deploy](https://github.com/open-spaced-repetition/ts-fsrs/actions/workflows/deploy.yml/badge.svg)](https://github.com/open-spaced-repetition/ts-fsrs/actions/workflows/deploy.yml)
10
10
 
11
11
  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
12
12
  developers apply FSRS to their flashcard applications, there by improving the user learning experience.
@@ -54,16 +54,16 @@ Grades.forEach(grade => { // [Rating.Again, Rating.Hard, Rating.Good, Rating.Eas
54
54
  ```
55
55
 
56
56
  More refer:
57
- - [Docs - Github Pages](https://ishiko732.github.io/ts-fsrs/)
58
- - [Example.html - Github Pages](https://ishiko732.github.io/ts-fsrs/example)
59
- - [Browser](https://github.com/ishiko732/ts-fsrs/blob/master/example/example.html) (ts-fsrs package using CDN)
57
+ - [Docs - Github Pages](https://open-spaced-repetition.github.io/ts-fsrs/)
58
+ - [Example.html - Github Pages](https://open-spaced-repetition.github.io/ts-fsrs/example)
59
+ - [Browser](https://github.com/open-spaced-repetition/ts-fsrs/blob/master/example/example.html) (ts-fsrs package using CDN)
60
60
  - [ts-fsrs-demo - Next.js+Prisma](https://github.com/ishiko732/ts-fsrs-demo)
61
61
 
62
62
 
63
63
  # Basic Use
64
64
 
65
65
  ## 1. **Initialization**:
66
- To begin, create an empty card instance and set the current date(default: current time from system)):
66
+ To begin, create an empty card instance and set the current date(default: current time from system):
67
67
 
68
68
  ```typescript
69
69
  import { Card, createEmptyCard } from "ts-fsrs";
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var t=require("seedrandom"),e=(t=>(t[t.New=0]="New",t[t.Learning=1]="Learning",t[t.Review=2]="Review",t[t.Relearning=3]="Relearning",t))(e||{}),a=(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",t))(a||{});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(),n=t.getSeconds();return`${e}-${d(a)}-${d(s)} ${d(i)}:${d(r)}:${d(n)}`}function d(t){return t<10?`0${t}`:`${t}`}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 o(this,t,e,a)};const n=[60,60,24,31,12],l=["second","min","hour","day","month","year"];function o(t,e,a,s=l){t=h(t),e=h(e),s.length!==l.length&&(s=l);let i,r=t.getTime()-e.getTime();for(r/=1e3,i=0;i<n.length&&!(r<n[i]);i++)r/=n[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 u(t){if("string"==typeof t){const a=t.charAt(0).toUpperCase(),s=t.slice(1).toLowerCase(),i=e[`${a}${s}`];if(void 0===i)throw new Error(`Invalid state:[${t}]`);return i}if("number"==typeof t)return t;throw new Error(`Invalid state:[${t}]`)}function y(t){if("string"==typeof t){const e=t.charAt(0).toUpperCase(),s=t.slice(1).toLowerCase(),i=a[`${e}${s}`];if(void 0===i)throw new Error(`Invalid rating:[${t}]`);return i}if("number"==typeof t)return t;throw new Error(`Invalid rating:[${t}]`)}const c=[a.Again,a.Hard,a.Good,a.Easy];class f{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,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{[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 _=[.4,.6,2.4,5.8,4.93,.94,.86,.01,1.49,.14,.94,2.18,.05,.34,1.26,.29,2.61],p=t=>({request_retention:(null==t?void 0:t.request_retention)||.9,maximum_interval:(null==t?void 0:t.maximum_interval)||36500,w:(null==t?void 0:t.w)||_,enable_fuzz:(null==t?void 0:t.enable_fuzz)||false});class g{param;intervalModifier;seed;DECAY=-.5;FACTOR=Math.pow(.9,1/this.DECAY)-1;constructor(t){this.param=p(t),this.intervalModifier=(Math.pow(this.param.request_retention,1/this.DECAY)-1)/this.FACTOR}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,s,i){t.again.difficulty=this.next_difficulty(e,a.Again),t.again.stability=this.next_forget_stability(e,s,i),t.hard.difficulty=this.next_difficulty(e,a.Hard),t.hard.stability=this.next_recall_stability(e,s,i,a.Hard),t.good.difficulty=this.next_difficulty(e,a.Good),t.good.stability=this.next_recall_stability(e,s,i,a.Good),t.easy.difficulty=this.next_difficulty(e,a.Easy),t.easy.stability=this.next_recall_stability(e,s,i,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 s=Math.max(2,Math.round(.95*e-1)),i=Math.round(1.05*e+1);return Math.floor(a*(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,s,i){const r=a.Hard===i?this.param.w[15]:1,d=a.Easy===i?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-s)*this.param.w[10])-1)*r*d)}next_forget_stability(t,e,a){return Number((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])).toFixed(2))}forgetting_curve(t,e){return Math.pow(1+this.FACTOR*t/e,this.DECAY)}}class w extends g{constructor(t){super(t)}preProcessCard(t){return{...t,state:u(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:y(t.rating),state:u(t.state),review:h(t.review)}}repeat=(t,a)=>{t=this.preProcessCard(t),a=this.preProcessDate(a);const s=new f(t,a).update_state(t.state);let i,r,d;switch(this.seed=String(a.getTime())+String(t.reps),t.state){case e.New:this.init_ds(s),s.again.due=a.scheduler(1),s.hard.due=a.scheduler(5),s.good.due=a.scheduler(10),i=this.next_interval(s.easy.stability),s.easy.scheduled_days=i,s.easy.due=a.scheduler(i,!0);break;case e.Learning:case e.Relearning:d=0,r=this.next_interval(s.good.stability),i=Math.max(this.next_interval(s.easy.stability),r+1),s.schedule(a,d,r,i);break;case e.Review:{const e=t.elapsed_days,n=t.difficulty,l=t.stability,o=this.forgetting_curve(e,l);this.next_ds(s,n,l,o),d=this.next_interval(s.hard.stability),r=this.next_interval(s.good.stability),d=Math.min(d,r),r=Math.max(r,d+1),i=Math.max(this.next_interval(s.easy.stability),r+1),s.schedule(a,d,r,i);break}}return s.record_log(t,a)};get_retrievability=(t,a)=>{if(t=this.preProcessCard(t),a=this.preProcessDate(a),t.state!==e.Review)return;const s=Math.max(a.diff(t.last_review,"days"),0);return(100*this.forgetting_curve(s,t.stability)).toFixed(2)+"%"};rollback=(t,s)=>{if(t=this.preProcessCard(t),(s=this.preProcessLog(s)).rating===a.Manual)throw new Error("Cannot rollback a manual rating");let i,r,d;switch(s.state){case e.New:i=s.due,r=void 0,d=0;break;case e.Learning:case e.Relearning:case e.Review:i=s.review,r=s.due,d=t.lapses-(s.rating===a.Again&&s.state===e.Review?1:0)}return{...t,due:i,stability:s.stability,difficulty:s.difficulty,elapsed_days:s.last_elapsed_days,scheduled_days:s.scheduled_days,reps:Math.max(0,t.reps-1),lapses:Math.max(0,d),state:s.state,last_review:r}};forget=(t,s,i=!1)=>{t=this.preProcessCard(t),s=this.preProcessDate(s);const r=t.state===e.New?0:s.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:s};return{card:{...t,due:s,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:i?0:t.reps,lapses:i?0:t.lapses,state:e.New,last_review:t.last_review},log:d}}}exports.FSRS=w,exports.FSRSVersion="3.3.0",exports.Grades=c,exports.Rating=a,exports.SchedulingCard=f,exports.State=e,exports.createEmptyCard=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}),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=y,exports.fixState=u,exports.formatDate=r,exports.fsrs=t=>new w(t||{}),exports.generatorParameters=p,exports.show_diff_message=o,module.exports=Object.assign(exports.default||{},exports);
1
+ "use strict";var N=require("seedrandom"),n=(e=>(e[e.New=0]="New",e[e.Learning=1]="Learning",e[e.Review=2]="Review",e[e.Relearning=3]="Relearning",e))(n||{}),d=(e=>(e[e.Manual=0]="Manual",e[e.Again=1]="Again",e[e.Hard=2]="Hard",e[e.Good=3]="Good",e[e.Easy=4]="Easy",e))(d||{});Date.prototype.scheduler=function(e,t){return y(this,e,t)},Date.prototype.diff=function(e,t){return m(this,e,t)},Date.prototype.format=function(){return v(this)},Date.prototype.dueFormat=function(e,t,a){return b(this,e,t,a)};function y(e,t,a){return new Date(a?u(e).getTime()+t*24*60*60*1e3:u(e).getTime()+t*60*1e3)}function m(e,t,a){if(!e||!t)throw new Error("Invalid date");const s=u(e).getTime()-u(t).getTime();let r=0;switch(a){case"days":r=Math.floor(s/(24*60*60*1e3));break;case"minutes":r=Math.floor(s/(60*1e3));break}return r}function v(e){const t=u(e),a=t.getFullYear(),s=t.getMonth()+1,r=t.getDate(),i=t.getHours(),l=t.getMinutes(),o=t.getSeconds();return`${a}-${f(s)}-${f(r)} ${f(i)}:${f(l)}:${f(o)}`}function f(e){return e<10?`0${e}`:`${e}`}const _=[60,60,24,31,12],p=["second","min","hour","day","month","year"];function b(e,t,a,s=p){e=u(e),t=u(t),s.length!==p.length&&(s=p);let r=e.getTime()-t.getTime(),i;for(r/=1e3,i=0;i<_.length&&!(r<_[i]);i++)r/=_[i];return`${Math.floor(r)}${a?s[i]:""}`}function u(e){if(typeof e=="object"&&e instanceof Date)return e;if(typeof e=="string"){const t=Date.parse(e);if(isNaN(t))throw new Error(`Invalid date:[${e}]`);return new Date(t)}else if(typeof e=="number")return new Date(e);throw new Error(`Invalid date:[${e}]`)}function g(e){if(typeof e=="string"){const t=e.charAt(0).toUpperCase(),a=e.slice(1).toLowerCase(),s=n[`${t}${a}`];if(s===void 0)throw new Error(`Invalid state:[${e}]`);return s}else if(typeof e=="number")return e;throw new Error(`Invalid state:[${e}]`)}function x(e){if(typeof e=="string"){const t=e.charAt(0).toUpperCase(),a=e.slice(1).toLowerCase(),s=d[`${t}${a}`];if(s===void 0)throw new Error(`Invalid rating:[${e}]`);return s}else if(typeof e=="number")return e;throw new Error(`Invalid rating:[${e}]`)}const P=[d.Again,d.Hard,d.Good,d.Easy];class M{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===n.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===n.New?(this.again.state=n.Learning,this.hard.state=n.Learning,this.good.state=n.Learning,this.easy.state=n.Review):t===n.Learning||t===n.Relearning?(this.again.state=t,this.hard.state=t,this.good.state=n.Review,this.easy.state=n.Review):t===n.Review&&(this.again.state=n.Relearning,this.hard.state=n.Review,this.good.state=n.Review,this.easy.state=n.Review,this.again.lapses+=1),this}schedule(t,a,s,r){return this.again.scheduled_days=0,this.hard.scheduled_days=a,this.good.scheduled_days=s,this.easy.scheduled_days=r,this.again.due=y(t,5),this.hard.due=a>0?y(t,a,!0):y(t,10),this.good.due=y(t,s,!0),this.easy.due=y(t,r,!0),this}record_log(t,a){return{[d.Again]:{card:this.again,log:{rating:d.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:a}},[d.Hard]:{card:this.hard,log:{rating:d.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:a}},[d.Good]:{card:this.good,log:{rating:d.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:a}},[d.Easy]:{card:this.easy,log:{rating:d.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:a}}}}}const R=.9,E=36500,D=[.4,.6,2.4,5.8,4.93,.94,.86,.01,1.49,.14,.94,2.18,.05,.34,1.26,.29,2.61],C=!1,L="3.4.0",$=e=>({request_retention:(e==null?void 0:e.request_retention)||R,maximum_interval:(e==null?void 0:e.maximum_interval)||E,w:(e==null?void 0:e.w)||D,enable_fuzz:(e==null?void 0:e.enable_fuzz)||C});function G(e,t){const a={due:e?u(e):new Date,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:0,lapses:0,state:n.New,last_review:void 0};return t&&typeof t=="function"?t(a):a}class H{param;intervalModifier;seed;DECAY=-.5;FACTOR=Math.pow(.9,1/this.DECAY)-1;constructor(t){this.param=$(t),this.intervalModifier=(Math.pow(this.param.request_retention,1/this.DECAY)-1)/this.FACTOR}init_ds(t){t.again.difficulty=this.init_difficulty(d.Again),t.again.stability=this.init_stability(d.Again),t.hard.difficulty=this.init_difficulty(d.Hard),t.hard.stability=this.init_stability(d.Hard),t.good.difficulty=this.init_difficulty(d.Good),t.good.stability=this.init_stability(d.Good),t.easy.difficulty=this.init_difficulty(d.Easy),t.easy.stability=this.init_stability(d.Easy)}next_ds(t,a,s,r){t.again.difficulty=this.next_difficulty(a,d.Again),t.again.stability=this.next_forget_stability(a,s,r),t.hard.difficulty=this.next_difficulty(a,d.Hard),t.hard.stability=this.next_recall_stability(a,s,r,d.Hard),t.good.difficulty=this.next_difficulty(a,d.Good),t.good.stability=this.next_recall_stability(a,s,r,d.Good),t.easy.difficulty=this.next_difficulty(a,d.Easy),t.easy.stability=this.next_recall_stability(a,s,r,d.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 a=N(this.seed)();t=Math.round(t);const s=Math.max(2,Math.round(t*.95-1)),r=Math.round(t*1.05+1);return Math.floor(a*(r-s+1)+s)}next_interval(t){const a=this.apply_fuzz(t*this.intervalModifier);return Math.min(Math.max(Math.round(a),1),this.param.maximum_interval)}next_difficulty(t,a){const s=t-this.param.w[6]*(a-3);return this.constrain_difficulty(this.mean_reversion(this.param.w[4],s))}constrain_difficulty(t){return Math.min(Math.max(Number(t.toFixed(2)),1),10)}mean_reversion(t,a){return this.param.w[7]*t+(1-this.param.w[7])*a}next_recall_stability(t,a,s,r){const i=d.Hard===r?this.param.w[15]:1,l=d.Easy===r?this.param.w[16]:1;return a*(1+Math.exp(this.param.w[8])*(11-t)*Math.pow(a,-this.param.w[9])*(Math.exp((1-s)*this.param.w[10])-1)*i*l)}next_forget_stability(t,a,s){return Number((this.param.w[11]*Math.pow(t,-this.param.w[12])*(Math.pow(a+1,this.param.w[13])-1)*Math.exp((1-s)*this.param.w[14])).toFixed(2))}forgetting_curve(t,a){return Math.pow(1+this.FACTOR*t/a,this.DECAY)}}class A extends H{constructor(t){super(t)}preProcessCard(t){return{...t,state:g(t.state),due:u(t.due),last_review:t.last_review?u(t.last_review):void 0}}preProcessDate(t){return u(t)}preProcessLog(t){return{...t,due:u(t.due),rating:x(t.rating),state:g(t.state),review:u(t.review)}}repeat(t,a,s){const r=this.preProcessCard(t);a=this.preProcessDate(a);const i=new M(r,a).update_state(r.state);this.seed=String(a.getTime())+String(r.reps);let l,o,h;switch(r.state){case n.New:this.init_ds(i),i.again.due=a.scheduler(1),i.hard.due=a.scheduler(5),i.good.due=a.scheduler(10),l=this.next_interval(i.easy.stability),i.easy.scheduled_days=l,i.easy.due=a.scheduler(l,!0);break;case n.Learning:case n.Relearning:h=0,o=this.next_interval(i.good.stability),l=Math.max(this.next_interval(i.easy.stability),o+1),i.schedule(a,h,o,l);break;case n.Review:{const S=r.elapsed_days,z=r.difficulty,w=r.stability,F=this.forgetting_curve(S,w);this.next_ds(i,z,w,F),h=this.next_interval(i.hard.stability),o=this.next_interval(i.good.stability),h=Math.min(h,o),o=Math.max(o,h+1),l=Math.max(this.next_interval(i.easy.stability),o+1),i.schedule(a,h,o,l);break}}const c=i.record_log(r,a);return s&&typeof s=="function"?s(c):c}get_retrievability=(t,a)=>{const s=this.preProcessCard(t);if(a=this.preProcessDate(a),s.state!==n.Review)return;const r=Math.max(a.diff(s.last_review,"days"),0);return(this.forgetting_curve(r,s.stability)*100).toFixed(2)+"%"};rollback(t,a,s){const r=this.preProcessCard(t),i=this.preProcessLog(a);if(i.rating===d.Manual)throw new Error("Cannot rollback a manual rating");let l,o,h;switch(i.state){case n.New:l=i.due,o=void 0,h=0;break;case n.Learning:case n.Relearning:case n.Review:l=i.review,o=i.due,h=r.lapses-(i.rating===d.Again&&i.state===n.Review?1:0);break}const c={...r,due:l,stability:i.stability,difficulty:i.difficulty,elapsed_days:i.last_elapsed_days,scheduled_days:i.scheduled_days,reps:Math.max(0,r.reps-1),lapses:Math.max(0,h),state:i.state,last_review:o};return s&&typeof s=="function"?s(c):c}forget(t,a,s=!1,r){const i=this.preProcessCard(t);a=this.preProcessDate(a);const l=i.state===n.New?0:a.diff(i.last_review,"days"),o={rating:d.Manual,state:i.state,due:i.due,stability:i.stability,difficulty:i.difficulty,elapsed_days:0,last_elapsed_days:i.elapsed_days,scheduled_days:l,review:a},h={card:{...i,due:a,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:s?0:i.reps,lapses:s?0:i.lapses,state:n.New,last_review:i.last_review},log:o};return r&&typeof r=="function"?r(h):h}}const T=e=>new A(e||{});exports.FSRS=A,exports.FSRSVersion=L,exports.Grades=P,exports.Rating=d,exports.SchedulingCard=M,exports.State=n,exports.createEmptyCard=G,exports.date_diff=m,exports.date_scheduler=y,exports.default_enable_fuzz=C,exports.default_maximum_interval=E,exports.default_request_retention=R,exports.default_w=D,exports.fixDate=u,exports.fixRating=x,exports.fixState=g,exports.formatDate=v,exports.fsrs=T,exports.generatorParameters=$,exports.show_diff_message=b,module.exports=Object.assign(exports.default||{},exports);
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","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.3.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 private readonly DECAY: number = -0.5;\n private readonly FACTOR: number = Math.pow(0.9, 1 / this.DECAY) - 1;\n\n constructor(param: Partial<FSRSParameters>) {\n this.param = generatorParameters(param);\n // Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45\n // The formula used is : I(r,s)= (r^{\\frac{1}{DECAY}-1}) \\times \\frac{s}{FACTOR}\n this.intervalModifier =\n (Math.pow(this.param.request_retention, 1 / this.DECAY) - 1) /\n this.FACTOR;\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 Number(\n (\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 ).toFixed(2),\n );\n }\n\n /**\n * The formula used is :\n * $$R(t,S) = (1 + FACTOR \\times \\frac{t}{9 \\cdot S})^{DECAY},$$\n * @param {number} elapsed_days t days since the last review\n * @param {number} stability Stability (interval when R=90%)\n * @return {number} r Retrievability (probability of recall)\n */\n forgetting_curve(elapsed_days: number, stability: number): number {\n return Math.pow(1 + (this.FACTOR * elapsed_days) / stability, this.DECAY);\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.forgetting_curve(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 (this.forgetting_curve(t, card.stability) * 100).toFixed(2) + \"%\";\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","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","DECAY","FACTOR","pow","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","forgetting_curve","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":"index.cjs","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}\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 interface CardInput extends Omit<Card, \"state\" | \"due\" | \"last_review\"> {\n state: StateType | State; // Card's state (New, Learning, Review, Relearning)\n due: DateInput; // Due date\n last_review?: DateInput | null; // Date of the last review (optional)\n}\n\nexport type DateInput = Date | number | string;\n\nexport interface ReviewLogInput\n extends Omit<ReviewLog, \"rating\" | \"state\" | \"due\" | \"review\"> {\n rating: RatingType | Rating; // Rating of the review (Again, Hard, Good, Easy)\n state: StateType | State; // Card's state (New, Learning, Review, Relearning)\n due: DateInput; // Due date\n review: DateInput; // Date of the last review\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 type { DateInput, Grade } from \"./models\";\nimport { 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(\n now: DateInput,\n t: number,\n isDay?: boolean,\n): Date {\n return new Date(\n isDay\n ? fixDate(now).getTime() + t * 24 * 60 * 60 * 1000\n : fixDate(now).getTime() + t * 60 * 1000,\n );\n}\n\nexport function date_diff(now: DateInput, pre: DateInput, unit: unit): number {\n if (!now || !pre) {\n throw new Error(\"Invalid date\");\n }\n const diff = fixDate(now).getTime() - fixDate(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(dateInput: DateInput): string {\n const date = fixDate(dateInput);\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: DateInput,\n last_review: DateInput,\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.4.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\n/**\n * Create an empty card\n * @param now Current time\n * @param afterHandler Convert the result to another type. (Optional)\n * @example\n * ```\n * const card: Card = createEmptyCard(new Date());\n * ```\n * @example\n * ```\n * interface CardUnChecked\n * extends Omit<Card, \"due\" | \"last_review\" | \"state\"> {\n * cid: string;\n * due: Date | number;\n * last_review: Date | null | number;\n * state: StateType;\n * }\n *\n * function cardAfterHandler(card: Card) {\n * return {\n * ...card,\n * cid: \"test001\",\n * state: State[card.state],\n * last_review: card.last_review ?? null,\n * } as CardUnChecked;\n * }\n *\n * const card: CardUnChecked = createEmptyCard(new Date(), cardAfterHandler);\n * ```\n */\nexport function createEmptyCard<R = Card>(\n now?: DateInput,\n afterHandler?: (card: Card) => R,\n): R {\n const emptyCard: Card = {\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 if (afterHandler && typeof afterHandler === \"function\") {\n return afterHandler(emptyCard);\n } else {\n return emptyCard as R;\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 private readonly DECAY: number = -0.5;\n private readonly FACTOR: number = Math.pow(0.9, 1 / this.DECAY) - 1;\n\n constructor(param: Partial<FSRSParameters>) {\n this.param = generatorParameters(param);\n // Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45\n // The formula used is : I(r,s)= (r^{\\frac{1}{DECAY}-1}) \\times \\frac{s}{FACTOR}\n this.intervalModifier =\n (Math.pow(this.param.request_retention, 1 / this.DECAY) - 1) /\n this.FACTOR;\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 Number(\n (\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 ).toFixed(2),\n );\n }\n\n /**\n * The formula used is :\n * $$R(t,S) = (1 + FACTOR \\times \\frac{t}{9 \\cdot S})^{DECAY},$$\n * @param {number} elapsed_days t days since the last review\n * @param {number} stability Stability (interval when R=90%)\n * @return {number} r Retrievability (probability of recall)\n */\n forgetting_curve(elapsed_days: number, stability: number): number {\n return Math.pow(1 + (this.FACTOR * elapsed_days) / stability, this.DECAY);\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): 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): ReviewLog {\n return {\n ..._log,\n due: fixDate(_log.due),\n rating: fixRating(_log.rating),\n state: fixState(_log.state),\n review: fixDate(_log.review),\n };\n }\n\n /**\n * @param card Card to be processed\n * @param now Current time or scheduled time\n * @param afterHandler Convert the result to another type. (Optional)\n * @example\n * ```\n * const card: Card = createEmptyCard(new Date());\n * const f = fsrs();\n * const recordLog = f.repeat(card, new Date());\n * ```\n * @example\n * ```\n * interface RevLogUnchecked\n * extends Omit<ReviewLog, \"due\" | \"review\" | \"state\" | \"rating\"> {\n * cid: string;\n * due: Date | number;\n * state: StateType;\n * review: Date | number;\n * rating: RatingType;\n * }\n *\n * interface RepeatRecordLog {\n * card: CardUnChecked; //see method: createEmptyCard\n * log: RevLogUnchecked;\n * }\n *\n * function repeatAfterHandler(recordLog: RecordLog) {\n * const record: { [key in Grade]: RepeatRecordLog } = {} as {\n * [key in Grade]: RepeatRecordLog;\n * };\n * for (const grade of Grades) {\n * record[grade] = {\n * card: {\n * ...(recordLog[grade].card as Card & { cid: string }),\n * due: recordLog[grade].card.due.getTime(),\n * state: State[recordLog[grade].card.state] as StateType,\n * last_review: recordLog[grade].card.last_review\n * ? recordLog[grade].card.last_review!.getTime()\n * : null,\n * },\n * log: {\n * ...recordLog[grade].log,\n * cid: (recordLog[grade].card as Card & { cid: string }).cid,\n * due: recordLog[grade].log.due.getTime(),\n * review: recordLog[grade].log.review.getTime(),\n * state: State[recordLog[grade].log.state] as StateType,\n * rating: Rating[recordLog[grade].log.rating] as RatingType,\n * },\n * };\n * }\n * return record;\n * }\n * const card: Card = createEmptyCard(new Date(), cardAfterHandler); //see method: createEmptyCard\n * const f = fsrs();\n * const recordLog = f.repeat(card, new Date(), repeatAfterHandler);\n * ```\n */\n repeat<R = RecordLog>(\n card: CardInput | Card,\n now: DateInput,\n afterHandler?: (recordLog: RecordLog) => R,\n ): R {\n const processedCard = this.preProcessCard(card);\n now = this.preProcessDate(now);\n const s = new SchedulingCard(processedCard, now).update_state(\n processedCard.state,\n );\n this.seed = String(now.getTime()) + String(processedCard.reps);\n let easy_interval, good_interval, hard_interval;\n switch (processedCard.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 = processedCard.elapsed_days;\n const last_d = processedCard.difficulty;\n const last_s = processedCard.stability;\n const retrievability = this.forgetting_curve(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 const recordLog = s.record_log(processedCard, now);\n if (afterHandler && typeof afterHandler === \"function\") {\n return afterHandler(recordLog);\n } else {\n return recordLog as R;\n }\n }\n\n get_retrievability = (\n card: CardInput | Card,\n now: Date,\n ): undefined | string => {\n const processedCard = this.preProcessCard(card);\n now = this.preProcessDate(now);\n if (processedCard.state !== State.Review) {\n return undefined;\n }\n const t = Math.max(now.diff(processedCard.last_review as Date, \"days\"), 0);\n return (\n (this.forgetting_curve(t, processedCard.stability) * 100).toFixed(2) + \"%\"\n );\n };\n\n /**\n *\n * @param card Card to be processed\n * @param log last review log\n * @param afterHandler Convert the result to another type. (Optional)\n * @example\n * ```\n * const now = new Date();\n * const f = fsrs();\n * const emptyCardFormAfterHandler = createEmptyCard(now);\n * const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now);\n * const { card, log } = repeatFormAfterHandler[Rating.Hard];\n * const rollbackFromAfterHandler = f.rollback(card, log);\n * ```\n *\n * @example\n * ```\n * const now = new Date();\n * const f = fsrs();\n * const emptyCardFormAfterHandler = createEmptyCard(now, cardAfterHandler); //see method: createEmptyCard\n * const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now, repeatAfterHandler); //see method: fsrs.repeat()\n * const { card, log } = repeatFormAfterHandler[Rating.Hard];\n * const rollbackFromAfterHandler = f.rollback(card, log, cardAfterHandler);\n * ```\n */\n rollback<R = Card>(\n card: CardInput | Card,\n log: ReviewLogInput,\n afterHandler?: (prevCard: Card) => R,\n ): R {\n const processedCard = this.preProcessCard(card);\n const processedLog = this.preProcessLog(log);\n if (processedLog.rating === Rating.Manual) {\n throw new Error(\"Cannot rollback a manual rating\");\n }\n let last_due, last_review, last_lapses;\n switch (processedLog.state) {\n case State.New:\n last_due = processedLog.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 = processedLog.review;\n last_review = processedLog.due;\n last_lapses =\n processedCard.lapses -\n (processedLog.rating === Rating.Again &&\n processedLog.state === State.Review\n ? 1\n : 0);\n break;\n }\n\n const prevCard: Card = {\n ...processedCard,\n due: last_due,\n stability: processedLog.stability,\n difficulty: processedLog.difficulty,\n elapsed_days: processedLog.last_elapsed_days,\n scheduled_days: processedLog.scheduled_days,\n reps: Math.max(0, processedCard.reps - 1),\n lapses: Math.max(0, last_lapses),\n state: processedLog.state,\n last_review: last_review,\n };\n if (afterHandler && typeof afterHandler === \"function\") {\n return afterHandler(prevCard);\n } else {\n return prevCard as R;\n }\n }\n\n /**\n *\n * @param card Card to be processed\n * @param now Current time or scheduled time\n * @param reset_count Should the review count information(reps,lapses) be reset. (Optional)\n * @param afterHandler Convert the result to another type. (Optional)\n * @example\n * ```\n * const now = new Date();\n * const f = fsrs();\n * const emptyCard = createEmptyCard(now);\n * const scheduling_cards = f.repeat(emptyCard, now);\n * const { card, log } = scheduling_cards[Rating.Hard];\n * const forgetCard = f.forget(card, new Date(), true);\n * ```\n *\n * @example\n * ```\n * interface RepeatRecordLog {\n * card: CardUnChecked; //see method: createEmptyCard\n * log: RevLogUnchecked; //see method: fsrs.repeat()\n * }\n *\n * function forgetAfterHandler(recordLogItem: RecordLogItem): RepeatRecordLog {\n * return {\n * card: {\n * ...(recordLogItem.card as Card & { cid: string }),\n * due: recordLogItem.card.due.getTime(),\n * state: State[recordLogItem.card.state] as StateType,\n * last_review: recordLogItem.card.last_review\n * ? recordLogItem.card.last_review!.getTime()\n * : null,\n * },\n * log: {\n * ...recordLogItem.log,\n * cid: (recordLogItem.card as Card & { cid: string }).cid,\n * due: recordLogItem.log.due.getTime(),\n * review: recordLogItem.log.review.getTime(),\n * state: State[recordLogItem.log.state] as StateType,\n * rating: Rating[recordLogItem.log.rating] as RatingType,\n * },\n * };\n * }\n * const now = new Date();\n * const f = fsrs();\n * const emptyCardFormAfterHandler = createEmptyCard(now, cardAfterHandler); //see method: createEmptyCard\n * const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now, repeatAfterHandler); //see method: fsrs.repeat()\n * const { card } = repeatFormAfterHandler[Rating.Hard];\n * const forgetFromAfterHandler = f.forget(card, date_scheduler(now, 1, true), false, forgetAfterHandler);\n * ```\n */\n forget<R = RecordLogItem>(\n card: CardInput | Card,\n now: DateInput,\n reset_count: boolean = false,\n afterHandler?: (recordLogItem: RecordLogItem) => R,\n ): R {\n const processedCard = this.preProcessCard(card);\n now = this.preProcessDate(now);\n const scheduled_days =\n processedCard.state === State.New\n ? 0\n : now.diff(processedCard.last_review as Date, \"days\");\n const forget_log: ReviewLog = {\n rating: Rating.Manual,\n state: processedCard.state,\n due: processedCard.due,\n stability: processedCard.stability,\n difficulty: processedCard.difficulty,\n elapsed_days: 0,\n last_elapsed_days: processedCard.elapsed_days,\n scheduled_days: scheduled_days,\n review: now,\n };\n const forget_card: Card = {\n ...processedCard,\n due: now,\n stability: 0,\n difficulty: 0,\n elapsed_days: 0,\n scheduled_days: 0,\n reps: reset_count ? 0 : processedCard.reps,\n lapses: reset_count ? 0 : processedCard.lapses,\n state: State.New,\n last_review: processedCard.last_review,\n };\n const recordLogItem: RecordLogItem = { card: forget_card, log: forget_log };\n if (afterHandler && typeof afterHandler === \"function\") {\n return afterHandler(recordLogItem);\n } else {\n return recordLogItem as R;\n }\n }\n}\n\n/**\n * Create a new instance of TS-FSRS\n * @param params FSRSParameters\n * @example\n * ```typescript\n * const f = fsrs();\n * ```\n * @example\n * ```typescript\n * const params: FSRSParameters = generatorParameters({ maximum_interval: 1000 });\n * const f = fsrs(params);\n * ```\n * @example\n * ```typescript\n * const f = fsrs({ maximum_interval: 1000 });\n * ```\n */\nexport const fsrs = (params?: Partial<FSRSParameters>) => {\n return new FSRS(params || {});\n};\n"],"names":["State","t","Rating","isDay","date_scheduler","pre","unit","date_diff","formatDate","last_review","timeUnit","show_diff_message","now","fixDate","diff","r","dateInput","date","year","month","day","hours","minutes","seconds","padZero","num","TIMEUNIT","TIMEUNITFORMAT","due","value","timestamp","fixState","firstLetter","restOfString","ret","fixRating","Grades","SchedulingCard","card","state","hard_interval","good_interval","easy_interval","default_request_retention","default_maximum_interval","default_w","default_enable_fuzz","FSRSVersion","generatorParameters","props","createEmptyCard","afterHandler","emptyCard","FSRSAlgorithm","param","s","last_d","last_s","retrievability","g","ivl","fuzz_factor","pseudorandom","min_ivl","max_ivl","newInterval","d","next_d","difficulty","init","current","hard_penalty","easy_bound","elapsed_days","stability","FSRS","_card","_date","_log","processedCard","interval","recordLog","log","processedLog","last_due","last_lapses","prevCard","reset_count","scheduled_days","forget_log","recordLogItem","fsrs","params"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -44,14 +44,18 @@ interface Card {
44
44
  state: State;
45
45
  last_review?: Date;
46
46
  }
47
- type CardInput = Card & {
47
+ interface CardInput extends Omit<Card, "state" | "due" | "last_review"> {
48
48
  state: StateType | State;
49
- };
49
+ due: DateInput;
50
+ last_review?: DateInput | null;
51
+ }
50
52
  type DateInput = Date | number | string;
51
- type ReviewLogInput = ReviewLog & {
53
+ interface ReviewLogInput extends Omit<ReviewLog, "rating" | "state" | "due" | "review"> {
52
54
  rating: RatingType | Rating;
53
55
  state: StateType | State;
54
- };
56
+ due: DateInput;
57
+ review: DateInput;
58
+ }
55
59
  interface FSRSParameters {
56
60
  request_retention: number;
57
61
  maximum_interval: number;
@@ -79,7 +83,37 @@ declare const default_w: number[];
79
83
  declare const default_enable_fuzz = false;
80
84
  declare const FSRSVersion: string;
81
85
  declare const generatorParameters: (props?: Partial<FSRSParameters>) => FSRSParameters;
82
- declare const createEmptyCard: (now?: DateInput) => Card;
86
+ /**
87
+ * Create an empty card
88
+ * @param now Current time
89
+ * @param afterHandler Convert the result to another type. (Optional)
90
+ * @example
91
+ * ```
92
+ * const card: Card = createEmptyCard(new Date());
93
+ * ```
94
+ * @example
95
+ * ```
96
+ * interface CardUnChecked
97
+ * extends Omit<Card, "due" | "last_review" | "state"> {
98
+ * cid: string;
99
+ * due: Date | number;
100
+ * last_review: Date | null | number;
101
+ * state: StateType;
102
+ * }
103
+ *
104
+ * function cardAfterHandler(card: Card) {
105
+ * return {
106
+ * ...card,
107
+ * cid: "test001",
108
+ * state: State[card.state],
109
+ * last_review: card.last_review ?? null,
110
+ * } as CardUnChecked;
111
+ * }
112
+ *
113
+ * const card: CardUnChecked = createEmptyCard(new Date(), cardAfterHandler);
114
+ * ```
115
+ */
116
+ declare function createEmptyCard<R = Card>(now?: DateInput, afterHandler?: (card: Card) => R): R;
83
117
 
84
118
  type unit = "days" | "minutes";
85
119
  type int = number & {
@@ -104,10 +138,10 @@ declare global {
104
138
  * @param isDay (可选)是否按天数单位进行偏移,默认为 false,表示按分钟单位计算偏移
105
139
  * @returns 偏移后的日期和时间对象
106
140
  */
107
- declare function date_scheduler(now: Date, t: number, isDay?: boolean): Date;
108
- declare function date_diff(now: Date, pre: Date, unit: unit): number;
109
- declare function formatDate(date: Date): string;
110
- declare function show_diff_message(due: Date, last_review: Date, unit?: boolean, timeUnit?: string[]): string;
141
+ declare function date_scheduler(now: DateInput, t: number, isDay?: boolean): Date;
142
+ declare function date_diff(now: DateInput, pre: DateInput, unit: unit): number;
143
+ declare function formatDate(dateInput: DateInput): string;
144
+ declare function show_diff_message(due: DateInput, last_review: DateInput, unit?: boolean, timeUnit?: string[]): string;
111
145
  declare function fixDate(value: unknown): Date;
112
146
  declare function fixState(value: unknown): State;
113
147
  declare function fixRating(value: unknown): Rating;
@@ -217,11 +251,161 @@ declare class FSRS extends FSRSAlgorithm {
217
251
  private preProcessCard;
218
252
  private preProcessDate;
219
253
  private preProcessLog;
220
- repeat: (card: CardInput, now: DateInput) => RecordLog;
221
- get_retrievability: (card: Card, now: Date) => undefined | string;
222
- rollback: (card: CardInput, log: ReviewLogInput) => Card;
223
- forget: (card: CardInput, now: DateInput, reset_count?: boolean) => RecordLogItem;
254
+ /**
255
+ * @param card Card to be processed
256
+ * @param now Current time or scheduled time
257
+ * @param afterHandler Convert the result to another type. (Optional)
258
+ * @example
259
+ * ```
260
+ * const card: Card = createEmptyCard(new Date());
261
+ * const f = fsrs();
262
+ * const recordLog = f.repeat(card, new Date());
263
+ * ```
264
+ * @example
265
+ * ```
266
+ * interface RevLogUnchecked
267
+ * extends Omit<ReviewLog, "due" | "review" | "state" | "rating"> {
268
+ * cid: string;
269
+ * due: Date | number;
270
+ * state: StateType;
271
+ * review: Date | number;
272
+ * rating: RatingType;
273
+ * }
274
+ *
275
+ * interface RepeatRecordLog {
276
+ * card: CardUnChecked; //see method: createEmptyCard
277
+ * log: RevLogUnchecked;
278
+ * }
279
+ *
280
+ * function repeatAfterHandler(recordLog: RecordLog) {
281
+ * const record: { [key in Grade]: RepeatRecordLog } = {} as {
282
+ * [key in Grade]: RepeatRecordLog;
283
+ * };
284
+ * for (const grade of Grades) {
285
+ * record[grade] = {
286
+ * card: {
287
+ * ...(recordLog[grade].card as Card & { cid: string }),
288
+ * due: recordLog[grade].card.due.getTime(),
289
+ * state: State[recordLog[grade].card.state] as StateType,
290
+ * last_review: recordLog[grade].card.last_review
291
+ * ? recordLog[grade].card.last_review!.getTime()
292
+ * : null,
293
+ * },
294
+ * log: {
295
+ * ...recordLog[grade].log,
296
+ * cid: (recordLog[grade].card as Card & { cid: string }).cid,
297
+ * due: recordLog[grade].log.due.getTime(),
298
+ * review: recordLog[grade].log.review.getTime(),
299
+ * state: State[recordLog[grade].log.state] as StateType,
300
+ * rating: Rating[recordLog[grade].log.rating] as RatingType,
301
+ * },
302
+ * };
303
+ * }
304
+ * return record;
305
+ * }
306
+ * const card: Card = createEmptyCard(new Date(), cardAfterHandler); //see method: createEmptyCard
307
+ * const f = fsrs();
308
+ * const recordLog = f.repeat(card, new Date(), repeatAfterHandler);
309
+ * ```
310
+ */
311
+ repeat<R = RecordLog>(card: CardInput | Card, now: DateInput, afterHandler?: (recordLog: RecordLog) => R): R;
312
+ get_retrievability: (card: CardInput | Card, now: Date) => undefined | string;
313
+ /**
314
+ *
315
+ * @param card Card to be processed
316
+ * @param log last review log
317
+ * @param afterHandler Convert the result to another type. (Optional)
318
+ * @example
319
+ * ```
320
+ * const now = new Date();
321
+ * const f = fsrs();
322
+ * const emptyCardFormAfterHandler = createEmptyCard(now);
323
+ * const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now);
324
+ * const { card, log } = repeatFormAfterHandler[Rating.Hard];
325
+ * const rollbackFromAfterHandler = f.rollback(card, log);
326
+ * ```
327
+ *
328
+ * @example
329
+ * ```
330
+ * const now = new Date();
331
+ * const f = fsrs();
332
+ * const emptyCardFormAfterHandler = createEmptyCard(now, cardAfterHandler); //see method: createEmptyCard
333
+ * const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now, repeatAfterHandler); //see method: fsrs.repeat()
334
+ * const { card, log } = repeatFormAfterHandler[Rating.Hard];
335
+ * const rollbackFromAfterHandler = f.rollback(card, log, cardAfterHandler);
336
+ * ```
337
+ */
338
+ rollback<R = Card>(card: CardInput | Card, log: ReviewLogInput, afterHandler?: (prevCard: Card) => R): R;
339
+ /**
340
+ *
341
+ * @param card Card to be processed
342
+ * @param now Current time or scheduled time
343
+ * @param reset_count Should the review count information(reps,lapses) be reset. (Optional)
344
+ * @param afterHandler Convert the result to another type. (Optional)
345
+ * @example
346
+ * ```
347
+ * const now = new Date();
348
+ * const f = fsrs();
349
+ * const emptyCard = createEmptyCard(now);
350
+ * const scheduling_cards = f.repeat(emptyCard, now);
351
+ * const { card, log } = scheduling_cards[Rating.Hard];
352
+ * const forgetCard = f.forget(card, new Date(), true);
353
+ * ```
354
+ *
355
+ * @example
356
+ * ```
357
+ * interface RepeatRecordLog {
358
+ * card: CardUnChecked; //see method: createEmptyCard
359
+ * log: RevLogUnchecked; //see method: fsrs.repeat()
360
+ * }
361
+ *
362
+ * function forgetAfterHandler(recordLogItem: RecordLogItem): RepeatRecordLog {
363
+ * return {
364
+ * card: {
365
+ * ...(recordLogItem.card as Card & { cid: string }),
366
+ * due: recordLogItem.card.due.getTime(),
367
+ * state: State[recordLogItem.card.state] as StateType,
368
+ * last_review: recordLogItem.card.last_review
369
+ * ? recordLogItem.card.last_review!.getTime()
370
+ * : null,
371
+ * },
372
+ * log: {
373
+ * ...recordLogItem.log,
374
+ * cid: (recordLogItem.card as Card & { cid: string }).cid,
375
+ * due: recordLogItem.log.due.getTime(),
376
+ * review: recordLogItem.log.review.getTime(),
377
+ * state: State[recordLogItem.log.state] as StateType,
378
+ * rating: Rating[recordLogItem.log.rating] as RatingType,
379
+ * },
380
+ * };
381
+ * }
382
+ * const now = new Date();
383
+ * const f = fsrs();
384
+ * const emptyCardFormAfterHandler = createEmptyCard(now, cardAfterHandler); //see method: createEmptyCard
385
+ * const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now, repeatAfterHandler); //see method: fsrs.repeat()
386
+ * const { card } = repeatFormAfterHandler[Rating.Hard];
387
+ * const forgetFromAfterHandler = f.forget(card, date_scheduler(now, 1, true), false, forgetAfterHandler);
388
+ * ```
389
+ */
390
+ forget<R = RecordLogItem>(card: CardInput | Card, now: DateInput, reset_count?: boolean, afterHandler?: (recordLogItem: RecordLogItem) => R): R;
224
391
  }
392
+ /**
393
+ * Create a new instance of TS-FSRS
394
+ * @param params FSRSParameters
395
+ * @example
396
+ * ```typescript
397
+ * const f = fsrs();
398
+ * ```
399
+ * @example
400
+ * ```typescript
401
+ * const params: FSRSParameters = generatorParameters({ maximum_interval: 1000 });
402
+ * const f = fsrs(params);
403
+ * ```
404
+ * @example
405
+ * ```typescript
406
+ * const f = fsrs({ maximum_interval: 1000 });
407
+ * ```
408
+ */
225
409
  declare const fsrs: (params?: Partial<FSRSParameters>) => FSRS;
226
410
 
227
- export { type Card, type CardInput, type DateInput, FSRS, type FSRSParameters, FSRSVersion, type Grade, Grades, Rating, type RatingType, type RecordLog, type RecordLogItem, type ReviewLog, SchedulingCard, State, type StateType, createEmptyCard, date_diff, date_scheduler, default_enable_fuzz, default_maximum_interval, default_request_retention, default_w, type double, fixDate, fixRating, fixState, formatDate, fsrs, generatorParameters, type int, show_diff_message };
411
+ export { type Card, type CardInput, type DateInput, FSRS, type FSRSParameters, FSRSVersion, type Grade, Grades, Rating, type RatingType, type RecordLog, type RecordLogItem, type ReviewLog, type ReviewLogInput, SchedulingCard, State, type StateType, createEmptyCard, date_diff, date_scheduler, default_enable_fuzz, default_maximum_interval, default_request_retention, default_w, type double, fixDate, fixRating, fixState, formatDate, fsrs, generatorParameters, type int, show_diff_message };
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import t from"seedrandom";var e=(t=>(t[t.New=0]="New",t[t.Learning=1]="Learning",t[t.Review=2]="Review",t[t.Relearning=3]="Relearning",t))(e||{}),a=(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",t))(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}`}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 u(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 y(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.3.0",m=t=>({request_retention:(null==t?void 0:t.request_retention)||.9,maximum_interval:(null==t?void 0:t.maximum_interval)||36500,w:(null==t?void 0:t.w)||g,enable_fuzz:(null==t?void 0: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;DECAY=-.5;FACTOR=Math.pow(.9,1/this.DECAY)-1;constructor(t){this.param=m(t),this.intervalModifier=(Math.pow(this.param.request_retention,1/this.DECAY)-1)/this.FACTOR}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 Number((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])).toFixed(2))}forgetting_curve(t,e){return Math.pow(1+this.FACTOR*t/e,this.DECAY)}}class M extends x{constructor(t){super(t)}preProcessCard(t){return{...t,state:u(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:y(t.rating),state:u(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.forgetting_curve(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.forgetting_curve(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 E=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,y as fixRating,u as fixState,r as formatDate,E as fsrs,m as generatorParameters,o as show_diff_message};
1
+ import P from"seedrandom";var d=(e=>(e[e.New=0]="New",e[e.Learning=1]="Learning",e[e.Review=2]="Review",e[e.Relearning=3]="Relearning",e))(d||{}),n=(e=>(e[e.Manual=0]="Manual",e[e.Again=1]="Again",e[e.Hard=2]="Hard",e[e.Good=3]="Good",e[e.Easy=4]="Easy",e))(n||{});Date.prototype.scheduler=function(e,t){return y(this,e,t)},Date.prototype.diff=function(e,t){return v(this,e,t)},Date.prototype.format=function(){return m(this)},Date.prototype.dueFormat=function(e,t,a){return b(this,e,t,a)};function y(e,t,a){return new Date(a?u(e).getTime()+t*24*60*60*1e3:u(e).getTime()+t*60*1e3)}function v(e,t,a){if(!e||!t)throw new Error("Invalid date");const s=u(e).getTime()-u(t).getTime();let r=0;switch(a){case"days":r=Math.floor(s/(24*60*60*1e3));break;case"minutes":r=Math.floor(s/(60*1e3));break}return r}function m(e){const t=u(e),a=t.getFullYear(),s=t.getMonth()+1,r=t.getDate(),i=t.getHours(),l=t.getMinutes(),o=t.getSeconds();return`${a}-${f(s)}-${f(r)} ${f(i)}:${f(l)}:${f(o)}`}function f(e){return e<10?`0${e}`:`${e}`}const _=[60,60,24,31,12],p=["second","min","hour","day","month","year"];function b(e,t,a,s=p){e=u(e),t=u(t),s.length!==p.length&&(s=p);let r=e.getTime()-t.getTime(),i;for(r/=1e3,i=0;i<_.length&&!(r<_[i]);i++)r/=_[i];return`${Math.floor(r)}${a?s[i]:""}`}function u(e){if(typeof e=="object"&&e instanceof Date)return e;if(typeof e=="string"){const t=Date.parse(e);if(isNaN(t))throw new Error(`Invalid date:[${e}]`);return new Date(t)}else if(typeof e=="number")return new Date(e);throw new Error(`Invalid date:[${e}]`)}function g(e){if(typeof e=="string"){const t=e.charAt(0).toUpperCase(),a=e.slice(1).toLowerCase(),s=d[`${t}${a}`];if(s===void 0)throw new Error(`Invalid state:[${e}]`);return s}else if(typeof e=="number")return e;throw new Error(`Invalid state:[${e}]`)}function x(e){if(typeof e=="string"){const t=e.charAt(0).toUpperCase(),a=e.slice(1).toLowerCase(),s=n[`${t}${a}`];if(s===void 0)throw new Error(`Invalid rating:[${e}]`);return s}else if(typeof e=="number")return e;throw new Error(`Invalid rating:[${e}]`)}const S=[n.Again,n.Hard,n.Good,n.Easy];class M{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===d.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===d.New?(this.again.state=d.Learning,this.hard.state=d.Learning,this.good.state=d.Learning,this.easy.state=d.Review):t===d.Learning||t===d.Relearning?(this.again.state=t,this.hard.state=t,this.good.state=d.Review,this.easy.state=d.Review):t===d.Review&&(this.again.state=d.Relearning,this.hard.state=d.Review,this.good.state=d.Review,this.easy.state=d.Review,this.again.lapses+=1),this}schedule(t,a,s,r){return this.again.scheduled_days=0,this.hard.scheduled_days=a,this.good.scheduled_days=s,this.easy.scheduled_days=r,this.again.due=y(t,5),this.hard.due=a>0?y(t,a,!0):y(t,10),this.good.due=y(t,s,!0),this.easy.due=y(t,r,!0),this}record_log(t,a){return{[n.Again]:{card:this.again,log:{rating:n.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:a}},[n.Hard]:{card:this.hard,log:{rating:n.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:a}},[n.Good]:{card:this.good,log:{rating:n.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:a}},[n.Easy]:{card:this.easy,log:{rating:n.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:a}}}}}const R=.9,E=36500,D=[.4,.6,2.4,5.8,4.93,.94,.86,.01,1.49,.14,.94,2.18,.05,.34,1.26,.29,2.61],$=!1,F="3.4.0",A=e=>({request_retention:(e==null?void 0:e.request_retention)||R,maximum_interval:(e==null?void 0:e.maximum_interval)||E,w:(e==null?void 0:e.w)||D,enable_fuzz:(e==null?void 0:e.enable_fuzz)||$});function H(e,t){const a={due:e?u(e):new Date,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:0,lapses:0,state:d.New,last_review:void 0};return t&&typeof t=="function"?t(a):a}class G{param;intervalModifier;seed;DECAY=-.5;FACTOR=Math.pow(.9,1/this.DECAY)-1;constructor(t){this.param=A(t),this.intervalModifier=(Math.pow(this.param.request_retention,1/this.DECAY)-1)/this.FACTOR}init_ds(t){t.again.difficulty=this.init_difficulty(n.Again),t.again.stability=this.init_stability(n.Again),t.hard.difficulty=this.init_difficulty(n.Hard),t.hard.stability=this.init_stability(n.Hard),t.good.difficulty=this.init_difficulty(n.Good),t.good.stability=this.init_stability(n.Good),t.easy.difficulty=this.init_difficulty(n.Easy),t.easy.stability=this.init_stability(n.Easy)}next_ds(t,a,s,r){t.again.difficulty=this.next_difficulty(a,n.Again),t.again.stability=this.next_forget_stability(a,s,r),t.hard.difficulty=this.next_difficulty(a,n.Hard),t.hard.stability=this.next_recall_stability(a,s,r,n.Hard),t.good.difficulty=this.next_difficulty(a,n.Good),t.good.stability=this.next_recall_stability(a,s,r,n.Good),t.easy.difficulty=this.next_difficulty(a,n.Easy),t.easy.stability=this.next_recall_stability(a,s,r,n.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 a=P(this.seed)();t=Math.round(t);const s=Math.max(2,Math.round(t*.95-1)),r=Math.round(t*1.05+1);return Math.floor(a*(r-s+1)+s)}next_interval(t){const a=this.apply_fuzz(t*this.intervalModifier);return Math.min(Math.max(Math.round(a),1),this.param.maximum_interval)}next_difficulty(t,a){const s=t-this.param.w[6]*(a-3);return this.constrain_difficulty(this.mean_reversion(this.param.w[4],s))}constrain_difficulty(t){return Math.min(Math.max(Number(t.toFixed(2)),1),10)}mean_reversion(t,a){return this.param.w[7]*t+(1-this.param.w[7])*a}next_recall_stability(t,a,s,r){const i=n.Hard===r?this.param.w[15]:1,l=n.Easy===r?this.param.w[16]:1;return a*(1+Math.exp(this.param.w[8])*(11-t)*Math.pow(a,-this.param.w[9])*(Math.exp((1-s)*this.param.w[10])-1)*i*l)}next_forget_stability(t,a,s){return Number((this.param.w[11]*Math.pow(t,-this.param.w[12])*(Math.pow(a+1,this.param.w[13])-1)*Math.exp((1-s)*this.param.w[14])).toFixed(2))}forgetting_curve(t,a){return Math.pow(1+this.FACTOR*t/a,this.DECAY)}}class C extends G{constructor(t){super(t)}preProcessCard(t){return{...t,state:g(t.state),due:u(t.due),last_review:t.last_review?u(t.last_review):void 0}}preProcessDate(t){return u(t)}preProcessLog(t){return{...t,due:u(t.due),rating:x(t.rating),state:g(t.state),review:u(t.review)}}repeat(t,a,s){const r=this.preProcessCard(t);a=this.preProcessDate(a);const i=new M(r,a).update_state(r.state);this.seed=String(a.getTime())+String(r.reps);let l,o,h;switch(r.state){case d.New:this.init_ds(i),i.again.due=a.scheduler(1),i.hard.due=a.scheduler(5),i.good.due=a.scheduler(10),l=this.next_interval(i.easy.stability),i.easy.scheduled_days=l,i.easy.due=a.scheduler(l,!0);break;case d.Learning:case d.Relearning:h=0,o=this.next_interval(i.good.stability),l=Math.max(this.next_interval(i.easy.stability),o+1),i.schedule(a,h,o,l);break;case d.Review:{const N=r.elapsed_days,z=r.difficulty,w=r.stability,L=this.forgetting_curve(N,w);this.next_ds(i,z,w,L),h=this.next_interval(i.hard.stability),o=this.next_interval(i.good.stability),h=Math.min(h,o),o=Math.max(o,h+1),l=Math.max(this.next_interval(i.easy.stability),o+1),i.schedule(a,h,o,l);break}}const c=i.record_log(r,a);return s&&typeof s=="function"?s(c):c}get_retrievability=(t,a)=>{const s=this.preProcessCard(t);if(a=this.preProcessDate(a),s.state!==d.Review)return;const r=Math.max(a.diff(s.last_review,"days"),0);return(this.forgetting_curve(r,s.stability)*100).toFixed(2)+"%"};rollback(t,a,s){const r=this.preProcessCard(t),i=this.preProcessLog(a);if(i.rating===n.Manual)throw new Error("Cannot rollback a manual rating");let l,o,h;switch(i.state){case d.New:l=i.due,o=void 0,h=0;break;case d.Learning:case d.Relearning:case d.Review:l=i.review,o=i.due,h=r.lapses-(i.rating===n.Again&&i.state===d.Review?1:0);break}const c={...r,due:l,stability:i.stability,difficulty:i.difficulty,elapsed_days:i.last_elapsed_days,scheduled_days:i.scheduled_days,reps:Math.max(0,r.reps-1),lapses:Math.max(0,h),state:i.state,last_review:o};return s&&typeof s=="function"?s(c):c}forget(t,a,s=!1,r){const i=this.preProcessCard(t);a=this.preProcessDate(a);const l=i.state===d.New?0:a.diff(i.last_review,"days"),o={rating:n.Manual,state:i.state,due:i.due,stability:i.stability,difficulty:i.difficulty,elapsed_days:0,last_elapsed_days:i.elapsed_days,scheduled_days:l,review:a},h={card:{...i,due:a,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:s?0:i.reps,lapses:s?0:i.lapses,state:d.New,last_review:i.last_review},log:o};return r&&typeof r=="function"?r(h):h}}const T=e=>new C(e||{});export{C as FSRS,F as FSRSVersion,S as Grades,n as Rating,M as SchedulingCard,d as State,H as createEmptyCard,v as date_diff,y as date_scheduler,$ as default_enable_fuzz,E as default_maximum_interval,R as default_request_retention,D as default_w,u as fixDate,x as fixRating,g as fixState,m as formatDate,T as fsrs,A as generatorParameters,b as show_diff_message};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","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.3.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 private readonly DECAY: number = -0.5;\n private readonly FACTOR: number = Math.pow(0.9, 1 / this.DECAY) - 1;\n\n constructor(param: Partial<FSRSParameters>) {\n this.param = generatorParameters(param);\n // Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45\n // The formula used is : I(r,s)= (r^{\\frac{1}{DECAY}-1}) \\times \\frac{s}{FACTOR}\n this.intervalModifier =\n (Math.pow(this.param.request_retention, 1 / this.DECAY) - 1) /\n this.FACTOR;\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 Number(\n (\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 ).toFixed(2),\n );\n }\n\n /**\n * The formula used is :\n * $$R(t,S) = (1 + FACTOR \\times \\frac{t}{9 \\cdot S})^{DECAY},$$\n * @param {number} elapsed_days t days since the last review\n * @param {number} stability Stability (interval when R=90%)\n * @return {number} r Retrievability (probability of recall)\n */\n forgetting_curve(elapsed_days: number, stability: number): number {\n return Math.pow(1 + (this.FACTOR * elapsed_days) / stability, this.DECAY);\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.forgetting_curve(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 (this.forgetting_curve(t, card.stability) * 100).toFixed(2) + \"%\";\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","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","DECAY","FACTOR","pow","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","forgetting_curve","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":"index.mjs","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}\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 interface CardInput extends Omit<Card, \"state\" | \"due\" | \"last_review\"> {\n state: StateType | State; // Card's state (New, Learning, Review, Relearning)\n due: DateInput; // Due date\n last_review?: DateInput | null; // Date of the last review (optional)\n}\n\nexport type DateInput = Date | number | string;\n\nexport interface ReviewLogInput\n extends Omit<ReviewLog, \"rating\" | \"state\" | \"due\" | \"review\"> {\n rating: RatingType | Rating; // Rating of the review (Again, Hard, Good, Easy)\n state: StateType | State; // Card's state (New, Learning, Review, Relearning)\n due: DateInput; // Due date\n review: DateInput; // Date of the last review\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 type { DateInput, Grade } from \"./models\";\nimport { 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(\n now: DateInput,\n t: number,\n isDay?: boolean,\n): Date {\n return new Date(\n isDay\n ? fixDate(now).getTime() + t * 24 * 60 * 60 * 1000\n : fixDate(now).getTime() + t * 60 * 1000,\n );\n}\n\nexport function date_diff(now: DateInput, pre: DateInput, unit: unit): number {\n if (!now || !pre) {\n throw new Error(\"Invalid date\");\n }\n const diff = fixDate(now).getTime() - fixDate(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(dateInput: DateInput): string {\n const date = fixDate(dateInput);\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: DateInput,\n last_review: DateInput,\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.4.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\n/**\n * Create an empty card\n * @param now Current time\n * @param afterHandler Convert the result to another type. (Optional)\n * @example\n * ```\n * const card: Card = createEmptyCard(new Date());\n * ```\n * @example\n * ```\n * interface CardUnChecked\n * extends Omit<Card, \"due\" | \"last_review\" | \"state\"> {\n * cid: string;\n * due: Date | number;\n * last_review: Date | null | number;\n * state: StateType;\n * }\n *\n * function cardAfterHandler(card: Card) {\n * return {\n * ...card,\n * cid: \"test001\",\n * state: State[card.state],\n * last_review: card.last_review ?? null,\n * } as CardUnChecked;\n * }\n *\n * const card: CardUnChecked = createEmptyCard(new Date(), cardAfterHandler);\n * ```\n */\nexport function createEmptyCard<R = Card>(\n now?: DateInput,\n afterHandler?: (card: Card) => R,\n): R {\n const emptyCard: Card = {\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 if (afterHandler && typeof afterHandler === \"function\") {\n return afterHandler(emptyCard);\n } else {\n return emptyCard as R;\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 private readonly DECAY: number = -0.5;\n private readonly FACTOR: number = Math.pow(0.9, 1 / this.DECAY) - 1;\n\n constructor(param: Partial<FSRSParameters>) {\n this.param = generatorParameters(param);\n // Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45\n // The formula used is : I(r,s)= (r^{\\frac{1}{DECAY}-1}) \\times \\frac{s}{FACTOR}\n this.intervalModifier =\n (Math.pow(this.param.request_retention, 1 / this.DECAY) - 1) /\n this.FACTOR;\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 Number(\n (\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 ).toFixed(2),\n );\n }\n\n /**\n * The formula used is :\n * $$R(t,S) = (1 + FACTOR \\times \\frac{t}{9 \\cdot S})^{DECAY},$$\n * @param {number} elapsed_days t days since the last review\n * @param {number} stability Stability (interval when R=90%)\n * @return {number} r Retrievability (probability of recall)\n */\n forgetting_curve(elapsed_days: number, stability: number): number {\n return Math.pow(1 + (this.FACTOR * elapsed_days) / stability, this.DECAY);\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): 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): ReviewLog {\n return {\n ..._log,\n due: fixDate(_log.due),\n rating: fixRating(_log.rating),\n state: fixState(_log.state),\n review: fixDate(_log.review),\n };\n }\n\n /**\n * @param card Card to be processed\n * @param now Current time or scheduled time\n * @param afterHandler Convert the result to another type. (Optional)\n * @example\n * ```\n * const card: Card = createEmptyCard(new Date());\n * const f = fsrs();\n * const recordLog = f.repeat(card, new Date());\n * ```\n * @example\n * ```\n * interface RevLogUnchecked\n * extends Omit<ReviewLog, \"due\" | \"review\" | \"state\" | \"rating\"> {\n * cid: string;\n * due: Date | number;\n * state: StateType;\n * review: Date | number;\n * rating: RatingType;\n * }\n *\n * interface RepeatRecordLog {\n * card: CardUnChecked; //see method: createEmptyCard\n * log: RevLogUnchecked;\n * }\n *\n * function repeatAfterHandler(recordLog: RecordLog) {\n * const record: { [key in Grade]: RepeatRecordLog } = {} as {\n * [key in Grade]: RepeatRecordLog;\n * };\n * for (const grade of Grades) {\n * record[grade] = {\n * card: {\n * ...(recordLog[grade].card as Card & { cid: string }),\n * due: recordLog[grade].card.due.getTime(),\n * state: State[recordLog[grade].card.state] as StateType,\n * last_review: recordLog[grade].card.last_review\n * ? recordLog[grade].card.last_review!.getTime()\n * : null,\n * },\n * log: {\n * ...recordLog[grade].log,\n * cid: (recordLog[grade].card as Card & { cid: string }).cid,\n * due: recordLog[grade].log.due.getTime(),\n * review: recordLog[grade].log.review.getTime(),\n * state: State[recordLog[grade].log.state] as StateType,\n * rating: Rating[recordLog[grade].log.rating] as RatingType,\n * },\n * };\n * }\n * return record;\n * }\n * const card: Card = createEmptyCard(new Date(), cardAfterHandler); //see method: createEmptyCard\n * const f = fsrs();\n * const recordLog = f.repeat(card, new Date(), repeatAfterHandler);\n * ```\n */\n repeat<R = RecordLog>(\n card: CardInput | Card,\n now: DateInput,\n afterHandler?: (recordLog: RecordLog) => R,\n ): R {\n const processedCard = this.preProcessCard(card);\n now = this.preProcessDate(now);\n const s = new SchedulingCard(processedCard, now).update_state(\n processedCard.state,\n );\n this.seed = String(now.getTime()) + String(processedCard.reps);\n let easy_interval, good_interval, hard_interval;\n switch (processedCard.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 = processedCard.elapsed_days;\n const last_d = processedCard.difficulty;\n const last_s = processedCard.stability;\n const retrievability = this.forgetting_curve(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 const recordLog = s.record_log(processedCard, now);\n if (afterHandler && typeof afterHandler === \"function\") {\n return afterHandler(recordLog);\n } else {\n return recordLog as R;\n }\n }\n\n get_retrievability = (\n card: CardInput | Card,\n now: Date,\n ): undefined | string => {\n const processedCard = this.preProcessCard(card);\n now = this.preProcessDate(now);\n if (processedCard.state !== State.Review) {\n return undefined;\n }\n const t = Math.max(now.diff(processedCard.last_review as Date, \"days\"), 0);\n return (\n (this.forgetting_curve(t, processedCard.stability) * 100).toFixed(2) + \"%\"\n );\n };\n\n /**\n *\n * @param card Card to be processed\n * @param log last review log\n * @param afterHandler Convert the result to another type. (Optional)\n * @example\n * ```\n * const now = new Date();\n * const f = fsrs();\n * const emptyCardFormAfterHandler = createEmptyCard(now);\n * const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now);\n * const { card, log } = repeatFormAfterHandler[Rating.Hard];\n * const rollbackFromAfterHandler = f.rollback(card, log);\n * ```\n *\n * @example\n * ```\n * const now = new Date();\n * const f = fsrs();\n * const emptyCardFormAfterHandler = createEmptyCard(now, cardAfterHandler); //see method: createEmptyCard\n * const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now, repeatAfterHandler); //see method: fsrs.repeat()\n * const { card, log } = repeatFormAfterHandler[Rating.Hard];\n * const rollbackFromAfterHandler = f.rollback(card, log, cardAfterHandler);\n * ```\n */\n rollback<R = Card>(\n card: CardInput | Card,\n log: ReviewLogInput,\n afterHandler?: (prevCard: Card) => R,\n ): R {\n const processedCard = this.preProcessCard(card);\n const processedLog = this.preProcessLog(log);\n if (processedLog.rating === Rating.Manual) {\n throw new Error(\"Cannot rollback a manual rating\");\n }\n let last_due, last_review, last_lapses;\n switch (processedLog.state) {\n case State.New:\n last_due = processedLog.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 = processedLog.review;\n last_review = processedLog.due;\n last_lapses =\n processedCard.lapses -\n (processedLog.rating === Rating.Again &&\n processedLog.state === State.Review\n ? 1\n : 0);\n break;\n }\n\n const prevCard: Card = {\n ...processedCard,\n due: last_due,\n stability: processedLog.stability,\n difficulty: processedLog.difficulty,\n elapsed_days: processedLog.last_elapsed_days,\n scheduled_days: processedLog.scheduled_days,\n reps: Math.max(0, processedCard.reps - 1),\n lapses: Math.max(0, last_lapses),\n state: processedLog.state,\n last_review: last_review,\n };\n if (afterHandler && typeof afterHandler === \"function\") {\n return afterHandler(prevCard);\n } else {\n return prevCard as R;\n }\n }\n\n /**\n *\n * @param card Card to be processed\n * @param now Current time or scheduled time\n * @param reset_count Should the review count information(reps,lapses) be reset. (Optional)\n * @param afterHandler Convert the result to another type. (Optional)\n * @example\n * ```\n * const now = new Date();\n * const f = fsrs();\n * const emptyCard = createEmptyCard(now);\n * const scheduling_cards = f.repeat(emptyCard, now);\n * const { card, log } = scheduling_cards[Rating.Hard];\n * const forgetCard = f.forget(card, new Date(), true);\n * ```\n *\n * @example\n * ```\n * interface RepeatRecordLog {\n * card: CardUnChecked; //see method: createEmptyCard\n * log: RevLogUnchecked; //see method: fsrs.repeat()\n * }\n *\n * function forgetAfterHandler(recordLogItem: RecordLogItem): RepeatRecordLog {\n * return {\n * card: {\n * ...(recordLogItem.card as Card & { cid: string }),\n * due: recordLogItem.card.due.getTime(),\n * state: State[recordLogItem.card.state] as StateType,\n * last_review: recordLogItem.card.last_review\n * ? recordLogItem.card.last_review!.getTime()\n * : null,\n * },\n * log: {\n * ...recordLogItem.log,\n * cid: (recordLogItem.card as Card & { cid: string }).cid,\n * due: recordLogItem.log.due.getTime(),\n * review: recordLogItem.log.review.getTime(),\n * state: State[recordLogItem.log.state] as StateType,\n * rating: Rating[recordLogItem.log.rating] as RatingType,\n * },\n * };\n * }\n * const now = new Date();\n * const f = fsrs();\n * const emptyCardFormAfterHandler = createEmptyCard(now, cardAfterHandler); //see method: createEmptyCard\n * const repeatFormAfterHandler = f.repeat(emptyCardFormAfterHandler, now, repeatAfterHandler); //see method: fsrs.repeat()\n * const { card } = repeatFormAfterHandler[Rating.Hard];\n * const forgetFromAfterHandler = f.forget(card, date_scheduler(now, 1, true), false, forgetAfterHandler);\n * ```\n */\n forget<R = RecordLogItem>(\n card: CardInput | Card,\n now: DateInput,\n reset_count: boolean = false,\n afterHandler?: (recordLogItem: RecordLogItem) => R,\n ): R {\n const processedCard = this.preProcessCard(card);\n now = this.preProcessDate(now);\n const scheduled_days =\n processedCard.state === State.New\n ? 0\n : now.diff(processedCard.last_review as Date, \"days\");\n const forget_log: ReviewLog = {\n rating: Rating.Manual,\n state: processedCard.state,\n due: processedCard.due,\n stability: processedCard.stability,\n difficulty: processedCard.difficulty,\n elapsed_days: 0,\n last_elapsed_days: processedCard.elapsed_days,\n scheduled_days: scheduled_days,\n review: now,\n };\n const forget_card: Card = {\n ...processedCard,\n due: now,\n stability: 0,\n difficulty: 0,\n elapsed_days: 0,\n scheduled_days: 0,\n reps: reset_count ? 0 : processedCard.reps,\n lapses: reset_count ? 0 : processedCard.lapses,\n state: State.New,\n last_review: processedCard.last_review,\n };\n const recordLogItem: RecordLogItem = { card: forget_card, log: forget_log };\n if (afterHandler && typeof afterHandler === \"function\") {\n return afterHandler(recordLogItem);\n } else {\n return recordLogItem as R;\n }\n }\n}\n\n/**\n * Create a new instance of TS-FSRS\n * @param params FSRSParameters\n * @example\n * ```typescript\n * const f = fsrs();\n * ```\n * @example\n * ```typescript\n * const params: FSRSParameters = generatorParameters({ maximum_interval: 1000 });\n * const f = fsrs(params);\n * ```\n * @example\n * ```typescript\n * const f = fsrs({ maximum_interval: 1000 });\n * ```\n */\nexport const fsrs = (params?: Partial<FSRSParameters>) => {\n return new FSRS(params || {});\n};\n"],"names":["State","t","Rating","isDay","date_scheduler","pre","unit","date_diff","formatDate","last_review","timeUnit","show_diff_message","now","fixDate","diff","r","dateInput","date","year","month","day","hours","minutes","seconds","padZero","num","TIMEUNIT","TIMEUNITFORMAT","due","value","timestamp","fixState","firstLetter","restOfString","ret","fixRating","Grades","SchedulingCard","card","state","hard_interval","good_interval","easy_interval","default_request_retention","default_maximum_interval","default_w","default_enable_fuzz","FSRSVersion","generatorParameters","props","createEmptyCard","afterHandler","emptyCard","FSRSAlgorithm","param","s","last_d","last_s","retrievability","g","ivl","fuzz_factor","pseudorandom","min_ivl","max_ivl","newInterval","d","next_d","difficulty","init","current","hard_penalty","easy_bound","elapsed_days","stability","FSRS","_card","_date","_log","processedCard","interval","recordLog","log","processedLog","last_due","last_lapses","prevCard","reset_count","scheduled_days","forget_log","recordLogItem","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.3.0",
3
+ "version": "3.4.0",
4
4
  "description": "ts-fsrs is a ES modules package based on TypeScript, used to implement the Free Spaced Repetition Scheduler (FSRS) algorithm. It helps developers apply FSRS to their flashcard applications, there by improving the user learning experience.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -16,7 +16,6 @@
16
16
  "devDependencies": {
17
17
  "@rollup/plugin-commonjs": "^25.0.7",
18
18
  "@rollup/plugin-node-resolve": "^15.2.3",
19
- "@rollup/plugin-terser": "^0.4.4",
20
19
  "@types/jest": "^29.5.8",
21
20
  "@types/node": "^20.9.1",
22
21
  "@types/seedrandom": "^3.0.8",
@@ -29,7 +28,7 @@
29
28
  "rimraf": "^5.0.5",
30
29
  "rollup": "^4.4.1",
31
30
  "rollup-plugin-dts": "^6.1.0",
32
- "rollup-plugin-esbuild": "^6.1.0",
31
+ "rollup-plugin-esbuild": "^6.1.1",
33
32
  "ts-jest": "^29.1.1",
34
33
  "tslib": "^2.6.2",
35
34
  "typedoc": "^0.25.3",
@@ -37,7 +36,7 @@
37
36
  },
38
37
  "scripts": {
39
38
  "lint": "eslint --fix src/ && prettier --write src/",
40
- "dev": "rollup -c -w",
39
+ "dev": "rollup -c rollup.config.ts --configPlugin esbuild -w",
41
40
  "test": "jest --passWithNoTests",
42
41
  "test:coverage": "jest --coverage",
43
42
  "prebuild": "rimraf ./dist",
@@ -60,12 +59,12 @@
60
59
  ],
61
60
  "repository": {
62
61
  "type": "git",
63
- "url": "git+https://github.com/ishiko732/ts-fsrs.git"
62
+ "url": "git+https://github.com/open-spaced-repetition/ts-fsrs.git"
64
63
  },
65
64
  "bugs": {
66
- "url": "https://github.com/ishiko732/ts-fsrs/issues"
65
+ "url": "https://github.com/open-spaced-repetition/ts-fsrs/issues"
67
66
  },
68
- "homepage": "https://github.com/ishiko732/ts-fsrs#readme",
67
+ "homepage": "https://github.com/open-spaced-repetition/ts-fsrs#readme",
69
68
  "engines": {
70
69
  "node": ">=16.0.0"
71
70
  }