@elsikora/bean

中文文档
1.0.0发布于 2026/02/24MITTypeScriptESM + CJS零依赖近 30 天下载 6501

零依赖性终端交互库用于提示,回旋器,和丰富的CLI输出.

中文使用文档
文档目录31 节
  1. 重点
  2. 目录
  3. 说明
  4. 实际世界使用案例
  5. 建筑
  6. QQ 技术堆栈
  7. * 特性
  8. 建筑
  9. 系统架构
  10. 数据流动
  11. 项目结构
  12. · 先决条件
  13. * 安装
  14. 使用量
  15. 快速启动
  16. 交互式提示
  17. 旋转器进度( P)
  18. 任务运行器
  19. 流利的 API
  20. 样式
  21. 输出格式
  22. * 路线图
  23. 问 题
  24. 为什么是零依赖?
  25. 豆子和粉笔+ora+询取者如何比较??
  26. 我能在非互动环境中用豆(CI/CD)吗?
  27. 我怎么测试用豆的代码?
  28. Bean支持Windows终端吗?
  29. 我能定制一下提示的视觉风格吗?
  30. 许可证
  31. 承认

project-logo

豆类

Zero-dependency 终端交互库用于提示器,自旋器,进度栏,任务跑取器,以及丰富的CLI输出——以TypeScript中干净的架构来建造.

ElsiKora TypeScript Node.js npm Rollup Vitest ESLint Prettier GitHub Actions Git

重点

  • • 真正零运行时间依赖性——从ANSI渲染到石墨分块的所有东西都是在内部建造的,保持了节点-模块的收缩
  • 将 " 全能CLI " 工具包替换为 " 粉笔+ " ora " + " 查询 " + " Listr2 " + " 墨水 " ,代之以单一、连贯的API表面
  • • 带有可注入端口的清洁建筑——交换时钟、输入、输出、编辑器和主题适配器,用于确定测试或自定义运行时间
  • QQ 流利 API 用于链路提示,自旋器,并输出为有单一表达式的优雅多步工作流程

目录

说明

豆子是综合的, 零依赖性 终端交互库,可以让CLI开发人员拥有建立美丽,互动的指令行体验所需的一切——而无需拉入单一运行时间依赖.

不像传统的CLI工具箱那样 chalk, (中文(简体) ), ora, (中文(简体) ), inquirer, (中文(简体) ), listr2,以及 ink (通常总计为200+过渡依赖),豆会将导体,回旋器,进度栏,任务跑取器,有风格的输出,表,树,diff,以及更能将从平地上到TypeScript所构建的单个,有凝聚力的包.

实际世界使用案例

  • CLI 脚手架工具: 构建交互式项目生成器,多步即时流出,回旋器用于文件操作,并用于依赖性安装的进度栏——全部来自一个导入.
  • CI/CD 管道运行器: 利用任务跑道带有回转,回滚,子任务,以及结构化的生命周期事件来协调搭建有丰富的终端反馈的管道.
  • 开发者工具: 创建linters,格式化器,以及代码生成器,提供美丽,信息化的输出,并配有表格,树,diffs,和有色日志等级别.
  • 内部 DevOps 脚本: 替换临时 console.log 带有结构化输出的语句,包括框、注释、分隔符、超链接和JSON格式.

建筑

豆子跟着 清洁建筑 具有明确分开的域、应用、基础设施和演示层的原则。 每个端口都是可注射的,使库能完全用假适配器进行测试——单位测试不需要真正的TTY.

QQ 技术堆栈

类别技术
语言类型脚本
运行时间节点.js
构建工具滚动
测试维特
线性ESLint, 高级
排雷中心/发改委GitHub 动作,语义释放
软件包管理器无

