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

isotropic-initializable

v0.12.0

Published

An observable initialization lifecycle

Downloads

28

Readme

isotropic-initializable

npm version License

An observable initialization lifecycle for JavaScript objects. Ensures parent-to-child initialization sequence, supports asynchronous initialization, and provides event-based completion notifications.

Why Use This?

  • Predictable Initialization Order: Ensures parent class initialization completes before child classes initialize
  • Asynchronous Support: Works with both synchronous and promise-based initialization methods
  • Observable Lifecycle Events: Subscribe to initialization events for greater control
  • Mixin Support: Works seamlessly with isotropic-make mixins
  • Selective Initialization: Configure which classes in the hierarchy should initialize
  • Error Handling: Built-in error propagation for initialization failures

The Initialization Problem

When using hierarchical inheritance in JavaScript, the standard pattern with isotropic-make (or ES6 classes) runs constructors from parent to child, but then instance methods run in reverse order (child to parent). For complex objects that need a reliable initialization sequence, this can be problematic.

The isotropic-initializable module solves this by providing a dedicated initialization phase that runs in a predictable parent-to-child order, ensuring each component has a stable foundation to build upon.

Installation

npm install isotropic-initializable

Basic Usage

import _Initializable from 'isotropic-initializable';
import _make from 'isotropic-make';

// Create a base component with initialization
const _BaseComponent = _make(_Initializable, {
        _initialize () {
            console.log('Base component initializing...');

            this.baseReady = true;

            // Can return a Promise if needed
        }
    }),
    // Create a derived component
    _EnhancedComponent = _make(_BaseComponent, {
        _initialize () {
            console.log('Enhanced component initializing...');

            // Safe to use this.baseReady here, parent initialization completes first

            this.enhancedReady = true;
        }
    });

{
    // Instance is automatically initialized during construction
    const component = _EnhancedComponent();
    // Outputs:
    // Base component initializing...
    // Enhanced component initializing...

    console.log(component.initialized); // true
}

Key Concepts

Initialization Order

  1. The isotropic-initializable module ensures initialization methods are called in parent-to-child order
  2. For each class in the inheritance chain, its _initialize method is called
  3. Mixins are initialized in the order they were defined
  4. Any asynchronous initialization is properly awaited before proceeding to child classes

Observable Lifecycle

The initialization process includes several observable events:

  1. initialize: Triggered when initialization begins
  2. initializeComplete: Triggered when all initialization has completed successfully
  3. initializeError: Triggered if an error occurs during initialization
const component = _Component({
    initialize: false
});

// Listen for complete initialization
component.on('initializeComplete', () => {
    console.log('Component is fully initialized!');
});

// Listen for initialization errors
component.on('initializeError', ({
    data
}) => {
    console.error('Initialization failed:', data.error);
});

// Start initialization
component.initialize();

Initialization Status

You can check the initialization status of any Initializable object:

const component = _Component();

console.log(component.initialized); // true if initialization completed

Examples

Basic Synchronous Initialization

import _Initializable from 'isotropic-initializable';
import _make from 'isotropic-make';

const _Widget = _make(_Initializable, {
    render () {
        document.body.appendChild(this.elements.container);
    },
    _initialize (config) {
        console.log('Initializing widget with:', config);

        this.elements = {};
        this.name = config.name;


        // Create DOM elements
        this.elements.container = document.createElement('div');
        this.elements.container.className = 'widget';
    }
});

{
    // Create and initialize
    const widget = _Widget({
        name: 'MyWidget'
    });

    // Already initialized
    widget.render();
}

Asynchronous Initialization

import _Initializable from 'isotropic-initializable';
import _make from 'isotropic-make';

const _DataComponent = _make(_Initializable, {
    displayData () {
        console.log('Displaying:', this.data);
    },
    async _initialize (config) {
        console.log('Loading data...');

        // API call
        this.data = await fetch(`https://api.example.com/data/${config.id}`).then(response => response.json());

        console.log('Data loaded!');
    }
});

{
    // Create and automatically initialize
    const component = _DataComponent({
        id: '123'
    });

    // Listen for completion
    component.on('initializeComplete', () => {
        component.displayData();
    });
}

Delayed Initialization

import _Initializable from 'isotropic-initializable';
import _make from 'isotropic-make';

