A declarative language for describing sets of domain names using structural patterns.
DDSL is a Domain-Specific Language about domains — a compact, human-readable way to express domain name spaces that expands into finite, deterministic sets.
import { ddsl, ddslDocument } from 'ddsl';
ddsl('{car,bike}.com');
// ['car.com', 'bike.com']
ddsl('car(s)?.com');
// ['car.com', 'cars.com']
ddsl('[^aeiou]{3}.com');
// All 3-letter domains using consonants and digits (29,791)
ddsl('[:c:][:v:][:c:].ai');
// All CVC .ai domains (2,205)
// Multi-line documents with variables
ddslDocument(`
@tlds = {com,net}
@env = {dev,staging,prod}
api.@env.example.@tlds
`);
// ['api.dev.example.com', 'api.dev.example.net', ...]npm install ddslParse and expand a DDSL expression in one step.
import { ddsl } from 'ddsl';
const domains = ddsl('{car,bike}.com');Parse and expand a multi-line DDSL document with variables.
import { ddslDocument } from 'ddsl';
const domains = ddslDocument(`
@tlds = {com,net,org}
# API endpoints
api.example.@tlds
cdn.example.@tlds
`);Parse a DDSL expression into an AST.
import { parse } from 'ddsl';
const ast = parse('car(s)?.com');Parse prepared document lines into a document AST. The optional lineNumbers array (parallel to lines) maps each line to its original source line number. When provided, ParseError.line reflects the actual source line rather than the preprocessed index — useful for highlighting errors in an editor.
import { parseDocument, prepareDocument } from 'ddsl';
// Without line tracking (backward compatible)
const { lines } = prepareDocument(input);
const doc = parseDocument(lines);
// With accurate line numbers in errors
const { lines, lineNumbers } = prepareDocument(input);
const doc = parseDocument(lines, lineNumbers);Expand a parsed AST into the full set of domain names. Throws ExpansionError if the expansion exceeds maxExpansion.
import { parse, expand } from 'ddsl';
const ast = parse('[a-z]{4}.ai');
const domains = expand(ast);Expand a parsed document into the full set of domain names. Throws ExpansionError if the expansion exceeds maxExpansion.
import { parseDocument, prepareDocument, expandDocument } from 'ddsl';
const lines = prepareDocument(input);
const doc = parseDocument(lines);
const domains = expandDocument(doc);Preview an expansion with a capped result set. Returns a PreviewResult with domains, total, and truncated. Throws ExpansionError if the total expansion size exceeds maxExpansion.
import { parse, preview } from 'ddsl';
const ast = parse('[a-z]{10}.com');
const result = preview(ast, 100, { maxExpansion: Infinity });
// { domains: [...100 items], total: 141167095653376, truncated: true }Preview a document expansion with a capped result set. Throws ExpansionError if the total expansion size exceeds maxExpansion.
import { parseDocument, prepareDocument, previewDocument } from 'ddsl';
const lines = prepareDocument(input);
const doc = parseDocument(lines);
const result = previewDocument(doc, 100);
// { domains: [...], total: number, truncated: boolean }Calculate the expansion size without expanding.
import { parse, expansionSize } from 'ddsl';
const ast = parse('[a-z]{10}.com');
expansionSize(ast); // 141,167,095,653,376Calculate the total expansion size of a document without expanding.
import { parseDocument, prepareDocument, documentExpansionSize } from 'ddsl';
const lines = prepareDocument(input);
const doc = parseDocument(lines);
documentExpansionSize(doc); // 18Strip whitespace from user input before parsing.
import { parse, prepare } from 'ddsl';
const ast = parse(prepare(' { car, bike }.com '));Prepare a multi-line document: strips comments, trims lines, removes empty lines, normalises case. Returns { lines, lineNumbers } where lineNumbers[i] is the 1-based original source line for lines[i].
import { prepareDocument, parseDocument } from 'ddsl';
const { lines, lineNumbers } = prepareDocument(`
@tlds = {com,net} # TLDs
example.@tlds
`);
// lines: ['@tlds = {com,net}', 'example.@tlds']
// lineNumbers: [2, 3]
try {
const doc = parseDocument(lines, lineNumbers);
} catch (e) {
if (e instanceof ParseError) {
console.log(e.line); // original source line (1-based), or undefined
console.log(e.position); // character position within that line
console.log(e.rawMessage); // message text without the 'Parse error at...' prefix
}
}ddsl('[a-z]{4}.ai', { maxExpansion: 500_000 });| Option | Type | Default | Description |
|---|---|---|---|
maxExpansion |
number |
1,000,000 |
Maximum domains to produce. Throws ExpansionError if exceeded. Set to Infinity to disable. |
| Element | Example | Description |
|---|---|---|
| Literal | car |
Fixed text (letters, digits, hyphens) |
| Alternation | {car,bike} |
Choice between options |
| Character class | [a-z] |
Single character (defaults to {1}) |
| Repetition | [a-z]{3} |
Fixed repetition |
| Range | [a-z]{2,4} |
Variable-length sequences |
| Negation | [^aeiou] |
Exclude characters |
| Named class (standalone) | [:v:] |
Vowels — one character, like [a-z]{1} |
| Named class (standalone) | [:c:] |
Consonants — one character |
| Named class (in bracket) | [[:v:]] |
Vowels inside a bracket class |
| Named class (in bracket) | [[:c:]0-9] |
Consonants and digits combined |
| Grouping | (abc) |
Group elements together |
| Group repetition | (ab){2,3} |
Repeat a group |
| Optional | (s)? |
Make a group optional |
| Variable | @name |
Reference a defined variable |
| Comment | # text |
Ignored (document mode) |
example.com → example.com
{car,bike}.com → car.com, bike.com
car(s)?.com → car.com, cars.com
[a-z].io → 26 one-letter domains
[^aeiou]{3}.com → 29,791 domains (consonants + digits)
[:c:][:v:][:c:].ai → 2,205 CVC domains
(ab){2,3}.com → abab.com, ababab.com
{smart{car,bike},fast{boat,plane}}.com → 4 domains
@tlds = {com,net,org}
@env = {dev,staging,prod}
# API endpoints
api.@env.example.@tlds
# CDN endpoints
cdn.@env.example.@tlds
Spec-first: behavior follows spec.md, v0.3.4 may change; breaking changes will be noted in CHANGELOG.md
Full specification for DDSl v0.3.4, v0.3.4 spec
The reference implementation is available at ddsl.app.
MIT