@bunli/core

中文文档
0.9.1发布于 2026/04/09MITTypeScriptES Module近 30 天下载 13万

Minimal, type-safe CLI framework for Bun

原始使用文档
文档目录27 节
  1. Installation
  2. Quick Start
  3. Features
  4. Core Concepts
  5. Commands
  6. Options
  7. Multi-Command CLIs
  8. TUI Renderer Buffer Mode
  9. API Reference
  10. defineCommand(config)
  11. option(schema, config)
  12. createCLI(config)
  13. defineConfig(config)
  14. Plugin System
  15. Basic Plugin
  16. Plugin Factory
  17. Using Plugins with Type Safety
  18. Plugin Development Utilities
  19. Module Augmentation
  20. Generated Helpers
  21. Runtime Validation
  22. Type Utilities
  23. Key Utilities
  24. Usage with Generated Types
  25. Related Packages
  26. Documentation
  27. License

@bunli/core

The minimal, type-safe CLI framework for Bun.

Installation

bun add @bunli/core

Quick Start

import { defineCommand, option } from "@bunli/core";
import { z } from "zod";

export default defineCommand({
  name: "greet",
  description: "A friendly greeting",
  options: {
    name: option(z.string().min(1), { description: "Name to greet", short: "n" }),
    excited: option(z.coerce.boolean().default(false), {
      description: "Add excitement",
      short: "e",
      argumentKind: "flag",
    }),
  },
  handler: async ({ flags }) => {
    const greeting = `Hello, ${flags.name}${flags.excited ? "!" : "."}`;
    console.log(greeting);
  },
});

Features

  • 🚀 Type-safe - Full TypeScript support with automatic type inference
  • ⚡ Fast - Powered by Bun's native speed
  • 📦 Zero config - Works out of the box with sensible defaults
  • 🎯 Minimal API - Learn once, use everywhere
  • 🔌 Extensible - Plugin system for custom functionality
  • 🧪 Testable - First-class testing utilities included

Core Concepts

Commands

Define commands with automatic type inference:

import { defineCommand } from "@bunli/core";

export default defineCommand({
  name: "build",
  description: "Build the project",
  handler: async () => {
    console.log("Building...");
  },
});

Options

Use the option helper with Standard Schema validation:

import { defineCommand, option } from "@bunli/core";
import { z } from "zod";

export default defineCommand({
  name: "deploy",
  description: "Deploy the application",
  options: {
    env: option(z.enum(["dev", "staging", "prod"]), { description: "Target environment" }),
    force: option(z.coerce.boolean().default(false), {
      description: "Force deployment",
      short: "f",
      argumentKind: "flag",
    }),
  },
  handler: async ({ flags }) => {
    // TypeScript knows:
    // flags.env is 'dev' | 'staging' | 'prod'
    // flags.force is boolean
  },
});

Multi-Command CLIs

Create complex CLIs with multiple commands:

import { createCLI, defineCommand } from "@bunli/core";

const build = defineCommand({
  name: "build",
  description: "Build the project",
  handler: async () => {},
});

const deploy = defineCommand({
  name: "deploy",
  description: "Deploy the project",
  handler: async () => {},
});

const test = defineCommand({
  name: "test",
  description: "Run tests",
  handler: async () => {},
});

const cli = await createCLI({
  name: "my-tool",
  version: "1.0.0",
  description: "My awesome CLI tool",
});

cli.command(build);
cli.command(deploy);
cli.command(test);

await cli.run();

TUI Renderer Buffer Mode

When using command.render, configure buffer behavior via tui.renderer.bufferMode:

import { defineConfig } from "@bunli/core";

export default defineConfig({
  tui: {
    renderer: {
      bufferMode: "alternate", // or 'standard'
    },
  },
});

Default policy:

  • 'standard' by default
  • set 'alternate' explicitly for fullscreen/blocking terminal flows

API Reference

defineCommand(config)

Creates a command definition with full type inference.

option(schema, config)

Creates a typed option with schema validation.

For boolean-style flags, set argumentKind: 'flag'.

verbose: option(z.boolean().default(false), {
  short: "v",
  description: "Verbose output",
  argumentKind: "flag",
});

Flag parsing contract:

  • --verbose sets the value to true
  • --verbose=true and --verbose=false are both valid
  • --verbose build does not consume build; it remains positional

Use the default value schema if you want the flag to be optional and strongly typed in handlers.

createCLI(config)

Creates a multi-command CLI application.

defineConfig(config)

Defines shared configuration for your CLI.

Plugin System

