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

@neuraldraft/sdk

v0.4.1

Published

Official TypeScript SDK for the Neural Draft API: site texts, pages, blog, bookings, forms, shop, social posts, webhooks, and several workspaces (agencies) in one client.

Readme

@neuraldraft/sdk

Official TypeScript SDK for the Neural Draft API: the editable texts and pictures of a site, pages, blog, bookings, forms, shop, social posts and webhooks, for one workspace or many.

npm version License: MIT

Neural Draft hosts websites (built in chat, deployed from Claude Code, Codex or Cursor with deploy_site, or hosted anywhere else) and runs what sits behind them: the CMS, blog, bookings, forms, newsletter, shop and social publishing. Every workspace (one per client for agencies) has its own database and its own API key.

Use this SDK from server-side code (a build step, a backend, a script): it talks to the v1 REST API with the workspace's secret key. On the site itself, use the nd.js blocks (<nd-booking>, <nd-form>, <nd-text>…) instead; they need no key. The docs cover both.

Zero dependencies, typed, ESM.

Install

npm install @neuraldraft/sdk

Node 20+ (built-in fetch), Bun, Deno (npm: import) or edge runtimes with fetch.

API keys

Each workspace has its own keys: dashboard at app.neuraldraft.io → Settings → API keys. A key only reaches its own workspace. Keep it in an environment variable, never in browser code or git.

One workspace

import { NeuralDraftClient } from "@neuraldraft/sdk";

const nd = NeuralDraftClient.fromEnv(); // reads NEURALDRAFT_API_KEY
// or: new NeuralDraftClient({ apiKey: process.env.NEURALDRAFT_API_KEY! })

const project = await nd.projects.me();
console.log(project.name, project.domain);