const _LazyComponent = _make(_Initializable, {
    _initialize() {
        console.log('Initializing expensive resources...');

        // Do expensive initialization
    }
});

{
    // Create without initializing
    const component = _LazyComponent({
        initialize: false
    });

    console.log(component.initialized); // false

    // Listen for completion
    component.on('initializeComplete', () => {
        console.log('Now ready to use!');
    });

    // Later, when needed
    button.addEventListener('click', () => {
        component.initialize();
    });
}

Multi-level Inheritance

import _Initializable from 'isotropic-initializable';
import _make from 'isotropic-make';

// Base component
const _UiComponent = _make(_Initializable, {
        _initialize (config) {
            console.log('UiComponent initializing');

            this.id = config.id || `ui-${Date.now()}`;
            this.element = document.createElement('div');
            this.element.id = this.id;
        }
    }),
    // Mid-level component
    _Container = _make(_UiComponent, {
        _initialize (config) {
            console.log('Container initializing');

            this.children = [];
            this.element.className = 'container';

            if (config.styles) {
                Object.assign(this.element.style, config.styles);
            }
        }
    }),
    // Leaf component
    _Panel = _make(_Container, {
        addContent (content) {
            this.content.appendChild(content);

            return this;
        },
        _initialize (config) {
            console.log('Panel initializing');

            // Create header
            this.header = document.createElement('header');
            this.header.textContent = config.title || 'Untitled Panel';
            this.element.appendChild(this.header);

            // Create content area
            this.content = document.createElement('div');
            this.content.className = 'panel-content';
            this.element.appendChild(this.content);
        }
    });

{
    // Create instance - initialization runs in order:
    // 1. _UiComponent._initialize
    // 2. _Container._initialize
    // 3. _Panel._initialize
    const panel = _Panel({
        id: 'main-panel',
        styles: {
            height: '300px',
            width: '500px'
        }
        title: 'System Status'
    });

    document.body.appendChild(panel.element);
}

Using with Mixins

import _Initializable from 'isotropic-initializable';
import _make from 'isotropic-make';

// Create mixins
const _Resizable = _make({
        resize (width, height) {
            this.width = width ?? this.width;
            this.height = height ?? this.height;
            this.updateSize();
        },
        updateSize () {
            console.log(`Setting size: ${this.width}x${this.height}`);

            // Update size
        },
        _initialize (config) {
            console.log('Initializing resize support');

            this.height = config.height ?? 200;
            this.width = config.width ?? 300;

            this.updateSize();
        }
    }),
    _Themeable = _make({
        applyTheme (theme) {
            console.log(`Applying theme: ${theme}`);

            // Apply theme styles
        },
        toggleTheme () {
            this.theme = this.theme === 'light' ?
                'dark' :
                'light';
            this.applyTheme(this.theme);
        }
        _initialize (config) {
            console.log('Initializing theme support');

            this.theme = config.theme ?? 'light';
            this.applyTheme(this.theme);
        }
    }),

    // Create a component with mixins
    _MyComponent = _make(_Initializable, [
        _Resizable,
        _Themeable
    ], {
        _initialize (config) {
            console.log('Initializing my component');

            this.title = config.title ?? 'Untitled';
            // Component-specific initialization
        }
    });

{
    // Create instance
    const component = _MyComponent({
        height: 600,
        theme: 'dark',
        title: 'Dashboard',
        width: 800
    });

    // Mixins are initialized in order:
    // 1. _Resizable._initialize
    // 2. _Themeable._initialize
    // 3. _MyComponent._initialize
}

Error Handling During Initialization

import _Error from 'isotropic-error';
import _Initializable from 'isotropic-initializable';
import _make from 'isotropic-make';

const _RiskyComponent = _make(_Initializable, {
    _eventInitializeError ({
        data: {
            error
        }
    }) {
        // This event handler method gets executed if initialization fails.
        console.error('Initialization failed:', error);
    },
    async _initialize (config) {
        if (!config.apiKey) {
            throw _Error({
                message: 'API key is required',
                name: 'ConfigurationError'
            });
        }

        // Attempt to connect
        const response = await fetch('https://api.example.com/connect', {
            headers: {
                'Authorization': `Bearer ${config.apiKey}`
            }
        });

        if (!response.ok) {
            throw _Error({
                details: await response.json(),
                message: `API returned ${response.status}`,
                name: 'ConnectionError'
            });
        }

        this.connection = await response.json();
    }
});

