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

@kdinisv/coflight

v0.3.0

Published

Tiny TypeScript library for deduplicating concurrent async calls by key. One real request, many awaiters, zero duplicate work.

Downloads

72

Readme

coflight

Tiny TypeScript library for deduplicating concurrent async calls by key. One real request, many awaiters, zero duplicate work.

English | Русский


Install

npm install @kdinisv/coflight

What It Does

Use coflight when several parts of your app can ask for the same resource at the same time:

  • the first call starts the real work
  • later calls with the same key wait for the same promise
  • optional TTL lets you reuse the fresh result for a short window
  • optional stale storage lets you fall back to the last successful value

Typical cases:

  • API and database lookups
  • SSR and server loaders
  • expensive config or service-discovery fetches
  • cron or worker overlap protection

Quick Start

import { createCoflight } from "@kdinisv/coflight";

interface User {
  id: string;
  name: string;
}

const users = createCoflight<string, User>();

export async function getUser(id: string, signal?: AbortSignal): Promise<User> {
  return users.run(
    `user:${id}`,
    ({ signal }) => fetch(`/api/users/${id}`, { signal }).then((r) => r.json()),
    { signal, timeout: 3_000, ttl: 5_000 },
  );
}

Result Sources

runDetailed() and refreshDetailed() return both the value and where it came from:

  • fresh — this call started the real operation
  • shared — this call joined an in-flight operation
  • cache — the value came from the TTL cache
  • stale — the value came from stale storage

API

createCoflight<K, V>(options?)

Creates an isolated coalescing group.

  • staleTtl?: number — how long to keep stale values in ms. Omit to keep them until replaced or forgotten. Set 0 to disable stale storage.
  • maxStaleEntries?: number — maximum number of stale entries to retain. Omit for no limit. Set 0 to disable stale storage.
const group = createCoflight<string, User>({
  staleTtl: 60_000,
  maxStaleEntries: 500,
});

group.run(key, fn, options?)

Runs fn for key, or joins an existing in-flight call with the same key.

const value = await group.run(
  "user:42",
  ({ signal }) => loadUser("42", signal),
  {
    timeout: 2_000,
    ttl: 5_000,
  },
);

Options:

  • signal?: AbortSignal — per-caller cancellation
  • timeout?: number — per-caller timeout in ms
  • ttl?: number — cache successful result for this many ms
  • staleIfError?: boolean — return the last successful value if the fresh call fails
  • swr?: boolean — return stale immediately and refresh in the background

group.runDetailed(key, fn, options?)

Same behavior as run, but returns { value, source }.

const result = await group.runDetailed("user:42", ({ signal }) =>
  loadUser("42", signal),
);

console.log(result.source);

group.warm(key, value, options?)

Seeds a value before traffic arrives.

  • ttl?: number — add the value to TTL cache
  • stale?: boolean — also seed stale storage, defaults to true
group.warm("user:42", cachedUser, { ttl: 2_000 });

Returns true if something was stored.

group.refresh(key, fn, options?)

Bypasses the TTL cache and forces a fresh execution. If the key is already running, the caller joins that in-flight work instead of starting a duplicate request.

await group.refresh("config:tenant-a", ({ signal }) =>
  loadConfig("tenant-a", signal),
);

group.refreshDetailed(key, fn, options?)

Same as refresh, but returns { value, source }.

group.forget(key)

Removes one key from tracked state.

group.forget("user:42");

group.clear()

Clears all tracked keys, cache entries, and stale values.

group.isRunning(key)

Returns true while a key is in flight.

group.stats()

Returns live counters and cumulative usage stats:

{
  inflight,
  cached,
  stale,
  requests,
  freshRuns,
  sharedRuns,
  cacheHits,
  staleHits,
  warmups,
  aborts,
  timeouts,
  swrHits,
  backgroundRefreshes,
  backgroundRefreshFailures,
}

group.drain()

Stops accepting new work and waits for all in-flight operations, including SWR background refreshes, to finish.

Use this for graceful shutdown when you want existing work to complete.

group.shutdown()

Aborts all in-flight operations and clears stored state immediately.

Use this when the process must stop now.

Patterns

