Reference

API

The public TypeScript API of the Appling packages, export by export, with one usage example per package.

Every tool in Appling is built on five public packages. Use them to build your own checks, reports, editors, generators or agents. All packages are ES modules for Node 24.

PackagePurpose
@appling/langGrammar, parser, scoping, validation, expression typing, canonical printer
@appling/lang/lspThe language server services
@appling/modelSemantic graph, edit operations, releases
@appling/codegenCode generation core: plans, registry, migrations
@appling/aiWorkspace access for AI, MCP server, chat

Language: @appling/lang

Parse and validate files with the Langium services. Install langium too, for URI and NodeFileSystem.

import { URI } from 'langium';
import { NodeFileSystem } from 'langium/node';
import { createApplingServices, print, type Model } from '@appling/lang';

const { shared } = createApplingServices(NodeFileSystem);
const document = await shared.workspace.LangiumDocuments.getOrCreateDocument(URI.file('/path/to/sales.appling'));
await shared.workspace.DocumentBuilder.build([document], { validation: true });

console.log(document.diagnostics);
const canonical = print(document.parseResult.value as Model);
ExportPurpose
createApplingServices(context)Creates the Langium services with Appling scoping and validation; returns { shared, Appling }
ApplingModule, ApplingServicesThe Appling service module and the services type
buildSources(sources)Builds in memory sources { [uri]: text } as one validated workspace; returns a Workspace ({ documents, services })
errorsOf(workspace)The error messages of a built workspace
print(root)A parsed model in canonical form, the text appling fmt writes
printRange(node)The canonical text of one node in its document with the gaps around it: { start, end, text } (PrintedRange)
modulePath(module)The dotted path of a module, such as a.b
projectionSourceEntity(projection)The entity behind a projection, also when its source is a query
ApplingScopeProviderThe scope provider; lookup(container, type, name) resolves a dotted name as a reference would
ExpressionTyperTypes expressions: checkModel(model) fills types, targets (the element each name stands for) and problems (Problem[])
show(type), typeOfRef(ref)The display name of a Type, such as Money?, and the Type a TypeRef declares
LAYER_OF, LayerThe layer of every top level declaration type
AST types and guardsModel, Module, Entity, isEntity and the rest of the generated AST

Language server: @appling/lang/lsp

import { startLanguageServer } from 'langium/lsp';
import { NodeFileSystem } from 'langium/node';
import { createApplingLspServices } from '@appling/lang/lsp';

startLanguageServer(createApplingLspServices({ connection, ...NodeFileSystem }).shared);
ExportPurpose
createApplingLspServices(context)The Appling services with Langium’s LSP services; returns { shared, Appling }
ApplingLspModuleThe Appling specific LSP services: formatter and hover
ApplingFormatterFormats a whole document into canonical form
ApplingHoverProviderHover with kind, qualified name, layer and doc comment

@appling/lang/monarch exports the Monarch grammar as default. Both are described in Editors.

Graph and edits: @appling/model

Build the semantic graph of in memory sources and apply an edit:

import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { URI } from 'langium';
import { buildSources, type Model } from '@appling/lang';
import { applyEdit, buildGraph, dependencies } from '@appling/model';

const uri = URI.file(resolve('models/shop.appling')).toString();
const sources = { [uri]: readFileSync('models/shop.appling', 'utf8') };

const { documents, services } = await buildSources(sources);
const graph = buildGraph(documents.map(d => d.parseResult.value as Model), services);
console.log(dependencies(graph)); // [{ from: 'sales', to: 'crm', count: 1 }]

const result = await applyEdit(sources, { op: 'add', parent: 'sales.Order', source: 'note: Text?' });
console.log(result.ok ? result.edits : result.errors);
ExportPurpose
buildGraph(models, services)Nodes and edges of resolved models, in document order
dependencies(graph)Module to module dependencies with the number of references behind each
isElement(node)Whether an AST node becomes a graph node
qualifiedNames(models)A map from every element AST node to its qualified name, in document order
parentElement(node, qns)The nearest element containing an AST node
Graph, GraphNode, GraphEdge, Dependency, Level, ExprData, TypeDataThe graph types, see Semantic graph
applyEdit(sources, operation, { release })Applies one edit operation, see Edit operations
EditOperation, EditResult, TextEditThe edit types
writeRelease(root, version, graph)Writes <root>/.appling/releases/<version>.json and returns its path; throws for an invalid or existing version
readReleases(root), latestRelease(releases)All releases of a workspace, and the one with the highest version
releasesDir(root)<root>/.appling/releases
migrationBlockName(version)The migration block for steps after a release: 1.0.0 gives since_v1_0_0
Release{ version, created, graph }
findApplingFiles(path)The .appling files at a path, searching folders recursively and skipping hidden folders and node_modules