* 特性

  • ✨ ** 交互式提示 ——文字,密码,确认,切换,编号,评分,选择,多选取,组多选取,原始列表,扩展,自动完成,日期,列表,树选,编辑器,以及具有验证,中止信号,超时,倒置值的由计划驱动的提示**
  • ✨ ** Spinner系统 —— 手动自旋器,带有成功/失败/警告/info/stop状态,前缀支持,已过期时间跟踪,状态更改回调, spinnerPromise 自动管理任务旋转器的旋转器管理器**
  • ✨ ** 进步栏 ——静态快照,动画化进步与自定义字符,制作回调,多进步与并列栏,分级跟踪亲子进度**
  • ✨ **任务运行器 - 与嵌入式子任务、重试、有条件的相接和同时执行任务 when 警卫、故障回滚、结构化的生命周期事件、可配置的日志前缀以及可选的持续日志条目**
  • ✨ ** Rich 输出格式 具有 ANSI- 意识列对齐、 树渲染、 JSON 漂亮/ 紧凑输出、 基于 LCS 的文本 diffs、 注释、 框、 分隔符、 列、 超链接和日志级别( info/success/ warn/error) **
  • ✨ ** 装配引擎 ——被命名为颜色,十六进制,RGB,256-彩色调色板,亮色变体,背景颜色,粗体/斜体/下行/斜通/上行/倒行/平分装饰,流畅的风格链条,有标记的模板文字,自定义主题,梯度文字,以及以优雅的下行分级方式自动分色检测**
  • ✨ ** 流畅的API ——连锁内线 ——导出 ——自旋器 ——输出 ——外出进入一个单一的表达式工作流程,与 bean.fluent()页:1
  • ✨ **零依赖图像渲染器 ——将像素阵列作为ASCII/Unicode艺术在终端直接进行,没有外部图像库**
  • ✨ ** 双模块输出 ——无害环境管理和共同司法体系的船舶都配有完整的类型Script申报和源地图**
  • ✨ ** Fully可注入结构 ——每个 I/O 端口(钟,输入,输出,编辑,主题,即时样式)被抽象在接口后,用于用假适配器进行决定性测试**

建筑

系统架构

flowchart TD
    Factory["createBeanAdapterFactory"]
    BeanAdapter["BeanAdapter (Facade)"]
    FluentApi["BeanFluentApi"]
    PromptService["BeanPromptService"]
    SpinnerService["BeanSpinnerService"]
    ProgressService["BeanProgressService"]
    TaskRunner["BeanTaskRunnerService"]
    StyleService["BeanStyleService"]
    UseCases["Use Cases (Text/Select/Multiselect/Spinner)"]
    DomainEntities["Domain Entities & Value Objects"]
    InputPort["IInputPortInterface"]
    OutputPort["IOutputPortInterface"]
    ClockPort["IClockPortInterface"]
    ThemePort["IThemePortInterface"]
    NodeAdapters["Node.js Adapters (stdin/stdout/readline)"]

    Factory --> BeanAdapter
    Factory --> NodeAdapters
    BeanAdapter --> FluentApi
    BeanAdapter --> PromptService
    BeanAdapter --> SpinnerService
    BeanAdapter --> ProgressService
    BeanAdapter --> TaskRunner
    BeanAdapter --> StyleService
    PromptService --> UseCases
    SpinnerService --> UseCases
    UseCases --> DomainEntities
    UseCases --> InputPort
    UseCases --> OutputPort
    UseCases --> ClockPort
    BeanAdapter --> ThemePort
    NodeAdapters --> InputPort
    NodeAdapters --> OutputPort
    NodeAdapters --> ClockPort

数据流动

sequenceDiagram
    participant User
    participant BeanAdapter
    participant PromptService
    participant TextUseCase
    participant InputPort
    participant OutputPort
    participant TextBuffer

    User->>BeanAdapter: bean.text({ message, validate })
    BeanAdapter->>PromptService: text(input)
    PromptService->>TextUseCase: execute(args)
    TextUseCase->>InputPort: enableRawMode()
    TextUseCase->>OutputPort: hideCursor()
    TextUseCase->>OutputPort: writeFrame(questionLine)
    loop Each Keystroke
        InputPort->>TextUseCase: onKeyEvent(event)
        TextUseCase->>TextBuffer: applyInsert/applyBackspace
        TextUseCase->>OutputPort: writeFrame(updatedLine)
    end
    TextUseCase->>InputPort: disableRawMode()
    TextUseCase->>OutputPort: showCursor()
    TextUseCase->>OutputPort: writeLine(completedLine)
    TextUseCase-->>PromptService: "user input"
    PromptService-->>BeanAdapter: "user input"
    BeanAdapter-->>User: "user input"

