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

chain-storage

v1.0.8

Published

## Example

Readme

ChainStorage

Example

import ChainStorage, {
    AssignRejecter
} from 'chain-storage';

function log(label, config) {
    const entries = `color: ${config.get("color")}; font-size: ${config.get("fontSize")}; font-weight: ${config.get("fontWeight")}`
    console.log(`%c${label} ==>`, entries, entries);
}

// Create a storage
var globalConfig = new ChainStorage({
    color: "#2b85e4",
    fontSize: 12,
    fontWeight: 500
});

// Monitor storage value changes Monitor storage value changes and print logs;
globalConfig.watch(function (key, value, oldValue) {
    console.log(`globalConfig.${key} has changed:`, oldValue, "==>", value);
})

// Register property assignment hijacking function, 
// if it does not conform to the specification, 
// return AssignRejecter to refuse assignment,
// otherwise return the new value
globalConfig.registerHandler("fontSize", function (value) {
    value = parseInt(value);
    if (Number.isNaN(value) || value < 8) return AssignRejecter;
    return value;
});

// Create a branch storage
var branchConfig = globalConfig.branch({
    fontSize: "14.5"
});

branchConfig.watch(function (key, value, oldValue) {
    console.log(`branchConfig.${key} has changed:`, oldValue, "==>", value);
})

// Create a leaf storage
var leafConfig = branchConfig.branch();
leafConfig.set({
    fontSize: "Invalid value",
    fontWeight: 700
});

console.log("\n\n\n==================  First round  ===================");
log("globalConfig", globalConfig);
log("branchConfig", branchConfig);
log("  leafConfig", leafConfig);
console.log("====================================================\n\n\n");

globalConfig.set({
    color: "#515a6e",
    fontSize: 10
});

leafConfig.set({
    color: "#ff9900"
});

console.log("\n\n\n================== Second round  ===================");
log("globalConfig", globalConfig);
log("branchConfig", branchConfig);
log("  leafConfig", leafConfig);
console.log("====================================================\n\n\n");

globalConfig.set("color", "#2db7f5");
branchConfig.set("color", "#ed4014")

console.log("\n\n\n==================  Third round  ===================");
log("globalConfig", globalConfig);
log("branchConfig", branchConfig);
log("  leafConfig", leafConfig);
console.log("====================================================\n\n\n");

Declare