Code generation: @appling/codegen

Plan generation for a graph without running it, as appling generate --plan does:

import { describe, loadExtension, plan } from '@appling/codegen';

const extension = await loadExtension('@appling/codegen-django', '.');
const result = plan(graph, extension, 'shop');
for (const { node, step } of result.steps) console.log(node, describe(step));
console.log(result.entries.filter(e => e.status !== 'generated').map(e => e.qn));

Configuration and extensions:

ExportPurpose
readConfig(root)<root>/appling.config.json as Config, or {}
loadExtension(name, root)Imports an extension package, resolved from root, and returns its default export
Config, Target, CONFIGThe configuration types and the file name
CodegenExtensionThe extension contract: name, prerequisites, test, setup, steps, migrate
Step, CommandStep, TemplateStep, TaskStepThe three step kinds
MapContext, PrerequisiteWhat an extension gets per node, and a tool it needs

Planning and running:

ExportPurpose
plan(graph, extension, name, options)The ordered steps and one registry entry per node (Plan)
describe(step)A one line description of a step, as --plan prints it
mapContext(graph, name, options)The MapContext passed to extensions
checkPrerequisites(prerequisites)One message per missing tool
runPlan(plan, out, log)Runs the steps into out and writes the registry
runSteps(steps, out, log)Runs steps without writing a registry
PROJECTThe node name of setup steps, (project)
PlannedStep, PlanA step with its node, and the result of plan

Registry:

ExportPurpose
readRegistry(out), writeRegistry(out, registry)<out>/.appling/tasks.json
Registry, RegistryEntry, Missing, StatusThe registry types, see Configuration
REGISTRYThe registry path inside a project, .appling/tasks.json

Migrations:

ExportPurpose
pendingChanges(graph, applied, stamp)The model’s migration steps not in applied, with each element’s qualified name before and now (MigrationChange[])
planMigrations(graph, extension, name, options, out, changes)Asks the extension’s migrate for the steps of every change
runMigrations(out, plan, changes, log)Runs them, adds the tasks to the registry and records the changes as applied
readApplied(out)The ids in <out>/.appling/migrations.json
MigrationChange, ProjectFiles, APPLIEDThe change type, read access to the project for extensions, and the applied file path

How to write an extension is described in Writing extensions.

AI: @appling/ai

Work with a workspace folder the way the MCP tools do:

import { Project, queryGraph } from '@appling/ai';

const project = new Project('models');
console.log(await project.check()); // [] when valid
console.log(queryGraph(await project.graph(), { kind: 'Entity' }));

const result = await project.edit({ op: 'rename', target: 'sales.Order.total', name: 'amount' });
if (result.ok) project.write(result.sources);

Workspace:

ExportPurpose
ProjectA workspace folder: sources(), check(), graph(), source(qn), edit(operation) with the latest release, write(sources), and the registry methods targets(), nextTask(target), updateTask(qn, status, note, target), runTests(target)
queryGraph(graph, query)What the graph_query tool returns
MAX_ATTEMPTSAttempts after which a registry entry stops coming back, 3
Sources{ [uri]: text }

Tools and MCP:

ExportPurpose
MODEL_TOOLS, REGISTRY_TOOLS, ToolsThe tool definitions: description, zod input shape and run(context, input)
operationSchemaThe zod schema of an edit operation
ToolContext, diskContext(project), ProposalContextWhere tools read and write: the files on disk, or a working copy that collects operations
createMcpServer(root)An MCP server with all tools and the implement_next_task prompt
serveHttp(root, port)The MCP server over Streamable HTTP on 127.0.0.1; returns { url, close() }
REGISTRY_PROMPT, CHAT_SYSTEMThe registry skill text and the chat system prompt

Chat:

ExportPurpose
chat(root, messages, model)One chat turn on a working copy; yields ChatEvents: text, tool, proposal, error
modelSettings(root), DEFAULT_MODEL, ModelSettingsThe ai section of appling.config.json with its defaults
languageModel(settings)The AI SDK model for the settings
Proposal, Hunk, ChatEventThe proposal and event types
hunks(root, before, after), graphDiff(before, after)The text and graph differences of a proposal

The tools and the chat are described in AI tools.