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

decorator-toolkit

v0.4.0

Published

Modern TC39 decorators for reducing repetitive code.

Downloads

68

Readme

Features

  • Built for modern TC39 decorators in TypeScript 5+
  • Covers sync and async method workflows
  • Includes caching, retry, timeout, debounce, throttling, delegation, rate limiting, lazy evaluation, and resource disposal

Installation

npm

$ npm install decorator-toolkit

# or using Bun
$ bun add decorator-toolkit

Usage

This package targets the standard TC39 decorator model. It is intended for TypeScript 5+ projects using standard decorators rather than legacy experimentalDecorators semantics.

Legacy TypeScript decorators are also available for projects that still use the older transform. Import them from decorator-toolkit/legacy or decorator-toolkit/<name>/legacy.

Compiler Setup

At minimum, use a modern TypeScript configuration that emits native class features and supports standard decorators:

{
	"compilerOptions": {
		"target": "ES2022",
		"module": "Node16",
		"moduleResolution": "Node16"
	}
}

[!NOTE] Method decorators in this package apply to methods only, bindAll applies to classes, and accessor decorators apply to accessor members only. Private members are not supported.

[!TIP] Decorators that use default behavior can be written as @decorator or @decorator(). This applies to bind, bindAll, cancelPrevious, cache, cacheAsync, delegate, dispose, execTime, lazy, readonly, and throttleAsync.

Basic Example

import {
	after,
	before,
	retry,
	timeout,
} from "decorator-toolkit";

class PaymentService {
	private readonly events: string[] = [];

	beforeSave(): void {
		this.events.push("before");
	}

	afterSave(params: { args: [string]; response: string; }): void {
		this.events.push(`after:${params.args[0]}:${params.response}`);
	}

	@before<PaymentService>({ func: "beforeSave" })
	@after<PaymentService, string, [string]>({ func: "afterSave", wait: true })
	@retry(3)
	@timeout(1_000)
	async save(id: string): Promise<string> {
		return `saved:${id}`;
	}
}

Caching And Rate Limiting

import {
	cache,
	rateLimit,
} from "decorator-toolkit";

class DirectoryService {
	@cache({ ttlMs: 5_000 })
	lookupUser(id: string): { id: string; name: string; } {
		return { id, name: `user:${id}` };
	}

	@rateLimit<DirectoryService, [string]>({
		allowedCalls: 10,
		timeSpanMs: 60_000,
		keyResolver: (userId) => userId,
	})
	openProfile(userId: string): string {
		return `/users/${userId}`;
	}
}

Accessor Decorators

readonly and refreshable are accessor decorators, so they must decorate accessor members. lazy decorates get accessors directly.

import {
	lazy,
	readonly,
	refreshable,
} from "decorator-toolkit";

class SessionStore {
	@readonly
	accessor id = crypto.randomUUID();

	@lazy
	get config(): object {
		return buildExpensiveConfig(); // computed once per instance
	}

	@refreshable<SessionStore, number>({
		dataProvider: "loadCounter",
		intervalMs: 5_000,
	})
	accessor counter: number | null = 0;

	async loadCounter(): Promise<number> {
		return Date.now();
	}
}

const store = new SessionStore();
store.counter = null;

Assigning null to a refreshable accessor stops future refresh cycles for that accessor.

Root And Subpath Imports

You can import from the root package:

import {
	delegate,
	timeout,
} from "decorator-toolkit";

Or import specific modules via subpaths:

import { cache } from "decorator-toolkit/cache";
import {
	timeout,
	TimeoutError,
} from "decorator-toolkit/timeout";

Legacy TypeScript decorators are available from the existing suffix subpaths, and decorator-toolkit/legacy re-exports the full legacy surface:

import { cache as legacyCache } from "decorator-toolkit/cache/legacy";
import {
	cache,
	timeout,
} from "decorator-toolkit/legacy";

Use the suffix path when you want one decorator only. Use the legacy barrel when you want several legacy decorators from a single import.

Documentation

Start with docs/README.md for grouped references and usage patterns. The decorator list below links to dedicated pages with current TC39 examples adapted from the legacy site.

Available Decorators

| Decorator | Purpose | | ---------------------------------------------------- | ------------------------------------------------------------------------ | | after | Runs a hook after a method call, optionally waiting for async resolution | | before | Runs a hook before a method call, optionally waiting for async hooks | | bind | Binds a method to its instance or class during initialization | | bindAll | Binds all public instance methods declared on a class | | cancelPrevious | Rejects the previous pending async invocation with CanceledPromise | | debounce | Coalesces rapid method calls into a later single execution | | delegate | Shares one in-flight async invocation across callers with the same key | | delay | Schedules method execution after a fixed delay | | dispose | Wires a method to Symbol.dispose or Symbol.asyncDispose | | execTime | Reports method execution duration | | cache | Caches synchronous method results | | cacheAsync | Caches async results and deduplicates pending async calls | | lazy | Computes a getter once per instance and caches the result | | multiDispatch | Starts multiple async attempts and resolves on the first success | | onError | Forwards thrown or rejected errors to a handler | | rateLimit | Limits how many calls may happen within a configured time window | | readonly | Makes an accessor write-protected | | refreshable | Refreshes an accessor from an async data provider on an interval | | retry | Retries async methods using a fixed or custom delay strategy | | throttle | Limits how often a method can run | | throttleAsync | Queues async calls and executes them with bounded concurrency | | timeout | Rejects slow async methods with TimeoutError |