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 🙏

© 2025 – Pkg Stats / Ryan Hefner

alpena

v1.0.0

Published

Signal Enhanced Web Apps

Readme

Alpena

A lightweight, dependency-aware reactive data structure for JavaScript applications.

Table of Contents

Introduction

Alpena is a reactive data structure designed to simplify state management in JavaScript applications. It allows you to create data signals that can be subscribed to, and automatically notifies subscribers when the signal's value changes. Additionally, signals can have dependencies on other signals, creating a reactive network where changes propagate efficiently.

Features

  • Reactive Data Handling: Subscribe to changes in data and react accordingly.
  • Dependency Management: Define dependencies between signals to create reactive data flows.
  • Immutable Updates: Ensure data integrity with deep equality checks.
  • Type Safety: Prevent unintended type changes with type checking on updates.
  • Convenience Methods: Utility methods like update for ease of use.

Installation

You can include Alpena in your project by importing the class directly. Since this is a standalone class, there is no need for installation via npm. Just ensure the class definition is included in your project.

// Ensure this import path matches where Alpena is located in your project.
import Alpena from './Alpena.js';

Getting Started

Begin by creating instances of Alpena and subscribing to changes. You can also define dependencies between signals to create more complex reactive relationships.

import Alpena from './Alpena.js';
const A = new Alpena(1);
const B = new Alpena('ABC');

B.addDependency(A);
B.subscribe((v, a)=>console.log('RESULT', v,a))

// when A changes B prints
// RESULT ABC 1
// RESULT ABC 2
// RESULT ABC 3
// RESULT ABC 4

setInterval(function () {
  A.update(v => v+1);
}, 1_000)

API Reference

Constructor

new Alpena(initialValue)

  • Parameters:

    • initialValue: The initial value of the signal.
  • Usage:

    const signal = new Alpena(100);

Properties

value

  • Description: Getter and setter for the signal's value.

  • Usage:

    const signal = new Alpena('Hello');
    
    // Get value
    console.log(signal.value); // Output: 'Hello'
    
    // Set value
    signal.value = 'World';

Methods

subscribe(callback)

  • Description: Subscribe to changes in the signal's value. The callback is invoked immediately with the current value upon subscription and subsequently whenever the value changes.

  • Parameters:

    • callback(value, ...dependencyValues): A function executed when the signal's value changes. Receives the current signal value and values of its dependencies.
  • Returns: A function that can be called to unsubscribe.

  • Usage:

    const unsubscribe = signal.subscribe((value, dep1, dep2) => {
      console.log(`Signal value: ${value}`);
    });
    
    // To unsubscribe
    unsubscribe();

unsubscribe(callback)

  • Description: Unsubscribe a previously subscribed callback.

  • Parameters:

    • callback: The callback function to remove from subscribers.
  • Usage:

    signal.unsubscribe(myCallback);

addDependency(...dependencies)

  • Description: Add one or more Alpena instances as dependencies. When a dependency changes, the signal's subscribers are notified.

  • Parameters:

    • dependencies: One or more Alpena instances to add as dependencies.
  • Usage:

    const signalA = new Alpena(1);
    const signalB = new Alpena(2);
    
    signalA.addDependency(signalB);

removeDependency(dependency)

  • Description: Remove a dependency from the signal.

  • Parameters:

    • dependency: The Alpena instance to remove.
  • Usage:

    signalA.removeDependency(signalB);

removeAllDependencies()

  • Description: Remove all dependencies from the signal.

  • Usage:

    signal.removeAllDependencies();

update(updateFunction)

  • Description: Update the signal's value using a function that receives the current value and returns a new value.

  • Parameters:

    • updateFunction(currentValue): A function that takes the current value and returns a new value.
  • Throws:

    • Error if the returned value is undefined or of a different type than the current value.
  • Usage:

    signal.update((current) => current + 1);

get()

  • Description: Get the current value of the signal.

  • Returns: The current value.

  • Usage:

    const value = signal.get();

set(value)

  • Description: Set a new value to the signal. Notifies subscribers if the value has changed.

  • Parameters:

    • value: The new value to set.
  • Usage:

    signal.set(42);

Examples

Basic Usage

import Alpena from './Alpena.js';

// Create a new signal
const counter = new Alpena(0);

// Subscribe to the signal
const unsubscribe = counter.subscribe((value) => {
  console.log(`Counter: ${value}`);
});

// Update the signal's value
counter.set(1); // Output: Counter: 1
counter.update((prev) => prev + 1); // Output: Counter: 2

// Unsubscribe when no longer needed
unsubscribe();

Dependency Management

import Alpena from './Alpena.js';

// Create signals
const firstName = new Alpena('Alice');
const lastName = new Alpena('Smith');
const fullName = new Alpena('Ms.');

// Define dependencies
fullName.addDependency(firstName, lastName);

// Subscribe to fullName changes
fullName.subscribe((value, first, last) => {
  const newFullName = `${value} ${first} ${last}`;
  console.log(`Full Name: ${newFullName}`);
});

// Change dependencies
firstName.set('Jane'); // Output: Full Name: Ms. Jane Smith
lastName.set('Doe'); // Output: Full Name: Ms. Jane Doe

Working with Arrays and Objects

import Alpena from './Alpena.js';

// Signal with an array
const items = new Alpena(['apple', 'banana']);

// Subscribe to changes
items.subscribe((list) => {
  console.log('Items:', list.join(', '));
});

// Update the array immutably
items.update((prevItems) => [...prevItems, 'cherry']); // Output: Items: apple, banana, cherry

// Signal with an object
const user = new Alpena({ name: 'Alice', age: 30 });

user.subscribe((data) => {
  console.log(`User: ${data.name}, Age: ${data.age}`);
});

// Update the object immutably
user.update((prevUser) => ({ ...prevUser, age: prevUser.age + 1 })); // Output: User: Alice, Age: 31

Utilities and Helpers

Alpena includes utility functions for internal use, such as deep equality checks (deepEqual), type checking (getType), and determining if a value is primitive (isPrimitive). These ensure signals behave predictably when dealing with complex data types.

Best Practices

  • Avoid Direct Mutation: When working with objects or arrays, use immutable update patterns to ensure changes are detected. For example, use spread operators or methods that return new arrays or objects.

    // Good
    signal.update((data) => ({ ...data, newProp: value }));
    
    // Bad (direct mutation)
    signal.value.newProp = value;
  • Consistent Types: Ensure that updates to a signal's value maintain the same data type to prevent errors.

  • Unsubscribe When Necessary: To prevent memory leaks, unsubscribe from signals when they are no longer needed, especially in frameworks or environments where components might unmount or be destroyed.

  • Manage Dependencies Carefully: When adding dependencies, remember that any change in a dependency will trigger notifications. Only add dependencies that are essential for the signal's logic.

  • Handle Errors Gracefully: Subscriber callbacks should handle potential errors to prevent unintended side effects.

    signal.subscribe((value) => {
      try {
        // Handle value
      } catch (error) {
        console.error('Error handling signal value:', error);
      }
    });