惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
C
Cisco Blogs
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
L
LINUX DO - 热门话题
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Security Archives - TechRepublic
Security Archives - TechRepublic
TaoSecurity Blog
TaoSecurity Blog
V2EX - 技术
V2EX - 技术
H
Heimdal Security Blog
S
Security Affairs
L
Lohrmann on Cybersecurity
Hacker News - Newest:
Hacker News - Newest: "LLM"
Simon Willison's Weblog
Simon Willison's Weblog
WordPress大学
WordPress大学
小众软件
小众软件
Security Latest
Security Latest
AWS News Blog
AWS News Blog
Apple Machine Learning Research
Apple Machine Learning Research
GbyAI
GbyAI
Engineering at Meta
Engineering at Meta
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
F
Full Disclosure
S
Schneier on Security
L
LangChain Blog
MyScale Blog
MyScale Blog
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
Google Online Security Blog
Google Online Security Blog
Scott Helme
Scott Helme
Stack Overflow Blog
Stack Overflow Blog
爱范儿
爱范儿
A
Arctic Wolf
Martin Fowler
Martin Fowler
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
The Register - Security
The Register - Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园_首页
Latest news
Latest news
F
Fortinet All Blogs
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
Hacker News: Ask HN
Hacker News: Ask HN

Echo JS

Desktop apps Introducing mermaid-lint: Stop Shipping Broken Diagrams How We Cut Slow Responses by 80% Migrating to Next.js App Router The quiet problem with unnecessary async - Matt Smith GitHub - ant-design/ant-design-cli: Ant Design on your command line. Query component knowledge, analyze project usage, and guide migrations — fully offline. Uncovering the Magic Behind Playwright React Performance Isn’t About useMemo — It’s About Render Boundaries T2 No Escape Hatches · Prickles GitHub - auto-agent-protocol/auto-agent-protocol: Automotive Agent Protocol GitHub - coactionjs/coaction: Zustand-style state management with built-in render tracking and cached computed state GitHub - williamtroup/Rattribute.js: ❓ A lightweight JavaScript library for automatically changing HTML element attributes based on responsive screen sizes. GitHub - williamtroup/Rink.js: 🔗 A JavaScript library for generating responsive HTML link targets. SVAR Kanban: Interactive Task Board Component for Svelte, React, and Vue Performance Cost of Popular 3rd Party Scripts date-light - Tiny date utilities for JavaScript When React Hooks Stop Scaling: Moving Complex State to Zustand - Oren Farhi Out Loud — Free AI Text to Speech Pocket DB — Embedded single-file NoSQL for Node.js billboard.js 4.0 release: Canvas rendering mode, 94.3% faster! GitHub - thegruber/linkpeek: Secure TypeScript link preview and URL metadata extractor for Open Graph, Twitter Cards, JSON-LD, Node/Bun/Deno/edge. GitHub - evoluteur/healing-frequencies: Play the healing frequencies of various sets of tuning forks: Solfeggio, Organs, Mineral nutrients, Ohm, Chakras, Cosmic octave, Otto, DNA nucleotides... or custom. Animated sine waves - 27 lines of pure JavaScript Framework | Neutralinojs GitHub - AllThingsSmitty/typescript-tips-everyone-should-know: ✅ A curated collection of practical TypeScript patterns that improve safety, readability, maintainability, and developer experience. 🧠 Heat.js : JavaScript Heat Map GitHub - iDev-Games/State-JS: State.js is a CSS‑reactive framework that makes UI state and updates flow through CSS instead of JavaScript logic. GitHub - yankouskia/gameplate: :video_game: Boilerplate for creating game with WebGL & Redux :game_die: GitHub - yankouskia/get-browser: 💻 Lightweight tool to identify the browser (mobile+desktop detection)📱 GitHub - yankouskia/is-incognito-mode: Identify whether browser is in incognito mode 👀
GitHub - jskits/loggerjs: A faster, more powerful isomorphic logger
2026-07-09 · via Echo JS

