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

mod-counter

v0.1.0

Published

A JavaScript data structure library for modular (cyclic) counters with event-driven design

Readme

mod-counter

A JavaScript data structure library for modular (cyclic) counters with event-driven architecture. Perfect for creating clocks, generating combinations, and managing cyclical sequences.

Features

  • ModCounter: Cyclic counter that wraps around at boundaries
  • ChainedCounter: Chain multiple counters for multi-dimensional counting (e.g., clocks, combinatorics)
  • Iterator Support: Built-in generator functions for efficient iteration
  • TypeScript-Ready: JSDoc annotations for better IDE support
  • Well-Tested: Comprehensive test suite

Installation

npm install mod-counter

Quick Start

const { ModCounter, ChainedCounter } = require('mod-counter');

// Simple counter from 0 to 9
const counter = new ModCounter(10);
console.log(counter.getCurrent()); // 0
counter.increase();
console.log(counter.getCurrent()); // 1

API Documentation

ModCounter

A counter that cycles through a range [lowerBound, upperBound). When the counter reaches the upper bound, it wraps back to the lower bound.

Constructor

new ModCounter(upperBound, options)

Parameters:

  • upperBound (number, required): The exclusive upper bound of the range
  • options (object, optional):
    • lowerBound (number): The inclusive lower bound (default: 0)
    • startValue (number): Starting value (default: lowerBound)

Throws:

  • Error if upperBound is not greater than lowerBound
  • Error if startValue is outside the valid range

Methods

getCurrent()

Returns the current value of the counter.

const counter = new ModCounter(5);
console.log(counter.getCurrent()); // 0
increase()

Increases the counter by one. Wraps to lower bound if upper bound is reached.

const counter = new ModCounter(3);
counter.increase(); // 1
counter.increase(); // 2
counter.increase(); // wraps to 0
subscribeOnReset(callback)

Subscribe to reset events. Returns this for chaining.

const counter = new ModCounter(3);
counter.subscribeOnReset((resetValue) => {
  console.log(`Counter reset to ${resetValue}`);
});
unsubscribeOnReset(callback)

Unsubscribe from reset events. Returns this for chaining.

iterateUntilReset()

Returns an iterator that yields values until a reset occurs.

const counter = new ModCounter(12, { lowerBound: 5 });
for (let v of counter.iterateUntilReset()) {
  console.log(v); // 5, 6, 7, 8, 9, 10, 11
}

ChainedCounter

A counter that chains multiple ModCounter instances together. When a counter resets, it triggers the next counter to increase (like carrying in arithmetic).

Constructor

new ChainedCounter(...modCounters)

Parameters:

  • ...modCounters (ModCounter instances): One or more counters to chain

Throws:

  • Error if no counters are provided
  • Error if any argument is not a ModCounter instance

Methods

getCurrent()

Returns an iterator that yields current values from all chained counters.

const d1 = new ModCounter(2);
const d2 = new ModCounter(5);
const chained = new ChainedCounter(d1, d2);
console.log([...chained.getCurrent()]); // [0, 0]
increase()

Increases the first counter in the chain. May trigger a cascade of increases.

chained.increase();
console.log([...chained.getCurrent()]); // [1, 0]
chained.increase();
console.log([...chained.getCurrent()]); // [0, 1]
subscribeOnReset(callback) / unsubscribeOnReset(callback)

Inherited from ModCounter. Triggers when the last counter in the chain resets.

Examples

Basic Counter

const { ModCounter } = require('mod-counter');

// Counter from 5 to 11 (inclusive)
const counter = new ModCounter(12, { lowerBound: 5 });

console.log(counter.getCurrent()); // 5
counter.increase();
console.log(counter.getCurrent()); // 6

// ... increase 5 more times ...
console.log(counter.getCurrent()); // 11
counter.increase(); // reset!
console.log(counter.getCurrent()); // 5

Reset Events

const { ModCounter } = require('mod-counter');

const counter = new ModCounter(7, { lowerBound: 2 });
counter.subscribeOnReset((resetValue) => {
  console.log(`Counter reset to: ${resetValue}`);
});

for (let i = 0; i < 10; i++) {
  counter.increase();
}
// Output: "Counter reset to: 2" (when it wraps from 6 to 2)

Clock Simulation

const { ModCounter, ChainedCounter } = require('mod-counter');

// Create counters for a 24-hour clock
const secondsCounter = new ModCounter(60);
const minutesCounter = new ModCounter(60);
const hoursCounter = new ModCounter(24);

// Chain them: seconds -> minutes -> hours
const clock = new ChainedCounter(secondsCounter, minutesCounter, hoursCounter);

// Print time every hour
const hourlyPrinter = new ModCounter(3600)
  .subscribeOnReset(() => {
    const [seconds, minutes, hours] = [...clock.getCurrent()];
    console.log(`${hours}:${minutes}:${seconds}`);
  });

// Simulate 100,000 seconds
for (let i = 0; i < 100000; i++) {
  clock.increase();
  hourlyPrinter.increase();
}

Powerset Generation

const { ModCounter, ChainedCounter } = require('mod-counter');

function* generatePowerset(arr) {
  // Create a binary counter for each element
  const counters = arr.map(() => new ModCounter(2));
  const chained = new ChainedCounter(...counters);

  // Iterate through all combinations
  for (const combinationIterator of chained.iterateUntilReset()) {
    const bits = [...combinationIterator];
    const subset = arr.filter((_, i) => bits[i] === 1);
    yield subset;
  }
}

const inputSet = [1, 2, 3];
for (const subset of generatePowerset(inputSet)) {
  console.log(subset);
}
// Output: [], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]

Testing

Run the test suite:

npm test

Run linting:

npm run lint

Specification Files

Detailed specifications are available in the /specs directory:

License

MIT © Ron Klein

Repository

https://github.com/kleinron/mod-counter

Issues

Report issues at: https://github.com/kleinron/mod-counter/issues