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

@saastemly/better-dns

v0.1.0

Published

DNS as part of the deployment — declare records, plan the diff, apply it. Cloudflare and Simply.com.

Readme

better-dns

DNS as part of the deployment, not a console tab.

Standing up a shop means pointing a domain at it: an apex record for the storefront, a CNAME for the API worker, and whatever the mail provider asks for so order confirmations do not land in spam. Every one of those is done by hand, in a registrar's UI, and it is the reason "deployed" and "live" are two different days.

import { dns } from "@saastemly/better-dns";
import { cloudflareDns } from "@saastemly/better-dns/providers/cloudflare";
import { simplyDns } from "@saastemly/better-dns/providers/simply";

dns({
  providers: {
    cloudflare: cloudflareDns({ apiToken: env.CLOUDFLARE_DNS_TOKEN }),
    simply: simplyDns({ apiKey: env.SIMPLY_API_KEY }),
  },
  zones: { "example.dk": "simply" },
  records: [
    { key: "api", zone: "example.dk", name: "api", type: "CNAME", value: "app.workers.dev" },
  ],
})
POST /dns/plan   { zone }                  # what a sync would do. writes nothing
POST /dns/sync   { zone, dryRun?, prune? } # make it so
GET  /dns/records?zone=…                   # desired state
POST /dns/records                          # an operator adds one
GET  /dns/syncs                            # what changed, and when

The safety property

A zone is not this plugin's to own. It holds the customer's MX records, their domain verification TXT, the CNAME their newsletter tool asked for. Deleting one takes down something unrelated to the shop, and the damage is invisible from here — mail simply stops arriving.

So:

  • A sync never removes a record at a name the app has not declared. The whole zone is read precisely so those records can be identified and left alone; they are reported as untouched.
  • prune is off by default, and even on it only reaches names the app declares. An extra record at a managed name is reported as a conflict rather than deleted.
  • Every apply can be a dry run first, and it is the same computation — a plan an operator approved and an apply that did something else would make the approval meaningless.
  • zones is an allow-list. A zone that is not in it is refused rather than falling through to whichever provider happens to be first, which on a typo would write your records into somebody else's registrar account and report success.
  • Removing a record from the desired state does not remove it from the zone. Deleting from a list and deleting from the internet should not be the same button.

Providers

| | auth | zone is addressed by | |---|---|---| | Cloudflare | API token, Zone:DNS:Edit (+ Zone:Zone:Read, or pass zoneIds) | an opaque id, looked up once and cached | | Simply.com | the API key alone, as a bearer token — or accountName for Basic | the domain name itself |

Four methods to add another: listRecords, createRecord, updateRecord, deleteRecord. Zone discovery lives inside the provider on purpose, because every provider identifies a zone differently and the plugin should not know which one it is talking to.

What the plan is careful about

Real zones make several things look like drift when they are not, and a sync that rewrote a record every time would eventually rewrite the wrong one:

  • TXT quoting. Providers disagree; compared unquoted.
  • Trailing dots and case on CNAME/MX/NS targets.
  • Cloudflare's automatic TTL — it stores 1 for anything proxied, whatever was asked for.
  • The proxy flag is only compared when the declaration mentions it, since most providers have no such concept.
  • Record sets — three MX at one name are matched by value, not by position, so declaring one does not rewrite the backup mail server into the primary.