LoggerJS is a monorepo of logging packages around a dependency-free, platform-neutral core. The same logger code runs in Node, browsers, workers, and edge runtimes. It is organized around three user-facing concepts plus one boundary rule:

  • Integrations collect logs automatically from platform behavior — browser console calls, script errors, fetch/XHR failures, Web Vitals, route changes, Node process crashes, HTTP servers, serverless handlers, queue and database clients. All opt-in.
  • Middleware / processors synchronously enrich, redact, sample, dedupe, rate-limit, fingerprint, buffer (fingers-crossed), route, and tag logs before delivery. Middleware run on raw records; processors run on projected events.
  • Transports deliver logs anywhere — console, pretty DevTools/terminal output, stdout, files, HTTP, IndexedDB, WebSocket, service workers, worker threads, OTLP, Sentry, Datadog, Elasticsearch, Loki, CloudWatch, SQL databases — with reusable batching, retry, backoff, and circuit-breaker wrappers where the destination needs them.
  • Codecs belong to transports. The pipeline keeps values raw; each destination owns its serialization. Built-in codecs are fast by default and never lose a log to an encoding error.

🌐 Truly isomorphic
Zero-dependency core, zero platform APIs, a type surface that compiles without DOM libs. One logger for Node, browsers, workers, and edge.

🎣 Automatic collection, first-class
35 opt-in integrations — 19 browser/frontend and 16 Node.js/server — turn platform behavior into structured logs.

Performance with receipts
Disabled levels cost ~3ns (pino parity). On the M1 Max reference machine, lean NDJSON runs at ~1.19× pino throughput and the prepared encoder at ~1.28× — faster than pino for equivalent output — measured with a drift-canceling A/B harness, checked into the benchmark matrix, and CI-gated. Ranking vs pino is CPU/V8-dependent.

🛟 Logs survive bad days
Crash-path flushSync, beacon on page close, offline replay, batch retry with circuit breakers, codecs that fall back instead of throwing.

🧩 Composable pipeline
27 runtime-neutral middleware and processors enrich, redact, sample, dedupe, rate-limit, fingerprint, route, and buffer — on raw records or projected events.

📚 Library-author friendly
getLogger(["my-lib"]) is a silent no-op until the host app calls configure() — log from libraries without forcing a dependency on users.

Table of Contents

  • Install
  • AI Skill
  • Quick Start
    • Node
    • Browser
    • Library authors
  • How It Works
  • Typed Events
  • Context Propagation
  • Performance
  • Packages
  • The Ecosystem
  • How It Compares
  • Documentation
  • Development
  • License

Install

Pick the package for your platform. Each platform package re-exports all of @loggerjs/core, so one install per app is enough to start.

# Node services
npm install @loggerjs/node @loggerjs/processors

# Browser apps
npm install @loggerjs/browser @loggerjs/processors

Using pnpm or yarn? Swap npm install for pnpm add / yarn add. Add vendor packages (@loggerjs/otel, @loggerjs/sentry, @loggerjs/datadog, …) only when you deliver to that destination.

All packages ship ESM + CJS with full TypeScript declarations and granular subpath exports (@loggerjs/node/transport-stdout, @loggerjs/browser/integration-console, …) so bundlers tree-shake to exactly what you import.

AI Skill

LoggerJS includes an installable AI skill for coding agents. Use it when you want an agent to add LoggerJS to a JavaScript or TypeScript app, choose the right runtime packages, configure production logging, or migrate from console, pino, winston, loglevel, debug, or an existing wrapper.

# Install the LoggerJS skill
npx skills add jskits/loggerjs --skill loggerjs

# Install it for a specific agent
npx skills add jskits/loggerjs --skill loggerjs --agent codex
npx skills add jskits/loggerjs --skill loggerjs --agent claude-code

# Use it once without installing
npx skills use jskits/loggerjs --skill loggerjs

Then ask your agent:

Use $loggerjs to add production-ready structured logging to this Node API.

The skill lives in skills/loggerjs, and the full usage guide is in docs/AI-SKILL.md.

Quick Start

Node

import {
  captureProcessIntegration,
  createLogger,
  stdoutTransport,
} from "@loggerjs/node";
import { redactProcessor } from "@loggerjs/processors";

const logger = createLogger({
  category: ["api"],
  level: "info",
  tags: { service: "checkout", env: process.env.NODE_ENV ?? "dev" },
  processors: [redactProcessor({ keys: ["password", /token/i] })],
  transports: [stdoutTransport()],
  integrations: [captureProcessIntegration()],
});

