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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@philosoph/module-api

v0.1.0

Published

The question-module contract: everything a module author needs, and nothing about the game.

Downloads

104

Readme

@philosoph/module-api

The contract between the Philosoph classroom-game engine and a question module — everything a module author needs, and nothing about the game itself. Depends on nothing.

A question module is a self-contained plugin: it generates questions, grades them, and says what to reveal. The engine knows about none of it — it is handed a registry and only ever calls the module's methods, so a module can define correctness however it likes without any change to the core.

Install

npm install @philosoph/module-api

The whole contract

import type { QuestionModule } from "@philosoph/module-api";

export const myModule: QuestionModule = {
  id,           // permanent, unique — it keys every recorded answer; never change it
  title,        // shown in the educator's module picker
  shortTitle,   // compact label for analytics/report charts
  description,
  generate(rng) { /* → { public: QuestionInstance, key: AnswerKey } */ },
  grade(key, submission) { /* boolean — correctness is yours */ },
  reveal(key) { /* what clients highlight, e.g. { correctOptionId } */ },
};

Compose a set of modules into a registry:

import { createRegistry } from "@philosoph/module-api";
const registry = createRegistry([myModule]); // throws on duplicate ids

The rules that matter

  1. All randomness comes from the injected rng. Math.random(), Date.now(), unsorted Object.keys, etc. break reproducibility — a session replays from its seed, so the same seed must always yield the same questions and the same option order. Use rng.int, rng.pick, rng.shuffle, rng.id.
  2. Never a second correct answer. Build distractors that are wrong, and check it.
  3. Ids are forever. A module id and its option ids appear in saved CSVs and manifests; changing one orphans every answer already recorded against it.
  4. Multiple-choice, for now. answerFormat is "multiple-choice"; a browser client can only collect an interaction it has a widget for. Grading and reveal are still yours — the engine never inspects a key.
  5. Skills are a shared, flat vocabulary. A question carries zero or more skill strings; reports group by them, so reuse the same wording across modules that mean the same thing.

A working example

A complete, commented reference module ships with the package and is compiled/type-checked with it, so it can't rot:

import { exampleModule } from "@philosoph/module-api/example";

It is intentionally not part of the main barrel, so it never lands in a real registry unless you import it on purpose. Copy it to start a new module.

How a module reaches the game

The engine is content-agnostic: it composes whatever module packages are installed. A publishable question-module package advertises itself with the keyword philosoph-question-modules in its package.json and exports MODULES: readonly QuestionModule[]; the engine's modules:sync step discovers and composes them. Your job is just to implement this contract and publish.

License

PolyForm Noncommercial 1.0.0 — free for noncommercial use (schools, nonprofits, research, personal projects); commercial use is not granted.