Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Centurylong/sanctifier/llms.txt

Use this file to discover all available pages before exploring further.

The Node.js entry of @sanctifier/sdk loads the WASM bundle synchronously via CommonJS require during module initialisation, so there is no async setup step. You can call analyze() immediately after importing.

Requirements

  • Node.js 18 or later. The package uses node:module built-ins and relies on the native WebAssembly runtime available since Node 8, but the test suite and the package engines field pin the minimum to 18.
  • No native add-ons. The analyzer runs entirely inside the WASM sandbox.

Imports

// Resolve via the package root — the exports map sends this to the Node build.
import { analyze, findingCodes, coreVersion, init, SanctifierError } from "@sanctifier/sdk";

// Or reference the Node entry explicitly.
import { analyze, analyzeSync } from "@sanctifier/sdk/node";
The main field in package.json points to ./dist/node/index.cjs, so require("@sanctifier/sdk") works without any bundler or transpiler. The module field points to ./dist/node/index.js (ESM) for bundlers that respect it.

CJS vs ESM exports

The package exports map controls which file is resolved:
ConditionFile
"." + importdist/node/index.js (ESM)
"." + requiredist/node/index.cjs (CJS)
"./node" + importdist/node/index.js
"./node" + requiredist/node/index.cjs
"./browser"dist/browser/index.js
If you import @sanctifier/sdk in a bundler that sets the browser condition (Vite, webpack with target: "web", etc.), the bundler will resolve the browser build instead. In pure Node.js no extra configuration is needed.

Complete working example

The snippet below mirrors the patterns used in the SDK’s own test suite.
import { readFile } from "node:fs/promises";
import {
  analyze,
  findingCodes,
  coreVersion,
  SanctifierError,
  type AnalysisReport,
  type Finding,
  type Severity,
} from "@sanctifier/sdk";

// 1. (Optional) Print the embedded core version for audit logs.
console.log("sanctifier-core:", coreVersion());

// 2. Read a Soroban contract source file.
const source = await readFile("contracts/token.rs", "utf8");

// 3. Analyze with a high-severity failure gate.
let report: AnalysisReport;
try {
  report = await analyze(source, { failOn: "high" });
} catch (err) {
  if (err instanceof SanctifierError) {
    console.error(
      `Build failed: ${err.report.summary.total} finding(s); ` +
      `threshold breached at '${err.threshold}'.`
    );
    console.error(`First offender: ${err.message}`);
    process.exit(1);
  }
  throw err;
}

// 4. Print the severity summary.
const { summary } = report;
console.log(
  `Analysis passed — ${summary.total} finding(s): ` +
  `${summary.critical} critical, ${summary.high} high, ` +
  `${summary.medium} medium, ${summary.low} low, ${summary.info} info`
);

// 5. Print each finding.
for (const finding of report.findings) {
  const line = finding.line != null ? `:${finding.line}` : "";
  console.log(
    `  [${finding.severity.toUpperCase()}] ${finding.code}  ${finding.location}${line}`
  );
  console.log(`    ${finding.message}`);
}

Using failOn to gate CI pipelines

Set failOn to the minimum severity that should block a build. analyze() rejects with a SanctifierError as soon as the first offending finding is encountered. The full report is attached to the error so you can still render a summary.
import { analyze, SanctifierError } from "@sanctifier/sdk";

async function auditContract(source: string): Promise<void> {
  try {
    // Fail the build on any high or critical finding.
    await analyze(source, { failOn: "high" });
    console.log("No high/critical findings — audit passed.");
  } catch (err) {
    if (err instanceof SanctifierError) {
      const { summary, findings } = err.report;
      console.error(`Audit failed (threshold: ${err.threshold})`);
      console.error(`  Total: ${summary.total}`);

      // The full findings list is available even when failOn throws.
      for (const f of findings) {
        if (f.severity === "critical" || f.severity === "high") {
          console.error(`  ${f.code}  ${f.severity}  ${f.message}`);
        }
      }
      process.exit(1);
    }
    throw err;
  }
}
SanctifierError attaches the full report, including lower-severity findings, not just the ones that tripped the threshold. Check err.threshold to know which severity level was configured as the gate.