logger.info("order created", { orderId: "ord_123" });
logger.error(new Error("card declined"), "payment failed", {
  orderId: "ord_123",
});

await logger.flush();

stdoutTransport() writes one NDJSON line per log; captureProcessIntegration() turns uncaught exceptions, unhandled rejections, and process warnings into structured events automatically.

Browser

import {
  browserHttpTransport,
  captureBrowserErrorsIntegration,
  captureConsoleIntegration,
  captureFetchIntegration,
  createLogger,
  memoryBrowserHttpOfflineQueue,
  pageLifecycleIntegration,
} from "@loggerjs/browser";
import { redactProcessor } from "@loggerjs/processors";

const logger = createLogger({
  category: ["web"],
  level: "info",
  processors: [redactProcessor()],
  transports: [
    browserHttpTransport({
      url: "/api/logs",
      offlineQueue: memoryBrowserHttpOfflineQueue({ maxEntries: 500 }),
      useBeaconOnPageHide: true,
    }),
  ],
  integrations: [
    captureConsoleIntegration({ levels: ["warn", "error"] }),
    captureBrowserErrorsIntegration(),
    captureFetchIntegration(),
    pageLifecycleIntegration(),
  ],
});

logger.info("page loaded");

Logs batch over HTTP, queue while offline, replay with backoff when the network returns, and attempt a best-effort sendBeacon flush when the tab closes.

Library authors

// inside your library — silent until the app configures it
import { getLogger } from "@loggerjs/core";

const logger = getLogger(["my-lib", "client"]);
logger.debug("handshake started"); // no-op until configure() runs

// inside the application
import { configure } from "@loggerjs/core";
import { stdoutTransport } from "@loggerjs/node";

await configure({
  transports: { stdout: stdoutTransport() },
  loggers: [{ category: ["my-lib"], level: "warn", transports: ["stdout"] }],
});

How It Works

Every log flows through one pipeline. The hot path is engineered to do as little as possible until a value is actually needed.

  logger.info("order created", { orderId })
        │
        ▼  level gate ──── disabled levels stop here (~5ns, no allocation)
        │
  ┌─────────────┐   lazy message · raw error · shared ctx/tags · no id yet
  │  LogRecord  │
  └──────┬──────┘
         │
   middleware ───────── sync · ordered · enrich / redact / drop on raw records
         │
   any processors? ──no──▶  record fast path — straight to transports, no projection
         │ yes
         ▼
  ┌─────────────┐   id assigned · message resolved · error normalized
  │  LogEvent   │
  └──────┬──────┘
         │
   processors ───────── sample · dedupe · fingerprint · route · fingers-crossed
         │
         ▼
   transports ───────── console · stdout · file · http · indexeddb · otlp · sentry · …
         │               shared batching · retry · backoff · circuit breaker
         ▼
   codec ────────────── each destination owns its serialization (fast, never throws)

LogRecord is the hot-path shape — it keeps the message function unevaluated, the error raw, and context/tags shared by reference. A logger with zero processors sends records straight to transports (the record fast path, no event projection). Adding any processor opts that logger into LogEvent projection so processors can see the resolved shape. See CONCEPTS.md for the full model.

Typed Events

Define an event once and log it with a checked payload — message and tags derive from the data.

import { defineEvent } from "@loggerjs/core";

const CheckoutCompleted = defineEvent<{ orderId: string; amountCents: number }>(
  {
    type: "checkout.completed",
    message: (event) => `checkout completed ${event.orderId}`,
    tags: { domain: "checkout" },
  },
);

logger.event(CheckoutCompleted, { orderId: "ord_123", amountCents: 4999 });

Context Propagation

Bind fields once and have them follow async execution across every await — no manual threading.

import { withContext } from "@loggerjs/core";
import { installAsyncLocalStorageContext } from "@loggerjs/node";

installAsyncLocalStorageContext(); // once at startup

await withContext({ requestId: "req_123" }, async () => {
  logger.info("request started"); // carries { requestId } across awaits
});

Performance

