Skip to main content

Core API

TypeScript/JavaScript API reference for @dotit/core.

npm install @dotit/core

Current version: 1.1.0. (Formerly published as @intenttext/core — those packages are deprecated with pointers; same code, same format.)

Parser

parseIntentText(source, options?)

Parse .it source into a document object.

import { parseIntentText } from "@dotit/core";

const doc = parseIntentText(`
title: Quarterly Report
meta: | author: Finance Team | date: 2026-03-31 | period: Q1

section: Revenue
text: Revenue grew 12% year-over-year.
metric: Revenue | value: $4.2M | target: $4.0M | status: above
`);

console.log(doc.metadata.title); // "Quarterly Report"
console.log(doc.blocks.length); // 5

Options:

interface ParseOptions {
extensions?: IntentExtension[]; // Custom block/inline parsers
includeHistorySection?: boolean; // Parse trust history (default: false)
}

Escaping: | (space-pipe-space) is the reserved property delimiter. Write a literal pipe as \| and a literal backslash as \\ — the parser unescapes them anywhere in content and property values, and documentToSource re-escapes on output, so escape round-trips are a fixpoint. Colons need no escaping inside content (quote: He said: watch this is fine).

parseIntentTextSafe(source, options?)

Like parseIntentText but collects errors instead of throwing.

import { parseIntentTextSafe } from "@dotit/core";

const result = parseIntentTextSafe(source);
if (result.errors.length > 0) {
console.error(result.errors);
} else {
const doc = result.document;
}

Renderer

renderHTML(document, options?)

Render to themed HTML.

import { parseIntentText, renderHTML } from "@dotit/core";

const doc = parseIntentText(source);
const html = renderHTML(doc, { theme: "corporate" });

renderPrint(document, options?)

Print-optimized HTML with @page rules, headers, footers, and watermarks.

import { renderPrint } from "@dotit/core";

const printHtml = renderPrint(doc, { theme: "print" });

collectPrintLayout(document)

Extract page, header, footer, and watermark blocks.

import { collectPrintLayout } from "@dotit/core";

const layout = collectPrintLayout(doc);
// layout.page, layout.header, layout.footer, layout.watermark, layout.breaks

Options:

interface RenderOptions {
theme?: string | IntentTheme; // Theme name or object
}

Theme

getBuiltinTheme(name)

import { getBuiltinTheme, listBuiltinThemes } from "@dotit/core";

const theme = getBuiltinTheme("corporate");
const names = listBuiltinThemes();
// ['corporate', 'minimal', 'warm', 'technical', 'print', 'legal', 'editorial', 'dark']

generateThemeCSS(theme, mode?)

Generate CSS custom properties from a theme object.

import { generateThemeCSS } from "@dotit/core";

const css = generateThemeCSS(theme, "web"); // or 'print'

registerBuiltinTheme(theme)

Register a custom theme for use by name.

import { registerBuiltinTheme } from "@dotit/core";

registerBuiltinTheme({
name: "brand",
version: "1.0",
fonts: {
body: "Georgia",
heading: "Georgia",
mono: "Courier",
size: "11pt",
leading: "1.6",
},
colors: {
text: "#1a1a1a",
heading: "#003366",
muted: "#666",
accent: "#003366",
border: "#ccc",
background: "#fff",
"code-bg": "#f5f5f5",
},
spacing: {
"page-margin": "1in",
"section-gap": "2rem",
"block-gap": "0.75rem",
indent: "0",
},
});

IntentTheme

interface IntentTheme {
name: string;
version: string;
description?: string;
author?: string;
fonts: ThemeFonts;
colors: ThemeColors;
spacing: ThemeSpacing;
blocks?: Record<string, Record<string, string | boolean>>;
print?: ThemePrint;
}

interface ThemeFonts {
body: string;
heading: string;
mono: string;
size: string;
leading: string;
}

interface ThemeColors {
text: string;
heading: string;
muted: string;
accent: string;
border: string;
background: string;
"code-bg": string;
"trust-approved"?: string;
"trust-signed"?: string;
"trust-frozen"?: string;
"trust-warning"?: string;
watermark?: string;
}

