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

@happy-technologies/tenancy

v0.1.0

Published

Transaction-scoped Postgres GUC wrapper and header/query tenant resolver shared across Happy Technologies products. Wrapper + resolver only: no RLS policy SQL, no table schemas, no FORCE-vs-ENABLE decision. Products own their isolation model.

Readme

@happy-technologies/tenancy

Transaction-scoped Postgres GUC wrapper and header/query tenant resolver, shared across Happy Technologies products. Source-shipped ESM (no build step); exports live at ./src/*. Postgres-only: pg is a peerDependency, the app owns its pool and pg version.

Extracted from happy-knowledge's withTenantScope (packages/compiler/src/store/metadata-db.ts:37-46) and withPlatformExemption (packages/compiler/src/store/tenants-db.ts:357-371), generalized so any consumer can set arbitrary session GUCs transaction-locally, plus its requestedTenant header/query resolver (packages/compiler/src/api/auth.ts:40-48).

Install

bun add @happy-technologies/tenancy pg

Usage

withGucScope

Run fn inside a transaction with every entry in gucs set via set_config(name, value, true) (transaction-local, so it never leaks onto the pooled connection after commit/rollback/release):

import { withGucScope } from "@happy-technologies/tenancy";
import { Pool } from "pg";

const pool = new Pool({ connectionString: process.env.DATABASE_URL });

await withGucScope(
  pool,
  { "app.tenant_id": tenantId, "app.platform_admin": "on" },
  async (client) => {
    return client.query("SELECT * FROM widgets"); // RLS-scoped
  },
);

withTenantScope

A thin specialization of withGucScope for the common single-tenant case, with room for extra GUCs in the same transaction:

import { withTenantScope } from "@happy-technologies/tenancy";

await withTenantScope(pool, { tenantId }, async (client) => {
  return client.query("SELECT * FROM widgets");
});

// with an extra GUC, e.g. a platform-admin exemption alongside the tenant scope
await withTenantScope(
  pool,
  { tenantId, gucs: { "app.platform_admin": "on" } },
  async (client) => client.query("SELECT * FROM widgets"),
);

If fn throws, the transaction rolls back and the checked-out client is released with no GUC set on it.

resolveTenant

Resolve the tenant id a request is scoped to, from a framework-agnostic plain-object view of the request: the X-Organization-Id header takes precedence (matched case-insensitively), then a configurable query param (default tenantId), then an optional path param. Returns undefined, never a default, when nothing resolves; the app decides whether that means reject or auto-provision.

import { resolveTenant } from "@happy-technologies/tenancy";

// e.g. inside Hono/Express middleware, after extracting plain objects:
const tenantId = resolveTenant(
  { headers: req.headers, query: req.query, params: req.params },
  { queryParam: "tenantId", pathParam: "orgId" },
);

Non-goals

This package is a wrapper and resolver only. It deliberately does NOT:

  • Define RLS policy SQL or table schemas. Consumers own their own migrations and the shape of their tenant-scoped tables.
  • Decide the isolation model. A direct tenant_id column (happy-knowledge) vs. a membership JOIN (happyhive) are both valid and stay app-local.
  • Decide FORCE vs ENABLE ROW LEVEL SECURITY.

Security note

FORCE ROW LEVEL SECURITY is the consumer's responsibility. This wrapper only sets a transaction-local GUC; it does not create, alter, or force any RLS policy. Without FORCE ROW LEVEL SECURITY on the scoped tables, table owners and superuser-privileged connections bypass RLS entirely, so a policy that relies on app.tenant_id provides no isolation guarantee for those roles. Adopting this wrapper does not by itself change, strengthen, or weaken any app's existing RLS enforcement; each app must verify and configure its own policies and FORCE/ENABLE posture.