Reference machine: Apple M1 Max (64 GB), Node v22.21.1, against pino 10.3.1 / winston 3.19.0 / LogTape 2.1.3. The loggerjs-vs-pino rows use the drift-canceling paired A/B harness (BENCH_AB, 22 runs); competitor rows are the sequential suite. Full methodology and the regression gate live in docs/BENCHMARKS.md, with checked-in machine evidence in docs/BENCHMARK-MATRIX.md.

Logger / path ns/op Relative
loggerjs — disabled level (lazy message) 3 parity with pino (9)
loggerjs — prepared lean NDJSON 224 1.28× pino (faster)
loggerjs — lean NDJSON, comparable line 242 1.19× pino (faster)
pino — NDJSON noop sink 287 1.00× baseline
loggerjs — full envelope (+id/seq/levelName) 307 ~0.9× pino, 3 extra fields/line
loggerjs — batch transport enqueue 172
Node console — noop stream 769 loggerjs ~3× faster
winston — JSON noop sink 2,726 loggerjs ~11× faster
LogTape — JSON lines noop sink 6,584 loggerjs ~27× faster

The hot path is deliberate: level gating before any allocation, lazy message resolution, frozen shared tags, memoized ids, a record fast path that skips event projection, and fragment-cached serialization — all guarded by pnpm bench:gate in CI. On the M1 Max reference, loggerjs lean and prepared edge out pino in paired A/B runs, but the ranking is CPU/V8-dependent; reproduce it on your own machine with BENCH_AB=1 pnpm bench:node and add broader evidence through the benchmark matrix. LoggerJS keeps one record per log so middleware, integrations, and multiple transports can observe it, and reaches pino's class without giving that pipeline up — see the architecture note.

Packages

Package Contents
@loggerjs/core Logger, record/event model, registry, context, middleware kernel, integration API, console/memory/test/batch transports, json/safe-json/ndjson codecs. Zero dependencies.
@loggerjs/browser HTTP / IndexedDB / WebSocket / service-worker / broadcast-channel transports, offline queues, ZIP export, 19 browser integrations
@loggerjs/node stdout / stderr / file / rotating-file / HTTP / syslog / worker transports, AsyncLocalStorage context, 16 Node integrations
@loggerjs/pretty Browser DevTools and Node terminal pretty output: styled console transport, ANSI stdout/stderr transports, and shared formatter
@loggerjs/processors redact, privacy-guard, sample, dynamic-sampler, rate-limit, dedupe, fingerprint, filter, route, level-override, normalize-error, stack-parser, enrich, tags, trace, fingers-crossed, breadcrumbs, schema-dev-check
@loggerjs/codecs fast-event-json (the performance codec), msgpackr, projector
@loggerjs/otel OTLP JSON mapping, OTLP/HTTP transport, OpenTelemetry log bridge, active-span trace processor
@loggerjs/sentry Sentry structured logs, breadcrumbs, exception/message capture
@loggerjs/datadog Datadog Logs intake transport
@loggerjs/elastic Elasticsearch bulk API transport
@loggerjs/loki Grafana Loki push transport
@loggerjs/cloudwatch CloudWatch Logs transport with built-in SigV4 signing
@loggerjs/database SQLite / Postgres / custom-adapter batch transports

Vendor HTTP transports speak wire protocols directly; SDK/provider adapters use the SDK object or provider your app already owns. No vendor SDKs are bundled.

The Ecosystem

Runtime support at a glance — what runs in browser, Node.js, or both
Capability family Browser / frontend Node.js / server Runtime-neutral
Integrations 19 first-party browser collectors 16 first-party Node.js collectors Core exposes the integration API; automatic capture lives in platform packages.
Transports HTTP, IndexedDB, WebSocket, service worker, BroadcastChannel, offline-first, pretty DevTools console stdout/stderr, files, rotation, HTTP, syslog, worker threads, database-backed transports, pretty terminal stdout/stderr console, memory, test, batch/retry/fallback wrappers; vendor HTTP transports can run where their credentials and fetch/runtime requirements are safe.
Processors / middleware All 27 supported All 27 supported @loggerjs/processors has no browser or Node.js platform dependency; only routed transport targets are runtime-specific.

See docs/INTEGRATIONS.md, docs/TRANSPORTS.md, and docs/PROCESSORS.md for the full support notes.