Bunli provides a powerful plugin system with compile-time type safety:

Basic Plugin

import type { BunliPlugin } from "@bunli/core/plugin";

interface MyPluginStore {
  apiKey: string;
  isAuthenticated: boolean;
}

const myPlugin: BunliPlugin<MyPluginStore> = {
  name: "my-plugin",
  version: "1.0.0",

  // Define the plugin's store
  store: {
    apiKey: "",
    isAuthenticated: false,
  },

  // Lifecycle hooks
  setup(context) {
    // One-time initialization
    context.updateConfig({ description: "Enhanced by my plugin" });
  },

  configResolved(config) {
    // Called after all configuration is resolved
  },

  beforeCommand(context) {
    // Called before each command - context.store is type-safe!
    const apiKey = process.env.API_KEY || "";
    context.setStoreValue("apiKey", apiKey);
    context.setStoreValue("isAuthenticated", apiKey.length > 0);
  },

  afterCommand(context) {
    // Called after each command with results
    if (context.error) {
      console.error("Command failed:", context.error);
    }
  },
};

Plugin Factory

Use createPlugin for better ergonomics:

import { createPlugin } from "@bunli/core/plugin";

type AuthOptions = {
  provider: "github" | "gitlab";
};

type User = {
  name: string;
};

type AuthStore = {
  token: string;
  user: User | null;
};

async function loadToken() {
  return "token";
}

async function fetchUser(_token: string): Promise<User> {
  return { name: "octocat" };
}

export const authPlugin = createPlugin<AuthOptions, AuthStore>((options) => ({
  name: `auth-${options.provider}`,
  store: {
    token: "",
    user: null,
  },
  async beforeCommand(context) {
    const token = await loadToken();
    context.setStoreValue("token", token);
    context.setStoreValue("user", await fetchUser(token));
  },
}));

Using Plugins with Type Safety

const cli = await createCLI({
  name: "my-cli",
  version: "1.0.0",
  plugins: [authPlugin({ provider: "github" }), myPlugin],
});

// In your commands, the store is fully typed!
cli.command({
  name: "deploy",
  description: "Deploy the application",
  handler: async ({ context }) => {
    // TypeScript knows about all plugin stores!
    if (!context || !context.getStoreValue("isAuthenticated")) {
      throw new Error("Not authenticated");
    }
    console.log(`Deploying as ${context.getStoreValue("user")?.name}`);
  },
});

Plugin Development Utilities

Bunli provides utilities for plugin development and testing:

import {
  createTestPlugin,
  composePlugins,
  testPluginHooks,
  assertPluginBehavior,
} from "@bunli/core/plugin";

// Create a test plugin
const testPlugin = createTestPlugin(
  { count: 0, message: "" },
  {
    beforeCommand(context) {
      const count = context.getStoreValue("count");
      context.setStoreValue("count", count + 1);
      console.log(`Count: ${context.getStoreValue("count")}`);
    },
  },
);

// Compose multiple plugins
const composedPlugin = composePlugins(
  testPlugin,
  createTestPlugin({ enabled: true }, { name: "metrics" }),
);

// Test plugin behavior
const results = await testPluginHooks(testPlugin, {
  config: { name: "test-cli", version: "1.0.0" },
  store: { count: 0, message: "test" },
});

assertPluginBehavior(results, {
  beforeCommandShouldSucceed: true,
});

Module Augmentation

Plugins can extend Bunli's interfaces:

declare module "@bunli/core/plugin" {
  interface EnvironmentInfo {
    isCI: boolean;
    ciProvider?: string;
  }
}

Generated Helpers

Bunli automatically generates type-safe helpers for your commands. These are auto-loaded when you set generated: true in your CLI config:

const cli = await createCLI({
  name: "my-cli",
  version: "1.0.0",
  generated: true, // Auto-load generated types
});

Available Helpers

import {
  listCommands,
  getCommandApi,
  getTypedFlags,
  validateCommand,
  findCommandByName,
  findCommandsByDescription,
  getCommandNames,
} from "./.bunli/commands.gen";

// List all available commands
const commands = listCommands();
// Result: ['build', 'dev', 'test', ...]

// Get command metadata
const buildMeta = getCommandApi("build");
console.log(buildMeta.description); // "Build the project"
console.log(buildMeta.options); // { outdir: {...}, watch: {...} }

// Get typed flags for a command
const flags = getTypedFlags("build");
// flags.outdir is typed as string | undefined
// flags.watch is typed as boolean | undefined

