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

@etamong-playground/feature-gate

v0.1.2

Published

Transport-agnostic feature gate primitives — release (monotonic time-window) and flag (operator-toggle) modes, with handler/validator extension points for detection pipelines.

Readme

@etamong-playground/feature-gate

About — One of several small shared libraries used across a personal "fleet" of small apps (error handling · audit logging · encryption-at-rest · i18n · UI · …). Authored and maintained with Claude Code (Anthropic's agentic CLI). Each README documents the design rationale behind the library.

This is a public repository — keep internal infrastructure details (hostnames, secret/Vault paths, private URLs, internal issue/MR references) out of code, comments, and commit messages.

A tiny, dependency-free toolkit for gating a code path behind two orthogonal decisions:

  • how it transitions (mode), and
  • what happens when reality drifts from the config (strict).

Most "feature flag" libraries collapse those into one knob. Keeping them separate is the whole point: a launch date and a kill-switch behave differently, and a compliance-sensitive gate needs louder drift detection than a beta cohort — but they all share one declaration model and one resolver.

Modes

| Mode | Transition | Use for | |---|---|---| | release | Monotonic time-window. Opens on effectiveDate and can never close. | Announced launches, ramps tied to a fixed date, policy effective dates. | | flag | Operator-toggled, bidirectional. Resolves from an override store, falling back to defaultOpen. | Kill-switches, beta cohorts, percentage rollouts. |

release is deliberately one-way: once users have seen a launched feature (or a policy has taken effect), silently retracting it is a data-integrity and trust problem. If you need to take something back, that's a flag.

Strict vs soft

Orthogonal to mode. A detection pipeline periodically validates declared gates against reality and produces GateSuspects:

  • strict: false — soft. Suspects are queued for operator review only. (a beta flag accidentally exposed to a non-beta user — investigate when convenient.)
  • strict: true — strict. Every registered handler fires for the suspect and returns routing metadata for downstream alerting. (a policy gate, a payment kill-switch, a security-relevant release.)

Transport agnosticity (the hard boundary)

No Slack/Discord/PagerDuty/webhook URL, channel name, bot token, or secret path appears in this library — or in any HandlerResponse field. Handlers return routing metadata only (appKey, tags, severity, ingestUrl, summary); whatever consumes them (an ops workflow, a cron, a CLI) resolves that metadata to an actual transport. The library never performs I/O for alerting and never learns where alerts go.

This keeps the library reusable (your transport, not mine) and keeps deployment topology out of a shared dependency.

Install

npm install @etamong-playground/feature-gate

Published to GitHub Packages under the @etamong-playground scope — point the scope at the registry in your .npmrc:

@etamong-playground:registry=https://npm.pkg.github.com

Quick example

import {
  declareGate,
  isGateOpen,
  registerStrictHandler,
} from "@etamong-playground/feature-gate";

// Boot: declare gates
declareGate({
  key: "checkout.new-flow",
  mode: "release", // opens automatically on the date; never closes
  strict: false,
  effectiveDate: "2026-07-15",
});

declareGate({
  key: "payments.kill-switch",
  mode: "flag", // operator-toggled, bidirectional
  strict: true,
  defaultOpen: true,
});

// Boot: attach a routing classifier — pure, returns metadata, performs no I/O
registerStrictHandler(async (suspect) => {
  if (suspect.gateKind !== "policy") return null; // opt out of suspects you don't own
  return {
    appKey: "billing",
    severity: "warn",
    tags: ["billing", "compliance"], // the workflow maps tags -> a channel
    summary: `gate ${suspect.gateKey} inconsistency: ${suspect.reason}`,
    // ingestUrl, if used, is supplied by the caller — never hardcoded here
  };
});

// Runtime: check a gate
if (await isGateOpen("checkout.new-flow")) {
  // ... show the new flow
}

Public surface

| Export | Purpose | |---|---| | declareGate(config) | Register a gate at boot (throws on a duplicate key) | | isGateOpen(key, ctx?) | Resolve open/closed, async (release: time-based; flag: override + default) | | releaseGateOpen(key, ctx?) | Synchronous open-check for release gates (e.g. a React render); throws for flag mode | | registerStrictHandler(fn) | Attach a routing classifier — pure, no side effects | | registerGateValidator(fn) | Attach a detection validator — returns GateSuspect[] | | runAllValidators() | Run every validator, concatenate results | | classify(suspects) | Map suspects through every handler | | listGates() / getGate(key) | Introspection (backoffice UIs) | | todayISO(tz?) | Calendar day YYYY-MM-DD in an IANA timezone (default Asia/Seoul) |

Design notes

  • Fails closed. isGateOpen returns false for an undeclared key, so an accidentally-deleted or misspelled gate disables the path rather than silently leaving it on.
  • Calendar days, not UTC. Release gates compare against the local calendar day (todayISO), because "launches on the 15th" means the 15th where the user is — UTC can be a day off near midnight.
  • Single-process registry. Gates and extension points live in module-level state, registered once at boot. Each worker / function instance / cron has its own registry after cold start; there is no shared global store. Persistence (flag overrides) is injected via the GateOverrideStore extension point.
  • Detection is pure. Validators return GateSuspect[] with no side effects; handlers return HandlerResponse with no side effects. All actual alerting happens in the caller.

Acknowledgements

No runtime dependencies. Built with tsup, type-checked with TypeScript, tested with Vitest (all MIT-licensed dev tooling).

License

MIT