TTL Cache

await group.run("article:home", fetchHomepage, { ttl: 10_000 });

Use when a short burst of repeated reads should reuse a fresh result.

Stale On Error

await group.run("flags", loadFlags, { staleIfError: true });

Use when serving the last good value is better than failing the request.

Stale While Revalidate

await group.run("service:billing", lookupService, { swr: true });

Use when fast responses matter more than always blocking on a refresh.

Graceful Stop

await group.drain();
group.shutdown();

Use drain() if you want current work to finish. Use shutdown() if you need to abort it.

Key Helpers

Available from both @kdinisv/coflight and @kdinisv/coflight/keys.

composeKey(...segments)

Builds a key from one or more escaped segments.

import { composeKey } from "@kdinisv/coflight";

const key = composeKey("user", "tenant-a", "42");

createKeyFactory(prefix)

Creates a reusable prefixed key builder.

import { createKeyFactory } from "@kdinisv/coflight/keys";

const userKey = createKeyFactory("user");

userKey("42");
userKey.scoped("tenant-a", "42");

createScopedKeyFactory(prefix, ...scopes)

Creates a key builder with fixed runtime arity.

import { createScopedKeyFactory } from "@kdinisv/coflight/keys";

const configKey = createScopedKeyFactory("config", "tenantId", "env");

configKey("acme", "prod");

createKeyNamespace(schema)

Creates a typed namespace of scoped key builders.

import { createKeyNamespace } from "@kdinisv/coflight/keys";

const keys = createKeyNamespace({
  user: ["tenantId", "userId"],
  session: ["sessionId"],
});

keys.user("acme", "42");
keys.session("sess-abc");

Examples

See the full examples in:

License

MIT


coflight на русском

English | Русский

Компактная TypeScript-библиотека для дедупликации параллельных async-вызовов по ключу. Один реальный запрос, множество ожидающих, ноль дублирующей работы.


Установка

npm install @kdinisv/coflight

Что делает библиотека

Используйте coflight, когда несколько частей приложения могут одновременно запросить один и тот же ресурс:

  • первый вызов запускает реальную работу
  • следующие вызовы с тем же ключом ждут тот же promise
  • опциональный TTL позволяет короткое время переиспользовать свежий результат
  • опциональное stale-хранилище позволяет вернуть последнее успешное значение

Типичные сценарии:

  • API и запросы к БД
  • SSR и server loaders
  • дорогие config- или service-discovery запросы
  • защита от наложения cron и worker-задач

Быстрый старт

import { createCoflight } from "@kdinisv/coflight";

interface User {
  id: string;
  name: string;
}

const users = createCoflight<string, User>();

export async function getUser(id: string, signal?: AbortSignal): Promise<User> {
  return users.run(
    `user:${id}`,
    ({ signal }) => fetch(`/api/users/${id}`, { signal }).then((r) => r.json()),
    { signal, timeout: 3_000, ttl: 5_000 },
  );
}

Источник результата

runDetailed() и refreshDetailed() возвращают не только значение, но и источник:

  • fresh — этот вызов действительно запустил работу
  • shared — этот вызов присоединился к уже выполняющейся операции
  • cache — значение пришло из TTL-кеша
  • stale — значение пришло из stale-хранилища

API

createCoflight<K, V>(options?)

Создаёт изолированную группу дедупликации.

  • staleTtl?: number — сколько хранить stale-значения в мс. Если не указывать, они живут до замены или forget. Значение 0 отключает stale-хранилище.
  • maxStaleEntries?: number — максимум stale-записей. Если не указывать, лимита нет. Значение 0 отключает stale-хранилище.
const group = createCoflight<string, User>({
  staleTtl: 60_000,
  maxStaleEntries: 500,
});

group.run(key, fn, options?)

Запускает fn для key или присоединяет вызов к уже идущей операции с тем же ключом.

const value = await group.run(
  "user:42",
  ({ signal }) => loadUser("42", signal),
  {
    timeout: 2_000,
    ttl: 5_000,
  },
);