Filtering findings by severity

report.findings is a plain array. Use standard Array methods to slice it by severity, code, or any other property.
import { analyze } from "@sanctifier/sdk";
import type { Finding, Severity } from "@sanctifier/sdk";

const SEVERITY_ORDER: Record<Severity, number> = {
  info: 0,
  low: 1,
  medium: 2,
  high: 3,
  critical: 4,
};

const report = await analyze(source);

// All findings at medium severity or above.
const actionable: Finding[] = report.findings.filter(
  (f) => SEVERITY_ORDER[f.severity] >= SEVERITY_ORDER["medium"]
);

// Only authentication findings.
const authFindings = report.findings.filter(
  (f) => f.category === "authentication"
);

// Group by severity.
const bySeverity = Object.groupBy(report.findings, (f) => f.severity);
console.log("High findings:", bySeverity.high?.length ?? 0);

Using analyzeSync

The analyzeSync export runs the same analysis synchronously. It is only available from the Node entry — the browser entry does not expose it.
import { analyzeSync } from "@sanctifier/sdk/node";

// Useful in CLI scripts where top-level await is unavailable.
const report = analyzeSync(source, { failOn: "critical" });
console.log(report.summary.total, "finding(s)");
For long-running processes or servers, prefer analyze() (async) so the WASM execution does not block the Node event loop. For one-shot scripts and CLI tools, analyzeSync is perfectly fine.

Querying the finding code catalog

findingCodes() returns the live catalog embedded in the WASM bundle — one entry per stable code from S001 through S016 plus any additions in the installed version.
import { findingCodes } from "@sanctifier/sdk";
import type { FindingCodeEntry } from "@sanctifier/sdk";

const catalog: FindingCodeEntry[] = findingCodes();

// Print every code with its category and description.
for (const entry of catalog) {
  console.log(`${entry.code}  [${entry.category}]  ${entry.description}`);
}

// Look up a specific code.
const s001 = catalog.find((e) => e.code === "S001");
console.log(s001?.description);
// → "Mutating function missing require_auth"

Checking the core version

Use coreVersion() to record the exact analyzer version in audit logs or to assert a minimum version in automated tooling.
import { coreVersion } from "@sanctifier/sdk";

const version = coreVersion();
console.log("Running sanctifier-core", version);

// Assert a minimum version in tests.
const [major, minor] = version.split(".").map(Number);
if (major < 1) {
  throw new Error(`sanctifier-core ${version} is below the required 1.x baseline`);
}

Running custom rules

Supply customRules in AnalyzeOptions to add project-specific regex checks on top of the built-in rule set. Each match surfaces as a finding with code S007.
import { analyze } from "@sanctifier/sdk";
import type { CustomRule } from "@sanctifier/sdk";

const projectRules: CustomRule[] = [
  {
    name: "no_hardcoded_keys",
    pattern: "G[A-Z2-7]{55}",  // Matches Stellar public key format
    severity: "error",
  },
  {
    name: "no_todo_comments",
    pattern: "TODO|FIXME|HACK",
    severity: "warning",
  },
];

const report = await analyze(source, { customRules: projectRules });

// Custom rule matches are also in report.raw.custom_rule_matches.
for (const match of report.raw.custom_rule_matches) {
  console.log(
    `[${match.severity}] Rule '${match.rule_name}' matched at line ${match.line}: ${match.snippet}`
  );
}

init() in Node.js

The Node entry exports a no-op init() that returns Promise.resolve() immediately. This lets you write isomorphic library code that calls init() before analyze() without branching on the runtime.
// Works in both Node and browser without any runtime check.
import { init, analyze } from "@sanctifier/sdk";

await init(); // no-op in Node, loads WASM in browsers
const report = await analyze(source);

Build docs developers (and LLMs) love