@echoes-io/utils
v1.4.0
Published
Utilities and types for Echoes - Multi-POV storytelling platform. Provides shared timeline configuration, content parsing, word counting, and validation schemas.
Downloads
470
Maintainers
Readme
@echoes-io/utils
Utilities and types for Echoes - a multi-POV digital storytelling platform.
Overview
This package provides shared utilities used across the Echoes ecosystem:
- Web application (Next.js)
- CLI tools
- MCP server
- LaTeX/PDF builder
Installation
npm install @echoes-io/utilsFeatures
Markdown Parser
Parse markdown files with YAML frontmatter to extract chapter metadata and content.
import { parseMarkdown, stripMarkdown } from '@echoes-io/utils';
const markdown = `---
pov: "alice"
title: "First Meeting"
date: "2024-01-01"
timeline: "main"
arc: "introduction"
episode: 1
part: 1
chapter: 1
summary: "Alice meets Bob for the first time"
location: "coffee shop"
outfit: "red dress"
kink: "slow burn"
---
# Chapter 1
Alice walked into the coffee shop...`;
const { metadata, content } = parseMarkdown(markdown);
console.log(metadata.pov); // "alice"
console.log(content); // "# Chapter 1\n\nAlice walked..."
// Remove markdown syntax
const plainText = stripMarkdown(content);
console.log(plainText); // "Chapter 1\n\nAlice walked..."Functions:
parseMarkdown()- Extract frontmatter and content from markdownstripMarkdown()- Remove markdown syntax from text
Metadata Fields:
pov- Point of view charactertitle- Chapter titledate- Chapter datetimeline- Timeline identifierarc- Story arcepisode,part,chapter- Numeric identifierssummary- Brief descriptionlocation- Setting locationoutfit- Character outfit (optional)kink- Content tags (optional)
Text Statistics
Calculate word count and reading statistics from markdown content.
import { getTextStats } from '@echoes-io/utils';
const markdown = `
# Chapter Title
This is **bold** and *italic* text with [a link](https://example.com).
`;
const stats = getTextStats(markdown);
console.log(stats);
// {
// words: 9,
// characters: 52,
// charactersNoSpaces: 43,
// paragraphs: 1,
// sentences: 1,
// readingTimeMinutes: 1
// }Features:
- Automatically removes markdown syntax using
stripMarkdown() - Removes HTML tags and comments
- Removes frontmatter YAML
- Counts words, characters (with/without spaces), paragraphs, sentences
- Calculates reading time (based on 200 words/minute)
Path Utilities
Generate chapter files following Echoes naming conventions.
import { generateChapterFile } from '@echoes-io/utils';
const metadata = {
pov: 'Alice',
title: 'First Meeting',
date: '2024-01-01',
timeline: 'main',
arc: 'Introduction Arc',
episode: 1,
part: 1,
chapter: 5,
summary: 'Alice meets Bob for the first time',
location: 'Coffee Shop',
outfit: 'Red dress',
kink: 'Slow burn'
};
const file = generateChapterFile(metadata, 'Alice walked into the coffee shop...');
console.log(file.path);
// → content/introduction-arc/ep01-first-meeting/ep01-ch005-alice-first-meeting.md
console.log(file.content);
// → ---
// pov: "Alice"
// title: "First Meeting"
// episode: 1
// chapter: 5
// ...
// ---
//
// Alice walked into the coffee shop...File Structure Convention:
content/
├── <arc-name>/
│ └── <ep01-episode-title>/
│ └── <ep01-ch001-pov-title>.mdFeatures:
- Automatic path generation from metadata
- Complete file with frontmatter and content
- Handles special characters in titles (slugification)
- Proper padding for episode (01) and chapter (001) numbers
Content Publishing Workflow
This repository provides a reusable GitHub Actions workflow for processing and publishing timeline content.
Usage in Timeline Repositories
- Create workflow file in your timeline repo at
.github/workflows/publish.yml:
name: Publish Timeline Content
on:
push:
branches: [main]
paths: ['content/**/*.md']
workflow_dispatch:
jobs:
publish:
uses: echoes-io/utils/.github/workflows/publish-content.yml@main
with:
timeline-name: 'main' # Change this for each timeline
content-path: 'content/'
web-app-url: 'https://your-web-app.com'
secrets:
WEB_APP_TOKEN: ${{ secrets.WEB_APP_TOKEN }}Add secret in your timeline repo:
- Go to Settings → Secrets and variables → Actions
- Add
WEB_APP_TOKENwith your web app authentication token
Organize content in your timeline repo:
timeline-repo/
├── content/
│ ├── arc1/
│ │ ├── chapter1.md
│ │ └── chapter2.md
│ └── arc2/
│ └── chapter3.md
└── .github/workflows/publish.ymlWhat the Workflow Does
- Processes all
.mdfiles in the specified directory - Extracts frontmatter metadata using
parseMarkdown() - Calculates text statistics using
getTextStats() - Uploads processed content to your web app via API
- Triggers automatically on content changes or manually
API Payload Format
The workflow sends processed content as JSON:
[
{
"file": "content/arc1/chapter1.md",
"metadata": {
"pov": "alice",
"title": "First Meeting",
"timeline": "main",
"arc": "introduction",
"episode": 1,
"part": 1,
"chapter": 1
},
"content": "# Chapter 1\n\nContent here...",
"stats": {
"words": 150,
"readingTimeMinutes": 1
},
"lastModified": "2024-01-01T12:00:00.000Z"
}
]Project Structure
utils/
├── lib/ # Source code
│ ├── index.ts # Public API exports
│ ├── types.ts # TypeScript interfaces
│ ├── markdown-parser.ts # Markdown parsing & stripping
│ ├── text-stats.ts # Text statistics calculation
│ └── path-utils.ts # Chapter file generation
├── test/ # Tests
│ ├── index.test.ts
│ ├── markdown-parser.test.ts
│ ├── text-stats.test.ts
│ └── path-utils.test.ts
└── package.jsonDevelopment
Scripts
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Lint code
npm run lint
# Fix linting issues
npm run lint:format
# Release (automated via GitHub Actions)
npm run releaseCommit Convention
This project uses Conventional Commits for automated versioning:
feat:- New features (minor version bump)fix:- Bug fixes (patch version bump)feat!:orBREAKING CHANGE:- Breaking changes (major version bump)docs:,style:,refactor:,test:,chore:- No version bump
Examples:
git commit -m "feat: add stripMarkdown function"
git commit -m "fix: handle empty frontmatter correctly"
git commit -m "feat!: change parseMarkdown return type"Tech Stack
- Language: TypeScript (strict mode)
- Testing: Vitest
- Linting: Biome
- Git Hooks: Husky + lint-staged
Adding New Utilities
- Create file in
lib/ - Create test in
test/ - Export from
index.ts - Run tests:
npm test - Lint:
npm run lint:format
Dependencies
Runtime
gray-matter- Parse YAML frontmatter from markdownremove-markdown- Strip markdown syntax for text analysis
Development
typescript- Type checking and compilationvitest- Testing framework@biomejs/biome- Linting and formattinghusky- Git hookslint-staged- Pre-commit linting
License
MIT
