
Security News
PHP Foundation Says Chainguard Broke Security Embargo Ahead of Emergency Patches
The PHP Foundation says Chainguard broke a security embargo ahead of critical RCE patches, as leaks disrupted the coordinated release.
@zenbase/llml
Advanced tools
Lightweight Language Markup Language - Convert nested data structures to human-readable, XML-like markup
The most elegant way to generate structured text in TypeScript/JavaScript.
LLML transforms your data into beautifully formatted XML-like markup with zero fuss and maximum flexibility. Perfect for context engineering, configuration generation, and structured document creation.
import { llml } from '@zenbase/llml';
// Simple values
console.log(llml({ greeting: "Hello World" }));
// Output: <greeting>Hello World</greeting>
// Lists become numbered items
console.log(llml({ tasks: ["Buy milk", "Walk dog", "Code LLML"] }));
// Output:
// <tasks>
// <tasks-1>Buy milk</tasks-1>
// <tasks-2>Walk dog</tasks-2>
// <tasks-3>Code LLML</tasks-3>
// </tasks>
// Complex nested structures
console.log(llml({
title: "My Project",
features: ["Fast", "Simple", "Powerful"],
config: { debug: true, version: "1.0" }
}));
// Output:
// <title>My Project</title>
// <features>
// <features-1>Fast</features-1>
// <features-2>Simple</features-2>
// <features-3>Powerful</features-3>
// </features>
// <config>
// <debug>true</debug>
// <version>1.0</version>
// </config>
# Using bun (recommended)
bun install
# Using npm
npm install
# Using yarn
yarn install
LLML uses an extensible formatter system. You can create custom formatters for specialized data types:
import { llml, vibeXML } from '@zenbase/llml';
// Custom formatters for domain objects
class User {
constructor(public name: string, public email: string) {}
}
class Money {
constructor(public amount: number, public currency: string) {}
}
// Create custom formatters
const customFormatters = new Map();
customFormatters.set(
(v: unknown): v is User => v instanceof User,
(v: User) => `${v.name} <${v.email}>`
);
customFormatters.set(
(v: unknown): v is Money => v instanceof Money,
(v: Money) => `${v.amount} ${v.currency}`
);
// Use with vibeXML
const formatters = vibeXML({ formatters: customFormatters });
const result = llml({
customer: new User("Alice", "alice@example.com"),
price: new Money(100, "USD")
}, formatters);
// Output:
// <customer>Alice <alice@example.com></customer>
// <price>100 USD</price>
Custom formatters take precedence over built-in ones. The first matching formatter wins:
// Override built-in boolean formatting
const customFormatters = new Map();
customFormatters.set(
(v: unknown): v is boolean => typeof v === "boolean",
(v: boolean) => v ? "YES" : "NO"
);
const formatters = vibeXML({ formatters: customFormatters });
const result = llml({ enabled: true, disabled: false }, formatters);
// Output:
// <enabled>YES</enabled>
// <disabled>NO</disabled>
const instructions = `
Step 1: Install LLML
Step 2: Import llml
Step 3: Create magic
`;
console.log(llml({ instructions }));
// Output:
// <instructions>
// Step 1: Install LLML
// Step 2: Import llml
// Step 3: Create magic
// </instructions>
const promptData = {
system: "You are a helpful assistant",
rules: [
"Be concise and clear",
"Provide examples when helpful",
"Ask clarifying questions"
],
context: {
userLevel: "beginner",
topic: "TypeScript programming"
}
};
console.log(llml(promptData));
// Output:
// <system>You are a helpful assistant</system>
// <rules>
// <rules-1>Be concise and clear</rules-1>
// <rules-2>Provide examples when helpful</rules-2>
// <rules-3>Ask clarifying questions</rules-3>
// </rules>
// <context>
// <userLevel>beginner</userLevel>
// <topic>TypeScript programming</topic>
// </context>
LLML automatically handles common JavaScript types:
// Dates, URLs, and other built-in objects
const data = {
timestamp: new Date("2023-01-01T00:00:00Z"),
homepage: new URL("https://example.com"),
enabled: true,
count: 42
};
console.log(llml(data));
// Output:
// <timestamp>Sun Jan 01 2023 00:00:00 GMT+0000 (Coordinated Universal Time)</timestamp>
// <homepage>https://example.com/</homepage>
// <enabled>true</enabled>
// <count>42</count>
Perfect for structuring complex prompts:
const prompt = llml({
role: "Senior TypeScript Developer",
task: "Code review the following function",
criteria: ["Performance", "Readability", "Best practices"],
code: functionToReview
});
Generate clean config files:
const config = llml({
database: { host: "localhost", port: 5432 },
features: ["logging", "caching", "monitoring"],
environment: "production"
});
Create structured documents:
const document = llml({
title: "API Documentation",
sections: ["Authentication", "Endpoints", "Examples"],
metadata: { version: "2.1", author: "Dev Team" }
});
Run the comprehensive test suite:
# Run tests once
bun test
# Run tests in watch mode
bun test:watch
# Run with coverage
bun test --coverage
The test suite covers:
LLML TypeScript supports:
# Clone the repo
git clone https://github.com/yourusername/llml.git
cd llml/ts
# Install dependencies
bun install
# Run tests
bun test
# Run tests in watch mode
bun test:watch
# Type checking
bun run tsc --noEmit
llml(data, formatters?)Parameters:
data: unknown - The data to convert to markupformatters?: Formatters - Optional custom formatters (defaults to vibeXML())Types:
type Predicate = (value: unknown) => boolean;
type Formatter = (
value: unknown,
llml: (data: unknown, formatters: Formatters) => string,
formatters: Formatters
) => string;
type Formatters = Iterable<[Predicate, Formatter]>;
Returns: string - The formatted markup
vibeXML(options?)Creates the default VibeXML formatters with optional customization:
Parameters:
options?: { formatters?: Formatters } - Custom formatters to merge with defaultsExamples:
// Simple usage with defaults
llml({ name: "John" })
// → <name>John</name>
// Custom formatters
class User {
constructor(public name: string) {}
}
const customFormatters = new Map();
customFormatters.set(
(v: unknown): v is User => v instanceof User,
(v: User) => `User: ${v.name}`
);
const formatters = vibeXML({ formatters: customFormatters });
llml({ admin: new User("Alice") }, formatters)
// → <admin>User: Alice</admin>
// Complex data
llml({
users: [
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 }
]
})
// → <users>
// <users-1>
// <name>Alice</name>
// <age>30</age>
// </users-1>
// <users-2>
// <name>Bob</name>
// <age>25</age>
// </users-2>
// </users>
For detailed information on creating custom formatters, see our Formatters Guide.
We love contributions! Whether it's:
Check out our contribution guidelines to get started.
MIT License - see LICENSE file for details.
If LLML makes your life easier, give us a star! ⭐
Made with ❤️ for the TypeScript community
LLML: Because beautiful markup shouldn't be hard.
FAQs
Lightweight Language Markup Language - Convert nested data structures to human-readable, XML-like markup
We found that @zenbase/llml demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.

Security News
The PHP Foundation says Chainguard broke a security embargo ahead of critical RCE patches, as leaks disrupted the coordinated release.

Security News
PHP releases emergency patches for two critical RCE vulnerabilities as agencies urge organizations to take unpatched sites offline.

Threat Research
/Security News
A supply chain attack compromised LiteLLM 1.107.0 on PyPI and Docker, spreading malware that steals cloud credentials, developer secrets, and AI provider keys.