项目结构

单击以扩展
bean/
├── .github/
│   ├── workflows/
│   │   ├── mirror-to-codecommit.yml
│   │   ├── qodana-quality-scan.yml
│   │   ├── release.yml
│   │   ├── snyk-security-scan.yml
│   │   └── test.yml
│   └── dependabot.yml
├── docs/
│   ├── api-reference/
│   │   ├── _meta.js
│   │   └── page.mdx
│   ├── architecture/
│   │   ├── _meta.js
│   │   ├── bean-adapter-modularization.plan.md
│   │   └── page.mdx
│   ├── core-concepts/
│   │   ├── _meta.js
│   │   └── page.mdx
│   ├── getting-started/
│   │   ├── _meta.js
│   │   └── page.mdx
│   ├── guides/
│   │   ├── _meta.js
│   │   └── page.mdx
│   ├── migration/
│   │   ├── _meta.js
│   │   ├── page.mdx
│   │   └── runtime-parity-breaking-changes.md
│   ├── _meta.js
│   └── page.mdx
├── examples/
│   ├── 01-getting-started/
│   │   └── main.ts
│   ├── 02-styling/
│   │   └── main.ts
│   ├── 03-prompts-basic/
│   │   └── main.ts
│   ├── 04-prompts-select/
│   │   └── main.ts
│   ├── 05-prompts-advanced/
│   │   └── main.ts
│   ├── 06-spinners/
│   │   └── main.ts
│   ├── 07-progress/
│   │   └── main.ts
│   ├── 08-task-runner/
│   │   └── main.ts
│   ├── 09-output-formatting/
│   │   └── main.ts
│   ├── 10-fluent-api/
│   │   └── main.ts
│   ├── README.md
│   └── tsconfig.json
├── scripts/
│   └── validate-docs.mjs
├── src/
│   ├── application/
│   │   ├── constant/
│   │   ├── interface/
│   │   ├── service/
│   │   ├── type/
│   │   ├── use-case/
│   │   └── index.ts
│   ├── composition/
│   │   ├── factory/
│   │   └── interface/
│   ├── domain/
│   │   ├── constant/
│   │   ├── entity/
│   │   ├── enum/
│   │   ├── interface/
│   │   ├── type/
│   │   ├── value-object/
│   │   └── index.ts
│   ├── infrastructure/
│   │   ├── adapter/
│   │   ├── di/
│   │   └── index.ts
│   ├── presentation/
│   │   ├── bean/
│   │   ├── interface/
│   │   ├── type/
│   │   └── index.ts
│   └── index.ts
├── test/
│   ├── e2e/
│   │   ├── fixtures/
│   │   └── integration/
│   └── unit/
│       ├── application/
│       ├── domain/
│       ├── fixtures/
│       ├── infrastructure/
│       └── presentation/
├── eslint.config.js
├── generated-logo.png
├── lint-staged.config.js
├── package-lock.json
├── package.json
├── prettier.config.js
├── README.md
├── release.config.js
├── rollup.config.js
├── rollup.test.config.js
├── tsconfig.build.json
├── tsconfig.json
├── vitest.end-to-end.config.js
└── vitest.unit.config.js

· 先决条件

  • 节点.js 18.0.0
  • npm >= 9.0.0

* 安装

# Install with npm
npm install @elsikora/bean

# Install with yarn
yarn add @elsikora/bean

# Install with pnpm
pnpm add @elsikora/bean

使用量

快速启动

import { createBeanAdapterFactory } from "@elsikora/bean";

const bean = createBeanAdapterFactory();

bean.intro({ message: "Welcome to my CLI!" });
bean.message({ message: "Setting things up..." });
bean.outro({ message: "All done!" });
bean.dispose();

交互式提示

import { SelectOptionValueObject, createBeanAdapterFactory } from "@elsikora/bean";

const bean = createBeanAdapterFactory();

// Text input with validation
const name = await bean.text({
    message: "What is your project name?",
    placeholder: "my-awesome-app",
    validate: (value) => (value.length < 2 ? "Name too short" : null),
});