declare const AssignRejecter: Readonly<{
    "@@Assign.rejecter": boolean;
}>;
declare class ChainStorage<S extends object = any, K extends keyof S = keyof S> {
    /**
     * @description Create an empty storage
     * @constructor
     */
    constructor();
    /**
     * @description Create a storage, use [[source]] as the value, and the prototype-chain is null
     * @constructor
     * @param { object } source
     */
    constructor(source: S);
    /**
     * @description Create a storage, use [[source]] as the value, use [[proto]] as the prototype-chain
     * @constructor
     * @param { object } source
     * @param { object | null } proto
     */
    constructor(source: S | undefined, proto: S | null);
    /**
     * @description Create a branch storage, the branch uses the current storage as the prototype-chain,
     * @description the modification on the branch will not affect the upper-level warehouse,
     * @description but the modification of the upper-level warehouse will affect the prototype-chain of its branch storage
     *
     * @description Create an empty branch storage
     * @returns { ChainStorage }
     */
    branch<T extends object = any>(): ChainStorage<S & T>;
    /**
     * @description Create an empty branch storage, use [[source]] as the value
     * @param { object } source
     * @returns { ChainStorage }
     */
    branch<T extends object = any>(source?: T): ChainStorage<S & T>;
    /**
     * @description Determine whether the storage has this property, it will go deep into the prototype-chain, similar to the "in" operator
     * @param { PropertyKey } key
     * @returns { boolean }
     */
    has(key: K): boolean;
    /**
     * @description Get storage object property [[key]]
     * @param { PropertyKey } key
     * @returns { any }
     */
    get(key: K): S[K] | undefined;
    /**
     * @description Use object to merge storage object,
     * @description will not merge deeply, just simply overwriting properties
     * @param { object } entries
     * @returns { this }
     */
    set(entries: Partial<S>): this;
    /**
     * @description Set a single property value
     * @param { PropertyKey } key
     * @param { any } value
     * @returns { this }
     */
    set(key: K, value: S[K]): this;
    /**
     * @description Delete a single property value
     * @param { PropertyKey } key
     * @returns { this }
     */
    delete(key: K): this;
    /**
     * @description Returns whether the storage is frozen
     * @returns { boolean }
     */
    isFrozen(): boolean;
    /**
     * @description Returns whether the storage is destroyed
     * @returns { boolean }
     */
    isDestroyed(): boolean;
    /**
     * @description To freeze the storage, you can provide a [[lock]] for unlocking.
     * @description If you don’t provide a [[lock]], this storage will not be unlocked.
     * @description Return true if the freeze is successful, false if the freeze fails (for example, it is already frozen by another lock)
     *
     * @description If you don't want your storage to be unlocked by others, please avoid using string / number / boolean / null.
     * @description If you use NaN as a [[lock]], this repository will never be unlocked, including yourself, because NaN !== NaN
     * @param { object? } lock
     * @returns { boolean }
     */
    freeze(lock?: any): boolean;
    /**
     * @description To unfreeze the storage, use the [[lock]] provided when locking to unlock
     * @description Return true if the unfreeze is successful, false if the unfreeze fails
     * @param { object? } lock
     * @returns { boolean }
     */
    unfreeze(lock: any): boolean;
    /**
     * @description Destroy the storage, the storage will be frozen but still readable,
     * @description all data monitoring on the warehouse will be cleared, and no new monitoring will be accepted
     * @returns { this }
     */
    destroy(): this;
    /**
     * @description Monitor changes in storage object properties
     * @param { PropertyKey } prop
     * @param { Function } watcher
     * @returns { this }
     */
    watch(prop: K, watcher: PropertyWatcher<S, K>): this;
    /**
     * @description Monitor changes in storage objects
     * @param { PropertyKey } prop
     * @param { Function } watcher
     * @returns { this }
     */
    watch(watcher: Watcher<S, K>): this;
    /**
     * @description Clear all monitors
     * @returns { this }
     */
    unwatch(): this;
    /**
     * @description Remove storage object monitoring
     * @param { Function } watcher
     * @returns { this }
     */
    unwatch(watcher: Watcher<S, K>): this;
    /**
     * @description Remove storage object properties monitoring
     * @param { Function } watcher
     * @returns { this }
     */
    unwatch(prop: K | Watcher<S, K>, watcher: PropertyWatcher<S, K>): this;
    /**
     * @description Register property assignment hijacking function to normalize attributes
     * @description After registration, if the property exists, it will be executed immediately
     * @param { PropertyKey } key
     * @param { Function } handler
     * @returns { this }
     */
    registerHandler(key: K, handler: PropertyHandler): this;
}
declare type AssignRejecter = object;
interface Watcher<S extends object = any, K extends keyof S = keyof S> {
    /**
     * @description Monitor object changes
     * @param { PropertyKey } key - Modified property
     * @param { any } value - New value
     * @param { any } oldValue - Old value
     */
    (key: K, value: S[K] | undefined, oldValue: S[K] | undefined): void;
}
interface PropertyWatcher<S extends object = any, K extends keyof S = keyof S> {
    /**
     * @description Monitor property changes
     * @param { any } value - New value
     * @param { any } oldValue - Old value
     */
    (value: S[K] | undefined, oldValue: S[K] | undefined): void;
}
interface PropertyHandler<S extends object = any, K extends keyof S = keyof S> {
    /**
     * @description Monitor property changes
     * @param { any } value - New value
     * @param { any } oldValue - Old value
     */
    (value: S[K], key: K): S[K] | AssignRejecter;
}
export default ChainStorage;
export { AssignRejecter };