@decaf-ts/utils

中文文档
2.0.6发布于 2026/11/07MITTypeScriptESM + CJS构建来源已验证近 30 天下载 3044

module management utils for decaf-ts

原始使用文档
文档目录16 节
  1. Decaf's Utils Module
  2. Description
  3. How to Use
  4. Examples
  5. CLI Module
  6. Input Module
  7. Utils Module
  8. Testing Helpers
  9. Writers Module
  10. Coding Principles
  11. Related
  12. Social
  13. Getting help
  14. Contributing
  15. Supporting
  16. License

Banner

Decaf's Utils Module

A comprehensive TypeScript utility library providing robust tools for command-line interfaces, input handling, output management, file system operations, HTTP requests, text processing, and more. This module serves as a foundation for building powerful CLI applications with standardized input parsing, flexible output formatting, and a rich set of utility functions.

Licence GitHub language count GitHub top language

Build & Test CodeQLSnyk Analysis Pages builder .github/workflows/release-on-tag.yaml

Open Issues Closed Issues Pull Requests Maintained

Forks Stars Watchers

Node Version NPM Version

Documentation available here

Minimal size: 34.4 KB kb gzipped

Description

The Decaf Utils module is a comprehensive TypeScript utility library designed to standardize APIs across repositories and provide a robust foundation for building command-line interface (CLI) applications. The library is organized into several key components:

CLI Module

The CLI module provides a structured framework for creating command-line applications:

  • Abstract Command class for implementing custom CLI commands with standardized input handling, logging, and execution flow
  • Command option handling with support for common flags like verbose, version, help, etc.
  • Standardized command execution flow with proper error handling

Input Module

The Input module offers tools for handling user input and command-line arguments:

  • UserInput class for creating interactive prompts with various input types (text, number, confirmation, etc.)
  • Support for input validation, formatting, and default values
  • Command-line argument parsing with type checking and validation
  • Methods for repeatedly asking for input until valid responses are received

Utils Module

The Utils module contains a wide range of utility functions:

  • File System Operations: Reading, writing, copying, and deleting files and directories
  • Package Management: Retrieving package information, managing dependencies, and version handling
  • HTTP Utilities: Simple client for downloading files from URLs
  • Text Processing: String interpolation, case conversion (camelCase, snake_case, etc.), and regular expression utilities
  • Environment Handling: Working with environment variables and configuration

Writers Module

The Writers module provides different strategies for handling command output:

  • OutputWriter interface defining a standard contract for output handling
  • StandardOutputWriter for handling standard command-line output with proper logging
  • RegexpOutputWriter for processing output with regular expressions and pattern matching

Output Module

The Output module offers utilities for formatting and displaying output:

  • Common output formatting functions
  • Banner display capabilities

Testing Helpers

Testing helpers are maintained in @decaf-ts/for-testing; this package no longer exports @decaf-ts/utils/tests.

This library serves as a light version of the Decaf CLI tool, providing all the essential utilities needed for building robust command-line applications with consistent input handling, output formatting, and error management.

How to Use

Examples

CLI Module

Creating a Custom Command

The Command class provides a foundation for creating custom CLI commands with standardized input handling and execution flow.

import { Command, CommandOptions } from '@decaf-ts/utils';

// Define input options interface
interface MyCommandOptions {
  filename: string;
  dryRun: boolean;
}

// Create a custom command class
class MyCommand extends Command<MyCommandOptions, string> {
  constructor() {
    // Define command options with their configurations
    const options: CommandOptions<MyCommandOptions> = {
      filename: {
        type: 'string',
        short: 'f',
        default: 'output.txt'
      },
      dryRun: {
        type: 'boolean',
        short: 'd',
        default: false
      }
    };

    super('my-command', options);
  }

  // Override the help method to provide custom help information
  protected help(): void {
    this.log.info(`
      Usage: my-command [options]

      Options:
        -f, --filename  Specify output filename (default: output.txt)
        -d, --dryRun    Run without making changes (default: false)
        -h, --help      Show help information
        -v, --version   Show version information
    `);
  }

  // Implement the run method to define command behavior
  protected async run(answers: { filename: string; dryRun: boolean }): Promise<string> {
    this.log.info(`Running command with filename: ${answers.filename}, dryRun: ${answers.dryRun}`);

    // Command implementation here

    return 'Command executed successfully';
  }
}

// Execute the command
async function main() {
  const command = new MyCommand();
  const result = await command.execute();
  console.log(result);
}

main().catch(console.error);

Managing Secrets with the Credentials Command

The CredentialsCommand provides a secure, cross-platform way to manage tokens and other secrets used by release scripts, CI pipelines, and local development. It replaces the old approach of storing tokens in plaintext files (.npmtoken, .token, .confluence-token) with OS keychain storage while preserving backwards compatibility.

