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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@leverage/core

v4.1.4

Published

Core utilities for Leverage

Downloads

22

Readme

What is Leverage?

👩‍💻 A small, fast, flexible system to orchestrate your next application!

With Leverage you can easily create:

Install

# Using NPM
npm install @leverage/core

# Using Yarn
yarn add @leverage/core

Examples

Just getting started with Leverage and want to learn from a real-world example? Take a look at these projects!

Documentation

Table Of Contents

Units

Leverage's job is to help wire together your application, installing things only once all dependencies are satisfied. To accomplish this, Leverage standardizes the "pieces" of an application into three different kinds:

  • Plugin
  • Component
  • Service

Each unit contains a configuration object that specifies two properties. First, config.is tells Leverage what kind of unit it is and can be either "plugin", "component", or "service". Second, config.type tells Leverage what type this unit is for. The config.type value can be any string value and serves as a "name" for a plugin and set of components.

Plugins

A Plugin is a unit that has an install() method which is passed any added components of the same type as the plugin. For example, if you create a plugin where config.type is "http", any components that also have config.type set to "http" will be passed to your plugin's install() method.

Here is an example with a plugin that simply logs out the components it installs:

// index.js
import { add } from "@leverage/core";
import plugin from "./plugin";
import component from "./component";

add(plugin, component);
// plugin.js
import { useConfig } from "@leverage/core";

export const init = () => {
    useConfig({
        is: "plugin",
        type: "example",
    });
};

export const install = (component) => {
    console.log("installing component: ", component);
    // => { init: Function, myValue: 42 }
};
// component.js
import { useConfig } from "@leverage/core";

export const init = () => {
    useConfig({
        is: "component",
        type: "example",
    });
};

export const myValue = 42;

Components

A Component is a unit that is passed to a Plugin of the same type. Components serve as ways to configure the systems that Plugins create. For example, a component with the type "http" may be used to configure a route for the HTTP server that the HTTP plugin manages. Here's an example of an HTTP component from @leverage/plugin-http:

// index.js
import { add } from "@leverage/core";
import { http } from "@leverage/plugin-http";
import component from "./component";

add(http, component);
// component.js
import { useHTTP } from "@leverage/plugin-http";

export const init = () => {
    useHTTP({
        route: "/:name",
        method: "get",
    });
};

// The HTTP plugin calls `component.http` when a request comes in.
export const http = (req, res) => {
    res.write(`Hello, ${req.params.name}!`);
    res.end();
};

Services

A Service is a unit that has access to the same hooks and lifecycle that other units do, but is not directly connected to a Plugin or Component. Services are great for abstracting databases, creating state stores, or other things that don't fit into the Plugin/Component model. Here's an example of how you might create a database service:

// index.js
import { add } from "@leverage/core";
import { http } from "@leverage/plugin-http";
import component from "./component";
import service from "./service";

add(http, component, service);
// service.js
import db from "my-db-library";
import { useConfig, useKeyRef, useInstallEffect } from "@leverage/core";

export const init = () => {
    useConfig({
        is: "service",
        type: "database",
    });

    const connectionRef = useKeyRef("connection");

    useInstallEffect(() => {
        db.connect((connection) => {
            connectionRef.current = connection;
        });
    });
};

export const findUser = (name) => {
    const connection = useKeyRef("connection");

    // Database isn't ready yet!
    if (!connection) return;

    return connection.find("user", { name });
};
// component.js
import { useDependencies, useService } from "@leverage/core";
import { useHTTP } from "@leverage/plugin-http";

export const init = () => {
    useHTTP({
        route: "/:name",
        method: "get",
    });

    useDependencies({
        service: "database",
    });
};

// The HTTP plugin calls `component.http` when a request comes in.
export const http = (req, res) => {
    const db = useService("database");

    const user = db.findUser(req.params.name);

    res.json(user);
    res.end();
};

Lifecycle

A Unit has two different parts to its lifecycle: uninitialized and installed.

Uninitialized

When a Unit is added to Leverage, it starts as uninitialized. Leverage will immediately run the Unit's init function. In the uninitialized phase, a Unit is limited in what hooks it has access to. During this phase, a Unit can use the following hooks:

  • useConfig
  • useDependencies
  • useEvent
  • useHooks
  • useInstallEffect
  • useIs
  • useKeyRef
  • useType
  • useUnit

In order to successfully initialize, a Unit's init function must configure the config.is and config.type properties using useConfig, useIs, or useType.

Installed