Options: apiKey (required), apiUrl (default https://api.neuraldraft.io/v1), userAgent, timeout (ms), fetch.

Several workspaces (agencies)

One key per client workspace, in one object. The format is the same as @neuraldraft/mcp 0.4 (NEURALDRAFT_API_KEYS), so one .env serves both.

import { NeuralDraftWorkspaces } from "@neuraldraft/sdk";

// NEURALDRAFT_API_KEYS="villa-lavanda=ndsk_live_…,linden-bakery=ndsk_live_…"
// NEURALDRAFT_WORKSPACE="linden-bakery"   (optional: the active one)
const ws = NeuralDraftWorkspaces.fromEnv();
// or: new NeuralDraftWorkspaces({ keys: { "villa-lavanda": "ndsk_live_…", "linden-bakery": "ndsk_live_…" } })

for (const name of ws.names()) {
  const { meta } = await ws.get(name).bookings.list({ status: "pending" });
  console.log(name, meta.total, "bookings waiting");
}

ws.use("villa-lavanda");          // switch the active workspace
await ws.current.siteTexts.list(); // ws.current = the active client

get(name) returns a normal NeuralDraftClient (created once, then reused). fromEnv falls back to a single NEURALDRAFT_API_KEY as workspace default.

Site texts: what the client can edit

The marked blocks of a site (data-translate="…", <nd-text key="…">, data-image-key="…" in a deployed site, and every text and picture on hosted pages) are the same blocks the client edits in the dashboard (Website → Edit site) or by asking Claude or ChatGPT. Changes are public at once, with no redeploy, and a redeploy never overwrites them.

const { blocks } = await nd.siteTexts.list({ search: "hours" });
// [{ kind: "text", key: "hours", text: "Mon–Sat 7:00–15:00", all_locales: {…} }, { kind: "image", key: "hero", url: "…" }]

const res = await nd.siteTexts.update({
  texts: { hours: "Mon–Sat 6:00–14:00" },          // simple HTML is fine: b, i, a, br
  images: { hero: "https://cdn.example.com/new-hero.webp" },
  lang: "en",                                      // default: the site's default language
});
// { language: "en", changed: ["hours", "hero"], unknown: [] }

Only keys that already exist are changed (unknown keys come back in unknown); pass createMissing: true to create them. Built on PUT /content/{key} and PUT /images/{key}. 0 credits.

Lower level, all content keys with every language:

const page1 = await nd.content.list({ lang: "de", search: "hero", page_size: 200 });
const one = await nd.content.get("hero.title", { lang: "fr" });
await nd.content.set("hero.title", "Willkommen", "de");
const job = await nd.content.translate("hero.title", ["fr", "it"]); // 10 credits per language

Pages and components

const pages = await nd.pages.list();
await nd.pages.create({ slug: "about", title: "About us", meta_title: "About Linden Bakery" });
await nd.pages.update(12, { meta_description: "Sourdough since 1998." });

await nd.components.register({
  html: '<section><h1 data-translate="hero.title">Fresh bread</h1></section>',
  intent: "hero",
  page_slug: "home",
});

Pages of the hosted site, in its own design

Add pages and sub-pages to a hosted site without writing HTML. The new page reuses one of the site's pages (its layout, header, footer and styles); its texts and pictures are its own.

const page = await nd.sitePages.create({
  title: "Wedding cakes",
  path: "services/wedding-cakes", // sub-pages work
  template: "/",                  // start from the home page (default)
  content: "copy",                // or "empty": a heading and a paragraph in the site's styles
  menu: true,                     // link it in the site's menu
});
// page.url → https://linden-bakery.neuraldraft.io/services/wedding-cakes (and /hr/… for every language)

await nd.siteTexts.update({ texts: { "page.services-wedding-cakes.home.title": "Wedding cakes in Bath" } });
await nd.sitePages.update("services/wedding-cakes", { published: false }); // take it offline
await nd.sitePages.delete("services/wedding-cakes");
const { data: all } = await nd.sitePages.list();

Scopes: pages:read, pages:write. Pages of the site as it was built can't be deleted.

pages are the API's page records with SEO meta. Hosted pages published with the connector's publish_site_page and sites deployed with deploy_site are managed through the connector; their editable blocks are in siteTexts.

Blog

// A post you wrote: 0 credits
const post = await nd.blogPosts.create({ title: "Our new rye", content: "<p>…</p>", language_code: "en" });
await nd.blogPosts.publish(post.id);

// Researched and written by our pipeline (with images): 60 credits, async
const job = await nd.blogPosts.generateAi({ topic: "How to store sourdough", word_count: 1200 });
const done = await nd.jobs.poll(job.id);

await nd.blogPosts.translate(post.id, ["de", "hr"]); // 10 credits per language
const list = await nd.blogPosts.list({ status: "published", lang: "en" });

Bookings

const services = await nd.booking.listServices({ status: "active" });
const svc = await nd.booking.createService({ name: "Check-up and clean", duration_minutes: 45, price: 6500, currency: "GBP" });
const free = await nd.booking.availability({ service_id: svc.id, from: "2026-11-02", to: "2026-11-06" });
// { "2026-11-02": [{ start, end, capacity_remaining }], … }

const pending = await nd.bookings.list({ status: "pending", from: "2026-11-01" });
await nd.bookings.confirm(pending.data[0].id);           // the customer gets an email
await nd.bookings.cancel(41, { reason: "Dentist ill" });
await nd.bookings.complete(40);
await nd.bookings.noShow(39);
const week = await nd.bookings.calendar({ from: "2026-11-02", to: "2026-11-08" });
const stats = await nd.bookings.stats(); // today, upcoming, pending, month revenue (cents)

await nd.booking.updateSettings({ auto_confirm_bookings: true, notification_email: "[email protected]" });

Also: bookings.get/create/update/delete, booking.getService/updateService/deleteService/getSettings.

Forms and newsletter

const replies = await nd.forms.listSubmissions({ search: "parking" });
const subs = await nd.forms.listSubscribers({ subscribed: true, language: "en" });
await nd.forms.deleteSubmission(replies.data[0].id);
await nd.forms.deleteSubscriber(17);

Shop

const products = await nd.products.list({ status: "active" });
await nd.products.update(42, { price: 2799 });          // minor units (cents)
const variants = await nd.products.listVariants(42);
await nd.products.createVariant(42, { name: "Large", price: 3299 });

const cats = await nd.productCategories.list();
await nd.productCategories.create({ name: "Bread" });

const orders = await nd.orders.list({ status: "paid" });
await nd.orders.update(orders.data[0].id, { status: "shipped", tracking_number: "RM123456GB" });
await nd.orders.refund(orders.data[0].id); // card orders only; full refund unless { amount }

Card payments go to the business's own Stripe account: connect it in the dashboard under Shop → Payouts. Without Stripe the shop takes pay-on-pickup, bank transfer or order requests.

Social posts

Schedule and publish posts with media you supply (your photos, videos, or ads made in Batchcraft). We don't offer AI image or video creation. We craft finished static ads and reels for your brand.

const accounts = await nd.social.listAccounts(); // health: ok | expiring | expired | inactive
const post = await nd.social.createPost({
  title: "Rye is back",
  platforms: ["facebook", "instagram"],
  facebook_content: "Our rye is back on Saturday.",
  instagram_content: "Rye is back 🍞 Saturday from 7:00.",
  media_url: "https://cdn.example.com/rye.jpg",
  external_ref: "rye-2026-11",                 // idempotency key
});
await nd.social.schedulePost(post.id, "2026-11-07T08:00:00Z");
// nd.social.publishPost(post.id) publishes now (public; returns a job)

Also: listPosts, getPost, updatePost, deletePost.

Webhooks

const ep = await nd.webhooks.createEndpoint({
  url: "https://example.com/hooks/neuraldraft",          // https only
  events: ["booking.confirmed", "order.paid", "content.changed"],
});
console.log(ep.signing_secret); // shown once; store it
const log = await nd.webhooks.listDeliveries({ endpoint_id: ep.id });
await nd.webhooks.retryDelivery(log.data[0].id);

Also: listEndpoints, updateEndpoint, deleteEndpoint, rotateSecret, getDelivery.

Project and nd.js on other websites

await nd.projects.update({ allowed_origins: ["https://www.client.co.uk"] });
const usage = await nd.projects.usage(); // credits balance and this period's spend

allowed_origins lets nd.js blocks loaded from the workspace address (https://<name>.neuraldraft.io/_nd/nd.js) run on a site hosted elsewhere. The workspace address, its custom domains and the saved website are always allowed. https only (http://localhost:<port> for testing), at most 20; the list is replaced, [] clears it.

Images

await nd.images.upload("hero", fileOrBlob, { filename: "hero.jpg" }); // your own picture
await nd.images.replace("hero", { url: "https://cdn.example.com/hero.webp" });
const all = await nd.images.list({ prefix: "gallery." });

images.generate() and replace(key, { regenerate: true }) are deprecated: AI image and video creation is switched off and the API answers 410 Gone with code media_generation_moved. Uploads and URL swaps work as before.

Credits

1 credit = $0.01. Only work our own AI pipeline does costs credits: a blog post 60, a translation 10 per language. Everything else through the API is free: content, page, image and component writes, bookings, forms, shop, social scheduling. Rate limits apply instead. Plans and credits: neuraldraft.io/pricing.

Errors

Every method throws ApiError on a non-2xx response (status, body with the API's JSON, path). status: 0 is a network failure or timeout.

import { ApiError } from "@neuraldraft/sdk";

try {
  await nd.blogPosts.generateAi({ topic: "…" });
} catch (err) {
  if (err instanceof ApiError) {
    const { code } = JSON.parse(err.body || "{}") as { code?: string };
    if (err.status === 402) { /* out of credits */ }
    else if (err.status === 410 && code === "media_generation_moved") { /* AI image/video creation is off */ }
    else if (err.status === 429) { /* rate limited: back off */ }
    else throw err;
  }
}

Pagination and jobs

Lists return { data, meta: { page, page_size, total, last_page } } (webhook deliveries use Laravel's { data, links, meta: { current_page, … } }). Slow work (blog writing, translations, publishing a social post now) returns a job: await nd.jobs.poll(job.id) waits until it is completed, failed or cancelled.

Upgrading from 0.3

Nothing was removed. New: NeuralDraftWorkspaces, NeuralDraftClient.fromEnv, siteTexts, content.list, bookings, booking.createService/updateService/deleteService/availability/getSettings/updateSettings, forms, orders, productCategories, product variants, social, webhooks, allowed_origins on projects.update. images.generate is deprecated (410).

Development

npm install
npm run lint && npm test && npm run build

Links

License

MIT