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

@theshoregroup/settings-core

v0.3.0

Published

Typed, identity-scoped key-value preferences system. Pure TypeScript — no HTTP, no ORM, no auth-library coupling.

Readme

@theshoregroup/settings-core

A simple way to store typed settings against a user or organization identity, use with @theshoregroup/settings-better-auth-adapter

Quickstart

Install

pnpm add @theshoregroup/settings-core zod@v4

Configuring

Persistence

To persist settings, use a store. This library includes an in-memory store suitable for development or proof-of-concept use - you'd swap this for a database/redis in production. For more information, see the store contract

Organizations

For most apps that don't have the idea of organizations, you can leave organizationMode disabled, which will mean you can only have user-level settings.

However for apps that do we have two additional primitives which are enabled by turning that config option on; org and org-user.

These do what you'd expect - apply a setting against an entire org (org) or against a user within an org (org-user).

Categories are walked in the following order:

  1. org-user
  2. user
  3. org
  4. default (if specified)

Settings Config

Each setting has a key, a schema, and a default value, along with what categories of identity it applies to, and metadata. These are set statically in your application — there's no dynamic registration. For the full picture (including the defineRegistry helper and a few compile-time gotchas), see Defining settings.

import { z } from "zod";
import {
  createSettings,
  createInMemoryStore,
} from "@theshoregroup/settings-core";

const settings = createSettings({
  organizationMode: true,
  store: createInMemoryStore(), // swap for a real adapter in production
  registry: {
    "notif_new-login-location": {
      categories: ["user"] as const,
      schema: z.boolean(),
      default: true,
      meta: {
        name: "New login location notifications",
        description: "Email me when I sign in from a new device.",
      },
    },
    "auth_allowed-email-domains": {
      categories: ["org"] as const,
      schema: z.array(z.string()),
      default: [] as ReadonlyArray<string>,
      meta: { name: "Allowed email domains", description: "…" },
    },
    "notif_supplier-update": {
      categories: ["org", "org-user"] as const,
      schema: z.enum(["all", "mentions", "none"]),
      default: "all" as const,
      meta: { name: "Supplier update notifications", description: "…" },
    },
  },
});

Usage

Identity

When using this package you'll need to provide an identity, which must resolve a userId, orgId as required. You should create an adapter that resolves these values from your authentication provider.

const identity = { userId: "u_123", orgId: "o_456" };

const { value, source } = await settings.resolve("notif_supplier-update", {
  identity,
});

Methods

We provide a set of methods for resolving and mutating settings. They all mostly follow the same pattern and should work the way you expect.

resolve(key, { identity, only? })
const { value, source } = await settings.resolve("notif_supplier-update", {
  identity,
  only: "org", // optional: restrict to a single category
});
update(key, value, { category, identity })

Writes a value at a specific category. Note that update(key, null) stores a real null, it doesn't delete the row — use reset for that.

await settings.update("notif_supplier-update", "mentions", {
  category: "org-user",
  identity,
});
reset(key, { category, identity })

Deletes the stored value at a category, falling back to the next link in the chain.

await settings.reset("notif_supplier-update", {
  category: "org-user",
  identity,
});
updateDynamic / resetDynamic

Runtime-key variants of update/reset, for when the key, value, or category come from the wire and can't be narrowed to literals.

await settings.updateDynamic(req.body.key, req.body.value, {
  category: req.body.category,
  identity,
});
list({ identity, filter? })

Resolves every setting the identity can see, optionally filtered by category or keys.

const all = await settings.list({ identity });
const orgSettings = await settings.list({ identity, filter: { category: "org" } });
purge(args)

Bulk-deletes stored values for an identity scope, e.g. on account deletion or org offboarding.

await settings.purge({ category: "user", userId: "u_123" });
scope(identity)

Bundles an identity so you don't have to pass it to every call.

const scoped = settings.scope(identity);
const all = await scoped.list();

Further Reading

  • Defining settings — how and why to write a registry entry, the defineRegistry helper, and the literal-tuple gotcha.
  • Concepts — domain model, resolution chain, identity, null semantics (including the update(k, null) vs reset trap).
  • Store contract — implementing a custom SettingsStore (Drizzle/Prisma/Kysely/etc.).
  • Errors — errors thrown by the library

License

MIT — see LICENSE.