Опции:

  • signal?: AbortSignal — отмена только для конкретного вызова
  • timeout?: number — timeout для конкретного вызова в мс
  • ttl?: number — кешировать успешный результат на это число миллисекунд
  • staleIfError?: boolean — вернуть последнее успешное значение, если свежий вызов завершился ошибкой
  • swr?: boolean — сразу вернуть stale и обновить значение в фоне

group.runDetailed(key, fn, options?)

То же поведение, что и у run, но возвращает { value, source }.

const result = await group.runDetailed("user:42", ({ signal }) =>
  loadUser("42", signal),
);

console.log(result.source);

group.warm(key, value, options?)

Прогревает значение до прихода трафика.

  • ttl?: number — положить значение в TTL-кеш
  • stale?: boolean — также положить в stale-хранилище, по умолчанию true
group.warm("user:42", cachedUser, { ttl: 2_000 });

Возвращает true, если данные были записаны.

group.refresh(key, fn, options?)

Игнорирует TTL-кеш и принудительно запускает свежее выполнение. Если операция уже идёт, вызов присоединяется к ней, а не создаёт дубликат.

await group.refresh("config:tenant-a", ({ signal }) =>
  loadConfig("tenant-a", signal),
);

group.refreshDetailed(key, fn, options?)

То же, что и refresh, но возвращает { value, source }.

group.forget(key)

Удаляет один ключ из отслеживаемого состояния.

group.forget("user:42");

group.clear()

Очищает все ключи, кеш и stale-значения.

group.isRunning(key)

Возвращает true, если операция по ключу сейчас выполняется.

group.stats()

Возвращает текущие счётчики и накопленную статистику:

{
  inflight,
  cached,
  stale,
  requests,
  freshRuns,
  sharedRuns,
  cacheHits,
  staleHits,
  warmups,
  aborts,
  timeouts,
  swrHits,
  backgroundRefreshes,
  backgroundRefreshFailures,
}

group.drain()

Перестаёт принимать новую работу и ждёт завершения всех текущих операций, включая фоновые SWR-обновления.

Используйте для graceful shutdown, когда нужно дать текущей работе завершиться.

group.shutdown()

Сразу прерывает все активные операции и очищает сохранённое состояние.

Используйте, когда процесс нужно остановить немедленно.

Паттерны

TTL-кеш

await group.run("article:home", fetchHomepage, { ttl: 10_000 });

Подходит, когда серия повторных чтений должна короткое время использовать свежий результат.

Stale при ошибке

await group.run("flags", loadFlags, { staleIfError: true });

Подходит, когда лучше отдать последнее корректное значение, чем завалить запрос.

Stale While Revalidate

await group.run("service:billing", lookupService, { swr: true });

Подходит, когда важнее быстрый ответ, чем ожидание обновления на каждом запросе.

Аккуратная остановка

await group.drain();
group.shutdown();

Используйте drain(), если текущая работа должна завершиться. Используйте shutdown(), если её нужно прервать.

Helper'ы для ключей

Доступны из @kdinisv/coflight и @kdinisv/coflight/keys.

composeKey(...segments)

Собирает ключ из одного или нескольких сегментов с автоматическим экранированием.

import { composeKey } from "@kdinisv/coflight";

const key = composeKey("user", "tenant-a", "42");

createKeyFactory(prefix)

Создаёт переиспользуемый билдер ключей с фиксированным префиксом.

import { createKeyFactory } from "@kdinisv/coflight/keys";

const userKey = createKeyFactory("user");

userKey("42");
userKey.scoped("tenant-a", "42");

createScopedKeyFactory(prefix, ...scopes)

Создаёт билдер ключей с фиксированной runtime-арностью.

import { createScopedKeyFactory } from "@kdinisv/coflight/keys";

const configKey = createScopedKeyFactory("config", "tenantId", "env");

configKey("acme", "prod");

createKeyNamespace(schema)

Создаёт типизированное пространство имён с builder'ами ключей.

import { createKeyNamespace } from "@kdinisv/coflight/keys";

const keys = createKeyNamespace({
  user: ["tenantId", "userId"],
  session: ["sessionId"],
});

keys.user("acme", "42");
keys.session("sess-abc");

Примеры

Полные примеры есть в:

Лицензия

MIT