// Select with search
const framework = await bean.select({
    message: "Pick a framework:",
    isSearchEnabled: true,
    options: [new SelectOptionValueObject({ label: "React", value: "react" }), new SelectOptionValueObject({ label: "Vue", value: "vue" }), new SelectOptionValueObject({ label: "Svelte", value: "svelte" })],
});

// Multiselect with groups
const features = await bean.groupMultiselect({
    message: "Select features:",
    options: [new SelectOptionValueObject({ group: "Auth", label: "OAuth 2.0", value: "oauth" }), new SelectOptionValueObject({ group: "Auth", label: "JWT", value: "jwt" }), new SelectOptionValueObject({ group: "DB", label: "PostgreSQL", value: "postgres" }), new SelectOptionValueObject({ group: "DB", label: "Redis", value: "redis" })],
});

// Confirm
const shouldInstall = await bean.confirm({
    message: "Install dependencies now?",
    isDefaultValue: true,
});

bean.dispose();

旋转器进度( P)

// Manual spinner
const spinner = bean.spinner({ text: "Compiling..." });
await doWork();
spinner.succeed("Compilation complete");

// Auto-managed spinner
const result = await bean.spinnerPromise({
    text: "Installing dependencies...",
    successText: "Dependencies installed",
    failText: "Installation failed",
    task: async () => {
        await installDeps();
        return "142 packages";
    },
});

// Concurrent spinner manager
const manager = bean.spinnerManager();
const api = manager.create({ id: "api", text: "Starting API..." });
const db = manager.create({ id: "db", text: "Connecting DB..." });
await connectDb();
db.succeed("Database connected");
await startApi();
api.succeed("API listening on :3000");

// Animated progress bar
const bar = bean.createProgress({ label: "Downloading", total: 100 });
for (let i = 0; i < 100; i++) {
    await delay(50);
    bar.increment();
}
bar.stop();

任务运行器

const summary = await bean.runTasks({
    isPersistLogs: true,
    logPrefix: "[release]",
    onEvent: (event) => console.log(event.status, event.taskPath),
    tasks: [
        {
            title: "Build",
            run: async () => {
                /* build logic */
            },
            subtasks: [
                {
                    title: "Lint",
                    run: async () => {
                        /* lint */
                    },
                },
                {
                    title: "Test",
                    run: async () => {
                        /* test */
                    },
                    retries: 2,
                },
            ],
        },
        {
            title: "Deploy",
            run: async () => {
                /* deploy */
            },
            rollback: async () => {
                /* rollback */
            },
            when: () => process.env.CI === "true",
        },
    ],
});
// summary: { succeeded: 3, failed: 0, skipped: 1, logs: [...] }

流利的 API

let projectName = "";
let framework = "";

await bean
    .fluent()
    .intro("Project Scaffolding Wizard")
    .text({
        message: "Project name?",
        onResolved: (v) => {
            projectName = v ?? "my-project";
        },
    })
    .select({
        message: "Framework?",
        onResolved: (v) => {
            framework = v ?? "none";
        },
        options: [new SelectOptionValueObject({ label: "React", value: "react" }), new SelectOptionValueObject({ label: "Vue", value: "vue" })],
    })
    .spinner({
        text: "Scaffolding...",
        task: async (handle) => {
            handle.update("Creating files...");
            await createProject();
        },
    })
    .outro(`Project "${projectName}" created with ${framework}!`)
    .run();

样式

// Named colors and decorations
bean.message({ message: bean.style({ color: "red", isBold: true, text: "Error!" }) });

// Hex, RGB, 256-color
bean.message({ message: bean.styleHex({ color: "#FF6B6B", text: "coral" }) });
bean.message({ message: bean.styleRgb({ rgb: { r: 50, g: 200, b: 150 }, text: "teal" }) });

// Fluent chain
bean.message({ message: bean.styleChain({ color: "yellow" }).bold().render("⚠ Warning") });

// Tagged template
const danger = bean.styleTemplate({ color: "red", isBold: true });
bean.message({ message: danger`Error: ${3} files failed` });

// Gradient text
bean.message({ message: bean.styleGradient({ from: "#ff0000", to: "#0000ff", text: "Rainbow" }) });

