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

@liiift-studio/foundry-backup

v1.0.2

Published

Backup CLI for Liiift foundry Sanity projects: raw document export plus a content-addressed asset mirror

Readme

@liiift-studio/foundry-backup

Backup CLI for the Liiift foundry Sanity projects (Darden, TDF, MCKL).

Each foundry repo keeps its own workflow file and its own secrets, so a failure is isolated to one foundry. The backup logic lives here, so a fix ships to all three with one publish instead of three hand-edited YAML files.

Usage

foundry-backup --project-id pdao575x --dataset production --exclude-ext zip

| Flag | Purpose | | --- | --- | | --project-id <id> | Sanity project ID (required) | | --dataset <name> | Dataset to back up (default production) | | --out <dir> | Where the tarball is written (default ./backups) | | --assets <mode> | Asset mirror target: none (default) or blob | | --exclude-ext <list> | Extensions to skip in the mirror, e.g. zip | | --blob-prefix <path> | Blob pathname prefix (default <projectId>/<dataset>) | | --concurrency <n> | Parallel uploads, 1-24 (default 8) | | --no-drafts | Exclude drafts from the export | | --dry-run | Report without writing |

Environment: SANITY_AUTH_TOKEN (required), BLOB_READ_WRITE_TOKEN (with --assets blob).

The Sanity token needs the sanity.project.datasets/read grant. A Deploy Studio token does not carry it — use an Editor robot token.

What it backs up, and what it does not

The document export runs with raw: true: every document is included (asset metadata records too) and asset references are preserved, but asset binaries are not downloaded. On MCKL that is 6 MB instead of 4.48 GB.

Sanity documents raw exports as intended for re-import into the same project. Restored into a fresh project, asset references dangle. So:

  • Document export alone protects against content loss - a bad migration, a mass delete, editor error. This is the common case.
  • Document export + asset mirror is what you need to rebuild elsewhere.

Order zips (order_*.zip) are excluded from the mirror by convention. They are regenerated from the order document plus current font files by the site's checkout code, and they dominate asset storage - typically the majority of a foundry's total. Note a regenerated zip is not byte-identical to what a customer received: fonts and EULA text may have changed since the order.

Asset mirror

Sanity asset IDs embed the content hash (file-<sha1>-<ext>), so an asset's bytes never change under a given ID. The mirror uses that as the blob key, which makes "already backed up" a key check and means nothing is ever re-downloaded. The first run moves the corpus; later runs move only new assets.

Blobs are never deleted, so an asset removed from Sanity by mistake stays recoverable here - something a snapshot cannot offer.

Recovery

Project IDs: darden ef4kzoyv, tdf j5q6yxwa, mckl pdao575x. Every command below needs an Editor robot token as SANITY_AUTH_TOKEN - a Deploy Studio token fails, it lacks sanity.project.datasets/read.

Download the tarball from the repo's Actions run (artifact <foundry>-backup-<n>).

First: is this actually a restore?

For one or a few bad documents, do not import a backup. The Studio's document history covers 90 days (maxRetentionDays) and reverts a single document surgically. A full import is a blunt instrument by comparison.

Preferred path - rehearse into a scratch dataset, then swap

This is the recommended route for any restore that matters. It costs nothing and it is the only way to see what you are getting before you commit to it.

# 1. Restore into a throwaway dataset, never straight over production
npx sanity@latest dataset create restore-check
npx sanity@latest dataset import <backup>.tar.gz restore-check \
  --replace --allow-assets-in-different-dataset --project-id <projectId>

# 2. Verify before trusting it. Expect the live count MINUS 13 system docs.
#    Point a Studio at restore-check, or query it directly.

# 3. Only then apply to production, or repoint the site at the verified dataset
npx sanity@latest dataset delete restore-check

Scenario A - content mistake, assets intact

The common case: bad migration, mass delete, editor error. Assets still live in the dataset, so references resolve and this genuinely works.

npx sanity@latest dataset import <backup>.tar.gz production \
  --replace --project-id <projectId>

--replace is a merge, not a rollback. It replaces documents whose IDs match and leaves everything else alone, so anything created after the backup survives the import. You get deleted documents back; you do not return the dataset to its backup-time state. If you need exactly that state, import into a fresh dataset instead of over the existing one.

Scenario B - dataset or project lost

npx sanity@latest dataset create production
npx sanity@latest dataset import <backup>.tar.gz production \
  --replace --allow-assets-in-different-dataset --allow-failing-assets

--allow-assets-in-different-dataset is required because a --raw export's asset records carry the original project and dataset. --allow-failing-assets is what makes the import complete, and its name is the honest summary: you get every document, reference and relationship, and broken images and fonts. Asset records import as metadata pointing at URLs that no longer resolve.

Closing that gap is the whole job of the asset mirror. Without it, Scenario B recovers structure but not binaries.

Add --skip-cross-dataset-references if references to another dataset block the import and you cannot restore the datasets in dependency order.

Do not use

--allow-system-documents imports Sanity's internal system.* documents. They are excluded from both export and import by design; an export being exactly 13 documents short of count(*[!(_type match "sanity.*")]) is those, not data loss.

Why there is no build step

This package ships its source directly: no tsup, no dist/. It deviates from the other @liiift-studio packages on purpose. It is the thing that runs when everything else is broken, so it has as few moving parts as possible - three small dependencies and no compile stage that could fail at publish time.

Testing

npm test