Skip to content

codedpro/itmaster-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@itmaster/sdk

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

Official client SDK for IT Master, the AI SEO content engine. Pull SEO-optimized, AI-generated articles into any standalone Next.js site — with turnkey sitemap, RSS, llms.txt, JSON-LD, IndexNow, metadata and webhook helpers — instead of re-implementing the Pull API + rendering layer in every site.

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
Pre-registry / offline installs
# 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.

Releases

No releases published

Packages

 
 
 

Contributors