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/content

v0.2.2

Published

Typed collections and batched content client for Spacefast.

Readme

@spacefast/content

Typed collections and a batched content client for the WordPress content kernel built into every Spacefast Space. The package works in browsers, Next.js server code, and Spacefast Zero.

import { collection, createContentClient, defineContent, fields } from "@spacefast/content";

const schema = defineContent({
  collections: {
    events: collection({
      name: "events",
      // Keep this durable identifier unchanged if the collection is renamed.
      resourceId: "events_01k4t7x8",
      label: "Events",
      singularLabel: "Event",
      fields: {
        venue: fields.text({ label: "Venue" }),
        starts_at: fields.date({ label: "Starts at", required: true }),
      },
    }),
  },
});

const content = createContentClient({ url: "https://example.spacefast.app", schema });
const result = await content.batch({
  events: content.collections.events.query({ limit: 10 }),
  posts: content.posts.query({ limit: 3 }),
  media: content.media.query({ where: [{ field: "id", operator: "in", value: [12, 18] }] }),
});

defineContent() gives your queries and results real types. It does not create collections — that's the next section's job.

The content model lives in your files

Your Space's content model is a file, not an API call. Put a content.config.ts at the Space root and any modules it imports under content/:

// content.config.ts
export default {
  collections: [
    {
      slug: "events",
      fields: [
        { name: "venue", type: "text" },
        { name: "starts_at", type: "date", required: true },
      ],
    },
  ],
  globals: [],
};

It's a Payload-style config, and it compiles when you publish the Space. The compiled model is bound to that version, so a rollback rolls the content model back with it — the version you serve and the shape of its content are never out of sync.

That's the only way the model changes. There is no endpoint that edits it behind publish's back, so what you read in Git is what's running. Two limits to stay under: 64 modules and 2 MiB of source in total.

Document writes

Documents are a different story — they change all the time, so they go over the API. Point the same client at Spacefast with a scoped API key:

const content = createContentClient({
  apiUrl: "https://api.spacefast.com",
  spaceId: "spc_...",
  token: process.env.SPACEFAST_TOKEN,
  schema,
});

await content.syncMarkdown({
  source: "content/hello.md",
  slug: "hello",
  title: "Hello",
  markdown: "# Hello",
  status: "publish",
});

That writes a document into an existing collection; it never adds or reshapes one. batch() sends one request to the Space content endpoint. Writes require a Spacefast management token; public reads never expose WordPress credentials.

Inside a Zero capsule, use the runtime-owned client instead of making an HTTP request:

import { capsule, query } from "@spacefast/zero/server";

const homeContent = query((ctx) =>
  ctx.content.batch({
    posts: { collection: "posts", limit: 3 },
    events: { collection: "events", limit: 10 },
  }),
);

export default capsule({ queries: { homeContent } });

Zero forwards that call as one bounded batch to the Space's WordPress content kernel. WordPress credentials and endpoint selection remain runtime-owned.