// Validate command arguments at runtime
const result = validateCommand("build", { outdir: "dist", watch: true });
if (result.success) {
  console.log("Valid arguments:", result.data);
} else {
  console.error("Validation errors:", result.errors);
}

// Find commands by name or description
const buildCmd = findCommandByName("build");
const devCommands = findCommandsByDescription("development");
const allNames = getCommandNames();

IDE Auto-Import Support

Configure TypeScript path mapping for better IDE support:

// tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "~commands/*": ["./.bunli/commands.gen.ts"]
    }
  }
}

Then use auto-imports:

// These will show up in IDE auto-complete
import { listCommands } from "~commands/helpers";
import { getCommandApi } from "~commands/api";
Generated helpers provide full type safety and runtime introspection for your CLI commands. They're automatically updated when you modify your command definitions.

Runtime Validation

Bunli provides runtime validation utilities for dynamic type checking:

import {
  validateValue,
  validateValues,
  isValueOfType,
  createValidator,
  createBatchValidator,
} from "@bunli/core";
import { z } from "zod";

// Validate a single value
const result = await validateValue("hello", z.string().min(1), {
  option: "message",
  command: "greet",
});

// Validate multiple values
const validated = await validateValues(
  { name: "John", age: 25 },
  {
    name: z.string(),
    age: z.number(),
  },
  "user",
);

// Check value types
const value: unknown = "hello";
if (isValueOfType(value, "string")) {
  console.log("Value is a string");
}

// Create reusable validators
const nameValidator = createValidator(z.string().min(1));
const userValidator = createBatchValidator({
  name: z.string(),
  age: z.number(),
});

Type Utilities

Bunli exports advanced TypeScript type utilities for complex type manipulation, especially useful when working with generated types:

import type {
  UnionToIntersection,
  MergeAll,
  Expand,
  DeepPartial,
  Constrain,
  IsAny,
} from "@bunli/core";

Key Utilities

UnionToIntersection - Convert union types to intersection types:

type Example = UnionToIntersection<{ a: string } | { b: number }>;
// Result: { a: string } & { b: number }

MergeAll - Merge multiple object types:

type Example = MergeAll<[{ a: string }, { b: number }, { c: boolean }]>;
// Result: { a: string; b: number; c: boolean }

Expand - Expand complex types for better IntelliSense:

type Example = Expand<{ nested: { deep: { value: string } } }>;
// Shows full type structure in IDE

DeepPartial - Make all properties optional recursively:

type Example = DeepPartial<{ user: { name: string; age: number } }>;
// Result: { user?: { name?: string; age?: number } }

Constrain - Constrain types with fallback:

type Example = Constrain<string, "a" | "b" | "c", "a">;
// Result: 'a' | 'b' | 'c' (or 'a' if string doesn't match)

Usage with Generated Types

These utilities work particularly well with generated command types:

import { getCommandApi, listCommands } from "./commands.gen";
import { UnionToIntersection, MergeAll } from "@bunli/core";

// Get all command options as a union
type AllCommandOptions = UnionToIntersection<
  ReturnType<typeof getCommandApi>[keyof CommandRegistry]["options"]
>;

// Merge all command metadata
type AllCommands = MergeAll<Array<{ name: string; description: string }>>;
These utilities are especially powerful when combined with generated types for creating CLI wrappers, documentation generators, and command analytics tools.

Documentation

License

MIT © Arya Labs, Inc.

版本与发布记录

@bunli/core 共发布 20 个版本,首次发布于 2025/06/18;过去 12 个月发布了 16 个版本。

版本发布日期距今
0.9.1latest2026/04/096 个月前
0.9.02026/03/287 个月前
0.8.22026/03/058 个月前
0.8.12026/03/058 个月前
0.8.02026/03/048 个月前
0.7.02026/03/018 个月前

关于 @bunli/core 的常见问题

@bunli/core 是什么?

Minimal, type-safe CLI framework for Bun

如何安装 @bunli/core?

在项目目录运行 npm install @bunli/core,或使用 pnpm add / yarn add / bun add。

@bunli/core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@bunli/core 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@bunli/core 可以商用吗?

@bunli/core 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@bunli/core 的下载量有多少?

根据 npm 官方下载统计,@bunli/core 在 2026/10/09 – 2026/11/07 这 30 天内被下载 130,049 次,日均约 4,335 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bunli/core 还在维护吗?

最新版本 0.9.1 发布于 2026/04/09(6 个月前);至今共发布 20 个版本,其中过去 12 个月发布了 16 个;首次发布于 2025/06/18。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。