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

@itmaster/sdk

v0.4.1

Published

Connect any Next.js site to IT Master — the AI SEO content engine that writes, validates & publishes expert articles that rank on Google and get cited by AI search: sitemap, robots, llms.txt, IndexNow, JSON-LD, metadata & webhooks.

Readme

@itmaster/sdk

npm version license: MIT types: TypeScript Next.js ESM

Official client SDK for IT Master — the AI SEO content engine. IT Master writes, cross-model–validates and publishes expert articles that rank on Google and get cited by AI search (ChatGPT, Perplexity, AI Overviews). This SDK connects any Next.js site to it: pull those articles and serve turnkey sitemap, RSS, llms.txt, JSON-LD, IndexNow, metadata and webhook helpers — no re-implementing the Pull API or rendering layer per site. See how it works · pricing.

The Fabric-Commerce tenants do not use this — they receive optimizations via Fabric's provider endpoints. This SDK is for independent sites that pull and display IT Master content (e.g. itmaster.uk, ai.ukvision.co.uk).

Features

  • Typed Pull API clientlistArticles, getArticle, incremental mirror (listArticlesPage).
  • Next.js App Router helpers — article Metadata, Article/FAQ JSON-LD, OG/Twitter tags.
  • Turnkey SEO routes — one-line robots.txt, sitemap.xml, rss.xml, llms.txt, IndexNow key.
  • Dashboard-controlled <head> — title templates, favicons, verification + analytics tags from site_config.
  • Push webhooks — HMAC-verified publish/takedown → revalidatePath, live in seconds.
  • Provider (write-back) mode — HMAC-signed contract for CMS-style sites the engine edits directly.
  • Zero runtime deps, ESM, ships full .d.ts types. next is an optional peer.

Install

npm install @itmaster/sdk
# or: pnpm add @itmaster/sdk   /   yarn add @itmaster/sdk
# straight from GitHub (builds on install):
npm install github:codedpro/itmaster-sdk
# from a packed tarball:
npm install /path/to/itmaster-sdk-0.2.0.tgz
# or a path dependency (sibling repos):
npm install file:../itmaster/itmaster-sdk

Quickstart — the init CLI

Scaffold a site's integration by selecting features — it writes only new files (client + the routes you pick + middleware), adds the env keys, and prints the two snippets it won't overwrite (layout.tsx <head> and the blog pages). src/app is auto-detected; existing files are skipped unless --force.

npx @itmaster/sdk init                 # interactive (y/n per feature)
# or non-interactive:
npx @itmaster/sdk init \
  --site <PUBLISH_SITE> --engine https://itmaster.uk \
  --features robots,sitemap,rss,llms,indexnow,webhook --yes
✚ wrote  lib/itmaster.ts
✚ wrote  app/robots.txt/route.ts
✚ wrote  app/api/indexnow-key/route.ts
✚ wrote  middleware.ts
✚ wrote  app/api/itmaster-webhook/route.ts
✚ env keys ensured in .env.local + .env.example (fill PULL_KEY)

Then fill PUBLISH_PULL_KEY, wire the two printed snippets, and connect Google (see the end). The sections below are what the CLI generates — reference them to do it by hand or to understand each piece.

Use

// lib/itmaster.ts (server-only)
import "server-only";
import { createClient } from "@itmaster/sdk";

export const itmaster = createClient({
  baseUrl: process.env.ITMASTER_API_URL!,   // engine origin
  site: process.env.PUBLISH_SITE!,           // your TargetSite slug
  pullKey: process.env.PUBLISH_PULL_KEY!,    // per-site key (server only)
});
// app/blog/page.tsx
const posts = await itmaster.listArticles({ limit: 50 });

// app/blog/[slug]/page.tsx
import { articleMetadata, articleJsonLd } from "@itmaster/sdk/next";
const a = await itmaster.getArticle(slug);
export const generateMetadata = () => articleMetadata(a!, { siteUrl: "https://itmaster.uk" });
const ld = articleJsonLd(a!, { siteUrl: "https://itmaster.uk" });

Feeds: itmaster.sitemap(), .rss(), .llmsTxt(), .robots(), .redirects(). Site config: itmaster.config(), .clusters(), .indexnowKey(). Incremental mirror: itmaster.listArticlesPage({ since }){ articles, next_since }. Push webhooks: verifyWebhookSignature(rawBody, sigHeader, secret) from @itmaster/sdk/next.

Turnkey routes (drop-in, dashboard-controlled)

