UNPKG

@axonotes/axogen

Version:

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

1,536 lines (1,510 loc) 126 kB
#!/usr/bin/env node var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports); // package.json var require_package = __commonJS((exports, module) => { module.exports = { name: "@axonotes/axogen", version: "0.5.7", description: "TypeScript-native configuration system that unifies typed environment variables, code generation, and task management for any project", type: "module", main: "dist/index.cjs", module: "dist/index.js", types: "dist/index.d.ts", exports: { ".": { import: { types: "./dist/index.d.ts", default: "./dist/index.js" }, require: { types: "./dist/index.d.ts", default: "./dist/index.cjs" } } }, bin: { axogen: "./bin/axogen.js" }, files: [ "bin/axogen.js", "bin/axogen.cmd", "dist" ], scripts: { build: "bun run build.ts", preview: "bun run format && bun test && bun run build && bun link", pub: "bun run format && bun test && bun run build && bun publish", "test:local": "bun run build && ./bin/axogen.js --version", "docs:start": "cd website && bun run start", "docs:build": "cd website && bun run build", "docs:serve": "cd website && bun run serve", format: "prettier --write .", check: "tsc --noEmit", prepare: "husky" }, homepage: "https://axonotes.github.io/axogen/", repository: { type: "git", url: "https://github.com/axonotes/axogen.git" }, bugs: { url: "https://github.com/axonotes/axogen/issues" }, funding: { type: "github", url: "https://github.com/sponsors/imgajeed76" }, keywords: [ "typescript", "configuration", "environment-variables", "config-management", "task-management", "cli", "dotenv", "yaml", "json", "toml", "template", "monorepo", "docker", "kubernetes", "type-safety", "zod", "code-generation", "env-files", "typescript-config", "config-sync", "developer-tools", "devtools", "build-tools", "typescript-validation", "environment-config", "config-system", "typescript-native", "type-safe-config", "env-management", "config-cli", "developer-productivity", "typescript-tools" ], author: { name: "Oliver Seifert", url: "https://github.com/imgajeed76" }, license: "MIT", dependencies: { "@dotenvx/dotenvx": "^1.48.3", "@iarna/toml": "^2.2.5", "@types/cson": "^7.20.3", "@types/hjson": "^2.4.6", "@types/ini": "^4.1.1", "@types/js-yaml": "^4.0.9", "@types/papaparse": "^5.3.16", ansis: "^4.1.0", chalk: "^5.4.1", commander: "^14.0.0", cson: "^8.4.0", "fast-xml-parser": "^5.2.5", handlebars: "^4.7.8", hjson: "^3.2.2", ini: "^5.0.0", jiti: "^2.4.2", "js-yaml": "^4.1.0", json5: "^2.2.3", "jsonc-parser": "^3.3.1", mustache: "^4.2.0", nunjucks: "^3.2.4", papaparse: "^5.5.3", "properties-file": "^3.5.13", tsx: "^4.20.3", zod: "^4.0.5" }, devDependencies: { "@types/bun": "latest", "@types/mustache": "^4.2.6", "@types/nunjucks": "^3.2.6", husky: "^9.1.7", "lint-staged": "^16.1.2", prettier: "3.6.2", tsup: "^8.5.0", typescript: "^5.8.3" }, peerDependencies: { typescript: "^5" }, "lint-staged": { "**/*": "prettier --write --ignore-unknown" }, engines: { node: ">=18", bun: ">=1.2" } }; }); // src/cli-helpers/index.ts import { Command as Command4 } from "commander"; // src/utils/helpers.ts function zodIssuesToErrors(issues) { return issues.map((issue) => { const field = formatFieldPath(issue.path); let type = "invalid"; let message = issue.message; switch (issue.code) { case "invalid_type": { const typedIssue = issue; const isMissing = issue.message.includes("received undefined") || issue.message.includes("Required") || issue.message.includes("required"); type = isMissing ? "missing" : "type"; if (type === "missing") { message = field ? `${field} is required` : "This field is required"; } else { if (typedIssue.expected) { message = field ? `${field} must be a ${typedIssue.expected}` : `Must be a ${typedIssue.expected}`; } else { const match = issue.message.match(/expected (\w+)/); const expectedType = match ? match[1] : "valid value"; message = field ? `${field} must be a ${expectedType}` : `Must be a ${expectedType}`; } } break; } case "too_small": { const sizeIssue = issue; if (sizeIssue.minimum !== undefined) { if (field.includes(".") || field.includes("[")) { if (sizeIssue.inclusive) { message = field ? `${field} must be at least ${sizeIssue.minimum}` : `Must be at least ${sizeIssue.minimum}`; } else { message = field ? `${field} must be greater than ${sizeIssue.minimum}` : `Must be greater than ${sizeIssue.minimum}`; } } else { const minValue = sizeIssue.minimum; if (typeof minValue === "number" && minValue < 1000 && minValue > -1000) { const unit = minValue === 1 ? "character" : "characters"; message = field ? `${field} must be at least ${minValue} ${unit}` : `Must be at least ${minValue} ${unit}`; } else { const comparator = sizeIssue.inclusive ? ">=" : ">"; message = field ? `${field} must be ${comparator} ${minValue}` : `Must be ${comparator} ${minValue}`; } } } break; } case "too_big": { const sizeIssue = issue; if (sizeIssue.maximum !== undefined) { if (field.includes(".") || field.includes("[")) { if (sizeIssue.inclusive) { message = field ? `${field} must be at most ${sizeIssue.maximum}` : `Must be at most ${sizeIssue.maximum}`; } else { message = field ? `${field} must be less than ${sizeIssue.maximum}` : `Must be less than ${sizeIssue.maximum}`; } } else { const maxValue = sizeIssue.maximum; if (typeof maxValue === "number" && maxValue < 1000 && maxValue > 0) { const unit = maxValue === 1 ? "character" : "characters"; message = field ? `${field} must be at most ${maxValue} ${unit}` : `Must be at most ${maxValue} ${unit}`; } else { const comparator = sizeIssue.inclusive ? "<=" : "<"; message = field ? `${field} must be ${comparator} ${maxValue}` : `Must be ${comparator} ${maxValue}`; } } } break; } case "invalid_format": { const formatIssue = issue; if (formatIssue.format) { const formatMessages = { email: "valid email address", url: "valid URL", uuid: "valid UUID", regex: "valid format", cuid: "valid CUID", cuid2: "valid CUID2", ulid: "valid ULID", datetime: "valid datetime", ip: "valid IP address", base64: "valid base64 string", nanoid: "valid NanoID" }; const formatName = formatMessages[formatIssue.format] || "valid format"; message = field ? `${field} must be a ${formatName}` : `Must be a ${formatName}`; } break; } case "invalid_value": { const valueIssue = issue; if (valueIssue.options && Array.isArray(valueIssue.options)) { message = field ? `${field} must be one of: ${valueIssue.options.join(", ")}` : `Must be one of: ${valueIssue.options.join(", ")}`; } else if (valueIssue.expected) { message = field ? `${field} must be ${valueIssue.expected}` : `Must be ${valueIssue.expected}`; } break; } case "unrecognized_keys": { const keysIssue = issue; if (keysIssue.keys && Array.isArray(keysIssue.keys)) { const keys = keysIssue.keys.join(", "); message = field ? `${field} contains unrecognized keys: ${keys}` : `Unrecognized keys: ${keys}`; } break; } case "invalid_union": { message = field ? `${field} does not match any of the expected types` : "Does not match any of the expected types"; break; } case "invalid_key": { message = field ? `${field} contains an invalid key` : "Contains an invalid key"; break; } case "invalid_element": { message = field ? `${field} contains an invalid element` : "Contains an invalid element"; break; } case "not_multiple_of": { const multipleIssue = issue; if (multipleIssue.multipleOf !== undefined) { message = field ? `${field} must be a multiple of ${multipleIssue.multipleOf}` : `Must be a multiple of ${multipleIssue.multipleOf}`; } break; } case "custom": { if (!message || message === "Invalid input") { message = field ? `${field} is invalid` : "Invalid value"; } break; } default: { if (!message || message === "Invalid input") { message = field ? `${field} is invalid` : "Invalid value"; } break; } } return { field, message, type }; }); } function formatFieldPath(path) { if (path.length === 0) return ""; return path.reduce((acc, segment, index) => { if (typeof segment === "number") { return `${acc}[${segment}]`; } if (index === 0) { return String(segment); } return `${acc}.${String(segment)}`; }, ""); } // src/cli-helpers/runner.ts import * as z2 from "zod"; // src/cli-helpers/zod_helpers.ts import * as z from "zod"; function getCommandHelp(command) { if (typeof command === "string" || typeof command === "function") { return; } return command.help; } function isStringCommand(command) { return typeof command === "object" && command.type === "string"; } function isAdvancedCommand(command) { return typeof command === "object" && command.type === "advanced"; } function isGroupCommand(command) { return typeof command === "object" && command.type === "group"; } function validateOptionsWithZod(rawOptions, optionsSchema) { const processedOptions = { ...rawOptions }; for (const [key, schema] of Object.entries(optionsSchema)) { const info = analyzeZodSchema(schema); if (info.baseType === "array" && typeof processedOptions[key] === "string") { processedOptions[key] = processedOptions[key].split(",").map((s) => s.trim()); } } return z.object(optionsSchema).parse(processedOptions); } function validateArgsWithZod(rawArgs, argsSchema) { const argNames = Object.keys(argsSchema); const argsObject = {}; for (let i = 0;i < argNames.length; i++) { const argName = argNames[i]; const value = rawArgs[i]; if (value !== undefined) { argsObject[argName] = value; } } try { return z.object(argsSchema).parse(argsObject); } catch (error) { if (error instanceof z.ZodError) { throw new z.ZodError([ ...error.issues.map((issue) => ({ ...issue, path: ["args", ...issue.path] })) ]); } throw error; } } function analyzeZodSchema(schema) { let currentSchema = schema; let isOptional = false; let description = undefined; while (currentSchema) { if (!description) { description = extractDescription(currentSchema); } const type = getZodType(currentSchema); if (type === "optional" || type === "nullable" || type === "default") { isOptional = true; if (canUnwrap(currentSchema)) { currentSchema = currentSchema.unwrap(); } else { break; } } else { break; } } const baseType = getZodType(currentSchema); return { baseType, isOptional, description }; } function getZodType(schema) { return schema._zod?.def?.type || "unknown"; } function extractDescription(schema) { try { const meta = schema.meta(); if (meta && typeof meta.description === "string") { return meta.description; } } catch {} return; } function canUnwrap(schema) { return typeof schema.unwrap === "function"; } // src/utils/console/themes.ts import chalk2 from "chalk"; // src/utils/console/formatter.ts import chalk from "chalk"; class XMLParser { input; position; constructor(input) { this.input = input; this.position = 0; } parse() { const root = []; const stack = [{ children: root }]; while (this.position < this.input.length) { if (this.peek() === "\\" && this.peekNext() === "<") { const text = this.parseText(); if (text.length > 0) { const parent = stack[stack.length - 1]; if (!parent) { throw new Error("No parent node found for text content"); } parent.children.push({ text, children: [] }); } } else if (this.peek() === "<") { const tag = this.parseTag(); if (tag) { if (tag.isClosing) { if (stack.length > 1) { stack.pop(); } } else { const node = { tag: tag.name, children: [] }; const parent = stack[stack.length - 1]; if (!parent) { throw new Error("No parent node found for new tag"); } parent.children.push(node); if (!tag.isSelfClosing) { stack.push(node); } } } } else { const text = this.parseText(); if (text.length > 0) { const parent = stack[stack.length - 1]; if (!parent) { throw new Error("No parent node found for text content"); } parent.children.push({ text, children: [] }); } } } return root; } peek() { return this.input[this.position] || ""; } peekNext() { return this.input[this.position + 1] || ""; } advance() { const char = this.input[this.position] || ""; this.position++; return char; } parseTag() { if (this.peek() !== "<") return null; this.advance(); let isClosing = false; if (this.peek() === "/") { isClosing = true; this.advance(); } let tagName = ""; while (this.position < this.input.length && this.peek() !== ">" && this.peek() !== "/" && !this.isWhitespace(this.peek())) { tagName += this.advance(); } while (this.position < this.input.length && this.peek() !== ">" && this.peek() !== "/") { this.advance(); } let isSelfClosing = false; if (this.peek() === "/") { isSelfClosing = true; this.advance(); } if (this.peek() === ">") { this.advance(); } return { name: tagName.trim(), isClosing, isSelfClosing }; } parseText() { let text = ""; while (this.position < this.input.length) { const current = this.peek(); if (current === "\\") { const next = this.peekNext(); if (next === "<" || next === ">") { text += next; this.advance(); this.advance(); } else if (next === "\\") { text += "\\"; this.advance(); this.advance(); } else { text += this.advance(); } } else if (current === "<") { break; } else { text += this.advance(); } } return text; } isWhitespace(char) { return /\s/.test(char); } } class TreeTransformer { transformers; constructor(transformers) { this.transformers = transformers; } transform(nodes) { return nodes.map((node) => this.transformNode(node)).join(""); } transformNode(node) { const childrenContent = node.children.map((child) => this.transformNode(child)).join(""); if (node.text !== undefined) { return node.text; } if (node.tag && this.transformers[node.tag]) { return this.transformers[node.tag](childrenContent); } console.warn(chalk.redBright(`No transformer found for tag: ${node.tag}. Returning content as-is.`)); return childrenContent; } } function parseAndTransform(input, transformers) { const parser = new XMLParser(input); const tree = parser.parse(); const transformer = new TreeTransformer(transformers); return transformer.transform(tree); } // src/env/helpers.ts import { exec } from "child_process"; import * as os from "os"; import * as path from "path"; import * as fs from "fs/promises"; function promisifyExec(command) { return new Promise((resolve, reject) => { exec(command, (error) => { if (error) { reject(error); } else { resolve(); } }); }); } async function setPersistentEnvVariable(key, value) { const platform2 = os.platform(); switch (platform2) { case "win32": await setWindowsPersistent(key, value); break; case "darwin": case "linux": await setUnixPersistent(key, value); break; default: throw new Error(`Cant set environment variable. Unsupported platform: ${platform2}`); } process.env[key] = value; } async function setWindowsPersistent(key, value) { try { await promisifyExec(`setx ${key} "${value}"`); } catch (error) { console.error(`Failed to set ${key} persistently for Windows user:`, error); throw new Error(`Failed to set environment variable ${key} on Windows`); } } async function setUnixPersistent(key, value) { const homeDir = os.homedir(); const shell = process.env.SHELL || "/bin/bash"; let profileFiles; if (shell.includes("zsh")) { profileFiles = [".zshrc", ".zprofile"]; } else if (shell.includes("bash")) { profileFiles = [".bashrc", ".bash_profile", ".profile"]; } else if (shell.includes("fish")) { profileFiles = [".config/fish/config.fish"]; } else { profileFiles = [".profile"]; } const exportLine = `export ${key}="${value}"`; for (const profileFile of profileFiles) { const filePath = path.join(homeDir, profileFile); try { await fs.access(filePath); const content = await fs.readFile(filePath, "utf-8"); const regex = new RegExp(`^export ${key}=.*$`, "m"); if (regex.test(content)) { const newContent = content.replace(regex, exportLine); await fs.writeFile(filePath, newContent); } else { await fs.appendFile(filePath, ` ${exportLine} `); } console.log(`---> Run 'source ~/${profileFile}' to apply changes or restart your terminal.`); break; } catch (error) {} } } // src/utils/console/themes.ts function hexToRgb(hex) { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); if (!result) throw new Error(`Invalid hex color: ${hex}`); return [ parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16) ]; } function rgbToHex(r, g, b) { return "#" + [r, g, b].map((x) => { const hex = Math.round(x).toString(16); return hex.length === 1 ? "0" + hex : hex; }).join(""); } function interpolateColor(color1, color2, factor) { const [r1, g1, b1] = hexToRgb(color1); const [r2, g2, b2] = hexToRgb(color2); const r = r1 + factor * (r2 - r1); const g = g1 + factor * (g2 - g1); const b = b1 + factor * (b2 - b1); return rgbToHex(r, g, b); } function generateNeutralScale(lightColor, darkColor) { const scalePoints = [0, 100, 200, 300, 400, 500, 600, 700, 800, 900]; const neutrals = {}; scalePoints.forEach((point) => { const factor = point / 900; neutrals[point.toString()] = interpolateColor(lightColor, darkColor, factor); }); return neutrals; } var vscode = { name: "vscode", description: "VS Code's default dark theme with balanced contrast", colors: { primary: "#007fd4", secondary: "#ff6b35", success: "#0dbc79", warning: "#ffcc02", danger: "#f14c4c", neutralLight: "#ffffff", neutralDark: "#1e1e1e" } }; var astrodark = { name: "astrodark", description: "Sophisticated dark theme with balanced vibrant colors", colors: { primary: "#CC83E3", secondary: "#50A4E9", success: "#75AD47", warning: "#D09214", danger: "#F8747E", neutralLight: "#ffffff", neutralDark: "#111317" } }; var aura = { name: "aura", description: "Vibrant neon-inspired dark theme with electric colors", colors: { primary: "#A277FF", secondary: "#4D8AFF", success: "#61FFCA", warning: "#FFCA85", danger: "#FF6767", neutralLight: "#ffffff", neutralDark: "#110F18" } }; var doomOne = { name: "doom-one", description: "Classic sophisticated dark theme with muted vibrancy", colors: { primary: "#c678dd", secondary: "#51afef", success: "#98be65", warning: "#ecbe7b", danger: "#ff6c6b", neutralLight: "#ffffff", neutralDark: "#1b2229" } }; var catppuccinMocha = { name: "catppuccin-mocha", description: "Soothing pastel theme for comfortable long sessions", colors: { primary: "#cba6f7", secondary: "#89b4fa", success: "#a6e3a1", warning: "#f9e2af", danger: "#f38ba8", neutralLight: "#ffffff", neutralDark: "#1e1e2e" } }; var themes = { vscode, astrodark, aura, "doom-one": doomOne, "catppuccin-mocha": catppuccinMocha }; var themeColorNames = [ "primary", "secondary", "success", "warning", "danger", "neutral_0", "neutral_100", "neutral_200", "neutral_300", "neutral_400", "neutral_500", "neutral_600", "neutral_700", "neutral_800", "neutral_900" ]; var colorizeColorNames = [ ...themeColorNames, "text", "textSecondary", "textMuted", "background", "backgroundLight", "border" ]; var DEFAULT_THEME = process.env.AXOGEN_THEME || "vscode"; var DEFAULT_ENABLE_COLOR = (process.env.AXOGEN_ENABLE_COLOR || "true").toLowerCase() === "true"; function getTheme(name) { if (!name || !(name in themes)) { return themes[DEFAULT_THEME]; } return themes[name]; } function isValidTheme(name) { return name in themes; } class ThemeManager { currentTheme; colorizer; neutralColors; transformers; enableColor = DEFAULT_ENABLE_COLOR; constructor() { this.currentTheme = getTheme(DEFAULT_THEME); this.neutralColors = generateNeutralScale(this.currentTheme.colors.neutralLight, this.currentTheme.colors.neutralDark); this.colorizer = this.createColorizer(this.currentTheme); this.transformers = this.createTransformers(); } createColorizer(theme) { const colorizer = {}; if (!this.enableColor) { colorizeColorNames.forEach((name) => { colorizer[name] = chalk2.white.bgBlack; }); return colorizer; } colorizer.primary = chalk2.hex(theme.colors.primary); if (theme.colors.secondary) { colorizer.secondary = chalk2.hex(theme.colors.secondary); } colorizer.success = chalk2.hex(theme.colors.success); colorizer.warning = chalk2.hex(theme.colors.warning); colorizer.danger = chalk2.hex(theme.colors.danger); Object.entries(this.neutralColors).forEach(([level, color]) => { colorizer[`neutral_${level}`] = chalk2.hex(color); }); colorizer.text = chalk2.hex(this.neutralColors["200"]); colorizer.textSecondary = chalk2.hex(this.neutralColors["600"]); colorizer.textMuted = chalk2.hex(this.neutralColors["400"]); colorizer.background = chalk2.hex(this.neutralColors["900"]); colorizer.backgroundLight = chalk2.hex(this.neutralColors["100"]); colorizer.border = chalk2.hex(this.neutralColors["300"]); return colorizer; } createTransformers() { return { b: (text) => this.colorizer.text.bold(text), d: (text) => this.colorizer.text.dim(text), i: (text) => this.colorizer.text.italic(text), u: (text) => this.colorizer.text.underline(text), s: (text) => this.colorizer.text.strikethrough(text), primary: (text) => this.colorizer.primary(text), secondary: (text) => this.colorizer.secondary(text), success: (text) => this.colorizer.success(text), warning: (text) => this.colorizer.warning(text), danger: (text) => this.colorizer.danger(text), error: (text) => this.colorizer.danger(text), muted: (text) => this.colorizer.textMuted(text), subtle: (text) => this.colorizer.textSecondary(text), text: (text) => this.colorizer.text(text), "dim-text": (text) => this.colorizer.textSecondary(text), r: (text) => chalk2.reset(text) }; } get colorize() { return this.colorizer; } get theme() { return this.currentTheme; } get neutrals() { return this.neutralColors; } setTheme(name, persistent = false) { if (!isValidTheme(name)) { throw new Error(`Invalid theme name: ${name}`); } this.currentTheme = getTheme(name); this.updateAll(); process.env.AXOGEN_THEME = this.currentTheme.name; if (!persistent) { return; } setPersistentEnvVariable("AXOGEN_THEME", this.currentTheme.name).catch(() => { console.warn("Failed to save theme to persistent environment"); }); } colorOutput(enable, persistent = false) { this.enableColor = enable; this.updateAll(); process.env.AXOGEN_ENABLE_COLOR = String(enable); if (!persistent) { return; } setPersistentEnvVariable("AXOGEN_ENABLE_COLOR", String(enable)).catch(() => { console.warn("Failed to save color output setting to persistent environment"); }); } updateAll() { this.neutralColors = generateNeutralScale(this.currentTheme.colors.neutralLight, this.currentTheme.colors.neutralDark); this.colorizer = this.createColorizer(this.currentTheme); this.transformers = this.createTransformers(); } getColor(colorName) { const theme = this.currentTheme; if (colorName.startsWith("neutral_")) { const level = colorName.split("_")[1]; return this.neutralColors[level] || theme.colors.neutralLight; } return theme.colors[colorName]; } getNeutralScale() { return { ...this.neutralColors }; } format(xml) { return parseAndTransform(xml, this.transformers); } } var themeManager = new ThemeManager; // src/utils/console/logger.ts function format(xml) { return themeManager.format(xml); } function logF(xml) { console.log(format(xml)); } var logger = { logF: (xml) => logF(xml), format: (xml) => format(xml), success: (message) => logF(`<success>${message}</success>`), error: (message) => logF(`<error>${message}</error>`), warn: (message) => logF(`<warning>${message}</warning>`), info: (message) => logF(` <primary>${message}</primary>`), debug: (message) => console.debug(message), trace: (message) => console.trace(message), file: (message, path2) => { logF(` <primary>+</primary> ${message} <subtle>${path2 ? `[${path2}]` : ""}</subtle>`); }, command: (message) => { logF(`<secondary>></secondary> ${message}`); }, start: (message) => { logF(`<primary>➤</primary> ${message}`); }, logIssues: (logConfig) => { console.log(); logF(`<error>${logConfig.title}</error>`); console.log(` ${logConfig.subtitle}`); console.log(); const groupedItems = logConfig.items.reduce((groups, item) => { if (!groups[item.level]) { groups[item.level] = []; } groups[item.level].push(item); return groups; }, {}); Object.entries(groupedItems).forEach(([levelName, items]) => { const levelConfig = logConfig.levels[levelName]; if (!levelConfig) return; logF(` <${levelConfig.color}>${levelName.toUpperCase()}</${levelConfig.color}> <subtle>${items.length}</subtle>`); items.forEach((item) => { const itemIcon = `<${levelConfig.color}>•</${levelConfig.color}>`; const key = `<primary>${item.key}</primary>`; const description = item.description; const extra = `<subtle>${item.extra ? ` [${item.extra}]` : ""}</subtle>`; logF(` ${itemIcon} ${key}: ${description}${extra}`); }); console.log(); }); logF(` <${logConfig.footerIconColor}>${logConfig.footerIcon}</${logConfig.footerIconColor}>${logConfig.footerIcon ? " " : ""}${logConfig.footer}`); console.log(); }, validation: (title, errors) => { const missing = errors.filter((e) => e.type === "missing"); const typeErrors = errors.filter((e) => e.type === "type"); const invalid = errors.filter((e) => e.type === "invalid" || !e.type); const summaryParts = [ missing.length > 0 && `${missing.length} missing`, typeErrors.length > 0 && `${typeErrors.length} type error${typeErrors.length !== 1 ? "s" : ""}`, invalid.length > 0 && `${invalid.length} invalid value${invalid.length !== 1 ? "s" : ""}` ].filter(Boolean); const subtitle = format(`<subtle>Found:</subtle> ${errors.length} validation error${errors.length !== 1 ? "s" : ""} <subtle>${summaryParts.join(" • ")}</subtle>`); const items = errors.map((error) => ({ level: error.type === "missing" ? "missing" : error.type === "type" ? "type-error" : "invalid", key: error.field || "field", description: error.message, extra: undefined })); logger.logIssues({ title: `✗ ${title}`, subtitle, levels: { missing: { color: "error" }, "type-error": { color: "error" }, invalid: { color: "error" } }, items, footer: "Fix these issues before continuing", footerIcon: "!", footerIconColor: "warning" }); }, security: (title, result) => { const summaryParts = [ result.highConfidenceCount > 0 && `${result.highConfidenceCount} high risk`, result.mediumConfidenceCount > 0 && `${result.mediumConfidenceCount} medium risk`, result.lowConfidenceCount > 0 && `${result.lowConfidenceCount} low risk` ].filter(Boolean); const subtitle = format(`<subtle>Found:</subtle> ${result.totalCount} potential secret${result.totalCount !== 1 ? "s" : ""} <subtle>${summaryParts.join(" • ")}</subtle>`); const items = result.secretsFound.map((issue) => ({ level: issue.confidence, key: issue.path || issue.key || "unknown", description: issue.reason, extra: issue.category })); logger.logIssues({ title: `⚠ ${title}`, subtitle, levels: { high: { color: "error" }, medium: { color: "warning" }, low: { color: "muted" } }, items, footer: "Generation blocked for security", footerIcon: "✗", footerIconColor: "error" }); }, header: (title) => { logF(`<secondary>${"═".repeat(Math.max(title.length + 4, 40))}</secondary>`); logF(`<secondary> ${title.toUpperCase()} </secondary>`); logF(`<secondary>${"═".repeat(Math.max(title.length + 4, 40))}</secondary>`); }, divider: (text) => { const barLength = 40; if (text) { const remaining = barLength - 2 - text.length; const padding = Math.max(0, Math.floor(remaining / 2)); const line = "─".repeat(padding); const equalizer = remaining % 2 === 0 ? "" : "─"; logF(`<subtle>${line} ${text} ${line}${equalizer}</subtle>`); } else { logF(`<subtle>${"─".repeat(barLength)}</subtle>`); } }, bullet: (text, level = 1) => { const indent = " ".repeat(level); logF(`<subtle>${indent}•</subtle> ${text}`); }, list: (items, level = 1) => { const indent = " ".repeat(level); items.forEach((item) => { logF(`<subtle>${indent}•</subtle> ${item}`); }); }, prefix: { command: (name, message) => { logF(`<secondary>[${name}]</secondary> ${message}`); } } }; // src/cli-helpers/runner.ts class CommandRunner { async executeCommand(command, options) { try { if (typeof command === "string") { return await this.executeStringCommand(command, options); } if (typeof command === "function") { return await this.executeFunctionCommand(command, options); } if (isStringCommand(command)) { return await this.executeStringCommand(command.command, options); } if (isAdvancedCommand(command)) { return await this.executeAdvancedCommand(command, options); } if (isGroupCommand(command)) { return await this.executeCommandGroup(command, options); } return { success: false, error: "Unknown command type" }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } } async executeStringCommand(command, options) { try { const prefix = this.extractCommandPrefix(command); const result = await liveExec(command, { cwd: options.global.cwd, env: options.global.process_env, outputPrefix: prefix }); return { success: result.exitCode === 0, exitCode: result.exitCode, error: result.exitCode !== 0 ? `Command exited with code ${result.exitCode}` : undefined }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } } extractCommandPrefix(command) { const trimmed = command.trim(); const cdAndMatch = trimmed.match(/cd\s+[^&]*&&\s*(.+)/); if (cdAndMatch) { return this.extractCommandPrefix(cdAndMatch[1]); } const pipeMatch = trimmed.match(/^([^|]+)/); const baseCommand = pipeMatch ? pipeMatch[1].trim() : trimmed; const words = baseCommand.split(/\s+/); const firstWord = words[0]; const commandMappings = { npm: "NPM", yarn: "YARN", pnpm: "PNPM", bun: "BUN", cargo: "CARGO", rustc: "RUST", node: "NODE", deno: "DENO", python: "PYTHON", python3: "PYTHON", pip: "PIP", git: "GIT", docker: "DOCKER", kubectl: "K8S", helm: "HELM", terraform: "TF", aws: "AWS", gcloud: "GCP", az: "AZURE", make: "MAKE", cmake: "CMAKE", gcc: "GCC", clang: "CLANG", go: "GO", javac: "JAVA", java: "JAVA", mvn: "MAVEN", gradle: "GRADLE", dotnet: "DOTNET", nuget: "NUGET", composer: "PHP", php: "PHP", ruby: "RUBY", gem: "GEM", bundle: "BUNDLE", rails: "RAILS", mix: "ELIXIR", iex: "ELIXIR", stack: "HASKELL", cabal: "HASKELL", swift: "SWIFT", xcodebuild: "XCODE", flutter: "FLUTTER", dart: "DART" }; const mapped = commandMappings[firstWord.toLowerCase()]; if (mapped) { return mapped; } if (firstWord.includes("/")) { const basename = firstWord.split("/").pop() || firstWord; return basename.toUpperCase(); } const withoutExt = firstWord.replace(/\.(exe|sh|bat|cmd|ps1)$/i, ""); return withoutExt.toUpperCase().slice(0, 8); } async executeFunctionCommand(fn, options) { try { const context = { global: options.global, config: options.config }; await fn(context); return { success: true }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; } } async executeAdvancedCommand(command, options) { try { const validatedOptions = command.options ? validateOptionsWithZod(options.options || {}, command.options) : {}; const validatedArgs = command.args ? validateArgsWithZod(options.args || [], command.args) : {}; await command.exec({ global: options.global, config: options.config, options: validatedOptions, args: validatedArgs }); return { success: true }; } catch (error) { if (error instanceof z2.ZodError) { const validationErrors = zodIssuesToErrors(error.issues); logger.validation(`Command validation failed: ${command.help || "No description"}`, validationErrors); return { success: false, error: "Validation failed" }; } return { success: false, error: error instanceof Error ? error.message : String(error) }; } } async executeCommandGroup(group, options) { const [subcommandName, ...remainingArgs] = options.args || []; if (!subcommandName) { if (group.help) { logger.info(group.help); console.log(); } logger.info("Available subcommands:"); console.log(); const commandRows = []; for (const [name, command] of Object.entries(group.commands)) { const help = getCommandHelp(command); commandRows.push({ key: name, value: help || "No description available" }); } if (commandRows.length > 0) { const maxKeyLength = Math.max(...commandRows.map((row) => row.key.length)); commandRows.forEach((row) => { const paddedKey = row.key.padEnd(maxKeyLength, " "); logger.bullet(`${paddedKey} - ${row.value}`); }); } else { logger.bullet("No subcommands available"); } return { success: true }; } const subcommand = group.commands[subcommandName]; if (!subcommand) { return { success: false, error: `Subcommand "${subcommandName}" not found` }; } return this.executeCommand(subcommand, { ...options, args: remainingArgs }); } } var commandRunner = new CommandRunner; // src/cli-helpers/index.ts import { spawn } from "node:child_process"; import { resolve as resolve4, isAbsolute } from "node:path"; // src/version.ts function getVersion() { if (true) { return "0.5.7"; } try { const packageJson = require_package(); return packageJson.version; } catch { return "dev"; } } // src/config/loader.ts import { resolve, join as join2 } from "node:path"; import { access as access2, constants } from "node:fs/promises"; import { z as z7 } from "zod"; import { createJiti } from "jiti"; // src/config/types/targets.ts var templateTargetEngines = [ "nunjucks", "handlebars", "mustache" ]; function json(config) { return { type: "json", ...config }; } // src/config/types/index.ts import * as z6 from "zod"; // src/config/types/zod_config.ts import * as z5 from "zod"; // src/config/types/zod_targets.ts import * as z3 from "zod"; var backupTargetOptionsSchema = z3.object({ enabled: z3.boolean().describe("Whether to create a backup of the target file").default(true).optional(), folder: z3.string().describe("The path to the backup folder. Defaults to '.axogen/backup/{{path}}'").default("").optional(), maxBackups: z3.number().describe("The maximum number of backups to keep. Defaults to 5").default(5).optional(), onConflict: z3.enum(["overwrite", "increment", "skip", "fail"]).describe("What to do when a backup file already exists. Defaults to 'increment'. Mostly not used since filenames are ISO timestamped (so pretty unique).").default("increment").optional() }); var backupTargetSchema = z3.union([ z3.boolean().describe("Whether to create a backup of the target file"), backupTargetOptionsSchema ]).default(false); var baseTargetSchema = z3.object({ path: z3.string().describe("The output path for the target"), schema: z3.custom((val) => { return val && typeof val === "object" && (("_def" in val) || ("_zod" in val)); }).describe("The Schema to validate the variables against").optional(), generate_meta: z3.boolean().describe("Whether to generate metadata for the target").default(false), condition: z3.boolean().describe("Condition to determine if the target should be generated").default(true).optional(), backup: backupTargetSchema.optional() }); var jsonTargetSchema = baseTargetSchema.extend({ type: z3.literal("json").describe("The type of the target, in this case JSON"), variables: z3.record(z3.string(), z3.any()).describe("Variables to be used in the target"), options: z3.custom().optional() }); var json5TargetSchema = baseTargetSchema.extend({ type: z3.literal("json5").describe("The type of the target, in this case JSON5"), variables: z3.record(z3.string(), z3.any()).describe("Variables to be used in the target"), options: z3.custom().optional() }); var jsoncTargetSchema = baseTargetSchema.extend({ type: z3.literal("jsonc").describe("The type of the target, in this case JSONC"), variables: z3.record(z3.string(), z3.any()).describe("Variables to be used in the target"), options: z3.custom().optional() }); var hjsonTargetSchema = baseTargetSchema.extend({ type: z3.literal("hjson").describe("The type of the target, in this case HJSON"), variables: z3.record(z3.string(), z3.any()).describe("Variables to be used in the target"), options: z3.custom().optional() }); var yamlTargetSchema = baseTargetSchema.extend({ type: z3.literal("yaml").describe("The type of the target, in this case YAML"), variables: z3.record(z3.string(), z3.any()).describe("Variables to be used in the target"), options: z3.custom().optional() }); var tomlTargetSchema = baseTargetSchema.extend({ type: z3.literal("toml").describe("The type of the target, in this case TOML"), variables: z3.record(z3.string(), z3.any()).describe("Variables to be used in the target") }); var iniTargetSchema = baseTargetSchema.extend({ type: z3.literal("ini").describe("The type of the target, in this case INI"), variables: z3.record(z3.string(), z3.any()).describe("Variables to be used in the target"), options: z3.custom().optional() }); var propertiesTargetSchema = baseTargetSchema.extend({ type: z3.literal("properties").describe("The type of the target, in this case Properties"), variables: z3.record(z3.string(), z3.any()).describe("Variables to be used in the target"), options: z3.custom().optional() }); var envTargetSchema = baseTargetSchema.extend({ type: z3.literal("env").describe("The type of the target, in this case Environment Variables"), variables: z3.record(z3.string(), z3.any()).describe("Variables to be used in the target") }); var xmlTargetSchema = baseTargetSchema.extend({ type: z3.literal("xml").describe("The type of the target, in this case XML"), variables: z3.record(z3.string(), z3.any()).describe("Variables to be used in the target"), options: z3.custom().optional() }); var csvTargetSchema = baseTargetSchema.extend({ type: z3.literal("csv").describe("The type of the target, in this case CSV"), variables: z3.array(z3.record(z3.string(), z3.any())).describe("Variables to be used in the target"), options: z3.custom().optional() }); var csonTargetSchema = baseTargetSchema.extend({ type: z3.literal("cson").describe("The type of the target, in this case CSON"), variables: z3.record(z3.string(), z3.any()).describe("Variables to be used in the target") }); var templateTargetSchema = baseTargetSchema.extend({ type: z3.literal("template").describe("The type of the target, in this case Template"), engine: z3.enum(templateTargetEngines).describe("The template engine to use").default("nunjucks"), template: z3.string().describe("The template string or file path"), variables: z3.record(z3.string(), z3.any()).describe("Variables to be used in the target") }); var anyTargetSchema = z3.discriminatedUnion("type", [ jsonTargetSchema, json5TargetSchema, jsoncTargetSchema, hjsonTargetSchema, yamlTargetSchema, tomlTargetSchema, iniTargetSchema, propertiesTargetSchema, envTargetSchema, xmlTargetSchema, csvTargetSchema, csonTargetSchema, templateTargetSchema ]).check((ctx) => { if (ctx.value.schema && ctx.value.variables) { const result = ctx.value.schema.safeParse(ctx.value.variables); if (!result.success) { result.error.issues.forEach((issue) => { ctx.issues.push({ code: "custom", message: issue.message, path: ["variables", ...issue.path], input: ctx.value }); }); } } }); var allTargetsSchema = z3.record(z3.string(), anyTargetSchema).describe("A record of targets where the key is the target name and the value is the target definition"); // src/config/types/zod_commands.ts import * as z4 from "zod"; var helpSchema = z4.string().describe("A description of the command, used for help output").optional(); var globalCommandContextSchema = z4.object({ cwd: z4.string().describe("The current working directory"), process_env: z4.record(z4.string(), z4.any()).describe("The process environment variables"), verbose: z4.boolean().describe("Whether the command is running in verbose mode") }); var zodTypeSchema = z4.custom((val) => { return val && typeof val === "object" && (("_zod" in val) || ("_def" in val)); }); var commandContextSchema = z4.object({ options: z4.record(z4.string(), zodTypeSchema).describe("The options for the command, as a record of Zod types").default({}), args: z4.record(z4.string(), zodTypeSchema).describe("The arguments for the command, as a record of Zod types").default({}), global: globalCommandContextSchema.describe("The global context for the command, including cwd and env"), config: axogenConfigSchema }); var simpleCommandContextSchema = z4.object({ global: globalCommandContextSchema.describe("The global context for the command, including cwd and env"), config: axogenConfigSchema }); var stringCommandSchema = z4.object({ type: z4.literal("string").describe("The type of the command, in this case a string command"), help: helpSchema, command: z4.string().describe("The command to be executed, as a string") }); var groupCommandSchema = z4.object({ type: z4.literal("group").describe("The type of the command, in this case a group command"), help: helpSchema, commands: z4.record(z4.string(), z4.lazy(() => anyCommandSchema)).describe("The commands in the group") }); var advancedCommandSchema = z4.object({ type: z4.literal("advanced").describe("The type of the command, in this case an advanced command"), help: helpSchema, options: z4.record(z4.string(), zodTypeSchema).describe("The options for the command, as a record of Zod types").default({}), args: z4.record(z4.string(), zodTypeSchema).describe("The arguments for the command, as a record of Zod types").default({}), exec: z4.custom((val) => { return typeof val === "function"; }).describe("The function to execute for the command") }); var simpleStringCommandSchema = z4.string().describe("A simple command string, which is executed directly"); var simpleCommandFunctionSchema = z4.custom((val) => { return typeof val === "function"; }).describe("A simple command function that takes a context object"); var anyCommandSchema = z4.union([ simpleStringCommandSchema, simpleCommandFunctionSchema, stringCommandSchema, groupCommandSchema, advancedCommandSchema ]); // src/config/types/zod_config.ts var axogenConfigSchema = z5.object({ type: z5.literal("AxogenConfig").optional(), watch: z5.array(z5.string()).optional(), targets: allTargetsSchema.optional(), commands: z5.record(z5.string(), anyCommandSchema).optional() }); // src/config/types/commands.ts function cmd(config) { if ("command" in config) { return { type: "string", command: config.command, help: config.help }; } if ("exec" in config) { return { type: "advanced", exec: config.exec, options: config.options, args: config.args, help: config.help }; } throw new Error("Invalid configuration object passed to cmd function"); } // src/config/types/index.ts function defineConfig(config2) { try { return axogenConfigSchema.parse({ ...config2, type: "AxogenConfig" }); } catch (error) { if (error instanceof z6.ZodError) { const validationErrors = zodIssuesToErrors(error.issues); logger.validation(`Configuration validation failed`, validationErrors); logger.logF(`<primary>\uD83D\uDCA1</primary> <d>Check your config file structure.</d>`); throw new Error("Configuration validation failed"); } throw new Error(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`); } } var conf = defineConfig({ targets: { someService: json({ path: "output/someService.json", schema: z6.object({ someVar: z6.string().describe("A variable for the service") }), variables: { someVar: "value" } }) }, commands: { someCommand: cmd({ command: "echo 'Hello, World!'", help: "A simple command that echoes 'Hello, World!'" }), simple: "test", simpleFunction: (context) => { context.config.targets.someService.variables.someVar; }, complex: cmd({ help: "A complex command with options and arguments", args: { someArg: z6.number().describe("An argument for the command") }, exec: (context) => { const arg = context.args.someArg; const someVar = context.config.targets.someService.variables.someVar; } }) } }); // src/config/loader.ts class ConfigLoader { async load(configPath) { const resolvedPath = await this.resolveConfigPath(configPath); try { let configInput; if (resolvedPath.endsWith(".ts")) { const jiti = await this.createJitiLoader(); configInput