Types
@barline/core is the public, zero-dependency package: types, the AST, and the
pattern primitives. It imports nothing — no Tone, no React, no DOM types — so a
song file’s vocabulary is small and stable. These are the types you’ll touch
most.
import type { Pattern, QueryContext, NoteEvent, NoteName, Note, TimeWindow, Beats, Position, SongDocument, SectionContext,} from "@barline/core";Pattern
Section titled “Pattern”The central contract. Three members; the rest of the system composes against it.
interface Pattern { readonly id: string; readonly length: Beats; query(window: TimeWindow, ctx: QueryContext): NoteEvent[]; readonly source?: { readonly line: number }; // call-site, set by leaf constructors}query must be pure and deterministic — see Custom patterns.
QueryContext
Section titled “QueryContext”Passed to every query. Two members matter to authors:
rng(seed: string): number— a seeded0..1draw. The only sanctioned randomness. Same seed → same number, so visualization matches playback.flags— the live flag values (read here for sub-beat control; the bar-snapshot view ist.flagsin a section body).
NoteEvent
Section titled “NoteEvent”What query returns — one scheduled note:
interface NoteEvent { note: MidiNote; // branded MIDI number velocity: number; // 0..1 duration: Beats; // length in beats offset: Beats; // start, relative to the window}Time and notes
Section titled “Time and notes”Beats/Positionare branded numbers — build them withbeats(n)/position(n)/bars(n), don’t pass raw numbers where they’re expected.TimeWindowis{ start: Position; end: Position }.NoteNameis the string form ("F1","Ab3","C4");Noteis aNoteName | MidiNote.noteToMidi()/midiNote()convert.
SectionContext
Section titled “SectionContext”The argument to a section body:
interface SectionContext { readonly tracks: Record<string, Track>; readonly flags: FlagValues; // snapshot at the bar boundary (quantized) readonly bpm: number; position(): Position; // position within the section, in beats inherit(sectionName: string): void;}SongDocument and barlineVersion
Section titled “SongDocument and barlineVersion”SongDocument is the versioned, serializable AST — the public schema that
persists and syncs. It is a public API:
BARLINE_VERSIONis the current schema version (currently2).- Any change to the shape bumps
barlineVersionand ships a migration. Fields are never repurposed. - Section bodies are stored as TypeScript source in
sources;structureis a derived index for the editor, LLM, and visuals — not the source of truth.
You rarely construct a SongDocument by hand; the studio and runtime own it.
But if you serialize or migrate songs, this is the contract to respect.
The non-negotiables
Section titled “The non-negotiables”- core has zero runtime dependencies. If it can’t run in a bare Node REPL, it doesn’t belong in core.
- Patterns are pure. Randomness only via
ctx.rng(seed). - Transforms are free functions, not methods — your
Patternclass never needs chaining methods. - The tagged-template grammar stays minimal (
x,./~,*N,[...],${velocity}). New power goes through interpolation or helpers. SongDocumentchanges require a version bump + migration.