输出格式

// Table
bean.table({ columns: ["Package", "Version"], rows: [["bean", "0.1.0"]] });

// Tree
bean.tree({ label: "src/", children: [{ label: "domain/" }, { label: "application/" }] });

// Diff
bean.diff({ before: "version: 0.1.0", after: "version: 0.2.0" });

// JSON
bean.json({ value: { name: "bean", deps: 0 }, isPretty: true });

// Box, Note, Divider
bean.box({ title: "Success", message: "Your CLI is ready!" });
bean.note({ title: "Tip", message: "Run `npm start` to begin." });
bean.divider();

* 路线图

单击以扩展
任务/ 特性状态
核心快速运行时间( 文本、 选择、 多选择、 确认)完毕
带有管理器和分级标签的自旋系统完毕
多进展和分级的进度栏完毕
有子任务、 重试和回滚的任务运行器完毕
具有梯度支持的全 ANSI 样式引擎完毕
流利的连锁工作流程 API完毕
产出丰富(表、树、diff、JSON、框、注)完毕
由 Schema 驱动的迅速生成(promptFromSchema(中文(简体) )完毕
与类型Script申报建立双重无害环境管理/CJS完毕
将BeanAdapter 提取到完全模块化的子服务完毕
完整的豆泥模块化(即时战略)执行中
执行地层边界的建筑测试执行中
用于自定义提示类型的插件系统执行中
快速信息国际化(i18n)执行中

问 题

单击以扩展

为什么是零依赖?

每一种依赖都是潜在的安全脆弱性、版本冲突以及捆绑大小的增加。 豆子从ANSI的逃生代码生成到石墨感知文本分解内部,将一切实现,因此你的项目得到了完整的CLI工具包而不会继承其他人的依赖树.

豆子和粉笔+ora+询取者如何比较??

豆以一入取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取出出出出出出出出出出出取取出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出出 它提供了粉笔的造型能力,ora的自旋器功能,查询器的即时系统,lister2的任务执行器,以及boxen/cli-table的输出格式——都有一个统一,型号安全的API.

我能在非互动环境中用豆(CI/CD)吗?

没错! 每种即时方法都接受 fallbackValue 如果没有 TTY 可用,则自动使用。 旋转器优雅地降解为静行输出。 这使得"豆子"既可以交互使用,也可以不交互使用.

我怎么测试用豆的代码?

豆的建筑围绕可注入口而建. 在测试中,使用 createBeanAdapterFactory 装假 inputPort, (中文(简体) ), outputPort,以及 clockPort 适应器. 测试套间广泛展示这种模式——见 test/e2e/fixtures/ 可重复使用的假适配器.

Bean支持Windows终端吗?

对 Bean检测到Unicode支持,并自动回落到ASCII等效的自旋框,框字符,树前缀等. 这个 BEAN_FORCE_ASCII=1 环境变量可以强制ASCII模式. 颜色级别检测尊重 NO_COLOR, (中文(简体) ), FORCE_COLOR, (中文(简体) ), COLORTERM,以及 TERM 环境变量.

我能定制一下提示的视觉风格吗?

当然 通过自定义 promptStylePort 以更改指针符号、检查标记、分隔符等。 通过自定义 themePort 重音,成功,危险, 哑音,信息, 和强大的文本样式。 使用 bean.createTheme() 用于部分重置.

许可证

这个项目的许可证根据 麻省理工学院。 。 。 .

承认


回到顶部

版本与发布记录

@elsikora/bean 共发布 2 个版本,首次发布于 2026/02/24;过去 12 个月发布了 2 个版本。

版本发布日期距今
1.0.0latest2026/02/248 个月前
1.0.0-dev.2beta2026/02/248 个月前

关于 @elsikora/bean 的常见问题

@elsikora/bean 是什么?

零依赖性终端交互库用于提示,回旋器,和丰富的CLI输出.

如何安装 @elsikora/bean?

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

@elsikora/bean 支持 TypeScript 吗?

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

@elsikora/bean 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@elsikora/bean 可以商用吗?

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

@elsikora/bean 的下载量有多少?

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

@elsikora/bean 还在维护吗?

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