Joyous Departures
Generate warm, heartfelt sign-off messages for email templates and other communication contexts.
Overview
A high-performance library that generates random, warm sign-off messages from a pre-curated corpus. The core generation logic is implemented in Rust for speed, with language bindings provided for TypeScript (npm package) and Python (PyPI package).
Over-engineered? Most definitely.
This project is an exploration of spec-first AI-assisted design and simultaneously targetting multiple consumer languages from a high-performance Rust core. It is also an ode to a cherished partner and the aspects they inspire deeply within myself and in many others ❤️
Most importantly, as ever, because it was fun!
Features
- 🚀 Fast: Message generation in <10ms
- 🌍 International: Support for multiple languages (with translator callback)
- 🎨 Customizable: Template variables (
{name},{location},{date},{time}) - 😊 Emoji Support: Emojis included by default, with option to strip
- 🌐 Browser Ready: TypeScript bindings use WASM for browser support
- 📦 Easy to Use: Simple API, works with pnpm and uv
Template Variables
Messages can include template variables that are replaced with custom values or defaults:
| Variable | Default Value | Format | Description |
|---|---|---|---|
{name} |
"Good Soul" |
Any string (max 50 chars) | Recipient's name |
{location} |
"The World" |
Any string | Location context |
{date} |
Current date | YYYY-MM-DD |
Current date in specified timezone |
{time} |
Current time | HH:MM |
Current time in specified timezone |
Note: Not all messages include template variables. Some messages are simple phrases without any variables.
Installation
TypeScript/JavaScript
pnpm add @siviter-xyz/joyous-departures
# or
npm install @siviter-xyz/joyous-departures
Python
uv pip install joyous-departures
Quick Start
See quickstart guide for detailed examples.
TypeScript
import { generateGoodbye } from '@siviter-xyz/joyous-departures';
const message = await generateGoodbye({
templateArgs: { name: 'Alice' }
});
console.log(message); // "Wishing you a joyous day, Alice ❤️"
Python
import asyncio
from joyous_departures import generate_goodbye
async def main():
message = await generate_goodbye(template_args={"name": "Alice"})
print(message) # "Wishing you a joyous day, Alice ❤️"
asyncio.run(main())
Local Development Setup
Prerequisites
-
Rust Toolchain (1.91.1+)
# Install Rust via rustup curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source ~/.cargo/env
-
Build Tools (for compiling Rust dependencies)
# Debian/Ubuntu/WSL sudo apt-get update sudo apt-get install build-essential clang # This provides: gcc, g++, make, clang, and other essential build tools # clang is needed for WASM builds (lz4 dependency)
-
Python (3.14+)
# Using uv (recommended) curl -LsSf https://astral.sh/uv/install.sh | sh uv python install 3.14
-
Node.js (18+) and pnpm
# Install Node.js via nvm (recommended) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash nvm install 18 nvm use 18 # Install pnpm npm install -g pnpm
-
Build Tools for Bindings
# Install wasm-pack (for TypeScript/WASM bindings) source ~/.cargo/env cargo install wasm-pack # Verify: wasm-pack --version # Install maturin (for Python bindings) # Option 1: Via pip (recommended, faster, avoids cargo dependency conflicts) pip install maturin # or with uv: uv pip install maturin --system # Option 2: Via cargo (requires build-essential, may have dependency conflicts) source ~/.cargo/env cargo install maturin # Verify: maturin --version
Development Workflow
-
Clone and Setup
git clone <repository-url> cd joyous-departures source ~/.cargo/env # If Rust is installed via rustup # Install pre-commit hooks (recommended) ./scripts/setup-git-hooks.sh
-
Run Rust Core Tests
source ~/.cargo/env # Ensure cargo is in PATH cd joy-generator cargo test # ✅ All 21 tests should pass cargo bench # Run benchmarks (shows ~615ns generation time)
-
Build and Test Python Bindings
cd bindings/python maturin develop # Build Python extension in development mode uv run pytest tests/test_e2e.py
-
Build and Test TypeScript Bindings
cd bindings/typescript source ~/.cargo/env wasm-pack build --target web --out-dir pkg pnpm install pnpm test # Note: Requires clang (see Prerequisites #2) for lz4 dependency
-
Run All Tests
# From repository root ./scripts/test.sh -
Quick Test Individual Components
# Test Python bindings ./scripts/test-python.sh # Test TypeScript/WASM bindings ./scripts/test-typescript.sh
Available Scripts
All scripts are located in the scripts/ directory:
scripts/test.sh- Run all test suites (Rust, Python, TypeScript)scripts/lint.sh- Run linting checks (format + clippy, same as CI)scripts/setup-git-hooks.sh- Install pre-commit hooks (runs linting automatically)scripts/test-python.sh- Quick test of Python bindings with examplesscripts/test-typescript.sh- Quick test of TypeScript/WASM bindingsscripts/build.sh- Build all packages (Rust workspace, WASM, Python)scripts/bench.sh- Run benchmarks for all componentsscripts/get-version.sh- Extract version from git tag (used by build processes)scripts/update-versions.sh- Update version in all package files (used by semantic-release)scripts/publish.sh- Publish packages to npm and PyPI (manual publishing)
Required Stack
Prerequisites:
- Rust 1.91.1+ (cargo, rustc)
- build-essential (gcc 14.2.0+) + clang
- wasm-pack 0.13.1+
- uv 0.9.6+
- pnpm 10.16.1+
- Node.js 18+
- Python 3.13+ (via uv)
- maturin 1.10.2+ (via uv venv)
Performance Benchmarks:
- Generation Time: ~615ns per message (0.000615ms)
- Target: <10ms
- Actual: 16,260x faster than target ✅
- Corpus Loading: ~10ns (cached after first load)
- Throughput: 1000+ concurrent calls/second ✅
- Memory: <10MB total footprint ✅
Run benchmarks with: cargo bench --manifest-path joy-generator/Cargo.toml
To Verify Your Setup:
# 1. Verify Rust core
source ~/.cargo/env
cd joy-generator
cargo test # Expected: "21 passed; 0 failed"
cargo bench # Expected: ~615ns for generate_goodbye
# 2. Verify Python bindings
cd bindings/python
uv venv --python 3.13 && source .venv/bin/activate
uv pip install maturin pytest pytest-asyncio
maturin develop
pytest tests/test_e2e.py # Expected: "8 passed"
# 3. Verify TypeScript/WASM (requires clang)
cd bindings/typescript
source ~/.cargo/env
wasm-pack build --target web --out-dir pkg
pnpm install && pnpm test # Expected: "10 passed"
# Or use the convenience scripts:
./scripts/test.sh # Run all tests
./scripts/lint.sh # Run linting checks (format + clippy, same as CI)
Pre-Commit Setup
Recommended: Install git hooks for automatic linting
The easiest way to ensure code quality is to install the pre-commit hook, which automatically runs linting checks before each commit:
# One-time setup (run this after cloning the repo)
./scripts/setup-git-hooks.sh
How it works: Git hooks in .git/hooks/ are not tracked by git (they're local to each repository clone). The hooks are stored in .githooks/ (which IS tracked), and setup-git-hooks.sh copies them to .git/hooks/ for each developer.
After setup, the pre-commit hook will automatically run ./scripts/lint.sh before each commit. If linting fails, the commit will be blocked.
Manual alternative:
If you prefer to run checks manually:
# 1. Run linting (format + clippy) - same as CI
./scripts/lint.sh
# 2. Run all tests
./scripts/test.sh
Why this matters: CI runs cargo clippy --all-targets --all-features -- -D warnings, which treats warnings as errors. The pre-commit hook ensures you catch the same issues before pushing.
Version Management
Automated Versioning with Semantic-Release:
The project uses semantic-release to automatically manage versions based on conventional commits:
feat:- Triggers minor version bump (0.1.0 → 0.2.0)fix:- Triggers patch version bump (0.1.0 → 0.1.1)feat!:orBREAKING CHANGE:- Triggers major version bump (0.1.0 → 1.0.0)
How it works:
- Make commits with conventional commit messages
- Push to
mainbranch - After CI passes, semantic-release automatically:
- Analyzes commits since last release
- Determines next version
- Updates
Cargo.toml,package.json, andpyproject.toml - Creates git tag (e.g.,
v0.1.1) - Generates/updates
CHANGELOG.md - Triggers publish workflow
Version Scripts:
scripts/get-version.sh- Extracts version from git tags (used by build processes)scripts/update-versions.sh- Updates version in all package files (used by semantic-release)
Publishing Prerequisites:
Before publishing, you need to add GitHub secrets:
- PYPI_API_TOKEN - PyPI API token (create at https://pypi.org/manage/account/token/)
- GH_PAT (required) - GitHub Personal Access Token with
reposcope- Required for semantic-release to push tags that trigger the publish workflow
- Tags pushed by GITHUB_TOKEN don't trigger workflows in GitHub Actions
- Create at https://github.com/settings/tokens
- Add as a repository secret named
GH_PAT
Initial npm Package Setup (One-time):
npm classic tokens are deprecated. You must publish the first version manually, then set up Trusted Publishing:
-
Publish the first version manually:
cd bindings/typescript npm login # This will prompt for your npm username, password, and 2FA code npm publish --access public
-
Set up Trusted Publishing on npmjs.com:
- Go to your package: https://www.npmjs.com/package/@siviter-xyz/joyous-departures
- Navigate to Settings → Trusted Publishers
- Click Add Trusted Publisher
- Select GitHub Actions
- Configure:
- Repository:
siviter-xyz/joyous-departures - Workflow file:
.github/workflows/publish.yml(must match exactly, including.ymlextension) - Environment name: (leave empty unless using environments)
- Repository:
- Save the configuration
-
Verify the workflow is ready:
- The workflow already has
id-token: writepermissions for OIDC - npm will be updated to the latest version automatically
- Future publishes will use OIDC authentication automatically
- The workflow already has
Note: Classic npm tokens are deprecated and will be revoked on December 9, 2025. Trusted Publishing is the only supported method for automated publishing.
Manual Publishing (if needed):
If you need to publish manually without semantic-release:
# 1. Create a version tag
git tag v0.1.0
# 2. Update version in all files
./scripts/update-versions.sh 0.1.0
# 3. Build packages
./scripts/build.sh
# 4. Publish
cd bindings/typescript && pnpm publish --access public
cd ../python && maturin publish
Troubleshooting
- "linker
ccnot found": Installbuild-essential(see Prerequisites #2) - "clang not found" (WASM builds): Install
clangviasudo apt-get install clang - "cargo: command not found": Run
source ~/.cargo/envor add~/.cargo/binto PATH - Python import errors: Ensure
maturin developwas run inbindings/python/venv - WASM build fails: Ensure
wasm-packis installed andclangis available - maturin install fails via cargo: Use
uv venvanduv pip install maturininstead
Documentation
License
MIT
Metadata
Release files for joyous-departures 1.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| joyous_departures-1.4.0-cp313-cp313-manylinux_2_34_x86_64.whl | CPython 3.13 | CPython 3.13 | Linux glibc 2.34+ x86-64 | Details |
Release files / joyous_departures-1.4.0-cp313-cp313-manylinux_2_34_x86_64.whl
| Download URL | joyous_departures-1.4.0-cp313-cp313-manylinux_2_34_x86_64.whl |
|---|---|
| Size | 561.8 kB |
| Tags | CPython 3.13 Linux glibc 2.34+ x86-64 |
|
SHA-256 checksum How to use checksums |
1b733adc7b1953ebf7863c7bacf3c49184ec6a09cf29e9e8f1ca8de1d77336cc
|
|
BLAKE2b-256 checksum How to use checksums |
bcbf38572379d032eb515f8065188a26b729c26f1e8b355a891960b241e3a56b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.9
|