Resolution order (checked in this order for get / resolveSecret()):

  1. Environment variable — highest priority; this is what CI uses
  2. OS keychain — macOS Keychain, Linux libsecret, Linux Python keyring, Windows Credential Manager
  3. Legacy plaintext file — deprecated fallback with a warning

Built-in secrets:

NameEnv varKeychain serviceAccountLegacy file
npmNPM_TOKENdecaf-ts:npmpublish.npmtoken
githubGH_TOKENdecaf-ts:githubgit.token
confluenceATLASSIAN_API_TOKENdecaf-ts:confluenceapi.confluence-token

Custom secret names are also supported — the env var defaults to <NAME>_TOKEN, the service to decaf-ts:<name>, and the legacy file to .<name>-token.

CLI usage (standalone)
# Resolve the npm publish token (prints to stdout, no trailing newline)
credentials --action get --name npm

# Resolve the github token using a non-default env var name
credentials --action get --name github --envVar GH_PAT

# Store a secret in the OS keychain
credentials --action store --name github --value ghp_xxxxxxxxxxxx

# Store a custom secret with fully overridden metadata
credentials --action store --name my-api \
  --envVar MY_API_TOKEN \
  --service "my-app:api" \
  --account "ci" \
  --value "sk_live_xxx"

# Interactive one-time setup (prompts for all built-in secrets, configures git helper)
credentials --action setup

# Setup and auto-delete legacy plaintext token files (.npmtoken, .token, .confluence-token)
credentials --action setup --rm

# Configure only the git credential helper (no secret enrollment)
credentials --action git-helper

# Use in a shell pipeline (e.g., set npm auth for publish)
npm config set //registry.npmjs.org/:_authToken "$(credentials --action get --name npm)"
CLI usage (via decaf CLI)
# All subcommands are also available under the decaf CLI
decaf utils credentials get --name npm
decaf utils credentials store --name github --value ghp_xxxxxxxxxxxx
decaf utils credentials setup
decaf utils credentials setup --rm   # auto-deletes legacy token files
decaf utils credentials git-helper

# View help for any subcommand
decaf utils credentials --help
decaf utils credentials get --help
CI usage (GitHub Actions / GitLab CI)

No keychain access is needed in CI — simply set the environment variable as a secret and resolveSecret() picks it up immediately:

# .github/workflows/release.yml
- name: Publish to npm
  env:
    NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
  run: |
    npm config set //registry.npmjs.org/:_authToken "$NPM_TOKEN"
    npm publish

The release scripts (tag-release, npm publish, git push) call resolveSecret("npm") and resolveSecret("github") internally, so they automatically prefer env vars in CI and the keychain locally.

Programmatic usage (TypeScript)
import { resolveSecret, hasSecret } from '@decaf-ts/utils';

// Resolves using the same order: env var -> keychain -> legacy file
// Throws if no source provides a value.
const token = resolveSecret('npm');

// Non-throwing check
if (hasSecret('github')) {
  const ghToken = resolveSecret('github');
}

// Custom secret with overrides
const apiKey = resolveSecret('my-api', {
  envVar: 'MY_API_TOKEN',
  service: 'my-app:api',
  account: 'ci',
  legacyFile: '.my-api-token',
});
Local setup walkthrough
# 1. Run interactive setup with --rm (enrolls npm, github, confluence in the keychain
#    and auto-deletes legacy plaintext files)
credentials --action setup --rm

# 2. Verify resolution works
credentials --action get --name npm    # should print the token
credentials --action get --name github # should print the token

# 3. Future release scripts will now use the keychain automatically

Building and Publishing the Aggregate Bundles

The BundleCommand replaces the legacy root bin/bundle.js script: it reads bundles.json and package-template.json (single-copy command assets shipped in @decaf-ts/utils), generates one @decaf-ts/dist-* aggregate package per bundles.json entry under --target (default <base-path>/bin/releases), resolves every bundled dependency's version from the workspace, merges npm overrides, then runs npm install and npm publish --access public in each generated bundle folder (waiting --timeout seconds between publishes). Tokens are resolved through the credentials resolver (env var -> OS keychain -> legacy file) and are never logged. --dry-run (or DRY_RUN=1) generates the manifests only.

# Preview the generated manifests without installing or publishing
bundle --dry-run

# Full run from the workspace root, custom target and publish pacing
bundle --target ./releases --timeout 30

# Via the decaf CLI wrapper
decaf utils bundle --dry-run
decaf utils bundle --base-path . --target ./releases --timeout 30

Staging the Documentation Folder

The BuildDocsCommand replaces the legacy bin/build-docs.sh script: it removes and recreates the docs folder (default <base-path>/docs) and copies the README (default <base-path>/README.md) into it as docs/README.md.

build-docs
build-docs --base-path ./integrations