25+ transports — core/browser/Node.js/pretty/vendor destinations plus reusable reliability wrappers

Core / runtime-neutral (@loggerjs/core) — consoleTransport · memoryTransport · testTransport, plus reliability wrappers batchTransport · retryTransport · fallbackTransport

Pretty developer UX (@loggerjs/pretty) — prettyConsoleTransport for browser DevTools or local consoles · prettyStdoutTransport / prettyStderrTransport for Node terminals · formatPrettyEvent for custom display sinks

Node.js / server (@loggerjs/node) — stdoutTransport · stderrTransport · fileTransport · rotatingFileTransport · nodeHttpTransport · nodeSyslogTransport · workerTransport

Browser / frontend (@loggerjs/browser) — browserHttpTransport · indexedDbTransport · browserWebSocketTransport · browserServiceWorkerTransport · browserBroadcastChannelTransport · offlineFirstTransport

For frontend support bundles, indexedDbTransport() is session-aware by default and can add a bounded localStorage spill for the async IndexedDB write tail:

import { createLogger } from "@loggerjs/core";
import { indexedDbTransport } from "@loggerjs/browser/transport-indexeddb";
import { downloadBlob, exportLogsToZip } from "@loggerjs/browser/export-zip";
import { privacyGuardProcessor, redactProcessor } from "@loggerjs/processors";

const supportStore = indexedDbTransport({
  dbName: "my-app-support-logs",
  localStorageSpill: { namespace: "my-app-support-logs" },
});

const logger = createLogger({
  category: ["web"],
  processors: [redactProcessor(), privacyGuardProcessor()],
  transports: [supportStore],
});

export async function downloadSupportLogs() {
  await logger.flush();
  downloadBlob(
    await exportLogsToZip(supportStore, {
      groupBySession: true,
      includeRecent: true,
      query: { order: "asc" },
    }),
    "support-logs.zip",
  );
}

Observability & vendorsotlpHttpTransport · openTelemetryLogBridgeTransport · sentryTransport · datadogLogsTransport · elasticTransport · lokiTransport · cloudWatchLogsTransport. HTTP wire transports depend on fetch/crypto and credential placement; SDK/provider adapters use the SDK object or provider your app already owns.

Databases / local app / backenddatabaseTransport · postgresTransport · sqliteTransport. These require application-provided database drivers and are intended for Node.js, Electron, CLIs, or backend workers.

See docs/TRANSPORTS.md for options and how to write your own.

35 integrations — 19 browser/frontend + 16 Node.js/server automatic collectors

Browser (19) — Console & errors: captureConsoleIntegration · captureBrowserErrorsIntegration · captureFrameworkErrorsIntegration · captureReportingIntegration Network: captureFetchIntegration · captureXHRIntegration · captureWebSocketIntegration Performance: captureWebVitalsIntegration · capturePerformanceIntegration Navigation: captureRouterIntegration · nextRouterIntegration · reactRouterIntegration · vueRouterIntegration · nuxtRouterIntegration Lifecycle & context: pageLifecycleIntegration · captureUserActionsIntegration · captureServiceWorkerIntegration · captureRuntimeHostIntegration · browserContextPropagationIntegration

Node (16) — Process & runtime: captureProcessIntegration · captureCliIntegration · diagnosticsChannelIntegration · serverlessIntegration HTTP frameworks: expressIntegration · fastifyIntegration · koaIntegration · hapiIntegration · nestMiddlewareIntegration Clients: nodeFetchIntegration · nodeHttpClientIntegration · redisIntegration · prismaIntegration · databaseIntegration Queues: queueIntegration · bullMqIntegration

Every integration uses re-entrancy guards and an unpatched-original registry so capture never loops. See docs/INTEGRATIONS.md for the integration API and how to write your own.

27 processors & middleware — runtime-neutral, synchronous, error-isolated, composable

