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

@spacefast/zero-compat-payloadcms

v0.4.1

Published

Translates a Payload CMS config into a Spacefast Zero content model.

Readme

@spacefast/zero-compat-payloadcms

Move a Payload CMS project onto Spacefast Zero.

import { compilePayloadConfig, formatTranslationRefusals } from "@spacefast/zero-compat-payloadcms";

const result = await compilePayloadConfig(JSON.parse(await readFile("payload.json", "utf8")));

if (result.status === "refused") {
  console.error(formatTranslationRefusals(result.report));
  process.exit(1);
}
// result.artifacts is the content model a publish declares.

Compatibility with another CMS is a translation you run once, not an adapter the platform runs forever. This package reads a Payload config, emits the content model Spacefast compiles into WordPress — native posts, pages and media, a zero_collection per Payload collection, and the Secure Custom Fields that back them — and hands back a report of what the translation cost. After that the Payload project is gone. The platform only ever runs Zero.

What it refuses, and why that is the point

A translation that quietly drops half its input is worse than one that stops, so every construct in the config lands in exactly one report entry:

| outcome | meaning | | --------- | ---------------------------------------------------------------------------------- | | mapped | translated with its meaning and its name intact | | renamed | translated, but Zero spells or enforces it differently; reason says what changed | | ignored | read and deliberately not carried, because it holds no content | | refused | no Zero construct holds it; reason says why and action says what to change |

One refusal withholds the whole content model. The line between renamed and refused is content, not comfort: losing a field loses documents, so an untranslatable field stops the run, while losing a rule about a field that does translate — an email format, a numeric bound, a uniqueness constraint — costs no content and is reported as a rename with the rule named.

Coverage

| Payload | Zero | | ----------------------------------------------------------------- | --------------------------------------------------------------- | | text, textarea | text, multiline from the type, maxLength carried | | email, code | text — renamed; format and language hints stop being enforced | | number, checkbox, date | number, boolean, datetime | | richText | blocks, any component | | select, radio | enum, multiple from hasMany | | relationship (one target) | reference | | upload | media | | row, collapsible, unnamed tab | children inlined | | group, named tab | children inlined under a name- prefix | | ui | ignored — an admin control storing no value | | collection posts / pages / an upload collection | adopts the native WordPress store | | other collections | a zero_collection resource with SCF fields | | global | a collection holding one entry | | collection versions | WordPress post status; maxPerDoc has no equivalent | | access.read | publicRead, and only when it serializes to a constant | | json, point, array, blocks, join | refused | | polymorphic relationship | refused | | localized fields, config localization | refused | | field or collection hooks / access / validate / endpoints | refused | | auth collections | refused — identity is Spacefast Auth's | | plugins | refused — they rewrite the config before Payload sees it |

Shape of the input

Payload's config is TypeScript, and payload.config.ts is executed, not parsed. This package reads the exported JSON form, because a translator that evaluated a customer's config would be running their server code to find out what their content is. Executing a TS config needs a sandbox and a capability model; it is a separate lane, not a flag on this one.

Unrecognized keys are refused rather than skipped: a key whose effect on content shape this translator does not know is exactly the silent loss the report exists to prevent.

Shape of the output

compilePayloadConfig returns the artifacts a published version declares under sourceMetadata.statticZero.contentModel — a ContentModelReleaseV1 and the four derived artifacts the control plane re-verifies before it stages content-model.php. contentModelSourceMetadata wraps them for a publish.

That target is the compiled content model, not the repo authoring format. The authoring format exists — sf zero import payloadcms calls translatePayloadConfig and lets @spacefast/zero-compile print the capsule content module the author edits next — and compilePayloadConfig is the other half, for callers that want the build output the publish path would have produced. Carrying both is cheap because the package is split: translate.ts knows Payload and produces the model in content-model.ts, emit.ts knows the platform and nothing else.

Not yet translated

  • Documents. A Payload config carries no content, so nothing is emitted under content/ and no Markdown sync bindings are declared. Translating a data export needs a Lexical-to-Markdown pass of its own.
  • Abilities. A translated content model declares content and nothing that runs. Emitting a query would mean emitting the executable behind it, which is the content model compiler's job.
  • sf CLI command. Reach the package directly until sf import payloadcms exists.