# Via the decaf CLI wrapper
decaf utils build-docs
decaf utils build-docs --base-path ./integrations

Input Module

Creating Interactive Prompts

The UserInput class allows you to create interactive prompts for collecting user input.

import { UserInput } from '@decaf-ts/utils';

async function collectUserInfo() {
  // Create a text input for name
  const nameInput = new UserInput('name')
    .setMessage('What is your name?')
    .setInitial('User');

  // Create a number input for age with validation
  const ageInput = new UserInput('age')
    .setType('number')
    .setMessage('How old are you?')
    .setMin(0)
    .setMax(120)
    .setValidate(value => {
      if (value < 18) return 'You must be at least 18 years old';
      return true;
    });

  // Create a confirmation input
  const confirmInput = new UserInput('confirm')
    .setType('confirm')
    .setMessage('Is this information correct?')
    .setInitial(true);

  // Ask for all inputs and get the answers
  const answers = await UserInput.ask([nameInput, ageInput, confirmInput]);

  console.log(`Hello ${answers.name}, you are ${answers.age} years old.`);
  console.log(`Information confirmed: ${answers.confirm ? 'Yes' : 'No'}`);

  return answers;
}

collectUserInfo().catch(console.error);

Using Convenience Methods for Input

UserInput provides static convenience methods for common input scenarios.

import { UserInput } from '@decaf-ts/utils';

