Skip to content

refresh — manifest

Manifest

Functions

hashSignature

function hashSignature(signature: string): string
TypeScript

Use hashSignature to generate a short, stable fingerprint of an API signature string so skrypt can detect when a function's interface has changed between documentation runs.

This is the mechanism behind skrypt's incremental generation: before regenerating docs for an element, skrypt hashes its current signature and compares it against the hash stored in manifest.json. If the hashes match, the element is skipped — saving AI API calls and keeping generation fast.

NameTypeRequiredDescription
signaturestringYesThe raw API signature to fingerprint — typically the full function or type declaration extracted from source code.

Returns a 16-character hex string (truncated SHA-256). Store this alongside the documented element in your manifest; on the next run, compare it against a freshly computed hash to decide whether re-generation is needed.

Heads up:

  • The hash is truncated to 16 characters for compactness. Collision probability is negligible for typical codebase sizes, but don't use this for security-sensitive purposes.
  • Whitespace and formatting are part of the input — function foo( x: string ) and function foo(x: string) produce different hashes. Normalize signatures before hashing if your scanner's output isn't stable.

Example:

import { createHash } from 'crypto'

function hashSignature(signature: string): string {
  return createHash('sha256').update(signature).digest('hex').slice(0, 16)
}

// Simulate a manifest entry from a previous skrypt run
const manifest: Record<string, { hash: string; lastGenerated: string }> = {
  'UserService.createUser': {
    hash: 'a3f8c21d9e4b7f02',
    lastGenerated: '2024-11-01T10:00:00Z',
  },
}

// Current signature extracted from source during this run
const currentSignature =
  'function createUser(email: string, role: "admin" | "member"): Promise<User>'

const currentHash = hashSignature(currentSignature)
const stored = manifest['UserService.createUser']

if (stored && stored.hash === currentHash) {
  console.log('Signature unchanged — skipping AI generation')
  console.log(`Reusing docs generated at ${stored.lastGenerated}`)
} else {
  console.log('Signature changed — regenerating documentation')
  console.log(`Old hash: ${stored?.hash ?? 'none'}`)
  console.log(`New hash: ${currentHash}`)
  // → trigger AI doc generation here, then update manifest
}

// Expected output (hashes will differ since stored hash is a placeholder):
// Signature changed — regenerating documentation
// Old hash: a3f8c21d9e4b7f02
// New hash: 9d4e1a7c3b82f051
TypeScript

buildManifestEntries

function buildManifestEntries(elements: APIElement[], outputPath: string): ManifestEntry[]
TypeScript

Use buildManifestEntries to convert your scanned API elements into manifest entries that map each element to its generated documentation file on disk.

Call this after scanning your source code and before writing the manifest file. It bridges the gap between raw APIElement objects (what the scanner produces) and the structured index that your doc site uses to resolve links, search, and navigation.

Each APIElement is mapped to a ManifestEntry that pairs the element's name with its expected output path, giving downstream tooling a stable lookup table for the entire API surface.

Parameters

NameTypeRequiredDescription
elementsAPIElement[]YesThe API elements returned by your scanner — each one becomes a single manifest entry. An empty array produces an empty manifest.
outputPathstringYesThe root directory where generated .mdx files will be written. Used to construct the file path for each entry — must match the -o path you passed to skrypt generate.

Returns

Returns a ManifestEntry[] where each entry contains the element's name and its resolved output path. Pass this array directly to your manifest writer to produce the manifest.json file that the doc site reads at build time.

Heads up

  • The output path is used as-is to construct entry paths — if it doesn't match the actual output directory your doc site serves from, links and navigation will silently resolve to the wrong locations.
  • This function does not write anything to disk; it only builds the data structure. You still need to serialize and write the result yourself.

Example:

import { join } from "path";
import { createHash } from "crypto";

interface APIElement {
  name: string;
  kind: "function" | "class" | "interface" | "type";
  signature: string;
  filePath: string;
  docstring?: string;
}

interface ManifestEntry {
  name: string;
  outputFile: string;
  kind: string;
  hash: string;
}