Once all dependencies for a Unit are satisfied, a unit will be installed by Leverage. During this phase, any install effects created with useInstallEffect are run. Future useInstallEffect calls will fire immediately. Additionally, plugins are passed components of the same type. During this phase, a Unit can use the following hooks:

  • useConfig
  • useDependencies
  • useEffect
  • useEvent
  • useHooks
  • useInstallEffect
  • useIs
  • useKeyRef
  • usePlugin
  • useRef
  • useService
  • useState
  • useType
  • useUnit

However, an install effect created with useInstallEffect is limited to the following hooks:

  • useConfig
  • useDependencies
  • useEvent
  • useHooks
  • useIs
  • useKeyRef
  • usePlugin
  • useService
  • useType
  • useUnit

Hooks

Hooks are functions that can be used inside of a Unit's methods to interact with Leverage. Common examples of use cases are to:

  • Configure a Unit
  • Run a function after a Unit has been installed
  • Manage Unit state
  • Get an installed Plugin or Service

A Unit's exposed methods are automatically wrapped to support hooks. Any functions that are not exported will not be wrapped with hook support.

useConfig

The useConfig hook can be used in two different ways. It can be used inside of an init function to set or get the configuration. After a unit is initialized, useConfig can be used to get the current configuration.

import { useConfig, useInstallEffect } from "@leverage/core";

export const init = () => {
    /*
     * Here, `useConfig` **sets** the configuration to...
     *
     *  {
     *      "is": "component",
     *      "type": "http"
     *  }
     *
     * Then this new configuration is returned and stored in the
     *  `config` variable.
     */
    const config = useConfig({
        is: "component",
        type: "http",
    });

    /*
     * Here, `useConfig` updates the configuration to...
     *
     *  {
     *      "is": "component",
     *      "type": "http",
     *      "http": {
     *          "route": "/",
     *          "method": "get"
     *      }
     *  }
     *
     * Then this new configuration is returned and stored in the
     *  `config` variable.
     */
    const newConfig = useConfig({
        http: {
            route: "/",
            method: "get",
        },
    });

    /*
     * Here, `useConfig` **gets** the configuration and stores it in the
     *  `currentConfig` variable.
     */
    const currentConfig = useConfig();

    useInstallEffect(() => {
        /*
         * Here, `useConfig` **gets** the configuration and stores it in
         *  the `finalConfig` variable.
         */
        const finalConfig = useConfig();
    });
};

export const http = () => {
    /*
     * Here, `useConfig` **gets** the configuration and stores it in the
     *  `config` variable.
     */
    const config = useConfig();
};

useDependencies

The useDependencies hook can be used in two different ways. It can be used inside of an init function to set or get the dependencies. After a unit is initialized, useDependencies can be used to get the current dependencies.

import { useDependencies, useInstallEffect } from "@leverage/core";

export const init = () => {
    /*
     * Here, `useDependencies` **sets** the configuration to...
     *
     *  {
     *      "plugins": ["http"],
     *      "services": ["db"]
     *  }
     *
     * Then this new dependency configuration is returned and stored in
     *  the `dependencies` variable.
     */
    const dependencies = useDependencies({
        plugins: ["http"],
        services: ["db"],
    });

    /*
     * Here, `useDependencies` updates the configuration to...
     *
     *  {
     *      "plugins": ["http", "websocket"],
     *      "services": ["db"]
     *  }
     *
     * Then this new dependency configuration is returned and stored in
     *  the `dependencies` variable.
     */
    const newDependencies = useDependencies({
        plugins: ["websocket"],
    });

    /*
     * Here, `useDependencies` **gets** the dependencies and stores it in
     *  the `currentDependencies` variable.
     */
    const currentDependencies = useDependencies();

    useInstallEffect(() => {
        /*
         * Here, `useDependencies` **gets** the dependencies and stores it
         *  in the `finalDependencies` variable.
         */
        const finalDependencies = useDependencies();
    });
};

export const callback = () => {
    /*
     * Here, `useDependencies` **gets** the dependencies and stores it in the
     *  `dependencies` variable.
     */
    const dependencies = useDependencies();
};

useEffect

The useEffect hook can be used once a unit has been installed to run side effects when the effect's dependencies change. The useEffect hook also supports a cleanup function which can be returned from the effect callback.

import { useEffect } from "@leverage/core";

export const init = () => {
    /* ... */
};