interface ThemeSpacing {
"page-margin": string;
"section-gap": string;
"block-gap": string;
indent: string;
}

interface ThemePrint {
"header-font-size"?: string;
"footer-font-size"?: string;
"header-color"?: string;
"footer-color"?: string;
}

Query

queryBlocks(document, options)

Execute a structured query against document blocks.

import { parseIntentText, queryBlocks } from "@dotit/core";

const doc = parseIntentText(source);
const result = queryBlocks(doc, "type=deadline sort:date:asc limit:10");
// result.blocks, result.total, result.matched

parseQuery(queryString)

Parse query syntax into a QueryOptions object.

import { parseQuery } from "@dotit/core";

const opts = parseQuery(
"type=task owner=Ahmed due<2026-03-01 sort:due:asc limit:10",
);

queryDocument(document, query?)

Simple, intuitive filter API. All conditions are ANDed; type arrays are ORed. Returns matching blocks (never mutates the document).

import { queryDocument } from "@dotit/core";

const tasks = queryDocument(doc, { type: "step" });
const urgent = queryDocument(doc, {
type: ["task", "deadline"],
section: "Scope",
properties: { priority: "high" },
limit: 10,
});
interface SimpleQueryOptions {
type?: string | string[];
content?: string | RegExp; // case-insensitive substring or regex
properties?: Record<string, string | RegExp>;
section?: string | RegExp;
limit?: number;
}

formatQueryResult(result, format?)

Format query results as "simple" text (default), "table", or "json".

import { formatQueryResult } from "@dotit/core";

const table = formatQueryResult(result, "table");
const json = formatQueryResult(result, "json");

Query types

interface QueryOptions {
where?: QueryClause[];
sort?: QuerySort[];
limit?: number;
offset?: number;
}

interface QueryClause {
field: string;
operator:
| "="
| "!="
| "<"
| ">"
| "<="
| ">="
| "contains"
| "startsWith"
| "exists";
value?: string | number | boolean;
}

interface QuerySort {
field: string;
direction: "asc" | "desc";
}

interface QueryResult {
blocks: IntentBlock[];
total: number;
matched: number;
}

Merge

mergeData(document, data, options?)

Resolve {{variable}} interpolations (dot paths, array indices, each: table loops).

import { parseIntentText, mergeData } from "@dotit/core";

const template = parseIntentText(`
title: Invoice {{number}}
text: Amount due: {{amount}}
`);

const merged = mergeData(template, { number: "INV-2847", amount: "$5,000" });

parseAndMerge(source, data, options?)

Parse and merge in one call. options.missing controls unresolved {{fields}}: "keep" (default — shows the marker, good while authoring) or "blank" (renders empty — use for finished documents so an invoice never prints {{customer.phone}}).

import { parseAndMerge } from "@dotit/core";

const doc = parseAndMerge(templateSource, invoiceData, { missing: "blank" });

Document styles

collectDocumentStyles(document) / documentStyleCSS(document, selectorMap?, prefix?)

Read a document's style: rules (sanitized; unknown targets dropped) and build the CSS for them. renderHTML/renderPrint call this automatically; pass a custom selectorMap to apply the same rules to your own markup (the web editor does exactly this for its live canvas).

import { collectDocumentStyles, documentStyleCSS } from "@dotit/core";

collectDocumentStyles(doc);
// [{ target: "section", declarations: "color: #0a7; font-weight: 600" }]

Trust

sealDocument(source, options)

Seal a document: appends a sign: line (optional) and a freeze: line carrying the SHA-256 content hash. Returns the updated source — store it exactly as returned (the hash covers the exact bytes).

import { sealDocument } from "@dotit/core";

const result = sealDocument(source, { signer: "Ahmed Al-Rashid", role: "CEO" });
// result.success, result.hash ("sha256:…"), result.source (sealed text), result.at

verifyDocument(source)

Verify document integrity against its seal.

import { verifyDocument } from "@dotit/core";

const result = verifyDocument(source);
// result.intact, result.hash, result.frozen, result.signers

computeDocumentHash(source)

Compute SHA-256 hash of document content (above history boundary).

import { computeDocumentHash } from "@dotit/core";

const hash = computeDocumentHash(source);
// "sha256:a1b2c3..."