function buildManifestEntries(
  elements: APIElement[],
  outputPath: string
): ManifestEntry[] {
  return elements.map((el) => ({
    name: el.name,
    outputFile: join(outputPath, `${el.name}.mdx`),
    kind: el.kind,
    hash: createHash("md5").update(el.signature).digest("hex"),
  }));
}

const scannedElements: APIElement[] = [
  {
    name: "createPayment",
    kind: "function",
    signature: "function createPayment(amount: number, currency: string): Promise<Payment>",
    filePath: "src/payments/create.ts",
    docstring: "Creates a new payment intent and returns the client secret.",
  },
  {
    name: "PaymentClient",
    kind: "class",
    signature: "class PaymentClient { constructor(apiKey: string) }",
    filePath: "src/payments/client.ts",
    docstring: "Main client for interacting with the payments API.",
  },
  {
    name: "WebhookHandler",
    kind: "class",
    signature: "class WebhookHandler { verify(payload: string, secret: string): boolean }",
    filePath: "src/webhooks/handler.ts",
  },
];

try {
  const outputDir = "./content/docs/api";
  const entries = buildManifestEntries(scannedElements, outputDir);

  console.log("Manifest entries:", JSON.stringify(entries, null, 2));
  // Expected output:
  // Manifest entries: [
  //   {
  //     "name": "createPayment",
  //     "outputFile": "content/docs/api/createPayment.mdx",
  //     "kind": "function",
  //     "hash": "a3f1c..."
  //   },
  //   {
  //     "name": "PaymentClient",
  //     "outputFile": "content/docs/api/PaymentClient.mdx",
  //     "kind": "class",
  //     "hash": "b92d4..."
  //   },
  //   ...
  // ]
} catch (err) {
  console.error("Failed to build manifest entries:", err);
}
TypeScript

writeManifest

function writeManifest(outputPath: string, entries: ManifestEntry[]): void
TypeScript

Use writeManifest to persist a documentation manifest to disk so skrypt can track what was generated, detect stale docs, and power incremental rebuilds.

Call this after your documentation entries have been generated — typically as the final step in a generation pipeline. It's what allows skrypt to know which files exist, when they were last built, and whether they need to be regenerated on the next run.

writeManifest writes a manifest.json file into a .skrypt/ directory inside your output path, creating that directory if it doesn't exist. The manifest is stamped with a generatedAt ISO timestamp alongside your entries.

Parameters

NameTypeRequiredDescription
outputPathstringYesRoot directory where your docs are written — the .skrypt/manifest.json file will be created inside this directory
entriesManifestEntry[]YesThe documentation entries to record — each entry represents a generated doc file and its metadata, used for change detection on subsequent runs

Returns

void. After this call, .skrypt/manifest.json exists on disk. On the next skrypt generate run, skrypt reads this file to skip unchanged entries and only regenerate what's new or modified.

Heads up

  • outputPath must be an absolute or correctly resolved relative path — if it points to the wrong directory, the manifest will be written there silently with no error.
  • The .skrypt/ directory is created automatically, but the parent outputPath directory must already exist.

Example:

import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'fs'
import { join } from 'path'
import { tmpdir } from 'os'

// Inline types — do not import from autodocs
interface ManifestEntry {
  slug: string
  title: string
  filePath: string
  hash: string
  generatedAt: string
}

interface DocManifest {
  generatedAt: string
  entries: ManifestEntry[]
}

const MANIFEST_DIR = '.skrypt'
const MANIFEST_FILE = 'manifest.json'

// Inline implementation matching skrypt's writeManifest behavior
function writeManifest(outputPath: string, entries: ManifestEntry[]): void {
  const manifestDir = join(outputPath, MANIFEST_DIR)
  mkdirSync(manifestDir, { recursive: true })

  const manifest: DocManifest = {
    generatedAt: new Date().toISOString(),
    entries,
  }

  writeFileSync(
    join(manifestDir, MANIFEST_FILE),
    JSON.stringify(manifest, null, 2),
    'utf-8'
  )
}

// Simulate a completed generation run
const outputDir = join(tmpdir(), 'my-api-docs')
mkdirSync(outputDir, { recursive: true })