export const callback = () => {
    /*
     * Here, `useEffect` will run on every call.
     */
    useEffect(() => {
        console.log("Hello, World!");
    });

    /*
     * Here, `useEffect` will run exactly once for the life of this unit.
     */
    useEffect(() => {
        console.log("Hello, World!");
    }, []);

    /*
     * Here, `useEffect` will run whenever `myValue` changes.
     */
    const myValue = Math.random();
    useEffect(() => {
        console.log("Hello, World!");
    }, [myValue]);

    /*
     * Here, `useEffect` will run once. When this unit is uninstalled or removed,
     *  the cleanup callback will be executed.
     */
    useEffect(() => {
        console.log("Hello, World!");

        return () => {
            console.log("Hello, Cleanup!");
        };
    }, []);
};

useHooks

The useHooks hook can be used to wrap functions in order to use hook calls inside of them. This is most useful for plugins in order to allow for asynchronous actions that may lack context otherwise.

import { useConfig, useHooks } from "@leverage/core";

export const init = () => {
    useConfig({
        is: "plugin",
        type: "example",
    });

    /*
     * Calling `useHooks` gives us a `withHooks` helper that
     *  is bound to the current Unit. In this case, it is the
     *  example plugin.
     */
    const withHooks = useHooks();

    /*
     * Here, we wrap a function using `withHooks`.
     */
    const handleAsyncAction = withHooks(() => {
        /*
         * Hooks can safely be used within this function.
         */
        const config = useConfig();
        // => { is: "plugin", type: "example" }
    });

    doSomethingAsync().then(handleAsyncAction);
};

useInstallEffect

The useInstallEffect hook can be used during initialization to run side effects once the Unit is installed. The useInstallEffect hook also supports a cleanup function which can be returned from the effect callback.

This hook is particularly useful for one-time configuration, expensive setup processes, or interacting with other installed Units.

import { useInstallEffect } from "@leverage/core";

export const init = () => {
    /*
     * Here, `useInstallEffect` runs once this unit is installed.
     *  When this unit is uninstalled or removed, the cleanup callback
     *  will be executed.
     */
    useInstallEffect(() => {
        console.log("Hello, World!");

        return () => {
            console.log("Hello, Cleanup!");
        };
    });
};

useIs

The useIs hook can be used in two different ways. It can be used inside of an init function to set or get the configuration's is value. After a unit is initialized, useIs can be used to get the current configuration's is value.

import { useIs, useInstallEffect } from "@leverage/core";

export const init = () => {
    /*
     * Here, `useIs` **sets** the configuration to...
     *
     *  {
     *      "is": "component",
     *  }
     *
     * Then this new configuration's `is` value is returned and stored in the
     *  `is` variable.
     */
    const is = useIs("component");

    /*
     * Here, `useIs` **gets** the configuration's `is` value and stores it in the
     *  `currentIs` variable.
     */
    const currentIs = useIs();

    useInstallEffect(() => {
        /*
         * Here, `useIs` **gets** the configuration's `is` value and stores it in
         *  the `finalIs` variable.
         */
        const finalIs = useIs();
    });
};

export const callback = () => {
    /*
     * Here, `useIs` **gets** the configuration's `is` and stores it in the
     *  `is` variable.
     */
    const is = useIs();
};

useKeyRef

The useKeyRef hook can be used to create or fetch an existing Ref for storing persistent data. This hook is particularly useful because it can be used during and after initialization.

import { useKeyRef, useInstallEffect } from "@leverage/core";

export const init = () => {
    /*
     * Here, `useKeyRef` creates a new ref using the key "my-key" and an initial
     *  value of 42. The returned Ref is an object that looks like:
     *
     *  {
     *      current: 42
     *  }
     */
    const ref = useKeyRef("my-key", 42);

    /*
     * Here, `useKeyRef` **gets** the Ref for "my-key". This is the same object as
     *  the variable `ref` holds.
     *
     *  ref === sameRef
     *  //=> true
     */
    const sameRef = useKeyRef("my-key");

    useInstallEffect(() => {
        /*
         * Here, `useKeyRef` **gets** the Ref for "my-key".
         */
        const stillSameRef = useKeyRef("my-key");
    });
};

export const callback = () => {
    /*
     * Here, `useKeyRef` **gets** the Ref for "my-key".
     */
    const ref = useKeyRef("my-key");
};

usePlugin

The usePlugin hook can be used to get an installed Plugin. This hook can only be used after initialization. In order to get a Plugin, the Plugin's type should be added to the Unit's dependencies.

import { usePlugin, useInstallEffect } from "@leverage/core";

export const init = () => {
    useDependencies({
        plugins: ["http"],
    });

    useInstallEffect(() => {
        /*
         * Here, `usePlugin` gets the "http" plugin.
         */
        const plugin = usePlugin("http");
    });
};

