Update readme and clean up

This commit is contained in:
Endeavorance 2025-04-02 15:41:20 -04:00
parent 6a3157762a
commit 435d555394
17 changed files with 320 additions and 486 deletions

225
README.md
View file

@ -1,6 +1,20 @@
# Playbill Builder CLI
# Muse
This is a CLI tool for compiling Markdown and YAML files into a validated Playbill for [Proscenium](https://proscenium.game)
A CLI for wrangling directories of source files.
Like a static generator, but for whatever.
## Overview
**Muse** is a CLI and toolchain for operating on directories of
json, yaml, toml, and markdown files. Each file can specify any shape
of data. Muse scans included files, parses them into data, and streams
the loaded data through processors and plugins.
Each processor can modify the data at compile time, as well as enact
side effects such as writing files to disk.
Muse does not edit source files, it only reads them in.
## Usage
@ -9,209 +23,24 @@ Usage:
muse [/path/to/binding.yaml] <options>
Options:
--check Only load and check the current binding and resources, but do not compile
--outfile, -o Specify the output file path. If not specified, output to stdout
--watch, -w Watch the directory for changes and recompile
--renderer, -r Specify the output renderer. Options: json, html
--stdout -s Output final data to stdout
--verbose, -v Enable verbose logging
--help, -h Show this help message
```
## Binding File
Each Muse project should specify a `binding.yaml` file with the following shape:
Each Muse project should specify a `binding` file with the following shape:
```yaml
name: My Playbill # Required
author: My Name # Required
version: 0.0.1 # Required
extend: ../another-playbill # Optional
include:
- ../another-project # Optional
files: # Optional
- "**/*.yaml"
styles: # Optional
- stylesheet.css
terms: # Optional
Some Term: Some Definition
Another Term: Another Definition
contentKey: "content" # Optional
options: # Optional
someOption: someValue
processors:
- first-processor
- second-processor
```
The **Binding** file is used to specify the metadata for the Playbill, as well as the files to be compiled. It is also the entry point for a project.
When using the `muse` CLI, you must provide either a path to a `binding.yaml` file or a directory which contains a `binding.yaml` file.
## Common Types
Many Playbill components share common properties that expect a value
from a predefined list. These are the common types used in the Playbill.
### `Roll` Options
- `none`
- `attack`
- `fate`
- `muscle`
- `focus`
- `knowledge`
- `charm`
- `cunning`
- `spark`
### `Damage Type` Options
- `phy` (denotes "physical damage")
- `arc` (denotes "arcane damage")
### `Prowess` Options
- `novice`
- `adept`
- `master`
### `Challenge` Options
- `novice`
- `adept`
- `master`
- `theatrical`
### `Ability Type` Options
- `action`
- `cue`
- `trait`
## Definition Shapes
These YAML representations of definitions show the properties available for each Playbill component.
Note that every Playbill component definition can define the following properties:
```yaml
id: an-id # defaults to a slugified version of the file name
name: Component Name # defaults to the file name
description: Markdown description # defaults to a placeholder
categories: # defaults to no categories
- list
- of
- categories
```
### Ability Definition
```yaml
$define: ability
type: <ability type> # Defaults to action
ap: 0 # AP cost to use this ability (default 0)
hp: 0 # HP cost to use this ability (default 0)
ep: 0 # EP cost to use this ability (default 0)
xp: 0 # XP cost to learn this ability (default 1)
damage: 0 # Damage dealt by this ability (default 0)
damageType: <damage type> # Type of damage dealt by this ability (default phy)
roll: <roll type> # Roll type for this ability (default none)
```
### Blueprint Definition
```yaml
$define: blueprint
species: species-id # ID of the species this blueprint is for
items: # List of item IDs (default empty)
- item-id
abilities: # List of ability IDs (default empty)
- ability-id
ap: 0 # Starting AP (default 4)
hp: 0 # Starting HP (default 5)
ep: 0 # Starting EP (default 1)
xp: 0 # Starting XP (default 0)
muscle: <prowess> # Starting muscle prowess (default novice)
focus: <prowess> # Starting focus prowess (default novice)
knowledge: <prowess> # Starting knowledge prowess (default novice)
charm: <prowess> # Starting charm prowess (default novice)
cunning: <prowess> # Starting cunning prowess (default novice)
spark: <prowess> # Starting spark prowess (default novice)
```
### Item Definition
```yaml
$define: item
type: <item type> # Defaults to wielded
rarity: <rarity> # Defaults to common
affinity: <roll type> # Defaults to none
damage: 0 # Defaults to 0
damageType: <damage type> # Defaults to phy
hands: 1 | 2 # Only for wielded items
slot: <item slot> # Only for worn items
```
#### `Item Type` Options
- `wielded`
- `worn`
- `trinket`
#### `Rarity` Options
- `common`
- `uncommon`
- `rare`
- `legendary`
#### `Item Slot` Options
- `headgear`
- `outfit`
- `gloves`
- `boots`
- `adornment`
### Method Definition
```yaml
$define: method
curator: Someone # The name of the curator
abilities: # A 2-d array of ability IDs
- [rank-1-ability-id-one, rank-1-ability-id-two]
- [rank-2-ability-id-one]
```
### Resource Definition
```yaml
$define: resource
type: <resource type> # Defaults to text
url: https://url.com/image.jpeg # Only for image resources
data: <a 2-d array of strings> # Only for table resources
```
#### `Resource Type` Options
- `text`
- `image`
- `table`
### Rule Definition
```yaml
$define: rule
overrule: another-rule-id # Optional
order: 0 # Optional
```
### Species Definition
```yaml
$define: species
hands: 2 # Defaults to 2
abilities: # A list of innate ability IDs
- some-ability-id
- another-ability-id
ap: 0 # Starting AP modifier
hp: 0 # Starting HP modifier
ep: 0 # Starting EP modifier
xp: 0 # Starting XP modifier
muscle: <prowess> # Defaults to novice
focus: <prowess> # Defaults to novice
knowledge: <prowess> # Defaults to novice
charm: <prowess> # Defaults to novice
cunning: <prowess> # Defaults to novice
spark: <prowess> # Defaults to novice
```

11
demo/main/binding.md Normal file
View file

@ -0,0 +1,11 @@
---
markdownKey: description
options:
dunks:
key: dunkaroos
processors:
- ./processors/scream.js
- ./processors/announce-slam-dunks.js
---
A markdown-powered binding file!

View file

@ -1,3 +0,0 @@
markdownKey: description
processors:
- ./processors/scream.js

View file

@ -0,0 +1,13 @@
export const name = "Announce Slam Dunks";
export const process = (binding, { dunks }) => {
const slamDunkKey = dunks?.key ?? "slamDunks";
for (const entry of binding.entries) {
if (slamDunkKey in entry.data) {
console.log(`Slam dunk!`);
}
}
return binding;
};

View file

@ -1,5 +1,5 @@
export const name = "Scream";
export function process(binding, opts) {
export function process(binding) {
const modifiedEntries = binding.entries.map(entry => {
if (binding.markdownKey in entry.data) {
entry.data = {

View file

@ -1 +1,2 @@
slamDunks = 100
player = "Flynn"
dunkaroos = 100

View file

@ -1,8 +1,17 @@
{
"name": "@proscenium/muse",
"version": "0.0.1",
"module": "index.ts",
"version": "0.1.0",
"module": "dist/index.js",
"exports": {
".": {
"import": "./dist/muse.js",
"types": "./dist/muse.d.ts"
}
},
"type": "module",
"files": [
"dist"
],
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@types/bun": "latest",
@ -20,9 +29,11 @@
},
"scripts": {
"fmt": "bunx --bun biome check --fix",
"build": "bun build ./src/index.ts --outfile muse --compile",
"demo": "bun run ./src/index.ts -- ./demo-data/great-spires/binding.yaml",
"clean": "rm -rf dist",
"types": "dts-bundle-generator -o dist/muse.d.ts --project ./tsconfig.json ./src/exports.ts",
"compile": "bun build ./src/cli.ts --outfile ./dist/muse --compile",
"build": "bun run clean && bun run compile && bun run types",
"build:install": "bun run build && mv muse ~/.local/bin/muse",
"types": "dts-bundle-generator -o types/muse.d.ts --project ./tsconfig.json ./src/types.ts"
"demo": "bun run ./src/cli.ts -- ./demo/main"
}
}

View file

@ -1,72 +0,0 @@
import { parseArgs } from "node:util";
import chalk from "chalk";
import { version } from "../package.json" with { type: "json" };
export const USAGE = `
${chalk.bold("muse")} - Compile and process troves of data
${chalk.dim(`v${version}`)}
Usage:
muse [/path/to/binding.yaml] <options>
Options:
--stdout -s Output final data to stdout
--verbose, -v Enable verbose logging
--help, -h Show this help message
`.trim();
/**
* A shape representing the arguments passed to the CLI
*/
export interface CLIArguments {
inputFilePath: string;
options: {
help: boolean;
verbose: boolean;
stdout: boolean;
};
}
/**
* Given an array of CLI arguments, parse them into a structured object
*
* @param argv The arguments to parse
* @returns The parsed CLI arguments
*
* @throws {CLIError} if the arguments are invalid
*/
export function parseCLIArguments(argv: string[]): CLIArguments {
const { values: options, positionals: args } = parseArgs({
args: argv,
options: {
help: {
short: "h",
default: false,
type: "boolean",
},
verbose: {
short: "v",
default: false,
type: "boolean",
},
stdout: {
short: "s",
default: false,
type: "boolean",
},
},
strict: true,
allowPositionals: true,
});
return {
inputFilePath: args[0] ?? "./binding.yaml",
options: {
help: options.help,
verbose: options.verbose,
stdout: options.stdout,
},
};
}

View file

@ -1,26 +1,42 @@
import path, { dirname } from "node:path";
import { Glob } from "bun";
import z from "zod";
import { FileNotFoundError, MuseError } from "./errors";
import { parseMuseFile, type MuseEntry } from "./muse-file";
import { parseMuseFile } from "./parse";
import {
type Binding,
BindingSchema,
type MuseEntry,
type MuseProcessor,
} from "./types";
import { resolveFilePath } from "./util";
// binding.yaml file schema
const BindingSchema = z.object({
include: z.array(z.string()).default([]),
markdownKey: z.string().default("content"),
files: z
.array(z.string())
.default(["**/*.yaml", "**/*.md", "**/*.toml", "**/*.json"]),
options: z.record(z.string(), z.any()).default({}),
processors: z.array(z.string()).default([]),
});
async function loadProcessor(
bindingDirname: string,
processorPath: string,
): Promise<MuseProcessor> {
const resolvedProcessorPath = path.resolve(bindingDirname, processorPath);
const { process, name, description } = await import(resolvedProcessorPath);
type ParsedBinding = z.infer<typeof BindingSchema>;
if (!process || typeof process !== "function") {
throw new MuseError(
`Processor at ${processorPath} does not export a process() function`,
);
}
const processorName = String(name ?? "Unnamed Processor");
const processorDescription = String(description ?? "No description provided");
return {
filePath: resolvedProcessorPath,
process: process as MuseProcessor["process"],
name: processorName,
description: processorDescription,
};
}
/**
* Given a path, find the binding.yaml file
* If the path is to a directory, check for a binding.yaml inside
* If the path is to a file, check if it exists and is a binding.yaml
* Given a path, find the binding file
* If the path is to a directory, check for a binding inside
* Check order: binding.json, binding.yaml, binding.toml, binding.md
* Otherwise throw an error
*
* @param bindingPath - The path to the binding file
@ -30,45 +46,50 @@ export async function resolveBindingPath(bindingPath: string): Promise<string> {
// If the path does not specify a filename, use binding.yaml
const inputLocation = Bun.file(bindingPath);
// If it is a directory, try again seeking a binding.yaml inside
// If it is a directory, try for the four main supported types
const stat = await inputLocation.stat();
if (stat.isDirectory()) {
return resolveBindingPath(path.resolve(bindingPath, "binding.yaml"));
}
const jsonPath = await resolveFilePath(
path.resolve(bindingPath, "binding.json"),
);
if (jsonPath) {
return jsonPath;
}
const yamlPath = await resolveFilePath(
path.resolve(bindingPath, "binding.yaml"),
);
if (yamlPath) {
return yamlPath;
}
const tomlPath = await resolveFilePath(
path.resolve(bindingPath, "binding.toml"),
);
if (tomlPath) {
return tomlPath;
}
const mdPath = await resolveFilePath(
path.resolve(bindingPath, "binding.md"),
);
if (mdPath) {
return mdPath;
}
// If it doesnt exist, bail
if (!(await inputLocation.exists())) {
throw new FileNotFoundError(bindingPath);
}
// Resolve the path
const exists = await inputLocation.exists();
if (!exists) {
throw new FileNotFoundError(bindingPath);
}
// If it is a file, return the path
return path.resolve(bindingPath);
}
export type MuseProcessorFn = (
binding: Binding,
opts: Record<string, unknown>,
) => Promise<Binding>;
export interface MuseProcessor {
readonly filePath: string;
readonly name: string;
readonly description: string;
readonly process: MuseProcessorFn;
}
export interface Binding {
readonly _raw: ParsedBinding;
readonly bindingPath: string;
readonly markdownKey: string;
readonly includedFiles: string[];
readonly entries: MuseEntry[];
readonly meta: Record<string, unknown>;
readonly options: Record<string, unknown>;
readonly processors: MuseProcessor[];
readonly imports: Binding[];
}
export async function loadBinding(initialPath: string): Promise<Binding> {
// Resolve the file location if possible
const bindingPath = await resolveBindingPath(initialPath);
@ -95,68 +116,41 @@ export async function loadBinding(initialPath: string): Promise<Binding> {
entries.push(...loadedBinding.entries);
}
// Collect file manifest from globs
const allFilePaths: string[] = [];
for (const thisGlob of parsedBinding.files) {
const glob = new Glob(thisGlob);
// Aggregate file paths to import
const includedFilePaths = parsedBinding.files
.flatMap((thisGlob) => {
return Array.from(
new Glob(thisGlob).scanSync({
cwd: bindingDirname,
absolute: true,
followSymlinks: true,
onlyFiles: true,
}),
);
})
.filter((filePath) => filePath !== bindingPath);
const results = glob.scanSync({
cwd: bindingDirname,
absolute: true,
followSymlinks: true,
onlyFiles: true,
});
// Load files
const loadedEntries = await Promise.all(
includedFilePaths.map((filePath) => {
return parseMuseFile(filePath, {
contentKey: parsedBinding.contentKey,
});
}),
);
allFilePaths.push(...results);
}
// Exclude the binding.yaml file
const includedFilePaths = allFilePaths.filter((filePath) => {
return filePath !== bindingPath;
});
const fileLoadPromises: Promise<MuseEntry[]>[] = [];
for (const filePath of includedFilePaths) {
fileLoadPromises.push(
parseMuseFile(filePath, {
markdownKey: parsedBinding.markdownKey,
}),
);
}
const mainEntries = await Promise.all(fileLoadPromises);
entries.push(...mainEntries.flat());
// Add loaded entries to main list
entries.push(...loadedEntries.flat());
// Load and check processors
const processors: MuseProcessor[] = [];
for (const processorPath of parsedBinding.processors) {
const resolvedProcessorPath = path.resolve(bindingDirname, processorPath);
const { process, name, description } = await import(resolvedProcessorPath);
if (!process || typeof process !== "function") {
throw new MuseError(
`Processor at ${processorPath} does not export a process() function`,
);
}
const processorName = String(name ?? "Unnamed Processor");
const processorDescription = String(
description ?? "No description provided",
);
processors.push({
filePath: resolvedProcessorPath,
process: process as MuseProcessor["process"],
name: processorName,
description: processorDescription,
});
}
const processors: MuseProcessor[] = await Promise.all(
parsedBinding.processors.map(loadProcessor.bind(null, bindingDirname)),
);
return {
_raw: parsedBinding,
bindingPath,
markdownKey: parsedBinding.markdownKey,
includedFiles: includedFilePaths,
contentKey: parsedBinding.contentKey,
entries,
options: parsedBinding.options,
processors: processors,

View file

@ -1,7 +1,9 @@
import { parseArgs } from "node:util";
import chalk from "chalk";
import { MuseError } from "./errors.ts";
import { loadBinding, type Binding } from "./binding";
import { type CLIArguments, parseCLIArguments, USAGE } from "./args";
import { MuseError } from "./errors";
import { loadBinding } from "./binding";
import { version } from "../package.json";
import type { Binding, CLIArguments } from "./types";
interface Loggers {
log: (msg: string) => void;
@ -13,8 +15,62 @@ enum ExitCode {
Error = 1,
}
export const USAGE = `
${chalk.bold("muse")} - Compile and process troves of data
${chalk.dim(`v${version}`)}
Usage:
muse [/path/to/binding.yaml] <options>
Options:
--stdout -s Output final data to stdout
--verbose, -v Enable verbose logging
--help, -h Show this help message
`.trim();
/**
* Given an array of CLI arguments, parse them into a structured object
*
* @param argv The arguments to parse
* @returns The parsed CLI arguments
*
* @throws {CLIError} if the arguments are invalid
*/
export function parseCLIArguments(argv: string[]): CLIArguments {
const { values: options, positionals: args } = parseArgs({
args: argv,
options: {
help: {
short: "h",
default: false,
type: "boolean",
},
verbose: {
short: "v",
default: false,
type: "boolean",
},
stdout: {
short: "s",
default: false,
type: "boolean",
},
},
strict: true,
allowPositionals: true,
});
return {
inputFilePath: args[0] ?? "./binding.yaml",
flags: {
help: options.help,
verbose: options.verbose,
stdout: options.stdout,
},
};
}
async function processBinding(
{ inputFilePath, options }: CLIArguments,
{ inputFilePath, flags }: CLIArguments,
{ log, verbose }: Loggers,
) {
// Load the binding
@ -37,7 +93,7 @@ async function processBinding(
const { process, name } = processor;
log(chalk.bold(`${name}`) + chalk.dim(` (${processor.description})`));
const thisStep = await process(lastStep, binding.options);
const thisStep = await process(lastStep, binding.options, flags);
processedSteps.push(thisStep);
}
@ -48,7 +104,7 @@ async function processBinding(
const finalState = processedSteps[processedSteps.length - 1];
const serialized = JSON.stringify(finalState.entries, null, 2);
if (options.stdout) {
if (flags.stdout) {
console.log(serialized);
}
return ExitCode.Success;
@ -56,16 +112,16 @@ async function processBinding(
async function main(): Promise<number> {
const cliArguments = parseCLIArguments(Bun.argv.slice(2));
const { options } = cliArguments;
const { flags } = cliArguments;
// If --help is specified, print usage and exit
if (options.help) {
if (flags.help) {
console.log(USAGE);
return ExitCode.Success;
}
const logFn = !options.stdout ? console.log : () => {};
const verboseFn = options.verbose
const logFn = !flags.stdout ? console.log : () => {};
const verboseFn = flags.verbose
? (msg: string) => {
console.log(chalk.dim(msg));
}

View file

@ -19,8 +19,6 @@ export class FileError extends MuseError {
}
}
export class MalformedResourceFileError extends FileError {}
export class FileNotFoundError extends FileError {
constructor(filePath: string) {
super("File not found", filePath);

2
src/exports.ts Normal file
View file

@ -0,0 +1,2 @@
export type * from "./types";
export const DEFAULT_CONTENT_KEY = "content";

View file

@ -2,30 +2,7 @@ import { normalize, extname } from "node:path";
import YAML from "yaml";
import TOML from "smol-toml";
import EMDY from "@endeavorance/emdy";
type UnknownRecord = Record<string, unknown>;
export type MuseFileType = "md" | "yaml" | "json" | "toml";
export interface MuseEntry {
_raw: string;
filePath: string;
data: Record<string, unknown>;
meta: Record<string, unknown>;
}
function parseFileType(ext: string): MuseFileType {
switch (ext) {
case "md":
return "md";
case "yaml":
return "yaml";
case "json":
return "json";
case "toml":
return "toml";
default:
throw new Error(`Unsupported file format: ${ext}`);
}
}
import type { MuseEntry, UnknownRecord } from "./types";
function parseYAMLEntries(text: string): UnknownRecord[] {
const parsedDocs = YAML.parseAllDocuments(text);
@ -75,16 +52,16 @@ function parseTOMLEntries(text: string): UnknownRecord[] {
}
interface ParseMuseFileOptions {
markdownKey?: string;
contentKey?: string;
}
export async function parseMuseFile(
rawFilePath: string,
{ markdownKey = "content" }: ParseMuseFileOptions = {},
{ contentKey = "content" }: ParseMuseFileOptions = {},
): Promise<MuseEntry[]> {
const filePath = normalize(rawFilePath);
const file = Bun.file(filePath);
const fileType = parseFileType(extname(filePath).slice(1));
const fileType = extname(filePath).slice(1);
const rawFileContent = await file.text();
const partial = {
@ -94,7 +71,7 @@ export async function parseMuseFile(
};
if (fileType === "md") {
const parsed = EMDY.parse(rawFileContent, markdownKey);
const parsed = EMDY.parse(rawFileContent, contentKey);
return [
{
...partial,

View file

@ -1,3 +1,55 @@
import type { Binding, MuseProcessorFn } from "./binding";
import type { MuseEntry } from "./muse-file";
export type { Binding, MuseProcessorFn, MuseEntry };
import { z } from "zod";
export interface CLIFlags {
help: boolean;
verbose: boolean;
stdout: boolean;
}
export interface CLIArguments {
inputFilePath: string;
flags: CLIFlags;
}
export const BindingSchema = z.object({
include: z.array(z.string()).default([]),
contentKey: z.string().default("content"),
files: z
.array(z.string())
.default(["**/*.yaml", "**/*.md", "**/*.toml", "**/*.json"]),
options: z.record(z.string(), z.any()).default({}),
processors: z.array(z.string()).default([]),
});
export interface Binding {
readonly _raw: z.infer<typeof BindingSchema>;
readonly bindingPath: string;
readonly contentKey: string;
readonly entries: MuseEntry[];
readonly meta: Record<string, unknown>;
readonly options: Record<string, unknown>;
readonly processors: MuseProcessor[];
readonly imports: Binding[];
}
export type MuseProcessorFn = (
binding: Binding,
opts: Record<string, unknown>,
flags: CLIFlags,
) => Promise<Binding>;
export interface MuseProcessor {
readonly filePath: string;
readonly name: string;
readonly description: string;
readonly process: MuseProcessorFn;
}
export interface MuseEntry {
_raw: string;
filePath: string;
data: Record<string, unknown>;
meta: Record<string, unknown>;
}
export type UnknownRecord = Record<string, unknown>;

27
src/util.ts Normal file
View file

@ -0,0 +1,27 @@
import { normalize } from "node:path";
/**
* Given a file path, ensure it is a file that exists
* Otherwise, return null
*
* @param filePath - The path to the file
* @returns A promise which resolves to the resolved file path or null
*/
export async function resolveFilePath(
filePath: string,
): Promise<string | null> {
const normalized = normalize(filePath);
const file = Bun.file(normalized);
const exists = await file.exists();
if (!exists) {
return null;
}
const stat = await file.stat();
if (stat.isDirectory()) {
return null;
}
return normalized;
}

View file

@ -27,7 +27,7 @@
"noPropertyAccessFromIndexSignature": false,
"baseUrl": "./src",
"paths": {},
"outDir": "./types",
"outDir": "./dist",
},
"include": [
"src/**/*.ts",

62
types/muse.d.ts vendored
View file

@ -1,62 +0,0 @@
// Generated by dts-bundle-generator v9.5.1
import z from 'zod';
export type MuseFileType = "md" | "yaml" | "json" | "toml";
export interface MuseFileContent {
type: MuseFileType;
text: string;
data: Record<string, unknown>;
}
declare const BindingSchema: z.ZodObject<{
include: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
name: z.ZodDefault<z.ZodString>;
author: z.ZodDefault<z.ZodString>;
description: z.ZodDefault<z.ZodString>;
version: z.ZodDefault<z.ZodString>;
files: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
options: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodAny>>;
processors: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
}, "strip", z.ZodTypeAny, {
options: Record<string, any>;
include: string[];
name: string;
author: string;
description: string;
version: string;
files: string[];
processors: string[];
}, {
options?: Record<string, any> | undefined;
include?: string[] | undefined;
name?: string | undefined;
author?: string | undefined;
description?: string | undefined;
version?: string | undefined;
files?: string[] | undefined;
processors?: string[] | undefined;
}>;
export type ParsedBinding = z.infer<typeof BindingSchema>;
export interface BindingMetadata {
name: string;
author: string;
description: string;
version: string;
}
export type MuseProcessorFn = (binding: Binding) => Promise<Binding>;
export interface MuseProcessor {
readonly filePath: string;
readonly process: MuseProcessorFn;
}
export interface Binding {
readonly _raw: ParsedBinding;
readonly bindingPath: string;
readonly includedFiles: string[];
readonly entries: MuseFileContent[];
readonly metadata: BindingMetadata;
readonly options: Record<string, unknown>;
readonly processors: MuseProcessor[];
readonly imports: Binding[];
}
export {};