const entries: ManifestEntry[] = [
  {
    slug: 'auth-create-user',
    title: 'createUser',
    filePath: join(outputDir, 'auth/create-user.mdx'),
    hash: 'a3f8c2d1e9b74056',
    generatedAt: new Date().toISOString(),
  },
  {
    slug: 'auth-delete-user',
    title: 'deleteUser',
    filePath: join(outputDir, 'auth/delete-user.mdx'),
    hash: 'b7e1d4c9f2a83017',
    generatedAt: new Date().toISOString(),
  },
]

try {
  writeManifest(outputDir, entries)

  const manifestPath = join(outputDir, '.skrypt', 'manifest.json')
  const written = JSON.parse(readFileSync(manifestPath, 'utf-8')) as DocManifest

  console.log('Manifest written to:', manifestPath)
  console.log('Generated at:', written.generatedAt)
  console.log('Entries recorded:', written.entries.length)
  // Manifest written to: /tmp/my-api-docs/.skrypt/manifest.json
  // Generated at: 2024-11-12T15:42:03.217Z
  // Entries recorded: 2
} catch (err) {
  console.error('Failed to write manifest:', err)
}
TypeScript

readManifest

function readManifest(outputPath: string): DocManifest | null
TypeScript

Use readManifest to load a previously generated documentation manifest so you can diff against it before regenerating docs.

Call this at the start of a generation pipeline to check whether docs already exist for a given output directory. If a manifest is present, you can compare file hashes or topic lists to skip unchanged files and avoid unnecessary AI calls.

readManifest looks for a manifest file inside a fixed .skrypt subdirectory within outputPath. If that file doesn't exist — such as on a first run — it returns null rather than throwing, so you can safely use it as an existence check.

Parameters

NameTypeRequiredDescription
outputPathstringYesPath to the directory where docs were previously generated — the same value you passed to skrypt generate -o. The manifest is read from <outputPath>/.skrypt/manifest.json.

Returns

Returns a DocManifest object containing metadata about the last generation run (file hashes, documented elements, topics), or null if no manifest exists at that path. Use the returned manifest to compare against the current source scan and selectively regenerate only what has changed.

Heads up

  • Returns null on a first run or if the output directory was created outside of skrypt generate — always null-check before accessing manifest properties.
  • If you manually delete or move the .skrypt subdirectory inside your output path, readManifest will return null even if your generated .mdx files are still present.

Example:

import { existsSync, readFileSync } from "fs";
import { join } from "path";

// Inline types — do not import from autodocs
interface DocElement {
  name: string;
  file: string;
  hash: string;
}

interface DocManifest {
  version: string;
  generatedAt: string;
  elements: DocElement[];
  topics: string[];
}

// Self-contained readManifest implementation
function readManifest(outputPath: string): DocManifest | null {
  const manifestPath = join(outputPath, ".skrypt", "manifest.json");
  if (!existsSync(manifestPath)) {
    return null;
  }
  const raw = readFileSync(manifestPath, "utf-8");
  return JSON.parse(raw) as DocManifest;
}

// --- Usage ---
const OUTPUT_DIR = "./docs";

try {
  const manifest = readManifest(OUTPUT_DIR);

  if (manifest === null) {
    console.log("No existing manifest found — this looks like a first run.");
    console.log("Proceeding with full documentation generation.");
  } else {
    console.log(`Manifest found (generated ${manifest.generatedAt})`);
    console.log(`  Version:  ${manifest.version}`);
    console.log(`  Elements: ${manifest.elements.length}`);
    console.log(`  Topics:   ${manifest.topics.join(", ")}`);
    // You would now diff manifest.elements against your current scan
    // to skip files whose hashes haven't changed.
  }
} catch (err) {
  console.error("Failed to read manifest:", err);
}

// Expected output (first run):
// No existing manifest found — this looks like a first run.
// Proceeding with full documentation generation.

// Expected output (subsequent run):
// Manifest found (generated 2024-11-03T14:22:10.000Z)
//   Version:  1.2.0
//   Elements: 42
//   Topics:   authentication, payments, webhooks
TypeScript
Was this helpful?