npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

lowkey-dolphinsr

v0.0.4

Published

Spaced repetition API for JavaScript

Downloads

6

Readme

DolphinSR: Spaced Repetition in JavaScript

DolphinSR implements spaced repetition in JavaScript. Specifically, it uses Anki's modifications to the SM2 algorithm including:

  • an initial mode for learning new cards
  • a mode for re-learning cards after forgetting them
  • reducing the number of self-assigned ratings from 6 to 4
  • factoring lateness into card scheduling
  • Anki's default configuration options

While DolphinSR is intentionally very similar to Anki's algorithm, it does deviate in a few ways:

  • improved support for adding reviews out of order (for example, due to network latency)
  • very different internal data structures (DolphinSR is largely written in a functional style to make testing and debugging easier, and does not rely on storing computed data or any SQL database)
  • only one kind of card

Installation

DolphinSR is an npm package. Install it with either yarn add dolphinsr or npm install --save dolphinsr.

It's strongly recommended that you use Flow to statically check your code when using DolphinSR. We rely on it exclusively for type-checking, and don't do any runtime validation of type arguments. For more information, visit the Flow webpage;

Quick Start


import { DolphinSR, generateId } from 'dolphinsr';
import type { Master, Review } from 'dolphinsr';

// Specify the combinations DolphinSR should make out of your master cards.
// Numbers refer to indexes on the card. (Don't worry and keep reading if you don't understand)
const chineseCombinations = [
  {front: [0], back: [1, 2]},
  {front: [1], back: [0, 2]},
  {front: [2], back: [0, 3]}
];
const frenchCombinations = [
  {front: [0], back: [1]},
  {front: [1], back: [0]}
];

// Create the master cards that DolphinSR will use spaced repetition to teach.
// Note: in a real program, you'd want to persist these somewhere (a database, localStorage, etc)
const vocab: Array<Master> = [
  {
    id: generateId(),
    combinations: chineseCombinations,
    fields: ['你好', 'nǐ hǎo', 'hello']
  },
  {
    id: generateId(),
    combinations: chineseCombinations,
    fields: ['世界', 'shìjiè', 'world']
  },
  {
    id: generateId(),
    combinations: frenchCombinations,
    fields: ['le monde', 'the world']
  },
  {
    id: generateId(),
    combinations: frenchCombinations,
    fields: ['bonjour', 'hello (good day)']
  }
];

// Create the datastore used to house reviews.
// Again, in a real app you'd want to persist this somewhere.
const reviews: Array<Review> = [];

// Create a new DolphinSR instance
const d = new DolphinSR();

// Add all of your vocab to the DolphinSR instance
d.addMasters(...vocab);

// Add any existing reviews to the DolphinSR instance
// (In this example, this doesn't do anything since reviews is empty.)
d.addReviews(...reviews);

// Now, DolphinSR can tell us what card to review next.
// Since generateId() generates a random ID, it could be any of the cards we added.
// For example, it could be:
//     {
//       master: <Id>,
//       combination: {front: [0], back: [1, 2]},
//       front: ['你好'],
//       back: ['nǐ hǎo', 'hello']
//     }
const card = d.nextCard();

// It will also give us statistics on the cards we have:
// Since we added 2 masters with 3 combinations (the Chinese vocab) and 2 masters with 2
// combinations (the French vocab), we will have 10 cards. Since we haven't reviewed any of them
// yet, they will all be in a "learning" state.
const stats = d.summary(); // => { due: 0, later: 0, learning: 10, overdue: 0 }

// Now, we can review the current card (probably triggered by a real app's UI)
// If we already knew the answer, we would create a review saying that it was "easy" to recall:
const review: Review = {
  // identify which card we're reviewing
  master: d.nextCard().master,
  combination: d.nextCard().combination,

  // store when we reviewed it
  ts: new Date(),

  // store how easy it was to remember
  rating: 'easy'
};
reviews.push(review); // in a real app, we'd store this persistently
d.addReviews(review);

// Since we reviewed the current card, and marked it easy to remember, DolphinSR will move it into
// 'review' mode, which resembles classic SM2 spaced repetition. So everything else will still be in
// 'learn' mode, and it will be scheduled to be reviewed later.
d.summary(); // => { due: 0, later: 1, learning: 9, overdue: 0 }

// This will show the next card to review.
d.nextCard();

API

generateId(): Id

This generates a new ID for a master card. It uses the uuid package under the hood. Always use generateId() to generate IDs for your masters.

new DolphinSR()

Create a new DolphinSR instance, d.

(new DolphinSR()).addMasters(...masters: Master[]): void

Add masters to the DolphinSR instance. Masters with duplicate IDs will cause a runtime exception.

(new DolphinSR()).addReviews(...reviews: Review[]): boolean

Add reviews to the DolphinSR instance.

addReviews() is significantly more efficient if reviews are sorted in ascending order by ts, and all chronologically come after the previous latest review for any card. If this condition is met, addReviews() will return false. Otherwise, it returns true.

(new DolphinSR()).summary(): { due: number, later: number, learning: number, overdue: number }

Returns summary statistics for cards. Each category (due, later, learning, overdue) is a count.

  • Due cards are cards that the algorithm has determined should be reviewed on the day of the call. (That is, the current date as reflected by new Date().)
  • Overdue cards are cards that the algorithm has determined should be reviewed earlier than the day of the call.
  • Learning cards are cards that are either new and don't have any reviews, or cards that were forgotten (reviewed with an again rating) and not yet re-learned.
  • Later cards are cards that will be due in the future.

(new DolphinSR()).nextCard(): ?Card

Returns the next card to be reviewed, or null if there are no cards that need to be reviewed. Any card classified as due, overdue or learning by .summary() could appear in .nextCard().

Note: .nextCard() is deterministic--there is a defined order for prioritizing cards that are in different classifications and within classifications. That means that for any given set of masters and reviews added to a DolphinSR instance, nextCard() will return the same result.

Types

Master

A Master is an object conforming to the following Flow signature:

{
  id: Id, // from generateId()
  fields: Array<Field>, // see Field
  combinations: Array<Combination>, // see Combination
}

Field

A Field is a unit of data in a master. It is type alias for string.

Combination

A Combination is an object representing how Fields in a master should be combined to fit on a card with a front and a back. It looks like this:

{front: number[], back: number[]}

Rating

A Rating is an enum describing how well a user knows a specific combination of a master card. It can be (in descending order of ease):

  • 'easy'
  • 'good'
  • 'hard'
  • 'again'

Review

A Review is an object describing a review of a card by a user. It should look like this:

{
  master: Id,
  combination: Combination,
  ts: Date,
  rating: Rating,
}

Card

A Card is an object returned by .nextCard() which describes a part of a master suitable for displaying to a user. It should look like this:

{
  master: Id,
  combination: Combination,
  front: Array<Field>,
  back: Array<Field>
}