async function quickInputExample() {
  // Ask for text input
  const name = await UserInput.askText('name', 'What is your name?', undefined, 'User');

  // Ask for number input
  const age = await UserInput.askNumber('age', 'How old are you?', 0, 120);

  // Ask for confirmation
  const confirm = await UserInput.askConfirmation('confirm', 'Is this information correct?', true);

  // Ask for text with validation and retry until valid
  const email = await UserInput.insistForText(
    'email',
    'What is your email address?',
    (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
    undefined,
    undefined,
    false,
    3 // Maximum 3 attempts
  );

  console.log(`Name: ${name}, Age: ${age}, Email: ${email}, Confirmed: ${confirm}`);
}

quickInputExample().catch(console.error);

Parsing Command-Line Arguments

import { UserInput, ParseArgsOptionsConfig } from '@decaf-ts/utils';

// Define command-line options
const options: ParseArgsOptionsConfig = {
  verbose: {
    type: 'boolean',
    short: 'V',
    default: false
  },
  output: {
    type: 'string',
    short: 'o',
    default: 'output.txt'
  },
  count: {
    type: 'string',
    short: 'c',
    default: '10'
  }
};

// Parse command-line arguments
const args = UserInput.parseArgs(options);

console.log('Parsed arguments:', args.values);
console.log('Positional arguments:', args.positionals);

Utils Module

File System Operations

import { 
  readFile, 
  writeFile, 
  patchFile, 
  getAllFiles, 
  copyFile, 
  renameFile, 
  deletePath 
} from '@decaf-ts/utils';

// Read a file
const content = readFile('./config.json');
console.log('File content:', content);

// Write to a file
writeFile('./output.txt', 'Hello, world!');

// Patch a file with replacements
patchFile('./template.txt', {
  '{{name}}': 'John Doe',
  '{{date}}': new Date().toISOString()
});

// Get all files in a directory
const files = getAllFiles('./src', (file) => file.endsWith('.ts'));
console.log('TypeScript files:', files);

// Copy a file
copyFile('./source.txt', './destination.txt');

// Rename a file
renameFile('./old-name.txt', './new-name.txt');

// Delete a file or directory
deletePath('./temp');

Package Management

import { 
  getPackage, 
  getPackageVersion, 
  setPackageAttribute, 
  getDependencies, 
  installDependencies 
} from '@decaf-ts/utils';

// Get package information
const pkg = getPackage();
console.log('Package:', pkg);

// Get package version
const version = getPackageVersion();
console.log('Version:', version);

// Set a package attribute
setPackageAttribute('version', '1.0.1');

// Get dependencies
async function manageDependencies() {
  const deps = await getDependencies();
  console.log('Production dependencies:', deps.prod);
  console.log('Development dependencies:', deps.dev);
  console.log('Peer dependencies:', deps.peer);

  // Install dependencies
  await installDependencies({
    prod: ['lodash', 'axios'],
    dev: ['typescript', 'jest']
  });
}

manageDependencies().catch(console.error);

HTTP Utilities

import { HttpClient } from '@decaf-ts/utils';

async function downloadExample() {
  try {
    // Download a file from a URL
    const content = await HttpClient.downloadFile('https://example.com/api/data.json');
    console.log('Downloaded content:', content);

    // Parse JSON content
    const data = JSON.parse(content);
    console.log('Parsed data:', data);

    return data;
  } catch (error) {
    console.error('Download failed:', error);
    throw error;
  }
}

downloadExample().catch(console.error);

Text Processing

import { 
  padEnd, 
  patchPlaceholders, 
  patchString, 
  toCamelCase, 
  toSnakeCase, 
  toKebabCase, 
  toPascalCase, 
  toENVFormat 
} from '@decaf-ts/utils';

// Pad a string
const padded = padEnd('Hello', 10, '-');
console.log(padded); // 'Hello-----'

// Replace placeholders
const template = 'Hello, ${name}! Today is ${day}.';
const filled = patchPlaceholders(template, {
  name: 'Alice',
  day: 'Monday'
});
console.log(filled); // 'Hello, Alice! Today is Monday.'

// Replace strings
const patched = patchString('Hello, world!', {
  'world': 'universe'
});
console.log(patched); // 'Hello, universe!'

// Case conversion
const text = 'hello world';
console.log(toCamelCase(text));   // 'helloWorld'
console.log(toSnakeCase(text));   // 'hello_world'
console.log(toKebabCase(text));   // 'hello-world'
console.log(toPascalCase(text));  // 'HelloWorld'
console.log(toENVFormat(text));   // 'HELLO_WORLD'

Testing Helpers

Testing helpers are maintained in @decaf-ts/for-testing; this package no longer provides the @decaf-ts/utils/tests subpath.

Writers Module

Using StandardOutputWriter

import { StandardOutputWriter } from '@decaf-ts/utils';

// Create a promise executor
const executor = {
  resolve: (value: string) => console.log(`Command succeeded with: ${value}`),
  reject: (error: Error) => console.error(`Command failed with: ${error.message}`)
};

// Create a standard output writer
const writer = new StandardOutputWriter('ls -la', executor);

// Handle command output
writer.data('File list output...');
writer.data('file1.txt');
writer.data('file2.txt');

// Handle command completion
writer.exit(0, ['Command executed successfully']);

Using RegexpOutputWriter

import { RegexpOutputWriter } from '@decaf-ts/utils';

// Create a promise executor
const executor = {
  resolve: (value: string) => console.log(`Found version: ${value}`),
  reject: (error: Error) => console.error(`Error: ${error.message}`)
};

// Create a regexp output writer that matches version numbers
const writer = new RegexpOutputWriter('node --version', executor, /v(\d+\.\d+\.\d+)/);

// Process output that contains a version number
writer.data('v14.17.0');  // This will automatically resolve with "v14.17.0"

// Process error output
writer.error('Command not found: node');  // This will be logged as an error

Coding Principles

  • group similar functionality in folders (analog to namespaces but without any namespace declaration)
  • one class per file;
  • one interface per file (unless interface is just used as a type);
  • group types as other interfaces in a types.ts file per folder;
  • group constants or enums in a constants.ts file per folder;
  • group decorators in a decorators.ts file per folder;
  • always import from the specific file, never from a folder or index file (exceptions for dependencies on other packages);
  • prefer the usage of established design patters where applicable:
    • Singleton (can be an anti-pattern. use with care);
    • factory;
    • observer;
    • strategy;
    • builder;
    • etc;

Readme Card

Social

LinkedIn

Languages

TypeScript JavaScript NodeJS ShellScript

Getting help

If you have bug reports, questions or suggestions please create a new issue.

Contributing

I am grateful for any contributions made to this project. Please read this to get started.

Supporting

The first and easiest way you can support it is by Contributing. Even just finding a typo in the documentation is important.

Financial support is always welcome and helps keep both me and the project alive and healthy.

So if you can, if this project in any way. either by learning something or simply by helping you save precious time, please consider donating.

License

This project is released under the MIT License.

By developers, for developers...

版本与发布记录

@decaf-ts/utils 共发布 128 个版本,首次发布于 2025/04/22;过去 12 个月发布了 72 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
2.0.6latest2026/11/075 天前
2.0.52026/11/075 天前
2.0.42026/11/075 天前
2.0.12026/11/065 天前
1.14.02026/10/1428 天前
1.12.22026/10/1428 天前

其他发布通道:0.11.10.experimental.1 → 0.11.1-0.experimental.1。

关于 @decaf-ts/utils 的常见问题

@decaf-ts/utils 是什么?

module management utils for decaf-ts

如何安装 @decaf-ts/utils?

在项目目录运行 npm install @decaf-ts/utils,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 bundle、modules、run-all、npm-link、npm-token、build-docs、credentials、mirror-repo、tag-release、build-scripts、release-chain、compile-matrix,可以用 npx bundle 直接运行。

@decaf-ts/utils 支持 TypeScript 吗?

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

@decaf-ts/utils 支持 ESM 还是 CommonJS?

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

@decaf-ts/utils 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@decaf-ts/utils 可以商用吗?

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

@decaf-ts/utils 的下载量有多少?

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

@decaf-ts/utils 还在维护吗?

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