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

@thi.ng/interceptors

v3.2.121

Published

Interceptor based event bus, side effect & immutable state handling

Readme

@thi.ng/interceptors

npm version npm downloads Mastodon Follow

[!NOTE] This is one of 214 standalone projects, maintained as part of the @thi.ng/umbrella ecosystem and anti-framework.

🚀 Please help me to work full-time on these projects by sponsoring me. Thank you! ❤️

Update 12/2022: This package is considered completed and no longer being updated with new features. Please consider using @thi.ng/rstream instead...

About

Interceptor based event bus, side effect & immutable state handling.

Event bus, interceptors, side effects

Interceptors: Event and Effect primitives

Reference

The idea of interceptors is quite similar to functional composition and AOP (aspect oriented programming). You want to reuse some functionality across components within your app. For example, if you have multiple actions which should be undoable, you can compose your main event handlers with the snapShot() interceptor, which requires a @thi.ng/atom/History-like instance and records a snapshot of the current app state, but else is completely invisible.

[UNDOABLE_EVENT]: [snapshot(), valueSetter("foo")]

Event Handlers

The idea of event handlers is being responsible to assign parameters to side effects, rather than executing effects themselves, is again mainly to do with the DRY-principle, instrumentation potential and performance. Most composed event handler chains are setup so that your "actual" main handler is last in line in the pre processing phase. If e.g. your event handlers would directly update the state atom, then any attached watches (derived views, cursors, other subscriptions) would be re-run each time. By assigning the updated state to, e.g., an FX_STATE event, we can avoid these interim updates and only apply the new state once all events in the current frame have been processed. Furthermore, a post interceptor might cancel the event due to validation errors etc.

Events vs Effects:

To briefly summarize the differences between event handlers & effects:

Event handlers are triggered by events, but each event handler is technically a chain of interceptors (even though many are just a single item). Even if you just specify a single function, it's internally translated into an array of interceptor objects like:

valueSetter("route") -> [{ pre: (...) => {[FX_STATE]: ...}, post: undefined }]

When processing an event, these interceptors are then executed first in ascending order for any pre functions and then backwards again for any post functions (only if there are any in the chain). So if you had defined an handler with this chain: [{pre: f1, post: f2}, {pre: f3}, {pre: f4, post: f5}], then the functions would be called in this order: f1, f3, f4, f5, f2. The post phase is largely intended for state/effect validation & logging post-update. I.e., interceptors commonly need pre only.

Like with trace() some interceptors DO have side effects, but they're really the exception to the rule. For example, snapshot() is idempotent since it only records a new snapshot if it's different from the last and trace(), but is typically used during development only - its side effect is outside the scope of your app (i.e. the console).

Great, but why?

In most apps there're far more event types/handlers than possible actions any component can take. So assigning them to registered side effects enables better code reuse. Another use-case is debugging. With a break point set at the beginning of processEffects() (in event-bus.ts) you can see exactly which side effects have occurred at each frame. This can be very helpful for debugging and avoid having to "keep everything in your head" or - as Rich Hickey would say - make your app "Easier to reason about".

More comprehensive description forthcoming. Please check the detailed commented source code and examples for now:

Status

COMPLETED - no further development planned

Search or submit any issues for this package

Related packages

  • @thi.ng/atom - Mutable wrappers for nested immutable values with optional undo/redo history and transaction support
  • @thi.ng/hdom - Lightweight vanilla ES6 UI component trees with customizable branch-local behaviors
  • @thi.ng/rdom - Lightweight, reactive, VDOM-less UI/DOM components with async lifecycle and @thi.ng/hiccup compatible

Installation

yarn add @thi.ng/interceptors

ESM import:

import * as iceps from "@thi.ng/interceptors";

Browser ESM import:

<script type="module" src="https://esm.run/@thi.ng/interceptors"></script>

JSDelivr documentation

Package sizes (brotli'd, pre-treeshake): ESM: 2.13 KB

Dependencies

Note: @thi.ng/api is in most cases a type-only import (not used at runtime)

Usage examples

Eight projects in this repo's /examples directory are using this package:

| Screenshot | Description | Live demo | Source | |:---------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------|:----------------------------------------------------------|:-----------------------------------------------------------------------------------------------| | | Minimal demo using interceptors with an async side effect | Demo | Source | | | Custom dropdown UI component for hdom | Demo | Source | | | Custom dropdown UI component w/ fuzzy search | Demo | Source | | | Event handling w/ interceptors and side effects | Demo | Source | | | Event handling w/ interceptors and side effects | Demo | Source | | | Complete mini SPA app w/ router & async content loading | Demo | Source | | | Interactive grid generator, SVG generation & export, undo/redo support | Demo | Source | | | Additive waveform synthesis & SVG visualization with undo/redo | Demo | Source |

API

Generated API docs

TODO

Authors

If this project contributes to an academic publication, please cite it as:

@misc{thing-interceptors,
  title = "@thi.ng/interceptors",
  author = "Karsten Schmidt and others",
  note = "https://thi.ng/interceptors",
  year = 2016
}

License

© 2016 - 2026 Karsten Schmidt // Apache License 2.0