UNPKG

@axonotes/axogen

Version:

TypeScript-native configuration system that unifies typed environment variables, code generation, and task management for any project

268 lines (218 loc) • 8.13 kB
<p align="center"> <a href="./"> <img src="assets/favicon.svg" alt="Axonotes Logo" width="150"/> </a> </p> <h1 align="center">Axogen</h1> <p align="center"> <strong>TypeScript-native configuration system for <em>any project, any language</em></strong> </p> <p align="center"> <a href="https://axonotes.github.io/axogen/">šŸ“– Documentation</a> | <a href="https://axonotes.github.io/axogen/docs/getting-started/">šŸš€ Quick Start</a> | <a href="https://discord.gg/myBMaaDeQu">šŸ’¬ Discord</a> </p> <p align="center"> <a href="https://www.npmjs.com/package/@axonotes/axogen"> <img src="https://img.shields.io/npm/v/@axonotes/axogen?style=flat-square&color=blue" alt="npm version" /> </a> <a href="https://www.npmjs.com/package/@axonotes/axogen"> <img src="https://img.shields.io/npm/dm/@axonotes/axogen?style=flat-square&color=green" alt="npm downloads" /> </a> <a href="https://github.com/axonotes/axogen/blob/main/LICENSE"> <img src="https://img.shields.io/github/license/axonotes/axogen?style=flat-square" alt="license" /> </a> <a href="https://github.com/axonotes/axogen/stargazers"> <img src="https://img.shields.io/github/stars/axonotes/axogen?style=flat-square&color=yellow" alt="stars" /> </a> </p> --- ## The Problem You know that moment when you change a port number and suddenly half your services can't talk to each other? Or when you spend 20 minutes debugging only to realize you forgot to update the WebSocket URL in three different places? In complex projects, configuration is scattered everywhere: ``` project/ ā”œā”€ā”€ api/.env # DATABASE_URL=postgres://... ā”œā”€ā”€ web/.env.local # NEXT_PUBLIC_API_URL=http://... ā”œā”€ā”€ docker-compose.yml # hardcoded ports and URLs ā”œā”€ā”€ k8s/configmap.yaml # same values again └── package.json # scripts with more hardcoded URLs ``` Every time you change a database URL or add a service, you're hunting through multiple files. Miss one? Good luck debugging why staging is broken. ## The Solution Define everything once in TypeScript, generate everywhere: ```typescript // axogen.config.ts import {defineConfig, loadEnv, env, json, group, cmd} from "@axonotes/axogen"; import {z} from "zod"; const config = loadEnv( z.object({ DATABASE_URL: z.url(), API_PORT: z.coerce.number().default(3001), WEB_PORT: z.coerce.number().default(3000), NODE_ENV: z .enum(["development", "staging", "production"]) .default("development"), }) ); export default defineConfig({ targets: { api: env({ path: "api/.env", variables: { DATABASE_URL: config.DATABASE_URL, PORT: config.API_PORT, NODE_ENV: config.NODE_ENV, }, }), web: env({ path: "web/.env.local", variables: { NEXT_PUBLIC_API_URL: `http://localhost:${config.API_PORT}`, NODE_ENV: config.NODE_ENV, }, }), docker: json({ path: "docker-config.json", variables: { apiPort: config.API_PORT, webPort: config.WEB_PORT, dbUrl: config.DATABASE_URL, }, }), }, commands: { dev: "docker-compose up -d", "dev:api": `cd api && npm run dev --port ${config.API_PORT}`, database: group({ commands: { migrate: "npm run migrate", seed: "npm run seed", backup: group({ commands: { create: cmd({ help: "Create a database backup", options: { name: z.string().describe("Backup name"), }, exec: (ctx) => console.log( `Creating backup: ${ctx.options.name}` ), }), }, }), }, }), }, }); ``` Put your actual values in `.env.axogen`: ```bash DATABASE_URL=postgresql://localhost:5432/myapp API_PORT=3001 WEB_PORT=3000 NODE_ENV=development ``` One command generates all your configs: ```bash axogen generate ``` Change `API_PORT` to 4000, regenerate, and every URL automatically updates. One source of truth, everything else just follows. ## ✨ Features - **šŸ›”ļø Full type safety** with Zod validation and TypeScript IntelliSense - **šŸ” Secret detection** prevents accidental commits of sensitive data - **šŸ“ 10+ file formats** - `.env`, JSON, YAML, TOML, XML, templates, and more - **šŸŽØ Template engine support** (Nunjucks, Handlebars, Mustache) - **⚔ Nested command system** with help text and typed arguments - **šŸŽÆ Conditional generation** based on environment or custom logic - **šŸ’¾ Automatic backups** before overwriting files - **šŸŒ Language-agnostic** - Works with Python, Go, Rust, Java, PHP, etc. - **⚔ Fast** - 10,000 configs generated in ~2 seconds ## šŸš€ Quick Start ```bash # Install npm install @axonotes/axogen # Create basic config echo 'import {defineConfig, env} from "@axonotes/axogen"; export default defineConfig({ targets: { app: env({ path: ".env", variables: { NODE_ENV: "development", PORT: 3000, }, }), }, });' > axogen.config.ts # Generate axogen generate # Run commands axogen run --help ``` ## šŸ¤” Why Not Just Use...? **Language-specific tools** (dotenv, Viper, dynaconf): Only work for one language. What about your Docker configs? Kubernetes manifests? **Infrastructure tools** (Terraform, Helm): Great for infrastructure, overkill for application config. **The key difference:** Axogen works for ANY project in ANY language. Your Python API, Go microservice, React frontend, Docker configs, and Kubernetes manifests - all from one TypeScript source of truth. ## šŸ“š Examples ### Multi-Service Monorepo ```typescript export default defineConfig({ targets: { auth: env({ path: "services/auth/.env", variables: {...} }), api: env({ path: "services/api/.env", variables: {...} }), web: env({ path: "apps/web/.env.local", variables: {...} }), docker: template({ path: "docker-compose.yml", template: "docker-compose.njk", variables: config, }), }, }); ``` ### Environment-Specific Configs ```typescript const config = getConfig(); // Your function that returns different values based on NODE_ENV export default defineConfig({ targets: { production: env({ path: "prod/.env", variables: config.production, condition: process.env.NODE_ENV === "production", }), }, }); ``` ## āš ļø Current Status This is v0.5.0 - actively developed and used for [AxonotesCore](https://github.com/axonotes/AxonotesCore), but still evolving. The API is getting more stable, but expect some changes before v1.0. Generated files are just normal `.env`, JSON, etc. - your apps never depend on Axogen at runtime. Worst case, you can stop using Axogen and keep the generated configs. ## šŸ¤ Contributing Found a bug? Have a feature idea? Open an issue or join the [Discord](https://discord.gg/myBMaaDeQu). ## šŸ“Š Star History <a href="https://www.star-history.com/#axonotes/axogen&Date"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=axonotes/axogen&type=Date&theme=dark" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=axonotes/axogen&type=Date" /> <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=axonotes/axogen&type=Date" /> </picture> </a> ## šŸ“„ License MIT --- <p align="center"> Built with ā¤ļø by <a href="https://github.com/imgajeed76">Oliver Seifert</a> </p>