ts-fsrs 3.5.4 → 3.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +42 -21
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# About The
|
|
6
6
|
[](https://www.npmjs.com/package/ts-fsrs)
|
|
7
7
|
[](https://www.npmjs.com/package/ts-fsrs)
|
|
8
|
+
[](https://codecov.io/gh/open-spaced-repetition/ts-fsrs)
|
|
8
9
|
[](https://github.com/open-spaced-repetition/ts-fsrs/actions/workflows/npm-publish.yml)
|
|
9
10
|
[](https://github.com/open-spaced-repetition/ts-fsrs/actions/workflows/deploy.yml)
|
|
10
11
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var G=require("seedrandom"),d=(t=>(t[t.New=0]="New",t[t.Learning=1]="Learning",t[t.Review=2]="Review",t[t.Relearning=3]="Relearning",t))(d||{}),n=(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))(n||{});Date.prototype.scheduler=function(t,e){return f(this,t,e)},Date.prototype.diff=function(t,e){return M(this,t,e)},Date.prototype.format=function(){return R(this)},Date.prototype.dueFormat=function(t,e,a){return E(this,t,e,a)};function f(t,e,a){return new Date(a?h(t).getTime()+e*24*60*60*1e3:h(t).getTime()+e*60*1e3)}function M(t,e,a){if(!t||!e)throw new Error("Invalid date");const s=h(t).getTime()-h(e).getTime();let i=0;switch(a){case"days":i=Math.floor(s/(24*60*60*1e3));break;case"minutes":i=Math.floor(s/(60*1e3));break}return i}function R(t){const e=h(t),a=e.getFullYear(),s=e.getMonth()+1,i=e.getDate(),r=e.getHours(),l=e.getMinutes(),o=e.getSeconds();return`${a}-${y(s)}-${y(i)} ${y(r)}:${y(l)}:${y(o)}`}function y(t){return t<10?`0${t}`:`${t}`}const p=[60,60,24,31,12],g=["second","min","hour","day","month","year"];function E(t,e,a,s=g){t=h(t),e=h(e),s.length!==g.length&&(s=g);let i=t.getTime()-e.getTime(),r;for(i/=1e3,r=0;r<p.length&&!(i<p[r]);r++)i/=p[r];return`${Math.floor(i)}${a?s[r]:""}`}function h(t){if(typeof t=="object"&&t instanceof Date)return t;if(typeof t=="string"){const e=Date.parse(t);if(isNaN(e))throw new Error(`Invalid date:[${t}]`);return new Date(e)}else if(typeof t=="number")return new Date(t);throw new Error(`Invalid date:[${t}]`)}function _(t){if(typeof t=="string"){const e=t.charAt(0).toUpperCase(),a=t.slice(1).toLowerCase(),s=d[`${e}${a}`];if(s===void 0)throw new Error(`Invalid state:[${t}]`);return s}else if(typeof t=="number")return t;throw new Error(`Invalid state:[${t}]`)}function D(t){if(typeof t=="string"){const e=t.charAt(0).toUpperCase(),a=t.slice(1).toLowerCase(),s=n[`${e}${a}`];if(s===void 0)throw new Error(`Invalid rating:[${t}]`);return s}else if(typeof t=="number")return t;throw new Error(`Invalid rating:[${t}]`)}const k=[n.Again,n.Hard,n.Good,n.Easy],T=[{start:2.5,end:7,factor:.15},{start:7,end:20,factor:.1},{start:20,end:1/0,factor:.05}];function S(t,e,a){let s=1;for(const l of T)s+=l.factor*Math.max(Math.min(t,l.end)-l.start,0);t=Math.min(t,a);let i=Math.max(2,Math.round(t-s));const r=Math.min(Math.round(t+s),a);return t>e&&(i=Math.max(i,e+1)),i=Math.min(i,r),{min_ivl:i,max_ivl:r}}class ${again;hard;good;easy;last_review;last_elapsed_days;copy(e){return{...e}}constructor(e,a){this.last_review=e.last_review||e.due,this.last_elapsed_days=e.elapsed_days,e.elapsed_days=e.state===d.New?0:a.diff(e.last_review,"days"),e.last_review=a,e.reps+=1,this.again=this.copy(e),this.hard=this.copy(e),this.good=this.copy(e),this.easy=this.copy(e)}update_state(e){return e===d.New?(this.again.state=d.Learning,this.hard.state=d.Learning,this.good.state=d.Learning,this.easy.state=d.Review):e===d.Learning||e===d.Relearning?(this.again.state=e,this.hard.state=e,this.good.state=d.Review,this.easy.state=d.Review):e===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(e,a,s,i){return this.again.scheduled_days=0,this.hard.scheduled_days=a,this.good.scheduled_days=s,this.easy.scheduled_days=i,this.again.due=f(e,5),this.hard.due=a>0?f(e,a,!0):f(e,10),this.good.due=f(e,s,!0),this.easy.due=f(e,i,!0),this}record_log(e,a){return{[n.Again]:{card:this.again,log:{rating:n.Again,state:e.state,due:this.last_review,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:e.scheduled_days,review:a}},[n.Hard]:{card:this.hard,log:{rating:n.Hard,state:e.state,due:this.last_review,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:e.scheduled_days,review:a}},[n.Good]:{card:this.good,log:{rating:n.Good,state:e.state,due:this.last_review,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:e.scheduled_days,review:a}},[n.Easy]:{card:this.easy,log:{rating:n.Easy,state:e.state,due:this.last_review,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:e.scheduled_days,review:a}}}}}const z=.9,F=36500,A=[.5701,1.4436,4.1386,10.9355,5.1443,1.2006,.8627,.0362,1.629,.1342,1.0166,2.1174,.0839,.3204,1.4676,.219,2.8237],C=!1,q="3.5.4",m=t=>({request_retention:(t==null?void 0:t.request_retention)||z,maximum_interval:(t==null?void 0:t.maximum_interval)||F,w:(t==null?void 0:t.w)||A,enable_fuzz:(t==null?void 0:t.enable_fuzz)||C});function I(t,e){const a={due:t?h(t):new Date,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:0,lapses:0,state:d.New,last_review:void 0};return e&&typeof e=="function"?e(a):a}const w=-.5,v=19/81;class H{param;intervalModifier;seed;constructor(e){this.param=new Proxy(m(e),this.params_handler_proxy()),this.intervalModifier=this.calculate_interval_modifier(this.param.request_retention)}get interval_modifier(){return this.intervalModifier}calculate_interval_modifier(e){if(e<=0||e>1)throw new Error("Requested retention rate should be in the range (0,1]");return+((Math.pow(e,1/w)-1)/v).toFixed(8)}get parameters(){return this.param}set parameters(e){this.update_parameters(e)}params_handler_proxy(){const e=this;return{set:function(a,s,i){return s==="request_retention"&&Number.isFinite(i)&&(e.intervalModifier=e.calculate_interval_modifier(Number(i))),a[s]=i,!0}}}update_parameters(e){const a=m(e);for(const s in a)if(s in this.param){const i=s;this.param[i]=a[i]}}init_ds(e){e.again.difficulty=this.init_difficulty(n.Again),e.again.stability=this.init_stability(n.Again),e.hard.difficulty=this.init_difficulty(n.Hard),e.hard.stability=this.init_stability(n.Hard),e.good.difficulty=this.init_difficulty(n.Good),e.good.stability=this.init_stability(n.Good),e.easy.difficulty=this.init_difficulty(n.Easy),e.easy.stability=this.init_stability(n.Easy)}next_ds(e,a,s,i){e.again.difficulty=this.next_difficulty(a,n.Again),e.again.stability=this.next_forget_stability(a,s,i),e.hard.difficulty=this.next_difficulty(a,n.Hard),e.hard.stability=this.next_recall_stability(a,s,i,n.Hard),e.good.difficulty=this.next_difficulty(a,n.Good),e.good.stability=this.next_recall_stability(a,s,i,n.Good),e.easy.difficulty=this.next_difficulty(a,n.Easy),e.easy.stability=this.next_recall_stability(a,s,i,n.Easy)}init_stability(e){return Math.max(this.param.w[e-1],.1)}init_difficulty(e){return+Math.min(Math.max(this.param.w[4]-(e-3)*this.param.w[5],1),10).toFixed(8)}apply_fuzz(e,a,s){if(!s||e<2.5)return Math.round(e);const i=G(this.seed)(),{min_ivl:r,max_ivl:l}=S(e,a,this.param.maximum_interval);return Math.floor(i*(l-r+1)+r)}next_interval(e,a,s=this.param.enable_fuzz){const i=Math.min(Math.max(1,Math.round(e*this.intervalModifier)),this.param.maximum_interval);return this.apply_fuzz(i,a,s)}next_difficulty(e,a){const s=e-this.param.w[6]*(a-3);return this.constrain_difficulty(this.mean_reversion(this.param.w[4],s))}constrain_difficulty(e){return Math.min(Math.max(+e.toFixed(8),1),10)}mean_reversion(e,a){return+(this.param.w[7]*e+(1-this.param.w[7])*a).toFixed(8)}next_recall_stability(e,a,s,i){const r=n.Hard===i?this.param.w[15]:1,l=n.Easy===i?this.param.w[16]:1;return+(a*(1+Math.exp(this.param.w[8])*(11-e)*Math.pow(a,-this.param.w[9])*(Math.exp((1-s)*this.param.w[10])-1)*r*l)).toFixed(8)}next_forget_stability(e,a,s){return+(this.param.w[11]*Math.pow(e,-this.param.w[12])*(Math.pow(a+1,this.param.w[13])-1)*Math.exp((1-s)*this.param.w[14])).toFixed(8)}forgetting_curve(e,a){return+Math.pow(1+v*e/a,w).toFixed(8)}}class P extends H{constructor(e){super(e)}preProcessCard(e){return{...e,state:_(e.state),due:h(e.due),last_review:e.last_review?h(e.last_review):void 0}}preProcessDate(e){return h(e)}preProcessLog(e){return{...e,due:h(e.due),rating:D(e.rating),state:_(e.state),review:h(e.review)}}repeat(e,a,s){const i=this.preProcessCard(e);a=this.preProcessDate(a);const r=new $(i,a).update_state(i.state);this.seed=String(a.getTime())+String(i.reps);let l,o,u;const c=i.elapsed_days;switch(i.state){case d.New:this.init_ds(r),r.again.due=a.scheduler(1),r.hard.due=a.scheduler(5),r.good.due=a.scheduler(10),l=this.next_interval(r.easy.stability,c),r.easy.scheduled_days=l,r.easy.due=a.scheduler(l,!0);break;case d.Learning:case d.Relearning:u=0,o=this.next_interval(r.good.stability,c),l=Math.max(this.next_interval(r.easy.stability,c),o+1),r.schedule(a,u,o,l);break;case d.Review:{const N=i.difficulty,x=i.stability,L=this.forgetting_curve(c,x);this.next_ds(r,N,x,L),u=this.next_interval(r.hard.stability,c),o=this.next_interval(r.good.stability,c),u=Math.min(u,o),o=Math.max(o,u+1),l=Math.max(this.next_interval(r.easy.stability,c),o+1),r.schedule(a,u,o,l);break}}const b=r.record_log(i,a);return s&&typeof s=="function"?s(b):b}get_retrievability=(e,a)=>{const s=this.preProcessCard(e);if(a=this.preProcessDate(a),s.state!==d.Review)return;const i=Math.max(a.diff(s.last_review,"days"),0);return(this.forgetting_curve(i,s.stability)*100).toFixed(2)+"%"};rollback(e,a,s){const i=this.preProcessCard(e),r=this.preProcessLog(a);if(r.rating===n.Manual)throw new Error("Cannot rollback a manual rating");let l,o,u;switch(r.state){case d.New:l=r.due,o=void 0,u=0;break;case d.Learning:case d.Relearning:case d.Review:l=r.review,o=r.due,u=i.lapses-(r.rating===n.Again&&r.state===d.Review?1:0);break}const c={...i,due:l,stability:r.stability,difficulty:r.difficulty,elapsed_days:r.last_elapsed_days,scheduled_days:r.scheduled_days,reps:Math.max(0,i.reps-1),lapses:Math.max(0,u),state:r.state,last_review:o};return s&&typeof s=="function"?s(c):c}forget(e,a,s=!1,i){const r=this.preProcessCard(e);a=this.preProcessDate(a);const l=r.state===d.New?0:a.diff(r.last_review,"days"),o={rating:n.Manual,state:r.state,due:r.due,stability:r.stability,difficulty:r.difficulty,elapsed_days:0,last_elapsed_days:r.elapsed_days,scheduled_days:l,review:a},u={card:{...r,due:a,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:s?0:r.reps,lapses:s?0:r.lapses,state:d.New,last_review:r.last_review},log:o};return i&&typeof i=="function"?i(u):u}reschedule(e,a={}){if(!Array.isArray(e))throw new Error("cards must be an array");const s=[];for(const i of e){if(_(i.state)!==d.Review||!i.last_review)continue;const r=Math.floor(i.scheduled_days),l=this.next_interval(+i.stability.toFixed(2),Math.round(i.elapsed_days),a.enable_fuzz??!0);if(l===r||l===0)continue;const o={...i};o.scheduled_days=l;const u=f(o.last_review,l,!0);a.dateHandler&&typeof a.dateHandler=="function"?o.due=a.dateHandler(u):o.due=u,s.push(o)}return s}}const O=t=>new P(t||{});exports.DECAY=w,exports.FACTOR=v,exports.FSRS=P,exports.FSRSAlgorithm=H,exports.FSRSVersion=q,exports.Grades=k,exports.Rating=n,exports.SchedulingCard=$,exports.State=d,exports.createEmptyCard=I,exports.date_diff=M,exports.date_scheduler=f,exports.default_enable_fuzz=C,exports.default_maximum_interval=F,exports.default_request_retention=z,exports.default_w=A,exports.fixDate=h,exports.fixRating=D,exports.fixState=_,exports.formatDate=R,exports.fsrs=O,exports.generatorParameters=m,exports.get_fuzz_range=S,exports.show_diff_message=E,module.exports=Object.assign(exports.default||{},exports);
|
|
1
|
+
"use strict";var G=require("seedrandom"),d=(t=>(t[t.New=0]="New",t[t.Learning=1]="Learning",t[t.Review=2]="Review",t[t.Relearning=3]="Relearning",t))(d||{}),n=(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))(n||{});Date.prototype.scheduler=function(t,e){return f(this,t,e)},Date.prototype.diff=function(t,e){return M(this,t,e)},Date.prototype.format=function(){return R(this)},Date.prototype.dueFormat=function(t,e,a){return E(this,t,e,a)};function f(t,e,a){return new Date(a?h(t).getTime()+e*24*60*60*1e3:h(t).getTime()+e*60*1e3)}function M(t,e,a){if(!t||!e)throw new Error("Invalid date");const s=h(t).getTime()-h(e).getTime();let i=0;switch(a){case"days":i=Math.floor(s/(24*60*60*1e3));break;case"minutes":i=Math.floor(s/(60*1e3));break}return i}function R(t){const e=h(t),a=e.getFullYear(),s=e.getMonth()+1,i=e.getDate(),r=e.getHours(),l=e.getMinutes(),o=e.getSeconds();return`${a}-${y(s)}-${y(i)} ${y(r)}:${y(l)}:${y(o)}`}function y(t){return t<10?`0${t}`:`${t}`}const p=[60,60,24,31,12],g=["second","min","hour","day","month","year"];function E(t,e,a,s=g){t=h(t),e=h(e),s.length!==g.length&&(s=g);let i=t.getTime()-e.getTime(),r;for(i/=1e3,r=0;r<p.length&&!(i<p[r]);r++)i/=p[r];return`${Math.floor(i)}${a?s[r]:""}`}function h(t){if(typeof t=="object"&&t instanceof Date)return t;if(typeof t=="string"){const e=Date.parse(t);if(isNaN(e))throw new Error(`Invalid date:[${t}]`);return new Date(e)}else if(typeof t=="number")return new Date(t);throw new Error(`Invalid date:[${t}]`)}function _(t){if(typeof t=="string"){const e=t.charAt(0).toUpperCase(),a=t.slice(1).toLowerCase(),s=d[`${e}${a}`];if(s===void 0)throw new Error(`Invalid state:[${t}]`);return s}else if(typeof t=="number")return t;throw new Error(`Invalid state:[${t}]`)}function D(t){if(typeof t=="string"){const e=t.charAt(0).toUpperCase(),a=t.slice(1).toLowerCase(),s=n[`${e}${a}`];if(s===void 0)throw new Error(`Invalid rating:[${t}]`);return s}else if(typeof t=="number")return t;throw new Error(`Invalid rating:[${t}]`)}const k=[n.Again,n.Hard,n.Good,n.Easy],T=[{start:2.5,end:7,factor:.15},{start:7,end:20,factor:.1},{start:20,end:1/0,factor:.05}];function S(t,e,a){let s=1;for(const l of T)s+=l.factor*Math.max(Math.min(t,l.end)-l.start,0);t=Math.min(t,a);let i=Math.max(2,Math.round(t-s));const r=Math.min(Math.round(t+s),a);return t>e&&(i=Math.max(i,e+1)),i=Math.min(i,r),{min_ivl:i,max_ivl:r}}class ${again;hard;good;easy;last_review;last_elapsed_days;copy(e){return{...e}}constructor(e,a){this.last_review=e.last_review||e.due,this.last_elapsed_days=e.elapsed_days,e.elapsed_days=e.state===d.New?0:a.diff(e.last_review,"days"),e.last_review=a,e.reps+=1,this.again=this.copy(e),this.hard=this.copy(e),this.good=this.copy(e),this.easy=this.copy(e)}update_state(e){return e===d.New?(this.again.state=d.Learning,this.hard.state=d.Learning,this.good.state=d.Learning,this.easy.state=d.Review):e===d.Learning||e===d.Relearning?(this.again.state=e,this.hard.state=e,this.good.state=d.Review,this.easy.state=d.Review):e===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(e,a,s,i){return this.again.scheduled_days=0,this.hard.scheduled_days=a,this.good.scheduled_days=s,this.easy.scheduled_days=i,this.again.due=f(e,5),this.hard.due=a>0?f(e,a,!0):f(e,10),this.good.due=f(e,s,!0),this.easy.due=f(e,i,!0),this}record_log(e,a){return{[n.Again]:{card:this.again,log:{rating:n.Again,state:e.state,due:this.last_review,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:e.scheduled_days,review:a}},[n.Hard]:{card:this.hard,log:{rating:n.Hard,state:e.state,due:this.last_review,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:e.scheduled_days,review:a}},[n.Good]:{card:this.good,log:{rating:n.Good,state:e.state,due:this.last_review,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:e.scheduled_days,review:a}},[n.Easy]:{card:this.easy,log:{rating:n.Easy,state:e.state,due:this.last_review,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:e.scheduled_days,review:a}}}}}const z=.9,F=36500,A=[.5701,1.4436,4.1386,10.9355,5.1443,1.2006,.8627,.0362,1.629,.1342,1.0166,2.1174,.0839,.3204,1.4676,.219,2.8237],C=!1,q="3.5.6",m=t=>({request_retention:(t==null?void 0:t.request_retention)||z,maximum_interval:(t==null?void 0:t.maximum_interval)||F,w:(t==null?void 0:t.w)||A,enable_fuzz:(t==null?void 0:t.enable_fuzz)||C});function I(t,e){const a={due:t?h(t):new Date,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:0,lapses:0,state:d.New,last_review:void 0};return e&&typeof e=="function"?e(a):a}const w=-.5,v=19/81;class H{param;intervalModifier;seed;constructor(e){this.param=new Proxy(m(e),this.params_handler_proxy()),this.intervalModifier=this.calculate_interval_modifier(this.param.request_retention)}get interval_modifier(){return this.intervalModifier}calculate_interval_modifier(e){if(e<=0||e>1)throw new Error("Requested retention rate should be in the range (0,1]");return+((Math.pow(e,1/w)-1)/v).toFixed(8)}get parameters(){return this.param}set parameters(e){this.update_parameters(e)}params_handler_proxy(){const e=this;return{set:function(a,s,i){return s==="request_retention"&&Number.isFinite(i)&&(e.intervalModifier=e.calculate_interval_modifier(Number(i))),a[s]=i,!0}}}update_parameters(e){const a=m(e);for(const s in a)if(s in this.param){const i=s;this.param[i]=a[i]}}init_ds(e){e.again.difficulty=this.init_difficulty(n.Again),e.again.stability=this.init_stability(n.Again),e.hard.difficulty=this.init_difficulty(n.Hard),e.hard.stability=this.init_stability(n.Hard),e.good.difficulty=this.init_difficulty(n.Good),e.good.stability=this.init_stability(n.Good),e.easy.difficulty=this.init_difficulty(n.Easy),e.easy.stability=this.init_stability(n.Easy)}next_ds(e,a,s,i){e.again.difficulty=this.next_difficulty(a,n.Again),e.again.stability=this.next_forget_stability(a,s,i),e.hard.difficulty=this.next_difficulty(a,n.Hard),e.hard.stability=this.next_recall_stability(a,s,i,n.Hard),e.good.difficulty=this.next_difficulty(a,n.Good),e.good.stability=this.next_recall_stability(a,s,i,n.Good),e.easy.difficulty=this.next_difficulty(a,n.Easy),e.easy.stability=this.next_recall_stability(a,s,i,n.Easy)}init_stability(e){return Math.max(this.param.w[e-1],.1)}init_difficulty(e){return+Math.min(Math.max(this.param.w[4]-(e-3)*this.param.w[5],1),10).toFixed(8)}apply_fuzz(e,a,s){if(!s||e<2.5)return Math.round(e);const i=G(this.seed)(),{min_ivl:r,max_ivl:l}=S(e,a,this.param.maximum_interval);return Math.floor(i*(l-r+1)+r)}next_interval(e,a,s=this.param.enable_fuzz){const i=Math.min(Math.max(1,Math.round(e*this.intervalModifier)),this.param.maximum_interval);return this.apply_fuzz(i,a,s)}next_difficulty(e,a){const s=e-this.param.w[6]*(a-3);return this.constrain_difficulty(this.mean_reversion(this.param.w[4],s))}constrain_difficulty(e){return Math.min(Math.max(+e.toFixed(8),1),10)}mean_reversion(e,a){return+(this.param.w[7]*e+(1-this.param.w[7])*a).toFixed(8)}next_recall_stability(e,a,s,i){const r=n.Hard===i?this.param.w[15]:1,l=n.Easy===i?this.param.w[16]:1;return+(a*(1+Math.exp(this.param.w[8])*(11-e)*Math.pow(a,-this.param.w[9])*(Math.exp((1-s)*this.param.w[10])-1)*r*l)).toFixed(8)}next_forget_stability(e,a,s){return+(this.param.w[11]*Math.pow(e,-this.param.w[12])*(Math.pow(a+1,this.param.w[13])-1)*Math.exp((1-s)*this.param.w[14])).toFixed(8)}forgetting_curve(e,a){return+Math.pow(1+v*e/a,w).toFixed(8)}}class P extends H{constructor(e){super(e)}preProcessCard(e){return{...e,state:_(e.state),due:h(e.due),last_review:e.last_review?h(e.last_review):void 0}}preProcessDate(e){return h(e)}preProcessLog(e){return{...e,due:h(e.due),rating:D(e.rating),state:_(e.state),review:h(e.review)}}repeat(e,a,s){const i=this.preProcessCard(e);a=this.preProcessDate(a);const r=new $(i,a).update_state(i.state);this.seed=String(a.getTime())+String(i.reps);let l,o,u;const c=i.elapsed_days;switch(i.state){case d.New:this.init_ds(r),r.again.due=a.scheduler(1),r.hard.due=a.scheduler(5),r.good.due=a.scheduler(10),l=this.next_interval(r.easy.stability,c),r.easy.scheduled_days=l,r.easy.due=a.scheduler(l,!0);break;case d.Learning:case d.Relearning:u=0,o=this.next_interval(r.good.stability,c),l=Math.max(this.next_interval(r.easy.stability,c),o+1),r.schedule(a,u,o,l);break;case d.Review:{const N=i.difficulty,x=i.stability,L=this.forgetting_curve(c,x);this.next_ds(r,N,x,L),u=this.next_interval(r.hard.stability,c),o=this.next_interval(r.good.stability,c),u=Math.min(u,o),o=Math.max(o,u+1),l=Math.max(this.next_interval(r.easy.stability,c),o+1),r.schedule(a,u,o,l);break}}const b=r.record_log(i,a);return s&&typeof s=="function"?s(b):b}get_retrievability=(e,a)=>{const s=this.preProcessCard(e);if(a=this.preProcessDate(a),s.state!==d.Review)return;const i=Math.max(a.diff(s.last_review,"days"),0);return(this.forgetting_curve(i,s.stability)*100).toFixed(2)+"%"};rollback(e,a,s){const i=this.preProcessCard(e),r=this.preProcessLog(a);if(r.rating===n.Manual)throw new Error("Cannot rollback a manual rating");let l,o,u;switch(r.state){case d.New:l=r.due,o=void 0,u=0;break;case d.Learning:case d.Relearning:case d.Review:l=r.review,o=r.due,u=i.lapses-(r.rating===n.Again&&r.state===d.Review?1:0);break}const c={...i,due:l,stability:r.stability,difficulty:r.difficulty,elapsed_days:r.last_elapsed_days,scheduled_days:r.scheduled_days,reps:Math.max(0,i.reps-1),lapses:Math.max(0,u),state:r.state,last_review:o};return s&&typeof s=="function"?s(c):c}forget(e,a,s=!1,i){const r=this.preProcessCard(e);a=this.preProcessDate(a);const l=r.state===d.New?0:a.diff(r.last_review,"days"),o={rating:n.Manual,state:r.state,due:r.due,stability:r.stability,difficulty:r.difficulty,elapsed_days:0,last_elapsed_days:r.elapsed_days,scheduled_days:l,review:a},u={card:{...r,due:a,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:s?0:r.reps,lapses:s?0:r.lapses,state:d.New,last_review:r.last_review},log:o};return i&&typeof i=="function"?i(u):u}reschedule(e,a={}){if(!Array.isArray(e))throw new Error("cards must be an array");const s=[];for(const i of e){if(_(i.state)!==d.Review||!i.last_review)continue;const r=Math.floor(i.scheduled_days),l=this.next_interval(+i.stability.toFixed(2),Math.round(i.elapsed_days),a.enable_fuzz??!0);if(l===r||l===0)continue;const o={...i};o.scheduled_days=l;const u=f(o.last_review,l,!0);a.dateHandler&&typeof a.dateHandler=="function"?o.due=a.dateHandler(u):o.due=u,s.push(o)}return s}}const O=t=>new P(t||{});exports.DECAY=w,exports.FACTOR=v,exports.FSRS=P,exports.FSRSAlgorithm=H,exports.FSRSVersion=q,exports.Grades=k,exports.Rating=n,exports.SchedulingCard=$,exports.State=d,exports.createEmptyCard=I,exports.date_diff=M,exports.date_scheduler=f,exports.default_enable_fuzz=C,exports.default_maximum_interval=F,exports.default_request_retention=z,exports.default_w=A,exports.fixDate=h,exports.fixRating=D,exports.fixState=_,exports.formatDate=R,exports.fsrs=O,exports.generatorParameters=m,exports.get_fuzz_range=S,exports.show_diff_message=E,module.exports=Object.assign(exports.default||{},exports);
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -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}\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\nexport type RescheduleOptions = {\n enable_fuzz?: boolean;\n dateHandler?: (date: Date) => DateInput;\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: Readonly<Grade[]> = [\n Rating.Again,\n Rating.Hard,\n Rating.Good,\n Rating.Easy,\n] as const;\n\nconst FUZZ_RANGES = [\n {\n start: 2.5,\n end: 7.0,\n factor: 0.15,\n },\n {\n start: 7.0,\n end: 20.0,\n factor: 0.1,\n },\n {\n start: 20.0,\n end: Infinity,\n factor: 0.05,\n },\n] as const;\n\nexport function get_fuzz_range(\n interval: number,\n elapsed_days: number,\n maximum_interval: number,\n) {\n let delta = 1.0;\n for (const range of FUZZ_RANGES) {\n delta +=\n range.factor * Math.max(Math.min(interval, range.end) - range.start, 0.0);\n }\n interval = Math.min(interval, maximum_interval);\n let min_ivl = Math.max(2, Math.round(interval - delta));\n const max_ivl = Math.min(Math.round(interval + delta), maximum_interval);\n if (interval > elapsed_days) {\n min_ivl = Math.max(min_ivl, elapsed_days + 1);\n }\n min_ivl = Math.min(min_ivl, max_ivl);\n return { min_ivl, max_ivl };\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.5701, 1.4436, 4.1386, 10.9355, 5.1443, 1.2006, 0.8627, 0.0362, 1.629,\n 0.1342, 1.0166, 2.1174, 0.0839, 0.3204, 1.4676, 0.219, 2.8237,\n];\nexport const default_enable_fuzz = false;\n\nexport const FSRSVersion: string = \"3.5.4\";\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\";\nimport { get_fuzz_range } from \"./help\";\n\n// Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-v4\nexport const DECAY: number = -0.5;\n// FACTOR = Math.pow(0.9, 1 / DECAY) - 1\nexport const FACTOR: number = 19 / 81;\n\nexport class FSRSAlgorithm {\n protected param!: FSRSParameters;\n protected intervalModifier!: number;\n protected seed?: string;\n\n constructor(params: Partial<FSRSParameters>) {\n this.param = new Proxy(\n generatorParameters(params),\n this.params_handler_proxy(),\n );\n this.intervalModifier = this.calculate_interval_modifier(\n this.param.request_retention,\n );\n }\n\n get interval_modifier(): number {\n return this.intervalModifier;\n }\n\n /**\n * Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45\n * The formula used is: I(r,s) = (r^(1/DECAY) - 1) * s / FACTOR\n * @param request_retention 0<request_retention<=1,Requested retention rate\n */\n calculate_interval_modifier(request_retention: number): number {\n if (request_retention <= 0 || request_retention > 1) {\n throw new Error(\"Requested retention rate should be in the range (0,1]\");\n }\n return +((Math.pow(request_retention, 1 / DECAY) - 1) / FACTOR).toFixed(8);\n }\n\n get parameters(): FSRSParameters {\n return this.param;\n }\n\n set parameters(params: Partial<FSRSParameters>) {\n this.update_parameters(params);\n }\n\n private params_handler_proxy(): ProxyHandler<FSRSParameters> {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const _this: FSRSAlgorithm = this;\n return {\n set: function (target, prop, value) {\n if (prop === \"request_retention\" && Number.isFinite(value)) {\n _this.intervalModifier = _this.calculate_interval_modifier(\n Number(value),\n );\n }\n // @ts-ignore\n target[prop] = value;\n return true;\n },\n };\n }\n\n private update_parameters(params: Partial<FSRSParameters>): void {\n const _params = generatorParameters(params);\n for (const key in _params) {\n if (key in this.param) {\n const paramKey = key as keyof FSRSParameters;\n this.param[paramKey] = _params[paramKey] as never;\n }\n }\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 ).toFixed(8);\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 * @param {number} elapsed_days t days since the last review\n * @param {number} enable_fuzz - This adds a small random delay to the new interval time to prevent cards from sticking together and always being reviewed on the same day.\n * @return {number} - The fuzzed interval.\n **/\n apply_fuzz(ivl: number, elapsed_days: number, enable_fuzz?: boolean): int {\n if (!enable_fuzz || ivl < 2.5) return Math.round(ivl) as int;\n const generator = pseudorandom(this.seed);\n const fuzz_factor = generator();\n const { min_ivl, max_ivl } = get_fuzz_range(\n ivl,\n elapsed_days,\n this.param.maximum_interval,\n );\n return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl) as int;\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 * @param {number} elapsed_days t days since the last review\n * @param {number} enable_fuzz - This adds a small random delay to the new interval time to prevent cards from sticking together and always being reviewed on the same day.\n */\n next_interval(\n s: number,\n elapsed_days: number,\n enable_fuzz: boolean = this.param.enable_fuzz,\n ): int {\n const newInterval = Math.min(\n Math.max(1, Math.round(s * this.intervalModifier)),\n this.param.maximum_interval,\n ) as int;\n return this.apply_fuzz(newInterval, elapsed_days, enable_fuzz);\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(+difficulty.toFixed(8), 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).toFixed(\n 8,\n );\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 ).toFixed(8);\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_f(D,S,R) = w_11\\cdot D^{-w_{12}}\\cdot ((S+1)^{w_{13}}-1) \\cdot e^{w_{14}\\cdot(1-R)}.$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @return {number} S^\\prime_f new stability after forgetting\n */\n next_forget_stability(d: number, s: number, r: number): number {\n return +(\n this.param.w[11] *\n Math.pow(d, -this.param.w[12]) *\n (Math.pow(s + 1, this.param.w[13]) - 1) *\n Math.exp((1 - r) * this.param.w[14])\n ).toFixed(8);\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 + (FACTOR * elapsed_days) / stability, DECAY).toFixed(8);\n }\n}\n","import { SchedulingCard } from \"./scheduler\";\nimport { date_scheduler, fixDate, fixRating, fixState } from \"./help\";\nimport {\n Card,\n CardInput,\n DateInput,\n FSRSParameters,\n Rating,\n RecordLog,\n RecordLogItem,\n RescheduleOptions,\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 const interval = processedCard.elapsed_days;\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, interval);\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, interval);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability, interval),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n case State.Review: {\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, interval);\n good_interval = this.next_interval(s.good.stability, interval);\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, interval),\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 * @param cards scheduled card collection\n * @param options Reschedule options,fuzz is enabled by default.If the type of due is not Date, please implement dataHandler.\n * @example\n * ```typescript\n * type CardType = Card & {\n * cid: number;\n * };\n * const reviewCard: CardType = {\n * cid: 1,\n * due: new Date(\"2024-03-17 04:43:02\"),\n * stability: 48.26139059062234,\n * difficulty: 5.67,\n * elapsed_days: 18,\n * scheduled_days: 51,\n * reps: 8,\n * lapses: 1,\n * state: State.Review,\n * last_review: new Date(\"2024-01-26 04:43:02\"),\n * };\n * const f = fsrs();\n * const reschedule_cards = f.reschedule([reviewCard]);\n * ```\n *\n */\n reschedule<T extends CardInput | Card>(\n cards: Array<T>,\n options: RescheduleOptions = {},\n ): Array<T> {\n if (!Array.isArray(cards)) {\n throw new Error(\"cards must be an array\");\n }\n const processedCard: T[] = [];\n for (const card of cards) {\n if (fixState(card.state) !== State.Review || !card.last_review) continue;\n const scheduled_days = Math.floor(card.scheduled_days) as int;\n const next_ivl = this.next_interval(\n +card.stability.toFixed(2),\n Math.round(card.elapsed_days),\n options.enable_fuzz ?? true,\n );\n if (next_ivl === scheduled_days || next_ivl === 0) continue;\n\n const processCard: T = { ...card };\n processCard.scheduled_days = next_ivl;\n const new_due = date_scheduler(processCard.last_review!, next_ivl, true);\n if (options.dateHandler && typeof options.dateHandler === \"function\") {\n processCard.due = options.dateHandler(new_due);\n } else {\n processCard.due = new_due;\n }\n processedCard.push(processCard);\n }\n return processedCard;\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","Rating","e","isDay","date_scheduler","pre","unit","date_diff","formatDate","last_review","timeUnit","show_diff_message","now","t","fixDate","diff","r","dateInput","date","year","month","day","hours","minutes","seconds","padZero","num","TIMEUNIT","TIMEUNITFORMAT","due","i","value","timestamp","fixState","firstLetter","restOfString","ret","fixRating","Grades","FUZZ_RANGES","get_fuzz_range","interval","elapsed_days","maximum_interval","delta","range","min_ivl","max_ivl","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","DECAY","FACTOR","FSRSAlgorithm","params","request_retention","_this","target","prop","_params","key","paramKey","s","last_d","last_s","retrievability","g","ivl","enable_fuzz","fuzz_factor","pseudorandom","newInterval","d","next_d","difficulty","init","current","hard_penalty","easy_bound","stability","FSRS","param","_card","_date","_log","processedCard","recordLog","log","processedLog","last_due","last_lapses","prevCard","reset_count","scheduled_days","forget_log","recordLogItem","cards","options","next_ivl","processCard","new_due","fsrs"],"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\nexport type RescheduleOptions = {\n enable_fuzz?: boolean;\n dateHandler?: (date: Date) => DateInput;\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: Readonly<Grade[]> = [\n Rating.Again,\n Rating.Hard,\n Rating.Good,\n Rating.Easy,\n] as const;\n\nconst FUZZ_RANGES = [\n {\n start: 2.5,\n end: 7.0,\n factor: 0.15,\n },\n {\n start: 7.0,\n end: 20.0,\n factor: 0.1,\n },\n {\n start: 20.0,\n end: Infinity,\n factor: 0.05,\n },\n] as const;\n\nexport function get_fuzz_range(\n interval: number,\n elapsed_days: number,\n maximum_interval: number,\n) {\n let delta = 1.0;\n for (const range of FUZZ_RANGES) {\n delta +=\n range.factor * Math.max(Math.min(interval, range.end) - range.start, 0.0);\n }\n interval = Math.min(interval, maximum_interval);\n let min_ivl = Math.max(2, Math.round(interval - delta));\n const max_ivl = Math.min(Math.round(interval + delta), maximum_interval);\n if (interval > elapsed_days) {\n min_ivl = Math.max(min_ivl, elapsed_days + 1);\n }\n min_ivl = Math.min(min_ivl, max_ivl);\n return { min_ivl, max_ivl };\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.5701, 1.4436, 4.1386, 10.9355, 5.1443, 1.2006, 0.8627, 0.0362, 1.629,\n 0.1342, 1.0166, 2.1174, 0.0839, 0.3204, 1.4676, 0.219, 2.8237,\n];\nexport const default_enable_fuzz = false;\n\nexport const FSRSVersion: string = \"3.5.6\";\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\";\nimport { get_fuzz_range } from \"./help\";\n\n/**\n * @default DECAY = -0.5\n */\nexport const DECAY: number = -0.5;\n/**\n * FACTOR = Math.pow(0.9, 1 / DECAY) - 1= 19 / 81\n *\n * $$\\text{FACTOR} = \\frac{19}{81}$$\n * @default FACTOR = 19 / 81\n */\nexport const FACTOR: number = 19 / 81;\n\n/**\n * @see https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45\n */\nexport class FSRSAlgorithm {\n protected param!: FSRSParameters;\n protected intervalModifier!: number;\n protected seed?: string;\n\n constructor(params: Partial<FSRSParameters>) {\n this.param = new Proxy(\n generatorParameters(params),\n this.params_handler_proxy(),\n );\n this.intervalModifier = this.calculate_interval_modifier(\n this.param.request_retention,\n );\n }\n\n get interval_modifier(): number {\n return this.intervalModifier;\n }\n\n /**\n * @see https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45\n *\n * The formula used is: $$I(r,s) = (r^{\\frac{1}{DECAY}} - 1) / FACTOR \\times s$$\n * @param request_retention 0<request_retention<=1,Requested retention rate\n * @throws {Error} Requested retention rate should be in the range (0,1]\n */\n calculate_interval_modifier(request_retention: number): number {\n if (request_retention <= 0 || request_retention > 1) {\n throw new Error(\"Requested retention rate should be in the range (0,1]\");\n }\n return +((Math.pow(request_retention, 1 / DECAY) - 1) / FACTOR).toFixed(8);\n }\n\n /**\n * Get the parameters of the algorithm.\n */\n get parameters(): FSRSParameters {\n return this.param;\n }\n\n /**\n * Set the parameters of the algorithm.\n * @param params Partial<FSRSParameters>\n */\n set parameters(params: Partial<FSRSParameters>) {\n this.update_parameters(params);\n }\n\n private params_handler_proxy(): ProxyHandler<FSRSParameters> {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const _this: FSRSAlgorithm = this;\n return {\n set: function (target, prop, value) {\n if (prop === \"request_retention\" && Number.isFinite(value)) {\n _this.intervalModifier = _this.calculate_interval_modifier(\n Number(value),\n );\n }\n // @ts-ignore\n target[prop] = value;\n return true;\n },\n };\n }\n\n private update_parameters(params: Partial<FSRSParameters>): void {\n const _params = generatorParameters(params);\n for (const key in _params) {\n if (key in this.param) {\n const paramKey = key as keyof FSRSParameters;\n this.param[paramKey] = _params[paramKey] as never;\n }\n }\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 * $$S_0 = \\max \\lbrace S_0,0.1\\rbrace $$\n\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 * $$D_0 = \\min \\lbrace \\max \\lbrace D_0(G),1 \\rbrace,10 \\rbrace$$\n * where the $$D_0(3)=w_4$$ when the first rating is good.\n *\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 ).toFixed(8);\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 * @param {number} elapsed_days t days since the last review\n * @param {number} enable_fuzz - This adds a small random delay to the new interval time to prevent cards from sticking together and always being reviewed on the same day.\n * @return {number} - The fuzzed interval.\n **/\n apply_fuzz(ivl: number, elapsed_days: number, enable_fuzz?: boolean): int {\n if (!enable_fuzz || ivl < 2.5) return Math.round(ivl) as int;\n const generator = pseudorandom(this.seed);\n const fuzz_factor = generator();\n const { min_ivl, max_ivl } = get_fuzz_range(\n ivl,\n elapsed_days,\n this.param.maximum_interval,\n );\n return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl) as int;\n }\n\n /**\n * @see The formula used is : {@link FSRSAlgorithm.calculate_interval_modifier}\n * @param {number} s - Stability (interval when R=90%)\n * @param {number} elapsed_days t days since the last review\n * @param {number} enable_fuzz - This adds a small random delay to the new interval time to prevent cards from sticking together and always being reviewed on the same day.\n */\n next_interval(\n s: number,\n elapsed_days: number,\n enable_fuzz: boolean = this.param.enable_fuzz,\n ): int {\n const newInterval = Math.min(\n Math.max(1, Math.round(s * this.intervalModifier)),\n this.param.maximum_interval,\n ) as int;\n return this.apply_fuzz(newInterval, elapsed_days, enable_fuzz);\n }\n\n /**\n * The formula used is :\n * $$\\text{next}_d = D - w_6 \\cdot (R - 2)$$\n * $$D^\\prime(D,R) = w_5 \\cdot D_0(2) +(1 - w_5) \\cdot \\text{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} $$\\text{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 \\lbrace \\max \\lbrace D_0,1 \\rbrace,10\\rbrace$$\n * @param {number} difficulty $$D \\in [1,10]$$\n */\n constrain_difficulty(difficulty: number): number {\n return Math.min(Math.max(+difficulty.toFixed(8), 1), 10);\n }\n\n /**\n * The formula used is :\n * $$w_7 \\cdot \\text{init} +(1 - w_7) \\cdot \\text{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).toFixed(\n 8,\n );\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}(\\text{if} G=2) \\cdot w_{16}(\\text{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 ).toFixed(8);\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_f(D,S,R) = w_{11}\\cdot D^{-w_{12}}\\cdot ((S+1)^{w_{13}}-1) \\cdot e^{w_{14}\\cdot(1-R)}$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @return {number} S^\\prime_f new stability after forgetting\n */\n next_forget_stability(d: number, s: number, r: number): number {\n return +(\n this.param.w[11] *\n Math.pow(d, -this.param.w[12]) *\n (Math.pow(s + 1, this.param.w[13]) - 1) *\n Math.exp((1 - r) * this.param.w[14])\n ).toFixed(8);\n }\n\n /**\n * The formula used is :\n * $$R(t,S) = (1 + \\text{FACTOR} \\times \\frac{t}{9 \\cdot S})^{\\text{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 + (FACTOR * elapsed_days) / stability, DECAY).toFixed(8);\n }\n}\n","import { SchedulingCard } from \"./scheduler\";\nimport { date_scheduler, fixDate, fixRating, fixState } from \"./help\";\nimport {\n Card,\n CardInput,\n DateInput,\n FSRSParameters,\n Rating,\n RecordLog,\n RecordLogItem,\n RescheduleOptions,\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 const interval = processedCard.elapsed_days;\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, interval);\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, interval);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability, interval),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n case State.Review: {\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, interval);\n good_interval = this.next_interval(s.good.stability, interval);\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, interval),\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 * @param cards scheduled card collection\n * @param options Reschedule options,fuzz is enabled by default.If the type of due is not Date, please implement dataHandler.\n * @example\n * ```typescript\n * type CardType = Card & {\n * cid: number;\n * };\n * const reviewCard: CardType = {\n * cid: 1,\n * due: new Date(\"2024-03-17 04:43:02\"),\n * stability: 48.26139059062234,\n * difficulty: 5.67,\n * elapsed_days: 18,\n * scheduled_days: 51,\n * reps: 8,\n * lapses: 1,\n * state: State.Review,\n * last_review: new Date(\"2024-01-26 04:43:02\"),\n * };\n * const f = fsrs();\n * const reschedule_cards = f.reschedule([reviewCard]);\n * ```\n *\n */\n reschedule<T extends CardInput | Card>(\n cards: Array<T>,\n options: RescheduleOptions = {},\n ): Array<T> {\n if (!Array.isArray(cards)) {\n throw new Error(\"cards must be an array\");\n }\n const processedCard: T[] = [];\n for (const card of cards) {\n if (fixState(card.state) !== State.Review || !card.last_review) continue;\n const scheduled_days = Math.floor(card.scheduled_days) as int;\n const next_ivl = this.next_interval(\n +card.stability.toFixed(2),\n Math.round(card.elapsed_days),\n options.enable_fuzz ?? true,\n );\n if (next_ivl === scheduled_days || next_ivl === 0) continue;\n\n const processCard: T = { ...card };\n processCard.scheduled_days = next_ivl;\n const new_due = date_scheduler(processCard.last_review!, next_ivl, true);\n if (options.dateHandler && typeof options.dateHandler === \"function\") {\n processCard.due = options.dateHandler(new_due);\n } else {\n processCard.due = new_due;\n }\n processedCard.push(processCard);\n }\n return processedCard;\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","Rating","e","isDay","date_scheduler","pre","unit","date_diff","formatDate","last_review","timeUnit","show_diff_message","now","t","fixDate","diff","r","dateInput","date","year","month","day","hours","minutes","seconds","padZero","num","TIMEUNIT","TIMEUNITFORMAT","due","i","value","timestamp","fixState","firstLetter","restOfString","ret","fixRating","Grades","FUZZ_RANGES","get_fuzz_range","interval","elapsed_days","maximum_interval","delta","range","min_ivl","max_ivl","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","DECAY","FACTOR","FSRSAlgorithm","params","request_retention","_this","target","prop","_params","key","paramKey","s","last_d","last_s","retrievability","g","ivl","enable_fuzz","fuzz_factor","pseudorandom","newInterval","d","next_d","difficulty","init","current","hard_penalty","easy_bound","stability","FSRS","param","_card","_date","_log","processedCard","recordLog","log","processedLog","last_due","last_lapses","prevCard","reset_count","scheduled_days","forget_log","recordLogItem","cards","options","next_ivl","processCard","new_due","fsrs"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -155,8 +155,20 @@ declare function get_fuzz_range(interval: number, elapsed_days: number, maximum_
|
|
|
155
155
|
max_ivl: number;
|
|
156
156
|
};
|
|
157
157
|
|
|
158
|
+
/**
|
|
159
|
+
* @default DECAY = -0.5
|
|
160
|
+
*/
|
|
158
161
|
declare const DECAY: number;
|
|
162
|
+
/**
|
|
163
|
+
* FACTOR = Math.pow(0.9, 1 / DECAY) - 1= 19 / 81
|
|
164
|
+
*
|
|
165
|
+
* $$\text{FACTOR} = \frac{19}{81}$$
|
|
166
|
+
* @default FACTOR = 19 / 81
|
|
167
|
+
*/
|
|
159
168
|
declare const FACTOR: number;
|
|
169
|
+
/**
|
|
170
|
+
* @see https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45
|
|
171
|
+
*/
|
|
160
172
|
declare class FSRSAlgorithm {
|
|
161
173
|
protected param: FSRSParameters;
|
|
162
174
|
protected intervalModifier: number;
|
|
@@ -164,12 +176,21 @@ declare class FSRSAlgorithm {
|
|
|
164
176
|
constructor(params: Partial<FSRSParameters>);
|
|
165
177
|
get interval_modifier(): number;
|
|
166
178
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
179
|
+
* @see https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45
|
|
180
|
+
*
|
|
181
|
+
* The formula used is: $$I(r,s) = (r^{\frac{1}{DECAY}} - 1) / FACTOR \times s$$
|
|
169
182
|
* @param request_retention 0<request_retention<=1,Requested retention rate
|
|
183
|
+
* @throws {Error} Requested retention rate should be in the range (0,1]
|
|
170
184
|
*/
|
|
171
185
|
calculate_interval_modifier(request_retention: number): number;
|
|
186
|
+
/**
|
|
187
|
+
* Get the parameters of the algorithm.
|
|
188
|
+
*/
|
|
172
189
|
get parameters(): FSRSParameters;
|
|
190
|
+
/**
|
|
191
|
+
* Set the parameters of the algorithm.
|
|
192
|
+
* @param params Partial<FSRSParameters>
|
|
193
|
+
*/
|
|
173
194
|
set parameters(params: Partial<FSRSParameters>);
|
|
174
195
|
private params_handler_proxy;
|
|
175
196
|
private update_parameters;
|
|
@@ -185,19 +206,21 @@ declare class FSRSAlgorithm {
|
|
|
185
206
|
next_ds(s: SchedulingCard, last_d: number, last_s: number, retrievability: number): void;
|
|
186
207
|
/**
|
|
187
208
|
* The formula used is :
|
|
188
|
-
* S_0(G) = w_{G-1}
|
|
189
|
-
* \max \
|
|
209
|
+
* $$ S_0(G) = w_{G-1}$$
|
|
210
|
+
* $$S_0 = \max \lbrace S_0,0.1\rbrace $$
|
|
211
|
+
|
|
190
212
|
* @param g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
191
213
|
* @return Stability (interval when R=90%)
|
|
192
214
|
*/
|
|
193
215
|
init_stability(g: Grade): number;
|
|
194
216
|
/**
|
|
195
217
|
* The formula used is :
|
|
196
|
-
* $$D_0(G) = w_4 - (G-3) \cdot w_5$$
|
|
197
|
-
*
|
|
198
|
-
* where the D_0(3)=w_4 when the first rating is good.
|
|
218
|
+
* $$D_0(G) = w_4 - (G-3) \cdot w_5 $$
|
|
219
|
+
* $$D_0 = \min \lbrace \max \lbrace D_0(G),1 \rbrace,10 \rbrace$$
|
|
220
|
+
* where the $$D_0(3)=w_4$$ when the first rating is good.
|
|
221
|
+
*
|
|
199
222
|
* @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
200
|
-
* @return {number} Difficulty D \in [1,10]
|
|
223
|
+
* @return {number} Difficulty $$D \in [1,10]$$
|
|
201
224
|
*/
|
|
202
225
|
init_difficulty(g: Grade): number;
|
|
203
226
|
/**
|
|
@@ -209,9 +232,7 @@ declare class FSRSAlgorithm {
|
|
|
209
232
|
**/
|
|
210
233
|
apply_fuzz(ivl: number, elapsed_days: number, enable_fuzz?: boolean): int;
|
|
211
234
|
/**
|
|
212
|
-
*
|
|
213
|
-
* constructor(param: Partial<FSRSParameters>)
|
|
214
|
-
* this.intervalModifier = 9 * (1 / this.param.request_retention - 1);
|
|
235
|
+
* @see The formula used is : {@link FSRSAlgorithm.calculate_interval_modifier}
|
|
215
236
|
* @param {number} s - Stability (interval when R=90%)
|
|
216
237
|
* @param {number} elapsed_days t days since the last review
|
|
217
238
|
* @param {number} enable_fuzz - This adds a small random delay to the new interval time to prevent cards from sticking together and always being reviewed on the same day.
|
|
@@ -219,22 +240,22 @@ declare class FSRSAlgorithm {
|
|
|
219
240
|
next_interval(s: number, elapsed_days: number, enable_fuzz?: boolean): int;
|
|
220
241
|
/**
|
|
221
242
|
* The formula used is :
|
|
222
|
-
*
|
|
223
|
-
* $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot
|
|
224
|
-
* @param {number} d Difficulty D \in [1,10]
|
|
243
|
+
* $$\text{next}_d = D - w_6 \cdot (R - 2)$$
|
|
244
|
+
* $$D^\prime(D,R) = w_5 \cdot D_0(2) +(1 - w_5) \cdot \text{next}_d$$
|
|
245
|
+
* @param {number} d Difficulty $$D \in [1,10]$$
|
|
225
246
|
* @param {Grade} g Grade (rating at Anki) [1.again,2.hard,3.good,4.easy]
|
|
226
|
-
* @return {number}
|
|
247
|
+
* @return {number} $$\text{next}_D$$
|
|
227
248
|
*/
|
|
228
249
|
next_difficulty(d: number, g: Grade): number;
|
|
229
250
|
/**
|
|
230
251
|
* The formula used is :
|
|
231
|
-
* $$\min \
|
|
232
|
-
* @param {number} difficulty D \in [1,10]
|
|
252
|
+
* $$\min \lbrace \max \lbrace D_0,1 \rbrace,10\rbrace$$
|
|
253
|
+
* @param {number} difficulty $$D \in [1,10]$$
|
|
233
254
|
*/
|
|
234
255
|
constrain_difficulty(difficulty: number): number;
|
|
235
256
|
/**
|
|
236
257
|
* The formula used is :
|
|
237
|
-
* $$w_7 \cdot init +(1 - w_7) \cdot current$$
|
|
258
|
+
* $$w_7 \cdot \text{init} +(1 - w_7) \cdot \text{current}$$
|
|
238
259
|
* @param {number} init $$w_2 : D_0(3) = w_2 + (R-2) \cdot w_3= w_2$$
|
|
239
260
|
* @param {number} current $$D - w_6 \cdot (R - 2)$$
|
|
240
261
|
* @return {number} difficulty
|
|
@@ -242,7 +263,7 @@ declare class FSRSAlgorithm {
|
|
|
242
263
|
mean_reversion(init: number, current: number): number;
|
|
243
264
|
/**
|
|
244
265
|
* The formula used is :
|
|
245
|
-
* $$S^\prime_r(D,S,R,G) = S\cdot(e^{w_8}\cdot (11-D)\cdot S^{-w_9}\cdot(e^{
|
|
266
|
+
* $$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}(\text{if} G=2) \cdot w_{16}(\text{if} G=4)+1)$$
|
|
246
267
|
* @param {number} d Difficulty D \in [1,10]
|
|
247
268
|
* @param {number} s Stability (interval when R=90%)
|
|
248
269
|
* @param {number} r Retrievability (probability of recall)
|
|
@@ -252,7 +273,7 @@ declare class FSRSAlgorithm {
|
|
|
252
273
|
next_recall_stability(d: number, s: number, r: number, g: Grade): number;
|
|
253
274
|
/**
|
|
254
275
|
* The formula used is :
|
|
255
|
-
* $$S^\prime_f(D,S,R) =
|
|
276
|
+
* $$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)}$$
|
|
256
277
|
* @param {number} d Difficulty D \in [1,10]
|
|
257
278
|
* @param {number} s Stability (interval when R=90%)
|
|
258
279
|
* @param {number} r Retrievability (probability of recall)
|
|
@@ -261,7 +282,7 @@ declare class FSRSAlgorithm {
|
|
|
261
282
|
next_forget_stability(d: number, s: number, r: number): number;
|
|
262
283
|
/**
|
|
263
284
|
* The formula used is :
|
|
264
|
-
* $$R(t,S) = (1 + FACTOR \times \frac{t}{9 \cdot S})^{DECAY}
|
|
285
|
+
* $$R(t,S) = (1 + \text{FACTOR} \times \frac{t}{9 \cdot S})^{\text{DECAY}}$$
|
|
265
286
|
* @param {number} elapsed_days t days since the last review
|
|
266
287
|
* @param {number} stability Stability (interval when R=90%)
|
|
267
288
|
* @return {number} r Retrievability (probability of recall)
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import G 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 M(this,e,t)},Date.prototype.format=function(){return R(this)},Date.prototype.dueFormat=function(e,t,a){return E(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 i=0;switch(a){case"days":i=Math.floor(s/(24*60*60*1e3));break;case"minutes":i=Math.floor(s/(60*1e3));break}return i}function R(e){const t=u(e),a=t.getFullYear(),s=t.getMonth()+1,i=t.getDate(),r=t.getHours(),l=t.getMinutes(),o=t.getSeconds();return`${a}-${f(s)}-${f(i)} ${f(r)}:${f(l)}:${f(o)}`}function f(e){return e<10?`0${e}`:`${e}`}const p=[60,60,24,31,12],g=["second","min","hour","day","month","year"];function E(e,t,a,s=g){e=u(e),t=u(t),s.length!==g.length&&(s=g);let i=e.getTime()-t.getTime(),r;for(i/=1e3,r=0;r<p.length&&!(i<p[r]);r++)i/=p[r];return`${Math.floor(i)}${a?s[r]:""}`}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 _(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 $(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 k=[n.Again,n.Hard,n.Good,n.Easy],T=[{start:2.5,end:7,factor:.15},{start:7,end:20,factor:.1},{start:20,end:1/0,factor:.05}];function D(e,t,a){let s=1;for(const l of T)s+=l.factor*Math.max(Math.min(e,l.end)-l.start,0);e=Math.min(e,a);let i=Math.max(2,Math.round(e-s));const r=Math.min(Math.round(e+s),a);return e>t&&(i=Math.max(i,t+1)),i=Math.min(i,r),{min_ivl:i,max_ivl:r}}class z{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,i){return this.again.scheduled_days=0,this.hard.scheduled_days=a,this.good.scheduled_days=s,this.easy.scheduled_days=i,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,i,!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 A=.9,F=36500,C=[.5701,1.4436,4.1386,10.9355,5.1443,1.2006,.8627,.0362,1.629,.1342,1.0166,2.1174,.0839,.3204,1.4676,.219,2.8237],H=!1,I="3.5.4",w=e=>({request_retention:(e==null?void 0:e.request_retention)||A,maximum_interval:(e==null?void 0:e.maximum_interval)||F,w:(e==null?void 0:e.w)||C,enable_fuzz:(e==null?void 0:e.enable_fuzz)||H});function q(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}const m=-.5,v=19/81;class N{param;intervalModifier;seed;constructor(t){this.param=new Proxy(w(t),this.params_handler_proxy()),this.intervalModifier=this.calculate_interval_modifier(this.param.request_retention)}get interval_modifier(){return this.intervalModifier}calculate_interval_modifier(t){if(t<=0||t>1)throw new Error("Requested retention rate should be in the range (0,1]");return+((Math.pow(t,1/m)-1)/v).toFixed(8)}get parameters(){return this.param}set parameters(t){this.update_parameters(t)}params_handler_proxy(){const t=this;return{set:function(a,s,i){return s==="request_retention"&&Number.isFinite(i)&&(t.intervalModifier=t.calculate_interval_modifier(Number(i))),a[s]=i,!0}}}update_parameters(t){const a=w(t);for(const s in a)if(s in this.param){const i=s;this.param[i]=a[i]}}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,i){t.again.difficulty=this.next_difficulty(a,n.Again),t.again.stability=this.next_forget_stability(a,s,i),t.hard.difficulty=this.next_difficulty(a,n.Hard),t.hard.stability=this.next_recall_stability(a,s,i,n.Hard),t.good.difficulty=this.next_difficulty(a,n.Good),t.good.stability=this.next_recall_stability(a,s,i,n.Good),t.easy.difficulty=this.next_difficulty(a,n.Easy),t.easy.stability=this.next_recall_stability(a,s,i,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).toFixed(8)}apply_fuzz(t,a,s){if(!s||t<2.5)return Math.round(t);const i=G(this.seed)(),{min_ivl:r,max_ivl:l}=D(t,a,this.param.maximum_interval);return Math.floor(i*(l-r+1)+r)}next_interval(t,a,s=this.param.enable_fuzz){const i=Math.min(Math.max(1,Math.round(t*this.intervalModifier)),this.param.maximum_interval);return this.apply_fuzz(i,a,s)}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(+t.toFixed(8),1),10)}mean_reversion(t,a){return+(this.param.w[7]*t+(1-this.param.w[7])*a).toFixed(8)}next_recall_stability(t,a,s,i){const r=n.Hard===i?this.param.w[15]:1,l=n.Easy===i?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)*r*l)).toFixed(8)}next_forget_stability(t,a,s){return+(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(8)}forgetting_curve(t,a){return+Math.pow(1+v*t/a,m).toFixed(8)}}class P extends N{constructor(t){super(t)}preProcessCard(t){return{...t,state:_(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:$(t.rating),state:_(t.state),review:u(t.review)}}repeat(t,a,s){const i=this.preProcessCard(t);a=this.preProcessDate(a);const r=new z(i,a).update_state(i.state);this.seed=String(a.getTime())+String(i.reps);let l,o,h;const c=i.elapsed_days;switch(i.state){case d.New:this.init_ds(r),r.again.due=a.scheduler(1),r.hard.due=a.scheduler(5),r.good.due=a.scheduler(10),l=this.next_interval(r.easy.stability,c),r.easy.scheduled_days=l,r.easy.due=a.scheduler(l,!0);break;case d.Learning:case d.Relearning:h=0,o=this.next_interval(r.good.stability,c),l=Math.max(this.next_interval(r.easy.stability,c),o+1),r.schedule(a,h,o,l);break;case d.Review:{const L=i.difficulty,x=i.stability,S=this.forgetting_curve(c,x);this.next_ds(r,L,x,S),h=this.next_interval(r.hard.stability,c),o=this.next_interval(r.good.stability,c),h=Math.min(h,o),o=Math.max(o,h+1),l=Math.max(this.next_interval(r.easy.stability,c),o+1),r.schedule(a,h,o,l);break}}const b=r.record_log(i,a);return s&&typeof s=="function"?s(b):b}get_retrievability=(t,a)=>{const s=this.preProcessCard(t);if(a=this.preProcessDate(a),s.state!==d.Review)return;const i=Math.max(a.diff(s.last_review,"days"),0);return(this.forgetting_curve(i,s.stability)*100).toFixed(2)+"%"};rollback(t,a,s){const i=this.preProcessCard(t),r=this.preProcessLog(a);if(r.rating===n.Manual)throw new Error("Cannot rollback a manual rating");let l,o,h;switch(r.state){case d.New:l=r.due,o=void 0,h=0;break;case d.Learning:case d.Relearning:case d.Review:l=r.review,o=r.due,h=i.lapses-(r.rating===n.Again&&r.state===d.Review?1:0);break}const c={...i,due:l,stability:r.stability,difficulty:r.difficulty,elapsed_days:r.last_elapsed_days,scheduled_days:r.scheduled_days,reps:Math.max(0,i.reps-1),lapses:Math.max(0,h),state:r.state,last_review:o};return s&&typeof s=="function"?s(c):c}forget(t,a,s=!1,i){const r=this.preProcessCard(t);a=this.preProcessDate(a);const l=r.state===d.New?0:a.diff(r.last_review,"days"),o={rating:n.Manual,state:r.state,due:r.due,stability:r.stability,difficulty:r.difficulty,elapsed_days:0,last_elapsed_days:r.elapsed_days,scheduled_days:l,review:a},h={card:{...r,due:a,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:s?0:r.reps,lapses:s?0:r.lapses,state:d.New,last_review:r.last_review},log:o};return i&&typeof i=="function"?i(h):h}reschedule(t,a={}){if(!Array.isArray(t))throw new Error("cards must be an array");const s=[];for(const i of t){if(_(i.state)!==d.Review||!i.last_review)continue;const r=Math.floor(i.scheduled_days),l=this.next_interval(+i.stability.toFixed(2),Math.round(i.elapsed_days),a.enable_fuzz??!0);if(l===r||l===0)continue;const o={...i};o.scheduled_days=l;const h=y(o.last_review,l,!0);a.dateHandler&&typeof a.dateHandler=="function"?o.due=a.dateHandler(h):o.due=h,s.push(o)}return s}}const U=e=>new P(e||{});export{m as DECAY,v as FACTOR,P as FSRS,N as FSRSAlgorithm,I as FSRSVersion,k as Grades,n as Rating,z as SchedulingCard,d as State,q as createEmptyCard,M as date_diff,y as date_scheduler,H as default_enable_fuzz,F as default_maximum_interval,A as default_request_retention,C as default_w,u as fixDate,$ as fixRating,_ as fixState,R as formatDate,U as fsrs,w as generatorParameters,D as get_fuzz_range,E as show_diff_message};
|
|
1
|
+
import G 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 M(this,e,t)},Date.prototype.format=function(){return R(this)},Date.prototype.dueFormat=function(e,t,a){return E(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 i=0;switch(a){case"days":i=Math.floor(s/(24*60*60*1e3));break;case"minutes":i=Math.floor(s/(60*1e3));break}return i}function R(e){const t=u(e),a=t.getFullYear(),s=t.getMonth()+1,i=t.getDate(),r=t.getHours(),l=t.getMinutes(),o=t.getSeconds();return`${a}-${f(s)}-${f(i)} ${f(r)}:${f(l)}:${f(o)}`}function f(e){return e<10?`0${e}`:`${e}`}const p=[60,60,24,31,12],g=["second","min","hour","day","month","year"];function E(e,t,a,s=g){e=u(e),t=u(t),s.length!==g.length&&(s=g);let i=e.getTime()-t.getTime(),r;for(i/=1e3,r=0;r<p.length&&!(i<p[r]);r++)i/=p[r];return`${Math.floor(i)}${a?s[r]:""}`}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 _(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 $(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 k=[n.Again,n.Hard,n.Good,n.Easy],T=[{start:2.5,end:7,factor:.15},{start:7,end:20,factor:.1},{start:20,end:1/0,factor:.05}];function D(e,t,a){let s=1;for(const l of T)s+=l.factor*Math.max(Math.min(e,l.end)-l.start,0);e=Math.min(e,a);let i=Math.max(2,Math.round(e-s));const r=Math.min(Math.round(e+s),a);return e>t&&(i=Math.max(i,t+1)),i=Math.min(i,r),{min_ivl:i,max_ivl:r}}class z{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,i){return this.again.scheduled_days=0,this.hard.scheduled_days=a,this.good.scheduled_days=s,this.easy.scheduled_days=i,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,i,!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 A=.9,F=36500,C=[.5701,1.4436,4.1386,10.9355,5.1443,1.2006,.8627,.0362,1.629,.1342,1.0166,2.1174,.0839,.3204,1.4676,.219,2.8237],H=!1,I="3.5.6",w=e=>({request_retention:(e==null?void 0:e.request_retention)||A,maximum_interval:(e==null?void 0:e.maximum_interval)||F,w:(e==null?void 0:e.w)||C,enable_fuzz:(e==null?void 0:e.enable_fuzz)||H});function q(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}const m=-.5,v=19/81;class N{param;intervalModifier;seed;constructor(t){this.param=new Proxy(w(t),this.params_handler_proxy()),this.intervalModifier=this.calculate_interval_modifier(this.param.request_retention)}get interval_modifier(){return this.intervalModifier}calculate_interval_modifier(t){if(t<=0||t>1)throw new Error("Requested retention rate should be in the range (0,1]");return+((Math.pow(t,1/m)-1)/v).toFixed(8)}get parameters(){return this.param}set parameters(t){this.update_parameters(t)}params_handler_proxy(){const t=this;return{set:function(a,s,i){return s==="request_retention"&&Number.isFinite(i)&&(t.intervalModifier=t.calculate_interval_modifier(Number(i))),a[s]=i,!0}}}update_parameters(t){const a=w(t);for(const s in a)if(s in this.param){const i=s;this.param[i]=a[i]}}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,i){t.again.difficulty=this.next_difficulty(a,n.Again),t.again.stability=this.next_forget_stability(a,s,i),t.hard.difficulty=this.next_difficulty(a,n.Hard),t.hard.stability=this.next_recall_stability(a,s,i,n.Hard),t.good.difficulty=this.next_difficulty(a,n.Good),t.good.stability=this.next_recall_stability(a,s,i,n.Good),t.easy.difficulty=this.next_difficulty(a,n.Easy),t.easy.stability=this.next_recall_stability(a,s,i,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).toFixed(8)}apply_fuzz(t,a,s){if(!s||t<2.5)return Math.round(t);const i=G(this.seed)(),{min_ivl:r,max_ivl:l}=D(t,a,this.param.maximum_interval);return Math.floor(i*(l-r+1)+r)}next_interval(t,a,s=this.param.enable_fuzz){const i=Math.min(Math.max(1,Math.round(t*this.intervalModifier)),this.param.maximum_interval);return this.apply_fuzz(i,a,s)}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(+t.toFixed(8),1),10)}mean_reversion(t,a){return+(this.param.w[7]*t+(1-this.param.w[7])*a).toFixed(8)}next_recall_stability(t,a,s,i){const r=n.Hard===i?this.param.w[15]:1,l=n.Easy===i?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)*r*l)).toFixed(8)}next_forget_stability(t,a,s){return+(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(8)}forgetting_curve(t,a){return+Math.pow(1+v*t/a,m).toFixed(8)}}class P extends N{constructor(t){super(t)}preProcessCard(t){return{...t,state:_(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:$(t.rating),state:_(t.state),review:u(t.review)}}repeat(t,a,s){const i=this.preProcessCard(t);a=this.preProcessDate(a);const r=new z(i,a).update_state(i.state);this.seed=String(a.getTime())+String(i.reps);let l,o,h;const c=i.elapsed_days;switch(i.state){case d.New:this.init_ds(r),r.again.due=a.scheduler(1),r.hard.due=a.scheduler(5),r.good.due=a.scheduler(10),l=this.next_interval(r.easy.stability,c),r.easy.scheduled_days=l,r.easy.due=a.scheduler(l,!0);break;case d.Learning:case d.Relearning:h=0,o=this.next_interval(r.good.stability,c),l=Math.max(this.next_interval(r.easy.stability,c),o+1),r.schedule(a,h,o,l);break;case d.Review:{const L=i.difficulty,x=i.stability,S=this.forgetting_curve(c,x);this.next_ds(r,L,x,S),h=this.next_interval(r.hard.stability,c),o=this.next_interval(r.good.stability,c),h=Math.min(h,o),o=Math.max(o,h+1),l=Math.max(this.next_interval(r.easy.stability,c),o+1),r.schedule(a,h,o,l);break}}const b=r.record_log(i,a);return s&&typeof s=="function"?s(b):b}get_retrievability=(t,a)=>{const s=this.preProcessCard(t);if(a=this.preProcessDate(a),s.state!==d.Review)return;const i=Math.max(a.diff(s.last_review,"days"),0);return(this.forgetting_curve(i,s.stability)*100).toFixed(2)+"%"};rollback(t,a,s){const i=this.preProcessCard(t),r=this.preProcessLog(a);if(r.rating===n.Manual)throw new Error("Cannot rollback a manual rating");let l,o,h;switch(r.state){case d.New:l=r.due,o=void 0,h=0;break;case d.Learning:case d.Relearning:case d.Review:l=r.review,o=r.due,h=i.lapses-(r.rating===n.Again&&r.state===d.Review?1:0);break}const c={...i,due:l,stability:r.stability,difficulty:r.difficulty,elapsed_days:r.last_elapsed_days,scheduled_days:r.scheduled_days,reps:Math.max(0,i.reps-1),lapses:Math.max(0,h),state:r.state,last_review:o};return s&&typeof s=="function"?s(c):c}forget(t,a,s=!1,i){const r=this.preProcessCard(t);a=this.preProcessDate(a);const l=r.state===d.New?0:a.diff(r.last_review,"days"),o={rating:n.Manual,state:r.state,due:r.due,stability:r.stability,difficulty:r.difficulty,elapsed_days:0,last_elapsed_days:r.elapsed_days,scheduled_days:l,review:a},h={card:{...r,due:a,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:s?0:r.reps,lapses:s?0:r.lapses,state:d.New,last_review:r.last_review},log:o};return i&&typeof i=="function"?i(h):h}reschedule(t,a={}){if(!Array.isArray(t))throw new Error("cards must be an array");const s=[];for(const i of t){if(_(i.state)!==d.Review||!i.last_review)continue;const r=Math.floor(i.scheduled_days),l=this.next_interval(+i.stability.toFixed(2),Math.round(i.elapsed_days),a.enable_fuzz??!0);if(l===r||l===0)continue;const o={...i};o.scheduled_days=l;const h=y(o.last_review,l,!0);a.dateHandler&&typeof a.dateHandler=="function"?o.due=a.dateHandler(h):o.due=h,s.push(o)}return s}}const U=e=>new P(e||{});export{m as DECAY,v as FACTOR,P as FSRS,N as FSRSAlgorithm,I as FSRSVersion,k as Grades,n as Rating,z as SchedulingCard,d as State,q as createEmptyCard,M as date_diff,y as date_scheduler,H as default_enable_fuzz,F as default_maximum_interval,A as default_request_retention,C as default_w,u as fixDate,$ as fixRating,_ as fixState,R as formatDate,U as fsrs,w as generatorParameters,D as get_fuzz_range,E as show_diff_message};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -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}\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\nexport type RescheduleOptions = {\n enable_fuzz?: boolean;\n dateHandler?: (date: Date) => DateInput;\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: Readonly<Grade[]> = [\n Rating.Again,\n Rating.Hard,\n Rating.Good,\n Rating.Easy,\n] as const;\n\nconst FUZZ_RANGES = [\n {\n start: 2.5,\n end: 7.0,\n factor: 0.15,\n },\n {\n start: 7.0,\n end: 20.0,\n factor: 0.1,\n },\n {\n start: 20.0,\n end: Infinity,\n factor: 0.05,\n },\n] as const;\n\nexport function get_fuzz_range(\n interval: number,\n elapsed_days: number,\n maximum_interval: number,\n) {\n let delta = 1.0;\n for (const range of FUZZ_RANGES) {\n delta +=\n range.factor * Math.max(Math.min(interval, range.end) - range.start, 0.0);\n }\n interval = Math.min(interval, maximum_interval);\n let min_ivl = Math.max(2, Math.round(interval - delta));\n const max_ivl = Math.min(Math.round(interval + delta), maximum_interval);\n if (interval > elapsed_days) {\n min_ivl = Math.max(min_ivl, elapsed_days + 1);\n }\n min_ivl = Math.min(min_ivl, max_ivl);\n return { min_ivl, max_ivl };\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.5701, 1.4436, 4.1386, 10.9355, 5.1443, 1.2006, 0.8627, 0.0362, 1.629,\n 0.1342, 1.0166, 2.1174, 0.0839, 0.3204, 1.4676, 0.219, 2.8237,\n];\nexport const default_enable_fuzz = false;\n\nexport const FSRSVersion: string = \"3.5.4\";\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\";\nimport { get_fuzz_range } from \"./help\";\n\n// Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-v4\nexport const DECAY: number = -0.5;\n// FACTOR = Math.pow(0.9, 1 / DECAY) - 1\nexport const FACTOR: number = 19 / 81;\n\nexport class FSRSAlgorithm {\n protected param!: FSRSParameters;\n protected intervalModifier!: number;\n protected seed?: string;\n\n constructor(params: Partial<FSRSParameters>) {\n this.param = new Proxy(\n generatorParameters(params),\n this.params_handler_proxy(),\n );\n this.intervalModifier = this.calculate_interval_modifier(\n this.param.request_retention,\n );\n }\n\n get interval_modifier(): number {\n return this.intervalModifier;\n }\n\n /**\n * Ref: https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45\n * The formula used is: I(r,s) = (r^(1/DECAY) - 1) * s / FACTOR\n * @param request_retention 0<request_retention<=1,Requested retention rate\n */\n calculate_interval_modifier(request_retention: number): number {\n if (request_retention <= 0 || request_retention > 1) {\n throw new Error(\"Requested retention rate should be in the range (0,1]\");\n }\n return +((Math.pow(request_retention, 1 / DECAY) - 1) / FACTOR).toFixed(8);\n }\n\n get parameters(): FSRSParameters {\n return this.param;\n }\n\n set parameters(params: Partial<FSRSParameters>) {\n this.update_parameters(params);\n }\n\n private params_handler_proxy(): ProxyHandler<FSRSParameters> {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const _this: FSRSAlgorithm = this;\n return {\n set: function (target, prop, value) {\n if (prop === \"request_retention\" && Number.isFinite(value)) {\n _this.intervalModifier = _this.calculate_interval_modifier(\n Number(value),\n );\n }\n // @ts-ignore\n target[prop] = value;\n return true;\n },\n };\n }\n\n private update_parameters(params: Partial<FSRSParameters>): void {\n const _params = generatorParameters(params);\n for (const key in _params) {\n if (key in this.param) {\n const paramKey = key as keyof FSRSParameters;\n this.param[paramKey] = _params[paramKey] as never;\n }\n }\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 ).toFixed(8);\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 * @param {number} elapsed_days t days since the last review\n * @param {number} enable_fuzz - This adds a small random delay to the new interval time to prevent cards from sticking together and always being reviewed on the same day.\n * @return {number} - The fuzzed interval.\n **/\n apply_fuzz(ivl: number, elapsed_days: number, enable_fuzz?: boolean): int {\n if (!enable_fuzz || ivl < 2.5) return Math.round(ivl) as int;\n const generator = pseudorandom(this.seed);\n const fuzz_factor = generator();\n const { min_ivl, max_ivl } = get_fuzz_range(\n ivl,\n elapsed_days,\n this.param.maximum_interval,\n );\n return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl) as int;\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 * @param {number} elapsed_days t days since the last review\n * @param {number} enable_fuzz - This adds a small random delay to the new interval time to prevent cards from sticking together and always being reviewed on the same day.\n */\n next_interval(\n s: number,\n elapsed_days: number,\n enable_fuzz: boolean = this.param.enable_fuzz,\n ): int {\n const newInterval = Math.min(\n Math.max(1, Math.round(s * this.intervalModifier)),\n this.param.maximum_interval,\n ) as int;\n return this.apply_fuzz(newInterval, elapsed_days, enable_fuzz);\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(+difficulty.toFixed(8), 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).toFixed(\n 8,\n );\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 ).toFixed(8);\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_f(D,S,R) = w_11\\cdot D^{-w_{12}}\\cdot ((S+1)^{w_{13}}-1) \\cdot e^{w_{14}\\cdot(1-R)}.$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @return {number} S^\\prime_f new stability after forgetting\n */\n next_forget_stability(d: number, s: number, r: number): number {\n return +(\n this.param.w[11] *\n Math.pow(d, -this.param.w[12]) *\n (Math.pow(s + 1, this.param.w[13]) - 1) *\n Math.exp((1 - r) * this.param.w[14])\n ).toFixed(8);\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 + (FACTOR * elapsed_days) / stability, DECAY).toFixed(8);\n }\n}\n","import { SchedulingCard } from \"./scheduler\";\nimport { date_scheduler, fixDate, fixRating, fixState } from \"./help\";\nimport {\n Card,\n CardInput,\n DateInput,\n FSRSParameters,\n Rating,\n RecordLog,\n RecordLogItem,\n RescheduleOptions,\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 const interval = processedCard.elapsed_days;\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, interval);\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, interval);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability, interval),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n case State.Review: {\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, interval);\n good_interval = this.next_interval(s.good.stability, interval);\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, interval),\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 * @param cards scheduled card collection\n * @param options Reschedule options,fuzz is enabled by default.If the type of due is not Date, please implement dataHandler.\n * @example\n * ```typescript\n * type CardType = Card & {\n * cid: number;\n * };\n * const reviewCard: CardType = {\n * cid: 1,\n * due: new Date(\"2024-03-17 04:43:02\"),\n * stability: 48.26139059062234,\n * difficulty: 5.67,\n * elapsed_days: 18,\n * scheduled_days: 51,\n * reps: 8,\n * lapses: 1,\n * state: State.Review,\n * last_review: new Date(\"2024-01-26 04:43:02\"),\n * };\n * const f = fsrs();\n * const reschedule_cards = f.reschedule([reviewCard]);\n * ```\n *\n */\n reschedule<T extends CardInput | Card>(\n cards: Array<T>,\n options: RescheduleOptions = {},\n ): Array<T> {\n if (!Array.isArray(cards)) {\n throw new Error(\"cards must be an array\");\n }\n const processedCard: T[] = [];\n for (const card of cards) {\n if (fixState(card.state) !== State.Review || !card.last_review) continue;\n const scheduled_days = Math.floor(card.scheduled_days) as int;\n const next_ivl = this.next_interval(\n +card.stability.toFixed(2),\n Math.round(card.elapsed_days),\n options.enable_fuzz ?? true,\n );\n if (next_ivl === scheduled_days || next_ivl === 0) continue;\n\n const processCard: T = { ...card };\n processCard.scheduled_days = next_ivl;\n const new_due = date_scheduler(processCard.last_review!, next_ivl, true);\n if (options.dateHandler && typeof options.dateHandler === \"function\") {\n processCard.due = options.dateHandler(new_due);\n } else {\n processCard.due = new_due;\n }\n processedCard.push(processCard);\n }\n return processedCard;\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","i","value","timestamp","fixState","firstLetter","restOfString","ret","fixRating","Grades","FUZZ_RANGES","get_fuzz_range","interval","elapsed_days","maximum_interval","delta","range","min_ivl","max_ivl","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","DECAY","FACTOR","FSRSAlgorithm","params","request_retention","_this","target","prop","_params","key","paramKey","s","last_d","last_s","retrievability","g","ivl","enable_fuzz","fuzz_factor","pseudorandom","newInterval","d","next_d","difficulty","init","current","hard_penalty","easy_bound","stability","FSRS","param","_card","_date","_log","processedCard","recordLog","log","processedLog","last_due","last_lapses","prevCard","reset_count","scheduled_days","forget_log","recordLogItem","cards","options","next_ivl","processCard","new_due","fsrs"],"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\nexport type RescheduleOptions = {\n enable_fuzz?: boolean;\n dateHandler?: (date: Date) => DateInput;\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: Readonly<Grade[]> = [\n Rating.Again,\n Rating.Hard,\n Rating.Good,\n Rating.Easy,\n] as const;\n\nconst FUZZ_RANGES = [\n {\n start: 2.5,\n end: 7.0,\n factor: 0.15,\n },\n {\n start: 7.0,\n end: 20.0,\n factor: 0.1,\n },\n {\n start: 20.0,\n end: Infinity,\n factor: 0.05,\n },\n] as const;\n\nexport function get_fuzz_range(\n interval: number,\n elapsed_days: number,\n maximum_interval: number,\n) {\n let delta = 1.0;\n for (const range of FUZZ_RANGES) {\n delta +=\n range.factor * Math.max(Math.min(interval, range.end) - range.start, 0.0);\n }\n interval = Math.min(interval, maximum_interval);\n let min_ivl = Math.max(2, Math.round(interval - delta));\n const max_ivl = Math.min(Math.round(interval + delta), maximum_interval);\n if (interval > elapsed_days) {\n min_ivl = Math.max(min_ivl, elapsed_days + 1);\n }\n min_ivl = Math.min(min_ivl, max_ivl);\n return { min_ivl, max_ivl };\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.5701, 1.4436, 4.1386, 10.9355, 5.1443, 1.2006, 0.8627, 0.0362, 1.629,\n 0.1342, 1.0166, 2.1174, 0.0839, 0.3204, 1.4676, 0.219, 2.8237,\n];\nexport const default_enable_fuzz = false;\n\nexport const FSRSVersion: string = \"3.5.6\";\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\";\nimport { get_fuzz_range } from \"./help\";\n\n/**\n * @default DECAY = -0.5\n */\nexport const DECAY: number = -0.5;\n/**\n * FACTOR = Math.pow(0.9, 1 / DECAY) - 1= 19 / 81\n *\n * $$\\text{FACTOR} = \\frac{19}{81}$$\n * @default FACTOR = 19 / 81\n */\nexport const FACTOR: number = 19 / 81;\n\n/**\n * @see https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45\n */\nexport class FSRSAlgorithm {\n protected param!: FSRSParameters;\n protected intervalModifier!: number;\n protected seed?: string;\n\n constructor(params: Partial<FSRSParameters>) {\n this.param = new Proxy(\n generatorParameters(params),\n this.params_handler_proxy(),\n );\n this.intervalModifier = this.calculate_interval_modifier(\n this.param.request_retention,\n );\n }\n\n get interval_modifier(): number {\n return this.intervalModifier;\n }\n\n /**\n * @see https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45\n *\n * The formula used is: $$I(r,s) = (r^{\\frac{1}{DECAY}} - 1) / FACTOR \\times s$$\n * @param request_retention 0<request_retention<=1,Requested retention rate\n * @throws {Error} Requested retention rate should be in the range (0,1]\n */\n calculate_interval_modifier(request_retention: number): number {\n if (request_retention <= 0 || request_retention > 1) {\n throw new Error(\"Requested retention rate should be in the range (0,1]\");\n }\n return +((Math.pow(request_retention, 1 / DECAY) - 1) / FACTOR).toFixed(8);\n }\n\n /**\n * Get the parameters of the algorithm.\n */\n get parameters(): FSRSParameters {\n return this.param;\n }\n\n /**\n * Set the parameters of the algorithm.\n * @param params Partial<FSRSParameters>\n */\n set parameters(params: Partial<FSRSParameters>) {\n this.update_parameters(params);\n }\n\n private params_handler_proxy(): ProxyHandler<FSRSParameters> {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const _this: FSRSAlgorithm = this;\n return {\n set: function (target, prop, value) {\n if (prop === \"request_retention\" && Number.isFinite(value)) {\n _this.intervalModifier = _this.calculate_interval_modifier(\n Number(value),\n );\n }\n // @ts-ignore\n target[prop] = value;\n return true;\n },\n };\n }\n\n private update_parameters(params: Partial<FSRSParameters>): void {\n const _params = generatorParameters(params);\n for (const key in _params) {\n if (key in this.param) {\n const paramKey = key as keyof FSRSParameters;\n this.param[paramKey] = _params[paramKey] as never;\n }\n }\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 * $$S_0 = \\max \\lbrace S_0,0.1\\rbrace $$\n\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 * $$D_0 = \\min \\lbrace \\max \\lbrace D_0(G),1 \\rbrace,10 \\rbrace$$\n * where the $$D_0(3)=w_4$$ when the first rating is good.\n *\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 ).toFixed(8);\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 * @param {number} elapsed_days t days since the last review\n * @param {number} enable_fuzz - This adds a small random delay to the new interval time to prevent cards from sticking together and always being reviewed on the same day.\n * @return {number} - The fuzzed interval.\n **/\n apply_fuzz(ivl: number, elapsed_days: number, enable_fuzz?: boolean): int {\n if (!enable_fuzz || ivl < 2.5) return Math.round(ivl) as int;\n const generator = pseudorandom(this.seed);\n const fuzz_factor = generator();\n const { min_ivl, max_ivl } = get_fuzz_range(\n ivl,\n elapsed_days,\n this.param.maximum_interval,\n );\n return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl) as int;\n }\n\n /**\n * @see The formula used is : {@link FSRSAlgorithm.calculate_interval_modifier}\n * @param {number} s - Stability (interval when R=90%)\n * @param {number} elapsed_days t days since the last review\n * @param {number} enable_fuzz - This adds a small random delay to the new interval time to prevent cards from sticking together and always being reviewed on the same day.\n */\n next_interval(\n s: number,\n elapsed_days: number,\n enable_fuzz: boolean = this.param.enable_fuzz,\n ): int {\n const newInterval = Math.min(\n Math.max(1, Math.round(s * this.intervalModifier)),\n this.param.maximum_interval,\n ) as int;\n return this.apply_fuzz(newInterval, elapsed_days, enable_fuzz);\n }\n\n /**\n * The formula used is :\n * $$\\text{next}_d = D - w_6 \\cdot (R - 2)$$\n * $$D^\\prime(D,R) = w_5 \\cdot D_0(2) +(1 - w_5) \\cdot \\text{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} $$\\text{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 \\lbrace \\max \\lbrace D_0,1 \\rbrace,10\\rbrace$$\n * @param {number} difficulty $$D \\in [1,10]$$\n */\n constrain_difficulty(difficulty: number): number {\n return Math.min(Math.max(+difficulty.toFixed(8), 1), 10);\n }\n\n /**\n * The formula used is :\n * $$w_7 \\cdot \\text{init} +(1 - w_7) \\cdot \\text{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).toFixed(\n 8,\n );\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}(\\text{if} G=2) \\cdot w_{16}(\\text{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 ).toFixed(8);\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_f(D,S,R) = w_{11}\\cdot D^{-w_{12}}\\cdot ((S+1)^{w_{13}}-1) \\cdot e^{w_{14}\\cdot(1-R)}$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @return {number} S^\\prime_f new stability after forgetting\n */\n next_forget_stability(d: number, s: number, r: number): number {\n return +(\n this.param.w[11] *\n Math.pow(d, -this.param.w[12]) *\n (Math.pow(s + 1, this.param.w[13]) - 1) *\n Math.exp((1 - r) * this.param.w[14])\n ).toFixed(8);\n }\n\n /**\n * The formula used is :\n * $$R(t,S) = (1 + \\text{FACTOR} \\times \\frac{t}{9 \\cdot S})^{\\text{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 + (FACTOR * elapsed_days) / stability, DECAY).toFixed(8);\n }\n}\n","import { SchedulingCard } from \"./scheduler\";\nimport { date_scheduler, fixDate, fixRating, fixState } from \"./help\";\nimport {\n Card,\n CardInput,\n DateInput,\n FSRSParameters,\n Rating,\n RecordLog,\n RecordLogItem,\n RescheduleOptions,\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 const interval = processedCard.elapsed_days;\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, interval);\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, interval);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability, interval),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n case State.Review: {\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, interval);\n good_interval = this.next_interval(s.good.stability, interval);\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, interval),\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 * @param cards scheduled card collection\n * @param options Reschedule options,fuzz is enabled by default.If the type of due is not Date, please implement dataHandler.\n * @example\n * ```typescript\n * type CardType = Card & {\n * cid: number;\n * };\n * const reviewCard: CardType = {\n * cid: 1,\n * due: new Date(\"2024-03-17 04:43:02\"),\n * stability: 48.26139059062234,\n * difficulty: 5.67,\n * elapsed_days: 18,\n * scheduled_days: 51,\n * reps: 8,\n * lapses: 1,\n * state: State.Review,\n * last_review: new Date(\"2024-01-26 04:43:02\"),\n * };\n * const f = fsrs();\n * const reschedule_cards = f.reschedule([reviewCard]);\n * ```\n *\n */\n reschedule<T extends CardInput | Card>(\n cards: Array<T>,\n options: RescheduleOptions = {},\n ): Array<T> {\n if (!Array.isArray(cards)) {\n throw new Error(\"cards must be an array\");\n }\n const processedCard: T[] = [];\n for (const card of cards) {\n if (fixState(card.state) !== State.Review || !card.last_review) continue;\n const scheduled_days = Math.floor(card.scheduled_days) as int;\n const next_ivl = this.next_interval(\n +card.stability.toFixed(2),\n Math.round(card.elapsed_days),\n options.enable_fuzz ?? true,\n );\n if (next_ivl === scheduled_days || next_ivl === 0) continue;\n\n const processCard: T = { ...card };\n processCard.scheduled_days = next_ivl;\n const new_due = date_scheduler(processCard.last_review!, next_ivl, true);\n if (options.dateHandler && typeof options.dateHandler === \"function\") {\n processCard.due = options.dateHandler(new_due);\n } else {\n processCard.due = new_due;\n }\n processedCard.push(processCard);\n }\n return processedCard;\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","i","value","timestamp","fixState","firstLetter","restOfString","ret","fixRating","Grades","FUZZ_RANGES","get_fuzz_range","interval","elapsed_days","maximum_interval","delta","range","min_ivl","max_ivl","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","DECAY","FACTOR","FSRSAlgorithm","params","request_retention","_this","target","prop","_params","key","paramKey","s","last_d","last_s","retrievability","g","ivl","enable_fuzz","fuzz_factor","pseudorandom","newInterval","d","next_d","difficulty","init","current","hard_penalty","easy_bound","stability","FSRS","param","_card","_date","_log","processedCard","recordLog","log","processedLog","last_due","last_lapses","prevCard","reset_count","scheduled_days","forget_log","recordLogItem","cards","options","next_ivl","processCard","new_due","fsrs"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(l,p){typeof exports=="object"&&typeof module!="undefined"?p(exports,require("seedrandom")):typeof define=="function"&&define.amd?define(["exports","seedrandom"],p):(l=typeof globalThis!="undefined"?globalThis:l||self,p(l.FSRS={},l.seedrandom))})(this,function(l,p){"use strict";var d=(t=>(t[t.New=0]="New",t[t.Learning=1]="Learning",t[t.Review=2]="Review",t[t.Relearning=3]="Relearning",t))(d||{}),n=(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))(n||{});Date.prototype.scheduler=function(t,e){return y(this,t,e)},Date.prototype.diff=function(t,e){return D(this,t,e)},Date.prototype.format=function(){return F(this)},Date.prototype.dueFormat=function(t,e,a){return z(this,t,e,a)};function y(t,e,a){return new Date(a?f(t).getTime()+e*24*60*60*1e3:f(t).getTime()+e*60*1e3)}function D(t,e,a){if(!t||!e)throw new Error("Invalid date");const r=f(t).getTime()-f(e).getTime();let s=0;switch(a){case"days":s=Math.floor(r/(24*60*60*1e3));break;case"minutes":s=Math.floor(r/(60*1e3));break}return s}function F(t){const e=f(t),a=e.getFullYear(),r=e.getMonth()+1,s=e.getDate(),i=e.getHours(),o=e.getMinutes(),u=e.getSeconds();return`${a}-${g(r)}-${g(s)} ${g(i)}:${g(o)}:${g(u)}`}function g(t){return t<10?`0${t}`:`${t}`}const b=[60,60,24,31,12],M=["second","min","hour","day","month","year"];function z(t,e,a,r=M){t=f(t),e=f(e),r.length!==M.length&&(r=M);let s=t.getTime()-e.getTime(),i;for(s/=1e3,i=0;i<b.length&&!(s<b[i]);i++)s/=b[i];return`${Math.floor(s)}${a?r[i]:""}`}function f(t){if(typeof t=="object"&&t instanceof Date)return t;if(typeof t=="string"){const e=Date.parse(t);if(isNaN(e))throw new Error(`Invalid date:[${t}]`);return new Date(e)}else if(typeof t=="number")return new Date(t);throw new Error(`Invalid date:[${t}]`)}function w(t){if(typeof t=="string"){const e=t.charAt(0).toUpperCase(),a=t.slice(1).toLowerCase(),r=d[`${e}${a}`];if(r===void 0)throw new Error(`Invalid state:[${t}]`);return r}else if(typeof t=="number")return t;throw new Error(`Invalid state:[${t}]`)}function A(t){if(typeof t=="string"){const e=t.charAt(0).toUpperCase(),a=t.slice(1).toLowerCase(),r=n[`${e}${a}`];if(r===void 0)throw new Error(`Invalid rating:[${t}]`);return r}else if(typeof t=="number")return t;throw new Error(`Invalid rating:[${t}]`)}const U=[n.Again,n.Hard,n.Good,n.Easy],V=[{start:2.5,end:7,factor:.15},{start:7,end:20,factor:.1},{start:20,end:1/0,factor:.05}];function C(t,e,a){let r=1;for(const o of V)r+=o.factor*Math.max(Math.min(t,o.end)-o.start,0);t=Math.min(t,a);let s=Math.max(2,Math.round(t-r));const i=Math.min(Math.round(t+r),a);return t>e&&(s=Math.max(s,e+1)),s=Math.min(s,i),{min_ivl:s,max_ivl:i}}var B=Object.defineProperty,O=Object.getOwnPropertySymbols,J=Object.prototype.hasOwnProperty,K=Object.prototype.propertyIsEnumerable,x=(t,e,a)=>e in t?B(t,e,{enumerable:!0,configurable:!0,writable:!0,value:a}):t[e]=a,Q=(t,e)=>{for(var a in e||(e={}))J.call(e,a)&&x(t,a,e[a]);if(O)for(var a of O(e))K.call(e,a)&&x(t,a,e[a]);return t},_=(t,e,a)=>(x(t,typeof e!="symbol"?e+"":e,a),a);class H{constructor(e,a){_(this,"again"),_(this,"hard"),_(this,"good"),_(this,"easy"),_(this,"last_review"),_(this,"last_elapsed_days"),this.last_review=e.last_review||e.due,this.last_elapsed_days=e.elapsed_days,e.elapsed_days=e.state===d.New?0:a.diff(e.last_review,"days"),e.last_review=a,e.reps+=1,this.again=this.copy(e),this.hard=this.copy(e),this.good=this.copy(e),this.easy=this.copy(e)}copy(e){return Q({},e)}update_state(e){return e===d.New?(this.again.state=d.Learning,this.hard.state=d.Learning,this.good.state=d.Learning,this.easy.state=d.Review):e===d.Learning||e===d.Relearning?(this.again.state=e,this.hard.state=e,this.good.state=d.Review,this.easy.state=d.Review):e===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(e,a,r,s){return this.again.scheduled_days=0,this.hard.scheduled_days=a,this.good.scheduled_days=r,this.easy.scheduled_days=s,this.again.due=y(e,5),this.hard.due=a>0?y(e,a,!0):y(e,10),this.good.due=y(e,r,!0),this.easy.due=y(e,s,!0),this}record_log(e,a){return{[n.Again]:{card:this.again,log:{rating:n.Again,state:e.state,due:this.last_review,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:e.scheduled_days,review:a}},[n.Hard]:{card:this.hard,log:{rating:n.Hard,state:e.state,due:this.last_review,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:e.scheduled_days,review:a}},[n.Good]:{card:this.good,log:{rating:n.Good,state:e.state,due:this.last_review,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:e.scheduled_days,review:a}},[n.Easy]:{card:this.easy,log:{rating:n.Easy,state:e.state,due:this.last_review,stability:e.stability,difficulty:e.difficulty,elapsed_days:e.elapsed_days,last_elapsed_days:this.last_elapsed_days,scheduled_days:e.scheduled_days,review:a}}}}}const j=.9,L=36500,N=[.5701,1.4436,4.1386,10.9355,5.1443,1.2006,.8627,.0362,1.629,.1342,1.0166,2.1174,.0839,.3204,1.4676,.219,2.8237],G=!1,W="3.5.6",R=t=>({request_retention:(t==null?void 0:t.request_retention)||j,maximum_interval:(t==null?void 0:t.maximum_interval)||L,w:(t==null?void 0:t.w)||N,enable_fuzz:(t==null?void 0:t.enable_fuzz)||G});function X(t,e){const a={due:t?f(t):new Date,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:0,lapses:0,state:d.New,last_review:void 0};return e&&typeof e=="function"?e(a):a}var Z=Object.defineProperty,ee=(t,e,a)=>e in t?Z(t,e,{enumerable:!0,configurable:!0,writable:!0,value:a}):t[e]=a,$=(t,e,a)=>(ee(t,typeof e!="symbol"?e+"":e,a),a);const E=-.5,S=19/81;class T{constructor(e){$(this,"param"),$(this,"intervalModifier"),$(this,"seed"),this.param=new Proxy(R(e),this.params_handler_proxy()),this.intervalModifier=this.calculate_interval_modifier(this.param.request_retention)}get interval_modifier(){return this.intervalModifier}calculate_interval_modifier(e){if(e<=0||e>1)throw new Error("Requested retention rate should be in the range (0,1]");return+((Math.pow(e,1/E)-1)/S).toFixed(8)}get parameters(){return this.param}set parameters(e){this.update_parameters(e)}params_handler_proxy(){const e=this;return{set:function(a,r,s){return r==="request_retention"&&Number.isFinite(s)&&(e.intervalModifier=e.calculate_interval_modifier(Number(s))),a[r]=s,!0}}}update_parameters(e){const a=R(e);for(const r in a)if(r in this.param){const s=r;this.param[s]=a[s]}}init_ds(e){e.again.difficulty=this.init_difficulty(n.Again),e.again.stability=this.init_stability(n.Again),e.hard.difficulty=this.init_difficulty(n.Hard),e.hard.stability=this.init_stability(n.Hard),e.good.difficulty=this.init_difficulty(n.Good),e.good.stability=this.init_stability(n.Good),e.easy.difficulty=this.init_difficulty(n.Easy),e.easy.stability=this.init_stability(n.Easy)}next_ds(e,a,r,s){e.again.difficulty=this.next_difficulty(a,n.Again),e.again.stability=this.next_forget_stability(a,r,s),e.hard.difficulty=this.next_difficulty(a,n.Hard),e.hard.stability=this.next_recall_stability(a,r,s,n.Hard),e.good.difficulty=this.next_difficulty(a,n.Good),e.good.stability=this.next_recall_stability(a,r,s,n.Good),e.easy.difficulty=this.next_difficulty(a,n.Easy),e.easy.stability=this.next_recall_stability(a,r,s,n.Easy)}init_stability(e){return Math.max(this.param.w[e-1],.1)}init_difficulty(e){return+Math.min(Math.max(this.param.w[4]-(e-3)*this.param.w[5],1),10).toFixed(8)}apply_fuzz(e,a,r){if(!r||e<2.5)return Math.round(e);const s=p(this.seed)(),{min_ivl:i,max_ivl:o}=C(e,a,this.param.maximum_interval);return Math.floor(s*(o-i+1)+i)}next_interval(e,a,r=this.param.enable_fuzz){const s=Math.min(Math.max(1,Math.round(e*this.intervalModifier)),this.param.maximum_interval);return this.apply_fuzz(s,a,r)}next_difficulty(e,a){const r=e-this.param.w[6]*(a-3);return this.constrain_difficulty(this.mean_reversion(this.param.w[4],r))}constrain_difficulty(e){return Math.min(Math.max(+e.toFixed(8),1),10)}mean_reversion(e,a){return+(this.param.w[7]*e+(1-this.param.w[7])*a).toFixed(8)}next_recall_stability(e,a,r,s){const i=n.Hard===s?this.param.w[15]:1,o=n.Easy===s?this.param.w[16]:1;return+(a*(1+Math.exp(this.param.w[8])*(11-e)*Math.pow(a,-this.param.w[9])*(Math.exp((1-r)*this.param.w[10])-1)*i*o)).toFixed(8)}next_forget_stability(e,a,r){return+(this.param.w[11]*Math.pow(e,-this.param.w[12])*(Math.pow(a+1,this.param.w[13])-1)*Math.exp((1-r)*this.param.w[14])).toFixed(8)}forgetting_curve(e,a){return+Math.pow(1+S*e/a,E).toFixed(8)}}var te=Object.defineProperty,ae=Object.defineProperties,ie=Object.getOwnPropertyDescriptors,I=Object.getOwnPropertySymbols,se=Object.prototype.hasOwnProperty,re=Object.prototype.propertyIsEnumerable,P=(t,e,a)=>e in t?te(t,e,{enumerable:!0,configurable:!0,writable:!0,value:a}):t[e]=a,m=(t,e)=>{for(var a in e||(e={}))se.call(e,a)&&P(t,a,e[a]);if(I)for(var a of I(e))re.call(e,a)&&P(t,a,e[a]);return t},v=(t,e)=>ae(t,ie(e)),ne=(t,e,a)=>(P(t,typeof e!="symbol"?e+"":e,a),a);class k extends T{constructor(e){super(e),ne(this,"get_retrievability",(a,r)=>{const s=this.preProcessCard(a);if(r=this.preProcessDate(r),s.state!==d.Review)return;const i=Math.max(r.diff(s.last_review,"days"),0);return(this.forgetting_curve(i,s.stability)*100).toFixed(2)+"%"})}preProcessCard(e){return v(m({},e),{state:w(e.state),due:f(e.due),last_review:e.last_review?f(e.last_review):void 0})}preProcessDate(e){return f(e)}preProcessLog(e){return v(m({},e),{due:f(e.due),rating:A(e.rating),state:w(e.state),review:f(e.review)})}repeat(e,a,r){const s=this.preProcessCard(e);a=this.preProcessDate(a);const i=new H(s,a).update_state(s.state);this.seed=String(a.getTime())+String(s.reps);let o,u,h;const c=s.elapsed_days;switch(s.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),o=this.next_interval(i.easy.stability,c),i.easy.scheduled_days=o,i.easy.due=a.scheduler(o,!0);break;case d.Learning:case d.Relearning:h=0,u=this.next_interval(i.good.stability,c),o=Math.max(this.next_interval(i.easy.stability,c),u+1),i.schedule(a,h,u,o);break;case d.Review:{const le=s.difficulty,Y=s.stability,oe=this.forgetting_curve(c,Y);this.next_ds(i,le,Y,oe),h=this.next_interval(i.hard.stability,c),u=this.next_interval(i.good.stability,c),h=Math.min(h,u),u=Math.max(u,h+1),o=Math.max(this.next_interval(i.easy.stability,c),u+1),i.schedule(a,h,u,o);break}}const q=i.record_log(s,a);return r&&typeof r=="function"?r(q):q}rollback(e,a,r){const s=this.preProcessCard(e),i=this.preProcessLog(a);if(i.rating===n.Manual)throw new Error("Cannot rollback a manual rating");let o,u,h;switch(i.state){case d.New:o=i.due,u=void 0,h=0;break;case d.Learning:case d.Relearning:case d.Review:o=i.review,u=i.due,h=s.lapses-(i.rating===n.Again&&i.state===d.Review?1:0);break}const c=v(m({},s),{due:o,stability:i.stability,difficulty:i.difficulty,elapsed_days:i.last_elapsed_days,scheduled_days:i.scheduled_days,reps:Math.max(0,s.reps-1),lapses:Math.max(0,h),state:i.state,last_review:u});return r&&typeof r=="function"?r(c):c}forget(e,a,r=!1,s){const i=this.preProcessCard(e);a=this.preProcessDate(a);const o=i.state===d.New?0:a.diff(i.last_review,"days"),u={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:o,review:a},h={card:v(m({},i),{due:a,stability:0,difficulty:0,elapsed_days:0,scheduled_days:0,reps:r?0:i.reps,lapses:r?0:i.lapses,state:d.New,last_review:i.last_review}),log:u};return s&&typeof s=="function"?s(h):h}reschedule(e,a={}){var r;if(!Array.isArray(e))throw new Error("cards must be an array");const s=[];for(const i of e){if(w(i.state)!==d.Review||!i.last_review)continue;const o=Math.floor(i.scheduled_days),u=this.next_interval(+i.stability.toFixed(2),Math.round(i.elapsed_days),(r=a.enable_fuzz)!=null?r:!0);if(u===o||u===0)continue;const h=m({},i);h.scheduled_days=u;const c=y(h.last_review,u,!0);a.dateHandler&&typeof a.dateHandler=="function"?h.due=a.dateHandler(c):h.due=c,s.push(h)}return s}}const de=t=>new k(t||{});l.DECAY=E,l.FACTOR=S,l.FSRS=k,l.FSRSAlgorithm=T,l.FSRSVersion=W,l.Grades=U,l.Rating=n,l.SchedulingCard=H,l.State=d,l.createEmptyCard=X,l.date_diff=D,l.date_scheduler=y,l.default_enable_fuzz=G,l.default_maximum_interval=L,l.default_request_retention=j,l.default_w=N,l.fixDate=f,l.fixRating=A,l.fixState=w,l.formatDate=F,l.fsrs=de,l.generatorParameters=R,l.get_fuzz_range=C,l.show_diff_message=z});
|
|
2
|
+
//# sourceMappingURL=index.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/fsrs/models.ts","../src/fsrs/help.ts","../src/fsrs/scheduler.ts","../src/fsrs/default.ts","../src/fsrs/algorithm.ts","../src/fsrs/fsrs.ts"],"sourcesContent":["export type StateType = \"New\" | \"Learning\" | \"Review\" | \"Relearning\";\n\nexport enum State {\n New = 0,\n Learning = 1,\n Review = 2,\n Relearning = 3,\n}\n\nexport type RatingType = \"Manual\" | \"Again\" | \"Hard\" | \"Good\" | \"Easy\";\n\nexport enum Rating {\n Manual = 0,\n Again = 1,\n Hard = 2,\n Good = 3,\n Easy = 4,\n}\n\ntype ExcludeManual<T> = Exclude<T, Rating.Manual>;\n\nexport type Grade = ExcludeManual<Rating>;\n\nexport interface ReviewLog {\n rating: Rating; // Rating of the review (Again, Hard, Good, Easy)\n state: State; // State of the review (New, Learning, Review, Relearning)\n due: Date; // Date of the last scheduling\n stability: number; // Memory stability during the review\n difficulty: number; // Difficulty of the card during the review\n elapsed_days: number; // Number of days elapsed since the last review\n last_elapsed_days: number; // Number of days between the last two reviews\n scheduled_days: number; // Number of days until the next review\n review: Date; // Date of the review\n}\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\nexport type RescheduleOptions = {\n enable_fuzz?: boolean;\n dateHandler?: (date: Date) => DateInput;\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: Readonly<Grade[]> = [\n Rating.Again,\n Rating.Hard,\n Rating.Good,\n Rating.Easy,\n] as const;\n\nconst FUZZ_RANGES = [\n {\n start: 2.5,\n end: 7.0,\n factor: 0.15,\n },\n {\n start: 7.0,\n end: 20.0,\n factor: 0.1,\n },\n {\n start: 20.0,\n end: Infinity,\n factor: 0.05,\n },\n] as const;\n\nexport function get_fuzz_range(\n interval: number,\n elapsed_days: number,\n maximum_interval: number,\n) {\n let delta = 1.0;\n for (const range of FUZZ_RANGES) {\n delta +=\n range.factor * Math.max(Math.min(interval, range.end) - range.start, 0.0);\n }\n interval = Math.min(interval, maximum_interval);\n let min_ivl = Math.max(2, Math.round(interval - delta));\n const max_ivl = Math.min(Math.round(interval + delta), maximum_interval);\n if (interval > elapsed_days) {\n min_ivl = Math.max(min_ivl, elapsed_days + 1);\n }\n min_ivl = Math.min(min_ivl, max_ivl);\n return { min_ivl, max_ivl };\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.5701, 1.4436, 4.1386, 10.9355, 5.1443, 1.2006, 0.8627, 0.0362, 1.629,\n 0.1342, 1.0166, 2.1174, 0.0839, 0.3204, 1.4676, 0.219, 2.8237,\n];\nexport const default_enable_fuzz = false;\n\nexport const FSRSVersion: string = \"3.5.6\";\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\";\nimport { get_fuzz_range } from \"./help\";\n\n/**\n * @default DECAY = -0.5\n */\nexport const DECAY: number = -0.5;\n/**\n * FACTOR = Math.pow(0.9, 1 / DECAY) - 1= 19 / 81\n *\n * $$\\text{FACTOR} = \\frac{19}{81}$$\n * @default FACTOR = 19 / 81\n */\nexport const FACTOR: number = 19 / 81;\n\n/**\n * @see https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45\n */\nexport class FSRSAlgorithm {\n protected param!: FSRSParameters;\n protected intervalModifier!: number;\n protected seed?: string;\n\n constructor(params: Partial<FSRSParameters>) {\n this.param = new Proxy(\n generatorParameters(params),\n this.params_handler_proxy(),\n );\n this.intervalModifier = this.calculate_interval_modifier(\n this.param.request_retention,\n );\n }\n\n get interval_modifier(): number {\n return this.intervalModifier;\n }\n\n /**\n * @see https://github.com/open-spaced-repetition/fsrs4anki/wiki/The-Algorithm#fsrs-45\n *\n * The formula used is: $$I(r,s) = (r^{\\frac{1}{DECAY}} - 1) / FACTOR \\times s$$\n * @param request_retention 0<request_retention<=1,Requested retention rate\n * @throws {Error} Requested retention rate should be in the range (0,1]\n */\n calculate_interval_modifier(request_retention: number): number {\n if (request_retention <= 0 || request_retention > 1) {\n throw new Error(\"Requested retention rate should be in the range (0,1]\");\n }\n return +((Math.pow(request_retention, 1 / DECAY) - 1) / FACTOR).toFixed(8);\n }\n\n /**\n * Get the parameters of the algorithm.\n */\n get parameters(): FSRSParameters {\n return this.param;\n }\n\n /**\n * Set the parameters of the algorithm.\n * @param params Partial<FSRSParameters>\n */\n set parameters(params: Partial<FSRSParameters>) {\n this.update_parameters(params);\n }\n\n private params_handler_proxy(): ProxyHandler<FSRSParameters> {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const _this: FSRSAlgorithm = this;\n return {\n set: function (target, prop, value) {\n if (prop === \"request_retention\" && Number.isFinite(value)) {\n _this.intervalModifier = _this.calculate_interval_modifier(\n Number(value),\n );\n }\n // @ts-ignore\n target[prop] = value;\n return true;\n },\n };\n }\n\n private update_parameters(params: Partial<FSRSParameters>): void {\n const _params = generatorParameters(params);\n for (const key in _params) {\n if (key in this.param) {\n const paramKey = key as keyof FSRSParameters;\n this.param[paramKey] = _params[paramKey] as never;\n }\n }\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 * $$S_0 = \\max \\lbrace S_0,0.1\\rbrace $$\n\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 * $$D_0 = \\min \\lbrace \\max \\lbrace D_0(G),1 \\rbrace,10 \\rbrace$$\n * where the $$D_0(3)=w_4$$ when the first rating is good.\n *\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 ).toFixed(8);\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 * @param {number} elapsed_days t days since the last review\n * @param {number} enable_fuzz - This adds a small random delay to the new interval time to prevent cards from sticking together and always being reviewed on the same day.\n * @return {number} - The fuzzed interval.\n **/\n apply_fuzz(ivl: number, elapsed_days: number, enable_fuzz?: boolean): int {\n if (!enable_fuzz || ivl < 2.5) return Math.round(ivl) as int;\n const generator = pseudorandom(this.seed);\n const fuzz_factor = generator();\n const { min_ivl, max_ivl } = get_fuzz_range(\n ivl,\n elapsed_days,\n this.param.maximum_interval,\n );\n return Math.floor(fuzz_factor * (max_ivl - min_ivl + 1) + min_ivl) as int;\n }\n\n /**\n * @see The formula used is : {@link FSRSAlgorithm.calculate_interval_modifier}\n * @param {number} s - Stability (interval when R=90%)\n * @param {number} elapsed_days t days since the last review\n * @param {number} enable_fuzz - This adds a small random delay to the new interval time to prevent cards from sticking together and always being reviewed on the same day.\n */\n next_interval(\n s: number,\n elapsed_days: number,\n enable_fuzz: boolean = this.param.enable_fuzz,\n ): int {\n const newInterval = Math.min(\n Math.max(1, Math.round(s * this.intervalModifier)),\n this.param.maximum_interval,\n ) as int;\n return this.apply_fuzz(newInterval, elapsed_days, enable_fuzz);\n }\n\n /**\n * The formula used is :\n * $$\\text{next}_d = D - w_6 \\cdot (R - 2)$$\n * $$D^\\prime(D,R) = w_5 \\cdot D_0(2) +(1 - w_5) \\cdot \\text{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} $$\\text{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 \\lbrace \\max \\lbrace D_0,1 \\rbrace,10\\rbrace$$\n * @param {number} difficulty $$D \\in [1,10]$$\n */\n constrain_difficulty(difficulty: number): number {\n return Math.min(Math.max(+difficulty.toFixed(8), 1), 10);\n }\n\n /**\n * The formula used is :\n * $$w_7 \\cdot \\text{init} +(1 - w_7) \\cdot \\text{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).toFixed(\n 8,\n );\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}(\\text{if} G=2) \\cdot w_{16}(\\text{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 ).toFixed(8);\n }\n\n /**\n * The formula used is :\n * $$S^\\prime_f(D,S,R) = w_{11}\\cdot D^{-w_{12}}\\cdot ((S+1)^{w_{13}}-1) \\cdot e^{w_{14}\\cdot(1-R)}$$\n * @param {number} d Difficulty D \\in [1,10]\n * @param {number} s Stability (interval when R=90%)\n * @param {number} r Retrievability (probability of recall)\n * @return {number} S^\\prime_f new stability after forgetting\n */\n next_forget_stability(d: number, s: number, r: number): number {\n return +(\n this.param.w[11] *\n Math.pow(d, -this.param.w[12]) *\n (Math.pow(s + 1, this.param.w[13]) - 1) *\n Math.exp((1 - r) * this.param.w[14])\n ).toFixed(8);\n }\n\n /**\n * The formula used is :\n * $$R(t,S) = (1 + \\text{FACTOR} \\times \\frac{t}{9 \\cdot S})^{\\text{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 + (FACTOR * elapsed_days) / stability, DECAY).toFixed(8);\n }\n}\n","import { SchedulingCard } from \"./scheduler\";\nimport { date_scheduler, fixDate, fixRating, fixState } from \"./help\";\nimport {\n Card,\n CardInput,\n DateInput,\n FSRSParameters,\n Rating,\n RecordLog,\n RecordLogItem,\n RescheduleOptions,\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 const interval = processedCard.elapsed_days;\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, interval);\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, interval);\n easy_interval = Math.max(\n this.next_interval(s.easy.stability, interval),\n good_interval + 1,\n );\n s.schedule(now, hard_interval, good_interval, easy_interval);\n break;\n case State.Review: {\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, interval);\n good_interval = this.next_interval(s.good.stability, interval);\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, interval),\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 * @param cards scheduled card collection\n * @param options Reschedule options,fuzz is enabled by default.If the type of due is not Date, please implement dataHandler.\n * @example\n * ```typescript\n * type CardType = Card & {\n * cid: number;\n * };\n * const reviewCard: CardType = {\n * cid: 1,\n * due: new Date(\"2024-03-17 04:43:02\"),\n * stability: 48.26139059062234,\n * difficulty: 5.67,\n * elapsed_days: 18,\n * scheduled_days: 51,\n * reps: 8,\n * lapses: 1,\n * state: State.Review,\n * last_review: new Date(\"2024-01-26 04:43:02\"),\n * };\n * const f = fsrs();\n * const reschedule_cards = f.reschedule([reviewCard]);\n * ```\n *\n */\n reschedule<T extends CardInput | Card>(\n cards: Array<T>,\n options: RescheduleOptions = {},\n ): Array<T> {\n if (!Array.isArray(cards)) {\n throw new Error(\"cards must be an array\");\n }\n const processedCard: T[] = [];\n for (const card of cards) {\n if (fixState(card.state) !== State.Review || !card.last_review) continue;\n const scheduled_days = Math.floor(card.scheduled_days) as int;\n const next_ivl = this.next_interval(\n +card.stability.toFixed(2),\n Math.round(card.elapsed_days),\n options.enable_fuzz ?? true,\n );\n if (next_ivl === scheduled_days || next_ivl === 0) continue;\n\n const processCard: T = { ...card };\n processCard.scheduled_days = next_ivl;\n const new_due = date_scheduler(processCard.last_review!, next_ivl, true);\n if (options.dateHandler && typeof options.dateHandler === \"function\") {\n processCard.due = options.dateHandler(new_due);\n } else {\n processCard.due = new_due;\n }\n processedCard.push(processCard);\n }\n return processedCard;\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","Rating","e","isDay","date_scheduler","pre","unit","date_diff","formatDate","last_review","timeUnit","show_diff_message","now","t","fixDate","diff","r","dateInput","date","year","month","day","hours","minutes","seconds","padZero","num","TIMEUNIT","TIMEUNITFORMAT","due","value","timestamp","fixState","firstLetter","restOfString","ret","fixRating","Grades","FUZZ_RANGES","get_fuzz_range","interval","elapsed_days","maximum_interval","delta","range","min_ivl","max_ivl","SchedulingCard","card","__publicField","__spreadValues","state","hard_interval","good_interval","easy_interval","default_request_retention","default_maximum_interval","default_w","default_enable_fuzz","FSRSVersion","generatorParameters","props","createEmptyCard","afterHandler","emptyCard","d","f","n","i","o","DECAY","FACTOR","FSRSAlgorithm","params","request_retention","_this","target","prop","_params","key","paramKey","s","last_d","last_s","retrievability","g","ivl","enable_fuzz","fuzz_factor","pseudorandom","newInterval","next_d","difficulty","init","current","hard_penalty","easy_bound","stability","x","L","I","C","P","S","c","p","h","m","FSRS","param","processedCard","_card","__spreadProps","_date","_log","recordLog","log","processedLog","last_due","last_lapses","prevCard","reset_count","scheduled_days","forget_log","recordLogItem","cards","options","_a","next_ivl","processCard","new_due","fsrs"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-fsrs",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.6",
|
|
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
|
+
"umd": "dist/index.umd.js",
|
|
6
7
|
"module": "dist/index.mjs",
|
|
7
8
|
"types": "dist/index.d.ts",
|
|
8
9
|
"keywords": [
|
|
@@ -32,14 +33,16 @@
|
|
|
32
33
|
"rollup-plugin-esbuild": "^6.1.1",
|
|
33
34
|
"ts-jest": "^29.1.1",
|
|
34
35
|
"tslib": "^2.6.2",
|
|
35
|
-
"typedoc": "^0.25.
|
|
36
|
+
"typedoc": "^0.25.13",
|
|
37
|
+
"typedoc-plugin-extras": "^3.0.0",
|
|
38
|
+
"typedoc-plugin-katex": "^0.1.2",
|
|
36
39
|
"typescript": "^5.2.2"
|
|
37
40
|
},
|
|
38
41
|
"scripts": {
|
|
39
42
|
"lint": "eslint --fix src/ && prettier --write src/",
|
|
40
43
|
"dev": "rollup -c rollup.config.ts --configPlugin esbuild -w",
|
|
41
|
-
"test": "jest --passWithNoTests",
|
|
42
|
-
"test
|
|
44
|
+
"test": "jest --config=jest.config.js --passWithNoTests",
|
|
45
|
+
"test::coverage": "jest --config=jest.config.js --coverage",
|
|
43
46
|
"prebuild": "rimraf ./dist",
|
|
44
47
|
"build": "rollup -c rollup.config.ts --configPlugin esbuild",
|
|
45
48
|
"build:types": "tsc --project ./tsconfig.json --declaration true",
|
|
@@ -48,7 +51,7 @@
|
|
|
48
51
|
"patch": "npm version patch",
|
|
49
52
|
"test_publish": "yalc publish",
|
|
50
53
|
"predocs": "rimraf ./docs",
|
|
51
|
-
"docs": "typedoc
|
|
54
|
+
"docs": "typedoc",
|
|
52
55
|
"clean": "rimraf ./dist ./docs"
|
|
53
56
|
},
|
|
54
57
|
"author": "ishiko",
|