export const callback = () => {
    /*
     * Here, `usePlugin` gets the "http" plugin.
     */
    const plugin = usePlugin("http");
};

useRef

The useRef hook can be used once a unit has been installed to create a Ref that can store persistent data.

import { useRef, useEffect } from "@leverage/core";

export const init = () => {
    /* ... */
};

export const callback = () => {
    /*
     * Here, `useRef` creates or gets an existing Ref, using an initial value of 42.*  *  The returned Ref is an object that looks like:
     *
     *  {
     *      current: 42
     *  }
     */
    const ref = useRef(42);
};

useService

The useService hook can be used to get an installed Service. This hook can only be used after initialization. In order to get a Service, the Service's type should be added to the Unit's dependencies.

import { useService, useInstallEffect } from "@leverage/core";

export const init = () => {
    useDependencies({
        services: ["db"],
    });

    useInstallEffect(() => {
        /*
         * Here, `useService` gets the "db" plugin.
         */
        const service = useService("db");
    });
};

export const callback = () => {
    /*
     * Here, `useService` gets the "http" plugin.
     */
    const service = useService("db");
};

useEmitter

The useEmitter hook can be used to get the event emitter object.

import { useEmitter } from "@leverage/core";

export const init = () => {
    useConfig({
        is: "plugin",
        type: "example",
    });

    /*
     * Here, `useEmitter` will get the emitter object.
     */
    const emitter = useEmitter();

    /*
     * The emitter can then be used to handle events.
     */
    emitter.emit("my-event");

    const handler = () => {};

    /*
     * Listen to "my-event" events.
     */
    emitter.on("my-event", handler);

    /*
     * Stop listening to "my-event" events.
     */
    emitter.off("my-event", handler);

    /*
     * Listen to "my-event" only once.
     */
    emitter.once("my-event", () => {});
};

useEvent

The useEvent hook can be used to handle events.

import { useEvent } from "@leverage/core";

export const init = () => {
    useConfig({
        is: "plugin",
        type: "example",
    });

    /*
     * Here, `useEvent` will handle the "example:message" event.
     *
     * For Example:
     *
     *  emit(
     *      "example:message",
     *      "Hello, World"
     *  )
     */
    useEvent("example:configure", (message) => {
        console.log(message);
        //=> logs "Hello, World"
    });
};

useState

The useState hook can be used once a unit has been installed to store persistent state between calls. This hook should only be used in the primary method for your Unit.

import { useState } from "@leverage/core";

export const init = () => {
    /* ... */
};

export const callback = () => {
    /*
     * Here, `useEffect` will run on every call.
     */
    const [value, setValue] = useState(42);

    /*
     * On the first call, the default value is used. On subsequent calls,
     *  the existing value is used.
     */
    value === 42;

    /*
     * Calling the state setter will update the value for the _next_ call.
     *  This does **not** update `value` in-place.
     */
    setValue(1337);
};

useType

The useType hook can be used in two different ways. It can be used inside of an init function to set or get the configuration's type value. After a unit is initialized, useType can be used to get the current configuration's is value.

import { useType, useInstallEffect } from "@leverage/core";

export const init = () => {
    /*
     * Here, `useType` **sets** the configuration to...
     *
     *  {
     *      "type": "http",
     *  }
     *
     * Then this new configuration's `type` value is returned and stored in the
     *  `type` variable.
     */
    const type = useType("http");

    /*
     * Here, `useType` **gets** the configuration's `type` value and stores it in the
     *  `currentType` variable.
     */
    const currentType = useType();

    useInstallEffect(() => {
        /*
         * Here, `useType` **gets** the configuration's `type` value and stores it in
         *  the `finalType` variable.
         */
        const finalType = useType();
    });
};

export const callback = () => {
    /*
     * Here, `useType` **gets** the configuration's `type` and stores it in the
     *  `type` variable.
     */
    const type = useType();
};

useUnit

The useUnit hook can be used to get the current unit. This is mostly useful when you want a reference to the current unit to pass between boundaries. useUnit can also be useful when you need to call the wrapped version of unit methods.

import { useConfig, useUnit } from "@leverage/core";

export const init = () => {
    useConfig({
        is: "plugin",
        type: "example",
    });

    /*
     * Here, `useType` gets the current unit.
     */
    const self = useUnit("http");

    /*
     * Any exported members can be accessed.
     */
    self.callback();
};

export const callback = () => {
    /* ... */
};