Wire these one-line re-exports and the whole site is controlled from the engine dashboard — robots, feeds, head tags, IndexNow and "publish → live in seconds".

// app/robots.txt/route.ts
import { createRobotsRoute } from "@itmaster/sdk/next";
import { itmaster } from "@/lib/itmaster";
export const GET = createRobotsRoute(itmaster);

// app/sitemap.xml/route.ts
import { createSitemapRoute } from "@itmaster/sdk/next";
import { itmaster } from "@/lib/itmaster";
export const GET = createSitemapRoute(itmaster);

// app/rss.xml/route.ts   — createRssRoute(itmaster)
// app/llms.txt/route.ts  — createLlmsRoute(itmaster)
// app/api/indexnow-key/route.ts — IndexNow ownership proof (no secret, no round-trip)
import { createIndexNowKeyRoute } from "@itmaster/sdk/next";
// Pass the engine site id (single-tenant) or a resolver (multi-tenant, from host).
// The SDK DERIVES the exact key the engine submits, so only your key resolves.
export const GET = createIndexNowKeyRoute(process.env.PUBLISH_SITE ?? "your-site");

// middleware.ts — rewrite the /{key}.txt request to the route
import { NextRequest, NextResponse } from "next/server";
export function middleware(req: NextRequest) {
  if (/^\/[a-f0-9]{32}\.txt$/.test(req.nextUrl.pathname))
    return NextResponse.rewrite(new URL("/api/indexnow-key", req.url));
  return NextResponse.next();
}
export const config = { matcher: ["/((?!_next/|favicon.ico).*)"] };
// app/api/itmaster-webhook/route.ts — publish/takedown → revalidate
import { createWebhookRoute } from "@itmaster/sdk/next";
import { revalidatePath } from "next/cache";
export const POST = createWebhookRoute({
  secret: process.env.PUBLISH_PUSH_SECRET!,
  revalidate: (a) => { revalidatePath(`/blog/${a.slug}`); revalidatePath("/blog"); },
  // onUpsert / onDelete are optional if you only rely on revalidation.
});

Head tags + metadata from the dashboard site_config. Emit verification/OG/favicon via the Metadata API (applyMeta in generateMetadata) and analytics via next/script — those are the only paths that actually render into <head> in the App Router:

// app/layout.tsx
import { applyMeta, headTagsFromConfig } from "@itmaster/sdk/next";
import Script from "next/script";
import { itmaster } from "@/lib/itmaster";

const base: import("next").Metadata = { title: { default: "…", template: "%s · …" } };

export async function generateMetadata() {
  const config = await itmaster.config().catch(() => null);
  return config ? applyMeta(base, config) : base;   // verification + OG + favicon
}

export default async function RootLayout({ children }) {
  const config = await itmaster.config().catch(() => null);
  const scripts = config ? headTagsFromConfig(config).scripts : [];   // GA4/GTM/Plausible/…
  return (
    <html>
      <body>
        {children}
        {scripts.map((s, i) =>
          s.src ? <Script key={i} src={s.src} strategy="afterInteractive" />
                : <Script key={i} id={`a${i}`} strategy="afterInteractive"
                          dangerouslySetInnerHTML={{ __html: s.innerHtml ?? "" }} />)}
      </body>
    </html>
  );
}

⚠️ Do not render the tags via <head><Fragment dangerouslySetInnerHTML></head> — a Fragment has no DOM node, so nothing reaches <head>. Use applyMeta + next/script as above. (renderHeadHtml remains for non-App-Router/SSR string use.)

// app/blog/[slug]/page.tsx — compose article metadata with the site config
import { articleMetadata, applyMeta } from "@itmaster/sdk/next";
const config = await itmaster.config();
let meta = articleMetadata(a!, { siteUrl });
if (config) meta = applyMeta(meta, config);   // title template/suffix, OG, twitter, favicon
export const generateMetadata = () => meta;

Provider (write-back) mode

For CMS-style sites the engine edits directly, mount the provider contract under app/api/external/[slug]/…. Implement only the handlers you need — the rest answer 501. Every POST is HMAC-verified (X-Thoth-Signature).

// app/api/external/[slug]/_routes.ts
import { createProviderRoutes } from "@itmaster/sdk/provider";
export const routes = createProviderRoutes({
  secret: process.env.PROVIDER_SECRET!,
  handlers: {
    snapshot: async (slug) => [/* SnapshotPage[] — your page inventory */],
    applySeoPatch: async (slug, patch) => { /* TODO write title/meta/og/jsonld */ return { ok: true }; },
    applyContent: async (slug, patch) => { /* TODO apply prose blocks */ return { ok: true }; },
    revalidate: async (slug) => { /* TODO purge ISR/CDN */ return { ok: true }; },
  },
});

