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

usub

v0.5.1

Published

Subscribe to any reactive sources

Downloads

1,352

Readme

USub

tests Version Badge size Badge size

lite version

Badge size Badge size

This javascript library provides utility functions for handling observables, signals, and asynchronous data streams across various reactive programming libraries. It supports flexible customization to integrate with different libraries, ensuring seamless subscription management and automatic cleanup.

Table of Contents

Installation

yarn: yarn add usub

npm: npm i usub

cdn: https://unpkg.com/usub

es: https://unpkg.com/usub?module

To use this utility, simply import it into your project:

import { is, api, sub, get } from "usub";

lite

The lite utility does not contain subscribe, and set method for observ-* patterns.

cdn: https://unpkg.com/usub/lite

es: https://unpkg.com/usub/lite?module

Lite Installation

To use lite version, simply import it into your project:

import { is, api, sub, get } from "usub/lite";

Usage

Basic Setup

The library exports four primary functions:

  • is: Checks if a value is considered an observable or reactive signal.
  • api: Provides utility functions that can be customized to work with different reactive libraries.
  • sub: Subscribes to an observable or other async/reactive patterns.
  • get: get function to retrieve reactive data

Subscribing to Observables

The sub function is used to subscribe to an observable or other async patterns. It handles various types of asynchronous inputs like promises, async iterables, and functions.

const observable = {
	subscribe: (next, error, complete) => {
		next(1);
		next(2);
		next(3);
		complete();
		return {
			unsubscribe: () => console.log("Unsubscribed"),
		};
	},
};

const unsubscribe = sub(observable)(console.log, console.error, () =>
	console.log("Complete")
);

Checking if an Object is Observable

Use the is function to check if a value is considered an observable by the library:

const observable = {
	subscribe: () => {},
};

console.log(is(observable)); // Output: true

Promise

{
	const promise = new Promise((resolve) => {
		setTimeout(() => {
			resolve("Hello, World!");
		}, 1000);
	});

	sub(promise)(
		(value) => console.log("Resolved value:", value),
		(error) => console.error("Error:", error),
		() => console.log("Promise complete")
	);
}

Customizing for Your Reactive Library

The library is designed to be easily customized for different reactive programming libraries. To integrate with your preferred library, you can customize the following api functions:

API Overview

  • api.any(target)(next, err, complete)

api.any is a placeholder within the api object that can be used to represent or handle any observable-like object.

Purpose

The primary purpose of api.any is to provide a centralized place for handling various types of observable objects, whether they are standard observables, promises, or custom implementations. This placeholder is particularly useful for abstraction and uniform handling of different observable sources in a consistent manner.

Default Behavior

By default, api.any is set to undefined. This means that if it is not explicitly assigned a function or object, it will not provide any observable functionality. You need to assign it a specific function or observable to make it work.

Usage

You can customize api.any to handle your specific observable implementations. For example, you might set api.any to a function that processes different observable types or provides default behavior for handling observable subscriptions and notifications.

  • api.effect(f)

Sets up the effect execution method. This function is where you define how to apply effects in your reactive library (e.g., createEffect in Solid.js, effect in Preact Signals).

  • api.is(v)

Defines how to check if a value is a signal or observable. This is where you identify reactive signals from your library (e.g., checking if a value is an instance of Signal).

  • api.get(v)

Specifies how to retrieve the current value from a signal or observable. This function is where you define how to extract the current value from your reactive signal (e.g., v?.value or v?.()).

  • api.cleanup()

Provides a function to handle cleanup logic. This can be used to define any custom cleanup behavior required when a subscription is no longer needed.

Example API Customization

Any source

export let v =
	(v, cb = []) =>
	(c) =>
		c === void 0
			? v
			: c.call
			? cb.splice.bind(cb, cb.push(c) - 1, 1, 0)
			: cb.map((f) => f && f((v = c)));

api.any = (target) => (next, error, complete) => target?.((v) => next(v));

const num = v(42);
let off = sub(num)(console.log);
num(20);
num(3);
off();
num(30);

Solidjs

const { createSignal, createEffect, cleanup } = require("solid-js");

api.effect = createEffect;
api.is = (v) => v?.name?.includes("readSignal");
api.get = (v) => v?.();
api.cleanup = cleanup; //optional

const [val, setVal] = createSignal(0);

sub(val)(console.log);
setVal(10);
setVal(20);

Preact Signals

const { effect, signal, Signal } = require("@preact/signals-core");

api.effect = effect;
api.is = (v) => v instanceof Signal;
api.get = (v) => v?.value;

const val = signal(0);

const stop = sub(val)((v) => {
	console.log(v);
});

val.value = 10;
val.value = 20;
stop();
val.value = 30;

USignal

const { effect, signal, Signal } = require("usignal");

api.effect = effect;
api.is = (v) => v instanceof Signal;
api.get = (v) => v?.value;

const val = signal(0);

const stop = sub(val)((v) => {
	console.log(v);
});

val.value = 10;
val.value = 20;
stop();
val.value = 30;

@webreflection/signal

const { effect, signal, Signal } = require("@webreflection/signal");

api.effect = effect;
api.is = (v) => v instanceof Signal;
api.get = (v) => v?.value;

const val = signal(0);

const stop = sub(val)((v) => {
	console.log(v);
});

val.value = 10;
val.value = 20;
stop();
val.value = 30;

ULive

const { effect, signal } = require("ulive");

api.effect = effect;
api.is = (v) => v?.peek;
api.get = (v) => v?.value;

const val = signal(0);

const stop = sub(val)((v) => {
	console.log(v);
});

val.value = 10;
val.value = 20;
stop();
val.value = 30;

RxJS

const { Subject } = require("rxjs");
const subject = new Subject();

let arr = [];
const unsub = sub(subject)(
	(v) => arr.push(v),
	(err) => arr.push(err),
	() => arr.push("end")
);

subject.next(1);
subject.next(2);
subject.complete();
console.log(arr);

unsub();

Async Iterable

const asyncIterable = {
	[Symbol.asyncIterator]() {
		return {
			i: 0,
			next() {
				if (this.i < 5)
					return new Promise((ok) =>
						setTimeout(() => ok({ value: this.i++, done: false }), 10)
					);
				return new Promise((ok) => ok({ done: true }));
			},
		};
	},
};

sub(asyncIterable)(console.log, console.error, () => console.log("end"));

Finalization and Cleanup

The library uses FinalizationRegistry to automatically clean up subscriptions when objects are garbage collected. This helps prevent memory leaks by ensuring that subscriptions are properly terminated when no longer needed.

License

This library is provided "as-is" under the MIT license. Feel free to use, modify, and distribute it in your projects.

Thanks and Inspiration