computeTrustDiff(before, after)

Compute semantic diff for trust history writing.

import { computeTrustDiff } from "@dotit/core";

const diff = computeTrustDiff(oldDoc, newDoc);
// diff.added, diff.removed, diff.modified, diff.moved, diff.unchanged

Trust types

interface SealOptions {
signer: string;
role?: string;
skipSign?: boolean; // freeze without adding a sign: line
}

interface SealResult {
success: boolean;
hash: string; // "sha256:…"
source: string; // the sealed text — store exactly as returned
at: string;
error?: string;
}

interface VerifyResult {
intact: boolean;
frozen: boolean;
frozenAt?: string;
signers?: Array<{
signer: string;
role?: string;
at: string;
valid: boolean;
signedCurrentVersion: boolean;
}>;
hash?: string;
expectedHash?: string;
error?: string;
warning?: string;
}

interface TrustDiff {
added: BlockSnapshot[];
removed: BlockSnapshot[];
modified: BlockSnapshot[];
moved: BlockSnapshot[];
unchanged: BlockSnapshot[];
}

History

updateHistory(previousSource, currentSource, options)

Compute diff between versions and write history section.

import { updateHistory } from "@dotit/core";

const updated = updateHistory(oldSource, newSource, { by: "Ahmed" });

parseHistorySection(raw)

Parse a history section string into structured data.

import { parseHistorySection } from "@dotit/core";

const { registry, revisions, registryIntact } =
parseHistorySection(historyText);

Index

buildShallowIndex(folder, files, coreVersion)

Build a shallow .it-index for a folder.

import { buildShallowIndex } from "@dotit/core";

const index = buildShallowIndex("./contracts", filesMap, "1.0.0");

buildIndexEntry(document, source, modifiedAt)

Extract metadata and block summaries for a single file.

import { buildIndexEntry } from "@dotit/core";

const entry = buildIndexEntry(doc, source, "2026-03-15T10:00:00Z");

composeIndexes(indexes)

Merge multiple folder indexes into a flat result list.

import { composeIndexes, queryComposed } from "@dotit/core";

const all = composeIndexes([contractsIndex, invoicesIndex]);
const deadlines = queryComposed(all, parseQuery("type=deadline"));

checkStaleness(index, document, source)

Check if an index entry needs refresh.

updateIndex(existing, filename, document, source, modifiedAt)

Update or add a single entry in an existing index.

Index types

interface ItIndex {
version: "1";
scope: "shallow";
folder: string;
built_at: string;
core_version: string;
files: Record<string, IndexFileEntry>;
}

interface IndexFileEntry {
hash: string;
modified_at: string;
metadata: { title?: string; type?: string; domain?: string; track?: boolean };
blocks: IndexBlockEntry[];
}

interface IndexBlockEntry {
type: string;
content: string;
section?: string;
properties: Record<string, string | number>;
}

interface ComposedResult {
file: string;
block: IndexBlockEntry;
}

Diff

diffDocuments(before, after)

Semantic diff between two parsed documents.

import { diffDocuments } from "@dotit/core";

const diff = diffDocuments(oldDoc, newDoc);
// diff.added, diff.removed, diff.modified, diff.unchanged
// diff.summary — "2 added, 1 removed, 3 modified, 10 unchanged"

Workflow

executeWorkflow(document, runtime)

Run a workflow document against a runtime. Policy enforcement and gate checks are applied before execution begins.

import { executeWorkflow } from "@dotit/core";

const result = await executeWorkflow(doc, {
executeStep: async (block) => {
// dispatch block to your agent/tool
return { status: "completed", output: "Done" };
},
evaluateDecision: async (block) => {
return { branch: "yes" };
},
checkGate: async (block) => {
return { passed: true };
},
dryRun: false,
});

console.log(result.status); // "completed"
console.log(result.executedSteps); // ["step-1", "step-2", ...]

Return value:

interface ExecutionResult {
status: "completed" | "gate_blocked" | "policy_blocked" | "error" | "dry_run";
executedSteps: string[];
skippedSteps: string[];
blockingGate?: string;
blockingPolicy?: string;
error?: string;
dryRunPlan?: string[];
}

Status reference:

StatusMeaning
completedAll steps executed successfully
gate_blockedA gate: check returned passed: false — execution halted at that gate
policy_blockedA policy: block's requires: gate was not satisfied before execution
errorA step threw an unhandled exception
dry_runRuntime dryRun: true — plan returned without execution

Runtime interface:

interface WorkflowRuntime {
executeStep: (block: IntentBlock) => Promise<StepResult>;
evaluateDecision?: (block: IntentBlock) => Promise<DecisionResult>;
checkGate?: (block: IntentBlock) => Promise<GateResult>;
dryRun?: boolean;
}

interface StepResult {
status: "completed" | "error";
output?: string;
error?: string;
}

interface GateResult {
passed: boolean;
reason?: string;
}

interface DecisionResult {
branch: string;
}

Policy enforcement:

Before the execution loop starts, executeWorkflow checks every policy: block for requires: gate. If the required gate: block has not passed, the function returns immediately with status: "policy_blocked" and does not execute any steps.

// policy enforcement example
const doc = parseIntentText(`
policy: | requires: gate | gate: security-review
step: Deploy to production
`);

const result = await executeWorkflow(doc, runtime);
// result.status === "policy_blocked" if security-review gate is unmet
// result.blockingPolicy === "policy-id"

extractWorkflow(document)

Extract a task DAG from step/gate/decision blocks without executing.

import { extractWorkflow } from "@dotit/core";

const graph = extractWorkflow(doc);
// graph.entryPoints, graph.steps, graph.executionOrder, graph.gatePositions
interface WorkflowGraph {
entryPoints: string[];
steps: Record<string, WorkflowStep>;
executionOrder: string[][]; // Topologically sorted layers
gatePositions: number[];
hasTerminal: boolean;
warnings: string[];
}

interface WorkflowStep {
block: IntentBlock;
dependsOn: string[];
dependedOnBy: string[];
isGate: boolean;
isTerminal: boolean;
isParallel: boolean;
}

Source

documentToSource(document)

Convert a parsed document back to .it source text.

import { documentToSource } from "@dotit/core";

const source = documentToSource(doc);

Aliases round-trip as written. When a line used an alias — including the Arabic keyword aliases (عنوانtitle, مهمةtask, صفrow, توقيعsign, …) — the parser records the keyword as written on block.keywordAlias, and documentToSource re-emits that form instead of normalizing to the canonical English keyword. An Arabic document stays Arabic, abstract: stays abstract:, and a sealed document keeps its hash through a parse → serialize cycle. Table keywords (أعمدة/صف, headers) are preserved the same way. Reserved characters are re-escaped on output (\|, \\), so escape round-trips are stable too.

Conversion

convertMarkdownToIntentText(markdown)

Convert Markdown to .it format.

import { convertMarkdownToIntentText } from "@dotit/core";

const itSource = convertMarkdownToIntentText("# My Doc\n\nSome text");

convertHtmlToIntentText(html)

Convert HTML to .it format.

import { convertHtmlToIntentText } from "@dotit/core";

const itSource = convertHtmlToIntentText("<h1>My Doc</h1><p>Some text</p>");

Validation

validateDocument(document, schema)

Validate a document against a schema.

import { validateDocument, PREDEFINED_SCHEMAS } from "@dotit/core";

const result = validateDocument(doc, PREDEFINED_SCHEMAS["project"]);
// result.valid, result.errors, result.warnings

validateDocumentSemantic(document)

Semantic validation: cross-references, duplicate IDs, empty sections, unresolved variables.

import { validateDocumentSemantic } from "@dotit/core";

const result = validateDocumentSemantic(doc);
// result.valid, result.issues

createSchema(name, config)

Create a custom validation schema.

import { createSchema } from "@dotit/core";

const schema = createSchema("invoice", {
requiredBlocks: ["title", "note"],
blockSchemas: {
title: { type: "title", content: { required: true, minLength: 3 } },
},
});

Predefined schemas

project, meeting, article, checklist, agentic

Ask (AI Query)

askDocuments(results, question, options?)

Natural language query over indexed documents. Requires ANTHROPIC_API_KEY.

import { askDocuments, composeIndexes } from "@dotit/core";

