Skip to content

Latest commit

 

History

History
154 lines (119 loc) · 7.41 KB

api.md

File metadata and controls

154 lines (119 loc) · 7.41 KB
title Programmatic API

Programmatic API

The package exposes a library entry point at structured-context/api for embedding the tooling in other applications. It runs the same Read → Validate → Graph pipeline the CLI uses; see architecture.md for that flow.

Main building blocks

Export Purpose
loadConfig() Load the resolved config (spaces, schemas) from the standard config locations.
createSpaceContext(spaceName, config, options?) Build a SpaceContext for a named space (resolves bundled, local, or plugin schemas).
readSpace(context) Read a space into a flat ReadSpaceResult (nodes, parseIssues, diagnostics, source).
loadSpaceGraph(spaceName, config, options?) Read, assemble, and optionally filter a space into a navigable SpaceGraph. options.hierarchy names the hierarchy that structures the graph (default: the main hierarchy).
assembleSpaceGraph(context, options?) Assemble (and optionally filter) a graph from a context you already hold — reuses the context instead of rebuilding it.
buildSpaceGraph(nodes, hierarchy?) Build a navigable SpaceGraph directly from an array of SpaceNodes and a Hierarchy definition.
exportSpaceGraph(nodes, metadata) Turn nodes and the schema's hierarchies into plain JSON for another process. See Space graph format.
spaceGraphFrom(data, options?) Build a SpaceGraph from that JSON. options.hierarchy names the hierarchy that structures it (default: the main hierarchy).
validateSpace(context, options?) Run full validation (schema, references, rules, duplicates) over a space. Semantic rules run only when semantic is passed; it takes the same values as sctx validate --semantic, with the provider coming from config and the environment.
validateFile(filePath, config) Validate a single file within its space.
listSchemas(options?) List the schemas available to a config: bundled, plugin-contributed and configured.
formatWikilink(target, alias?) Format a target into an Obsidian wikilink ([[target]] or `[[target
parseWikilink(link) Parse a wikilink or target string into { target, anchor?, displayText? }.
wikilinkToTarget(link) Extract the plain target lookup key from a wikilink string.
buildTargetIndex(nodes) Build a fast lookup index mapping link target strings to nodes (marking ambiguous targets as null).

ValidationResult.ruleViolations carries each violation's effective severity (error, warning or info). Only error severity violations should be treated as failures; validateFile reports warning and info rule violations under warnings rather than errors.

Each ValidationResult entry about a node carries its nodeId, and, where the entry is tied to a location, an optional source ({ file, line?, column? }). duplicateErrors lists a sources location for each node that shares the id. See Validation results and nodes.

A SpaceGraph classifies nodes against one hierarchy, and indexes child nodes and typed edges in both directions.

Space graph format

exportSpaceGraph returns the space graph format:

{ "formatVersion": 1, "hierarchies": [/* Hierarchy */], "mainHierarchy": "…", "nodes": [/* SpaceNode */] }

Edges are each node's resolvedParents. An exported subset keeps its edges to nodes outside the subset. The receiver picks the hierarchy when it builds the graph.

structured-context/browser holds what a browser needs to load the data, and imports no Node built-ins. Everything in it is also exported from structured-context/api. Derive other types from its exports, such as Parameters<typeof spaceGraphFrom>[0] for the data.

// Server
const { nodes } = await readSpace(context);
const data = exportSpaceGraph(nodes, context.schema.metadata); // or graph.nodes.values() for a filtered graph

// Browser
import { spaceGraphFrom } from 'structured-context/browser';
const graph = spaceGraphFrom(data, { hierarchy: 'work' });

sctx dump <space> --graph prints the same data. The JSON Schema for each released formatVersion ships in schemas/generated/space-graph/, named by version, and latest.json is the newest.

Testing utilities

Test fixture builders are available via structured-context/testing for plugin and integration tests:

import {
  makeNode,
  makeSpaceNode,
  makeHierarchy,
  makeLevel,
  makeRelationship,
  makeParentRef,
} from 'structured-context/testing';

Usage

import {
  loadConfig,
  loadSpaceGraph,
  assembleSpaceGraph,
  createSpaceContext,
  readSpace,
  validateSpace,
} from 'structured-context/api';

const config = loadConfig();

// Common path: read, assemble, and filter in one call.
// `filter` resolves a named view from the space config, falling back to a raw filter expression.
const graph = await loadSpaceGraph('my-space', config, { filter: 'my-view' });

// Read once, then assemble a graph and validate from the same read and context.
// Reusing the context avoids re-reading the space *and* recompiling the schema.
const context = createSpaceContext('my-space', config);
const readResult = await readSpace(context);
const sharedGraph = await assembleSpaceGraph(context, { readResult });
const validation = await validateSpace(context, { readResult });

Validation vs. graph assembly

loadSpaceGraph keeps every node whose type the schema defines, including nodes with field errors such as an invalid enum value. Nodes of an unknown type are left out. The graph is not a validation pass: use validateSpace (or validateFile) for field errors, broken references, rule violations and duplicates. Each entry in its nodeErrors lists the node's issues, one formatted message per problem.

Hand-assembled config

config does not have to come from loadConfig() — you can construct a Config in memory and pass it to createSpaceContext, loadSpaceGraph, or validateFile, the same way the CLI does. Provide each space's path and a resolvable schema (absolute paths are simplest):

import { loadSpaceGraph, type Config } from 'structured-context/api';

const config: Config = {
  spaces: [{ name: 'my-space', path: '/abs/path/to/space', schema: '/abs/path/to/schema.json' }],
};
const graph = await loadSpaceGraph('my-space', config, { configDir: process.cwd() });

Relative paths in a space's plugin config are resolved against the directory of the config file that defined the space. A hand-assembled Config has no such file, so you must pass an explicit configDir to anchor them (createSpaceContext, loadSpaceGraph, and validateFile all accept it). Omitting it throws rather than silently guessing the working directory — prefer absolute paths so the anchor never matters.

Plugin render hooks

Plugins declare render formats via render.formats and implement render(context, graph, options). The hook may return a single string or multi-file output:

import type { RenderHook, RenderResult, RenderMultiFileOutput } from 'structured-context/api';

const myRenderHook: RenderHook = {
  formats: [{ name: 'my-format', description: 'Outputs multiple files' }],
  render(context, graph, options) {
    return {
      files: [
        { path: 'section-1.md', content: '...' },
        { path: 'subfolder/section-2.md', content: '...' },
      ],
    };
  },
};