// app/api/external/[slug]/snapshot/route.ts
import { routes } from "../_routes";
export const GET = (req: Request, ctx: { params: Promise<{ slug: string }> }) =>
  ctx.params.then(({ slug }) => routes.snapshot(req, slug));
// …and health/route.ts, seo-patch/route.ts, content/route.ts, revalidate/route.ts likewise.

API surface

| Import | Exports | | --- | --- | | @itmaster/sdk | createClient, types (PublishedArticle, SiteConfig, Redirect, …) | | @itmaster/sdk/next | articleMetadata, articleJsonLd, applyMeta, headTagsFromConfig, renderHeadHtml, deriveIndexNowKey, createRobotsRoute/createSitemapRoute/createRssRoute/createLlmsRoute/createIndexNowKeyRoute, createWebhookRoute, verifyWebhookSignature | | @itmaster/sdk/provider | createProviderRoutes (HMAC write-back contract) | | npx @itmaster/sdk init | CLI scaffolder (client + routes + middleware + env) |

Connect Google (zero-click)

Once the head + IndexNow routes are live, one engine call verifies the site in Search Console, submits the sitemap, and turns on indexing:

curl -X POST "https://itmaster.uk/v1/publish/sites/<site>/connect/google/auto" \
  -H "Authorization: Bearer <INTERNAL_TOKEN>"

| feature | needs | | --- | --- | | verification meta / analytics | config() working ⇒ pull key set | | IndexNow | the key route + middleware rewrite (no pull key) | | Google Indexing | site connected (service account + indexing scope) | | instant publish | webhook route + engine push_url / push_secret | | articles at all | engine generating content for the site (automation on) |

Build

npm install && npm run build   # → dist/ (ESM + .d.ts)

Learn more

This package everywhere: @itmaster/sdk on npm · source on GitHub (codedpro/itmaster-sdk) · report an issue

License

MIT © IT Master.

Let the engine optimise your pages (SEO patch route)

The publish webhook receives articles. This receives optimisations for pages you already have: rewritten <title>, meta description, Open Graph and JSON-LD from the engine's patcher, and prose rewrites from its content-rewriter.

Your site stays in control. The engine proposes; your handler decides what to persist — IT Master never needs credentials into your database.

// app/api/itmaster-seo-patch/route.ts
import { createSeoPatchRoute } from "@itmaster/sdk/next";
import { revalidatePath } from "next/cache";

export const POST = createSeoPatchRoute({
  secret: process.env.ITMASTER_PATCH_SECRET!,   // same value as your push_secret

  // Rewritten metadata. The values live under `fields`.
  applySeoPatch: async (patch) => {
    await db.seoOverrides.upsert({
      path: patch.path!,
      title: patch.fields?.title,
      description: patch.fields?.description,
      ogTitle: patch.fields?.ogTitle,
      jsonLd: patch.fields?.customJsonLd,
    });
    // Returning nothing counts as applied; return false to decline this patch.
  },

  // Optional — prose rewrites. `apply === false` means STAGE for human review.
  applyContentPatch: async (patch, _source, apply) => {
    await db.copyDrafts.save(patch.target!, patch.text!, { published: apply });
  },

  revalidate: (paths) => paths.forEach((p) => revalidatePath(p)),
});

Then enable it for your site — one call:

curl -X PUT https://itmaster.uk/v1/seo/sites/<site-id>/modules \
  -H "Authorization: Bearer $INTERNAL_TOKEN" -H "Content-Type: application/json" \
  -d '{"patcher": true}'

The response includes a preflight block: the engine signs a no-op request to your route and reports whether it answered. If preflight.ok is false, patches cannot be delivered yet — deploy the route first. Enable optimizer and content_rewrite the same way.

By default the engine posts to <base_url>/api/itmaster-seo-patch. Host it elsewhere by sending {"patch_url": "https://…"} in the same call.

Verification. Every request carries X-Thoth-Signature, a hex HMAC-SHA256 of the raw body keyed by your secret — the same scheme as the publish webhook, so both routes verify identically. A bad signature is rejected with 401 before your handler runs. Partial success is normal: one failing patch is reported in results and never fails the batch.