const all = composeIndexes([index1, index2]);
const answer = await askDocuments(all, "Which contracts expire this quarter?");
interface AskOptions {
maxTokens?: number; // default: 1024
format?: "text" | "json";
}

Core types

IntentBlock

interface IntentBlock {
id: string;
type: BlockType;
keywordAlias?: string; // keyword AS WRITTEN when the line used an alias (incl. Arabic) — re-emitted on serialize
content: string;
originalContent?: string;
properties?: Record<string, string | number>;
inline?: InlineNode[];
children?: IntentBlock[];
table?: {
headers?: string[];
rows: string[][];
headersKeyword?: string; // keyword as written for the headers line (e.g. أعمدة, headers)
rowKeyword?: string; // keyword as written for row lines (e.g. صف)
};
}

IntentDocument

interface IntentDocument {
version?: string;
blocks: IntentBlock[];
metadata?: IntentDocumentMetadata;
diagnostics?: Diagnostic[];
history?: HistorySection;
}

IntentDocumentMetadata

interface IntentDocumentMetadata {
title?: string;
summary?: string;
language?: "ltr" | "rtl";
agent?: string;
model?: string;
context?: Record<string, string>;
version?: string;
tracking?: { version: string; by: string; active: boolean };
signatures?: Array<{
signer: string;
role?: string;
at: string;
hash: string;
valid?: boolean;
}>;
freeze?: { at: string; hash: string; status: "locked" };
meta?: Record<string, string>;
}

BlockType

Union type covering all 38 canonical keywords plus extension namespace blocks.

Canonical (38 total):

  • Document Identity (4): title, summary, meta, context
  • Structure (3): section, sub, toc
  • Content (7): text, info, quote, cite, code, image, link
  • Tasks (3): task, done, ask
  • Data (3): columns, row, metric
  • Agentic Workflow (7): step, decision, gate, trigger, result, policy, audit
  • Trust (5): track, approve, sign, freeze, amendment
  • Layout (6): page, header, footer, watermark, break, style

Extension blocks:

Extension blocks have the form x-ns: type (e.g., x-agent: loop, x-doc: def). They are typed as { type: string; namespace: string } and passed through the renderer without core evaluation. See Extension keywords →.

InlineNode

type InlineNode =
| { type: "text"; value: string }
| { type: "bold"; value: string }
| { type: "italic"; value: string }
| { type: "strike"; value: string }
| { type: "highlight"; value: string }
| { type: "code"; value: string }
| { type: "inline-quote"; value: string }
| { type: "inline-note"; value: string }
| { type: "date"; value: string }
| { type: "mention"; value: string }
| { type: "tag"; value: string }
| { type: "label"; value: string }
| { type: "link"; value: string; url: string }
| { type: "footnote-ref"; value: string };

Diagnostic

interface Diagnostic {
severity: "error" | "warning";
message: string;
line: number;
column: number;
code: string;
}

ALIASES

Record mapping alias keywords to their canonical types. Includes callout aliases (warning:info: with type: warning), shorthand forms, per-category aliases, and the 33 registered Arabic aliases (عنوانtitle, قسمsection, مهمةtask, مؤشرmetric, اعتمادapprove, تجميدfreeze, …) — an Arabic document gets full canonical semantics, and aliases serialize back as written. See Aliases Reference.

KEYWORDS

Array of all recognized keyword strings — 38 canonical keywords plus their registered aliases.

Server-side PDFs — @dotit/pdf

Core stays zero-dependency; real PDF bytes on a server (email attachments, compliance archiving, batch statement runs) come from the opt-in companion package:

npm i @dotit/pdf
npm i puppeteer # or: puppeteer-core + your system Chrome (CHROME_PATH)
import { issuePDF } from "@dotit/pdf";

const { source, hash, at, pdf } = await issuePDF(templateSource, data, {
signer: "Acme Billing",
});
// `source` is the SEALED .it text — store it on the record (the verifiable legal
// artifact); `pdf` is the bytes you email/archive.

Also: issueDocument() (same flow minus Chrome — returns print-ready HTML for sidecars like Gotenberg), renderPDF(), htmlToPDF(), and createPdfRenderer() for batch runs. Full guide: ERP / App Integration.