{
    const component = _RiskyComponent({
        // Missing apiKey
    });
}

Selective Initialization

Sometimes you may want to skip initialization of certain parent classes or mixins:

import _Initializable from 'isotropic-initializable';
import _make from 'isotropic-make';

const _Logger = _make({
        log (message) {
            this.logs.push(`[${new Date().toISOString()}] ${message}`);

            console.log(message);
        },
        _initialize() {
            console.log('Logger initializing');
            this.logs = [];
        }
    }),
    _Storage = _make({
        load (key) {
            return this.data[key];
        },
        save (key, value) {
            this.data[key] = value;
        },
        _initialize () {
            console.log('Storage initializing');

            this.data = {};
        }
    }),

    // Component that uses _Logger and _Storage, but doesn't want
    // to initialize _Storage (maybe to use a custom implementation)
    _MyComponent = _make(_Initializable, [
        _Logger,
        _Storage
    ], {
        // Override storage methods
        load (key) {
            this.log(`Loading ${key}`);

            return this.data.get(key);
        },
        save (key, value) {
            this.log(`Saving ${key}`);

            this.data.set(key, value);
        },
        // Skip initialization of Storage
        _doNotInitialize: Storage,
        // _doNotInitialize could also be an Array or a Set
        _initialize () {
            console.log('MyComponent initializing');

            // Custom storage implementation
            this.data = new Map();
        }
    });

{
    // Create instance
    const component = _MyComponent();

    // Only _Logger and _MyComponent will initialize, _Storage will be skipped
}

Advanced: Completing the Lifecycle

The full lifecycle of an Initializable instance includes:

  1. Construction (_init in isotropic-make)
  2. Initialization (_initialize methods)
  3. Initialization complete event
  4. Usage
  5. Destruction (destroy method)

It's not always necessary to destroy an instance. If there isn't anything that requires explicit cleanup, the garbage collector will take care of it.

import _Initializable from 'isotropic-initializable';
import _make from 'isotropic-make';

const _Resource = _make(_Initializable, {
    use () {
        console.log(`Using resource: ${this.name}`);

        // Use the resource
    },
    _destroy (...args) {
        console.log(`Cleaning up resource: ${this.name}`);

        closeResource(this.handle);

        // Call parent destroy method
        return Reflect.apply(_Initializable.prototype._destroy, this, args);
    },
    _init (...args) {
        Reflect.apply(_Initializable.prototype._init, this, args);

        console.log('Resource constructed');

        return this;
    },
    _initialize (config) {
        console.log('Resource initializing');

        this.name = config.name;
        this.handle = openResource(this.name);
    },
    _initializeComplete (config) {
        console.log('Resource initialization complete');
    }
});

{
    // Create with automatic initialization
    const resource = _Resource({
        name: 'database'
    });

    // Use the resource
    resource.use();

    // Later, clean up
    resource.destroy();
}

API Reference

Constructor Options

_Initializable({
  initialize: true // Whether to automatically initialize (default: true)
});

Instance Properties

  • initialized (Boolean): Whether initialization has completed successfully

Instance Methods

  • initialize(...args): Start or restart initialization with the given arguments
  • destroy(...args): Clean up and destroy the instance

Protected Methods

  • _initialize(...args): Define initialization behavior (implemented by subclasses)
  • _initializeComplete(...args): Called when initialization completes (can be overridden)
  • _initializeError(): Called when initialization fails (can be overridden)

Events

  • initialize: Triggered when initialization begins
  • initializeComplete: Triggered when initialization completes successfully
  • initializeError: Triggered if initialization fails, with error data

Integration with Other isotropic Modules

isotropic-initializable works seamlessly with other modules in the isotropic ecosystem:

  • isotropic-error: Nested error reporting for initialization failures
  • isotropic-later: Asynchronous utilities
  • isotropic-make: Create constructor functions with inheritance and mixins
  • isotropic-pubsub: Event system for the observable lifecycle

Contributing

Please refer to CONTRIBUTING.md for contribution guidelines.

Issues

If you encounter any issues, please file them at https://github.com/ibi-group/isotropic-initializable/issues