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

@wyrhta/core

v0.7.1

Published

Shared foundation for Wyrhta Labs services: identity, auth, HTTP kit, household, DB conventions

Readme

@wyrhta/core

Shared foundation library for Wyrhta Labs household-manager services (Heorth, KithLedger). Published on npm, and consumed as a normal versioned dependency — never a workspace/local link, because each service is an independent repo:

npm install @wyrhta/core
{
  "dependencies": {
    "@wyrhta/core": "^0.7.1"
  }
}

Every release is published from CI by trusted publishing (ADR 0011), so each tarball carries a provenance attestation naming the workflow, repository, and commit that produced it — npm audit signatures verifies it.

Pinning the git tag directly still works and is the right way to test an unreleased change against a consumer:

{ "@wyrhta/core": "github:Wyrhta-Labs/wyrhta-core#v0.7.1" }

Note that this form builds core from source during install, so it needs git and a TypeScript toolchain on the machine running npm install.

Either way, a change here only reaches consumers when a new version is released and the consumer's package.json is deliberately bumped.

Module map

Each subpath below is an independent exports entry — import only what you need.

| Subpath | What it provides | |---|---| | @wyrhta/core/config | Zod-based environment parsing: baseEnvSchema + parseEnv, plus emptyToUndefined (blank value = absent) and parseEnvOrExit (parse-or-exit startup guard for a full app schema). JWT_SECRET is optional in the base schema — verifier-only deployments hold no secret; apps that sign HS256 re-declare it as required in extra. Also loadDotEnv({ path, skip }), the opt-in local-dev .env reader: exported variables always win and a missing file is not an error. It is the one place core reads a file — a deliberate exception to "core never reads env or files", confined to the module that is the environment | | @wyrhta/core/lib | API-key crypto helpers, structured logger, and ISO 8601 duration arithmetic (parseDuration / isPositiveDuration / addDuration) over the fixed-step subset both recurrence expanders use — an RRULE is rejected, and so is a duration that parses but does not advance | | @wyrhta/core/http | Response envelope (ok/err), pagination helpers, and requestId / securityHeaders / rateLimit / errorHandler / trimTrailingSlash Hono middleware. trimTrailingSlash replaces Hono's own: it emits a path-only Location, because an absolute one carries the scheme, host and prefix the app sees rather than the ones the client used, which is wrong behind a proxy. rateLimit keys on the first X-Forwarded-For entry by default, so it is only sound behind a proxy that replaces (not appends to) that header; the optional resolveIp injects an app-supplied key instead. errorHandler is the one core export that reads NODE_ENV — it omits Zod field details in production; createErrorHandler({ validationDetails }) decides that explicitly instead | | @wyrhta/core/identity | users + api_keys schema, argon2 password hashing, JWT issuing/verification — HS256 shared secret or RS256 / EdDSA keys with kid selection, optional iss / aud claims and clock-skew leeway — plus key loading and JWKS document construction, roles (admin / adult / child) | | @wyrhta/core/auth | Auth-scheme dispatch and Hono guards (requireAuth, requireJwt, requireRole); the consuming app injects the API-key lookup and, optionally, the expected JWT issuer/audience, public verification keys, and clock-skew leeway | | @wyrhta/core/household | DB-enforced singleton household | | @wyrhta/core/testing | assertTestDatabase() — the destructive-suite guard: an integration suite that truncates tables refuses to run unless DATABASE_URL names a database ending in _test. Reachable ONLY on this subpath; it is in no other barrel, so nothing test-only can be imported from application code | | @wyrhta/core/db | Drizzle ORM / postgres.js client factory, a migrations runner, and SQLSTATE classification (pgErrorCode / isPgError) that walks the cause chain — drizzle-orm >= 0.44 wraps driver errors, so reading error.code directly misses every real violation. isUniqueViolation in ./identity is isPgError(e, '23505') |

What core is NOT

  • No business domains — no bills, chores, contacts, ledgers, or any other service-specific concept lives here.
  • No UI.
  • DB-agnostic where it touches application tables: core does not assume or own app-level tables outside of its own (identity, household). Apps inject their own lookups (e.g. the API-key lookup in ./auth) rather than core reaching into app schema.
  • No speculative features. New capabilities land demand-driven only — when a consumer concretely needs them, not in anticipation of future use.

Domain error codes

Core signals expected, caller-actionable failures by throwing a bare Error whose message is an UPPER_SNAKE_CASE code matching ^[A-Z][A-Z0-9_]{1,63}$ — e.g. INVALID_TOKEN, TOKEN_EXPIRED, INVALID_ISSUER, INVALID_AUDIENCE, UNKNOWN_KEY_ID, INVALID_ALGORITHM, INVALID_KEY_MATERIAL, MISSING_JWT_VERIFICATION_KEY, CONFLICT. Anything else that escapes core (driver errors, Zod internals, non-Error throws) is an unexpected failure and carries no such contract.

The distinction is deliberate and consumer-facing: a caller may match on the shape to decide what is safe to surface to a client and what must be collapsed into a generic message. Keep new core error codes in this style.

This convention used to be implemented and documented in the ./mcp scaffold, which was removed in 0.3.0 (ADR 0008). It is documented here because core's own modules — ./identity, ./auth — still depend on it.

Release discipline

  • Every change ships as a semver tag plus a CHANGELOG.md entry.
  • Pre-1.0: a minor bump may break compatibility; a patch bump is safe to take without review.
  • Consumers upgrade by a deliberate pin-bump of the #vX.Y.Z tag in their package.json — there is no auto-update.

Requirements

  • Node.js >= 24. The package is ESM-only; require() from CJS tooling such as drizzle-kit works via Node's require(ESM) support. That support landed in 22.12, which was the floor until 0.7.0 — every consumer now builds and ships on Node 24, so 24 is what CI covers and what engines declares.

Development

npm run build          # tsc --project tsconfig.json
npm run typecheck      # tsc --noEmit
npm test               # vitest run
npm run test:watch     # vitest (watch mode)
npm run db:generate    # drizzle-kit generate
npm run db:migrate     # drizzle-kit migrate
npm run db:push        # drizzle-kit push
npm run db:studio      # drizzle-kit studio