Redaction & privacy: redactProcessor · privacyGuardProcessor Sampling & volume: sampleProcessor · dynamicSamplerProcessor · rateLimitProcessor · dedupeProcessor · coalesceProcessor Enrichment & tagging: enrichProcessor / enrichMiddleware · tagsProcessor / tagsMiddleware · typeProcessor / typeMiddleware · contextProcessor / contextMiddleware · traceContextProcessor / traceContextMiddleware Errors: normalizeErrorProcessor · fingerprintProcessor · stackParserProcessor · symbolicateStackProcessor Routing & control: routeProcessor · filterProcessor · levelOverrideProcessor Buffering: fingersCrossedProcessor · breadcrumbBufferProcessor Development: schemaDevCheckProcessor

Middleware run on raw records before id/message/error work; processors run on projected events. The processor package is platform-neutral and works in browser, Node.js, workers, and edge runtimes; only route/fingers-crossed targets depend on transports available in that runtime. See docs/PROCESSORS.md for ordering guidance.

8 codecs — serialization owned by the transport, fast by default, never throws

jsonCodec · safeJsonCodec · ndjsonCodec · metricsCodec (core) — fastEventJsonCodec (the performance codec) · pinoCompatCodec · msgpackrCodec · projectorCodec (@loggerjs/codecs).

Codecs fall back to a safe representation on circular references instead of throwing, and increment a codec.fallback meta counter so silent degradation is observable. See docs/CODECS.md.

How It Compares

LoggerJS shines when the logging problem spans browser and server collection from one mental model — and when you want logs delivered to your own destinations (HTTP, files, your DB, Loki/Elasticsearch, OTLP) rather than a single vendor's SaaS, from a zero-dependency core that runs under strict CSP, on edge/Workers, and offline. A fair, repo-sourced snapshot (full matrix and sources in docs/COMPARISON.md):

Capability LoggerJS Pino Winston LogTape
Isomorphic (browser + Node, one API) ⚠️ ⚠️
Automatic collection (integrations) ✅ 19 browser / 16 Node ⚠️ ⚠️
Built-in batching / retry / offline ⚠️ ⚠️ ⚠️
Transport-owned codecs ⚠️ ⚠️ ⚠️
Library-safe (silent until configured) ⚠️ ⚠️
Direct Node JSON throughput ✅ 1.19× pino slower slower

On the direct Node JSON path loggerjs and pino are in the same class — on the M1 Max reference loggerjs lean is ~1.19× pino, while on other CPUs pino can lead (it's CPU/V8-dependent; reproduce with BENCH_AB; see the checked-in benchmark matrix). LoggerJS reaches that throughput while adding a record pipeline that works the same in the browser, captures automatically, and delivers reliably.

Documentation

Doc Contents
Getting Started Install, first loggers, levels, context, typed events, registry
Concepts The pipeline model: records, events, middleware, processors, transports, codecs
Transports Every built-in transport, batch reliability options, writing your own
Pretty Output Browser DevTools and Node terminal pretty output UX
Integrations All integrations, the integration API, writing your own
Processors The middleware/processor toolbox and ordering guidance
Codecs Serialization ownership, fast-by-default semantics, custom codecs
Performance Tuning guide: fast path, codec choice, batching
Operations Privacy defaults, offline queues, crash paths, delivery reliability
Production Recipes Browser HTTP/offline, Node stdout+OTLP, Loki/Datadog deployments
API Stability v1 stable API subset and pre-1.0 compatibility policy
Benchmarks Methodology, measured snapshot, regression gate, size budgets
Comparison How LoggerJS compares with Pino, Winston, LogTape, Bunyan, and lighter tools
Migration Coming from pino, winston, or console.log
AI Skill Install and use the LoggerJS AI skill with coding agents
Architecture The full design document and recorded decisions
Contributing Repo workflow, CI gates, engineering conventions
Release Versioning and publish workflow

Runnable examples live in examples/: Node basics, browser basics, OpenTelemetry, Sentry.

Development

Use Node >=22.13 for repository development and the full pnpm check gate. Published packages are smoke-tested as packed consumers on Node 20.19, 22, and 24; Node 20.19 is the runtime compatibility floor, not the repo toolchain floor.

pnpm install
pnpm check        # format, lint, typecheck, test, build, size budgets, API reports, pack checks
pnpm bench        # node + browser benchmarks
pnpm bench:gate   # performance regression gate (also runs in CI)

See docs/CONTRIBUTING.md for conventions and the rules CI enforces.

License

MIT © JS Kits