@tq1086/jaf-cli
v0.4.9
Published
JAF - JSON API Format: A simple CLI for executing HTTP requests with variable management
Downloads
699
Readme
JAF - JSON API Flow CLI
版本: 0.4.4
JAF (JSON API Flow) 是一种轻量级脚本语言,用于快速测试 JSON API。支持变量、路径级联、登录态管理和响应断言。
设计原则:
- 理解、生成优先于执行
- 只支持 JSON,无其他格式
- 线性执行,无循环条件
- 人类可读,LLM 友好
- 够用即可,避免过度设计
- Fast-fail 策略,简化错误处理
功能特性
- 简单语法: 易读的 JAF 格式,用于定义 HTTP 请求
- 变量管理: 支持环境变量、脚本变量和响应捕获
- 内置变量: UUID、时间戳和随机数生成
- 变量替换: 在 URL、请求头和请求体中自动替换变量
- 路径级联: 支持基础 URL 的智能 URL 路径处理
- 类型转换: 对捕获的变量进行自动类型转换
- Session 管理: 支持登录态管理和头部注入
- 响应断言: 支持断言检查和期望验证
- 响应记录: 将响应保存到输出文件
- 彩色输出: 清晰的彩色终端输出,便于调试
安装
npm i @tq1086/jaf-cli使用方法
# 运行 JAF 脚本(同时输出到文件和控制台)
jaf example.jaf
# 指定输出目录
jaf example.jaf -o ./output
# 抑制控制台输出,只写入文件
jaf example.jaf --no-stdout
# 将输出内容复制到剪贴板
jaf example.jaf --clipboard
# 显示详细信息(每个步骤的详细输出)
jaf example.jaf --verbose
# 组合使用:只输出到文件和剪贴板
jaf example.jaf --no-stdout --clipboard
# 显示帮助信息
jaf --help
# 显示版本信息
jaf --version命令行选项
--verbose: 打印每个步骤的详细信息,包括:- Tokenizing 阶段详情
- Parsing 阶段详情(指令和请求数量)
- 变量替换前后对比
- 请求和响应的详细信息
- 带彩色输出的不同级别信息
--no-stdout: 抑制控制台输出,但仍输出文件路径和摘要信息- 与
--verbose选项兼容 - 不影响文件输出功能
- 与
--clipboard: 将输出内容复制到剪贴板- 支持 Windows、macOS、Linux 平台
- Linux 需要 xclip 或 xsel
- 提供清晰的错误提示和降级处理
JAF 语法
基础语法
#! 变量定义、默认值、路径前缀、session(单行)
#@ 请求配置与断言(单行,可累积)
#> 记录实际收到的响应(单行,可累积,只读)
{} HTTP 请求(JSON,可多行)
# 行内注释(单行)指令
设置基础 URL(路径级联)
#!base = https://api.example.com设置基础 URL 后,可以使用:
- 完整 URL:
https://other.com/path(原样使用) - 协议相对:
//api.example.com/path(使用基础 URL 的协议) - 绝对路径:
/users(替换基础 URL 的路径) - 相对路径:
users(追加到基础 URL 的路径)
定义脚本变量
#!var USER_ID = 123
#!var API_KEY = "secret-key"
#!var USER_NAME = "张三"使用环境变量
#!env API_KEY
#!env API_KEY ?= default-value设置默认请求参数
#!default {"method": "POST", "headers": {"Content-Type": "application/json"}}Session 管理
#!session {"headers": {"Authorization": "Bearer @{token}"}}
#!session begin
# ... 带有 session 请求头的请求 ...
#!session end请求配置与断言(#@)
#@ timeout 5000 # 设置超时
#@ capture $.token auth_token # 捕获响应路径到变量
#@ capture $.expires exp:number # 带类型转换
#@ expect status ok # 期望精确值
#@ expect code <number> # 期望类型
#@ assert $status == 200 # 断言条件
#@ ignore error # 忽略错误响应记录(#>)
#> record $.body # 记录完整响应体
#> record $.status # 记录状态码
#> record $header.Content-Type # 记录响应头
#> record $.items[0] # 记录数组元素请求
# GET 请求
{"method": "GET", "uri": "/users"}
# 带请求体的 POST 请求
{"method": "POST", "uri": "/users", "headers": {"Content-Type": "application/json"}, "body": {"name": "John"}}
# PUT 请求
{"method": "PUT", "uri": "/users/@{USER_ID}", "headers": {"Content-Type": "application/json"}, "body": {"name": "Updated"}}
# DELETE 请求
{"method": "DELETE", "uri": "/users/@{USER_ID}"}变量引用
- 环境变量:
${ENV_VAR}或${ENV_VAR?=default} - 脚本变量:
@{scriptVar}(在路径中自动进行 URL 编码) - 捕获变量:
@{capturedVar}
内置变量
@{_uuid_}: 生成随机 UUID@{_timestamp_}: 当前 Unix 时间戳(秒)@{_timestamp_ms_}: 当前 Unix 时间戳(毫秒)@{_iso_timestamp_}: ISO 8601 格式时间戳(如2024-01-15T10:30:00.000Z)@{_random_}: 0 到 1 之间的随机数@{_random_}: 0 到 1 之间的随机数
注意: 内置变量是只读的,不能被用户定义的变量覆盖。
URL 编码
路径中的变量会自动进行 URL 编码:
#!var USER_NAME = "张三"
{"method": "GET", "uri": "/users/@{USER_NAME}"}
# URL 变为: /users/%E5%BC%A0%E4%B8%89示例
基本 API 测试
#!base = https://jsonplaceholder.typicode.com
#!var USER_ID = 1
# 获取所有用户
{"method": "GET", "uri": "/users"}
# 获取特定用户并捕获数据
{"method": "GET", "uri": "/users/@{USER_ID}"}
#@ capture $.id user_id
#@ capture $.name user_name
#@ capture $.email user_email
# 使用捕获的用户 ID 创建帖子
{"method": "POST", "uri": "/posts", "headers": {"Content-Type": "application/json"}, "body": {"title": "Test", "userId": @{user_id}}}路径级联
#!base = https://api.example.com/v1
# 完整 URL(忽略基础 URL)
{"method": "GET", "uri": "https://other.com/data"}
# 协议相对(使用基础 URL 的协议)
{"method": "GET", "uri": "//api2.example.com/users"}
# 绝对路径(替换基础 URL 的路径)
{"method": "GET", "uri": "/users"}
# 相对路径(追加到基础 URL 的路径)
{"method": "GET", "uri": "posts"}
# 结果: https://api.example.com/v1/postsSession 管理
#!base = https://api.example.com
#!var USERNAME = "test"
#!var PASSWORD = "secret"
# 登录并捕获 token
{"method": "POST", "uri": "/login", "headers": {"Content-Type": "application/json"}, "body": {"username": @{USERNAME}, "password": @{PASSWORD}}}
#@ capture $.token auth_token
# 定义 session 头部(多行累积)
#!session {"headers": {"Authorization": "Bearer @{auth_token}"}}
# 开始 session(此时才展开变量)
#!session begin
# session 中的所有请求都包含授权请求头
{"method": "GET", "uri": "/profile"}
{"method": "GET", "uri": "/settings"}
# 结束 session(移除授权请求头)
#!session end类型转换与断言
#!base = https://api.example.com
# 捕获各种类型
{"method": "GET", "uri": "/user/@{USER_ID}"}
#@ capture $.id user_id:number
#@ capture $.name user_name:string
#@ capture $.isActive active:boolean
#@ capture $.roles roles:array
#@ capture $.metadata meta:object
# 断言检查
#@ assert $status == 200
#@ assert $.id > 0
#@ expect id <string>
#@ expect name "test"
# 使用捕获的变量
{"method": "POST", "uri": "/users/@{user_id}/update", "headers": {"Content-Type": "application/json"}, "body": {"name": @{user_name}, "active": @{active}}}输出
响应保存到以下格式的文件中:<origin>.output.<yyyymmdd>.<seq>.jaf.txt
输出文件包含:
- 原始 JAF 文件的全部内容
- 在每个请求的下一行添加
#>输出记录实际响应
示例输出:
{"method": "GET", "uri": "/users"}
#> {"id":1,"name":"Leanne Graham"}
{"method": "GET", "uri": "/users/1"}
#> {"id":1,"name":"Leanne Graham","email":"[email protected]"}
{"method": "POST", "uri": "/posts", "body": {"title": "Test"}}
#> {"id":101,"title":"Test","userId":1}开发
# 安装依赖
pnpm install
# 运行测试
pnpm test
# 运行测试并生成覆盖率报告
pnpm test:coverage
# 构建
pnpm build
# 监视模式(开发)
pnpm dev
# 代码检查
pnpm lint
# 修复代码检查问题
pnpm lint:fix
# 格式化代码
pnpm format
# 类型检查
pnpm type-check项目结构
src/
├── cli/ # CLI 入口
├── executor/ # HTTP 请求执行
├── formatter/ # 输出格式化
├── parser/ # JAF 语法解析器
├── runtime/ # Session 管理
├── tokenizer/ # JAF 语法词法分析器
├── types/ # TypeScript 类型定义
├── validator/ # 断言和 Schema 验证
└── variables/ # 变量管理常见问题
Q: 如何处理身份验证?
A: 使用环境变量或捕获的 token:
#!env API_KEY
#!default {"headers": {"Authorization": "Bearer ${API_KEY}"}}Q: 可以在请求头中使用变量吗?
A: 可以,变量在请求头和请求体中都有效:
{"method": "GET", "uri": "/users", "headers": {"X-User-ID": @{USER_ID}}}Q: 如果捕获失败会发生什么?
A: 默认情况下,捕获失败会停止执行。可以使用 #@ ignore error 来忽略错误:
#@ ignore error
#@ capture $.optional_field fieldQ: 如何测试非 JSON 响应?
A: JAF 只支持 JSON 响应,非 JSON 响应会报错停止执行。
Q: 变量替换是如何工作的?
A: 变量替换是文本级别的。变量值会被转换为字符串并直接插入到文本中。用户需要确保替换后的结果是合法的 JSON。
示例:
#!var NUM = 42
{"method": "POST", "uri": "/data", "body": {"value": @{NUM}}}
# 结果: {"method": "POST", "uri": "/data", "body": {"value": 42}}注意事项:
- 变量值会被转换为字符串后插入
- 如果变量包含特殊字符,可能会导致 JSON 格式错误
- 使用类型转换确保变量值正确
- 内置变量是只读的,不能被覆盖
Q: 如何使用 --verbose 选项调试?
A: 使用 --verbose 选项查看每个步骤的详细信息:
jaf example.jaf --verbose这将显示:
- Tokenizing 和 Parsing 阶段的详细信息
- 变量替换前后对比
- 请求和响应的完整内容
- 带彩色输出的调试信息
Q: --no-stdout 选项有什么用?
A: --no-stdout 选项抑制控制台输出,但仍输出文件路径和摘要:
jaf example.jaf --no-stdout适用于:
- 自动化脚本中只关心文件输出
- 减少控制台输出干扰
- 与
--verbose选项兼容(verbose 信息仍会显示)
Q: 如何在 Linux 上使用 --clipboard 选项?
A: Linux 需要安装 xclip 或 xsel:
# Ubuntu/Debian
sudo apt-get install xclip
# Fedora/RHEL
sudo dnf install xclip
# Arch
sudo pacman -S xclip安装后即可使用:
jaf example.jaf --clipboardQ: JAF 支持哪些变量类型?
A: JAF 只支持 primitive 类型(string/number/boolean/null),不支持 object/array。
Q: 如何隐藏 C 堆栈错误?
A: 在 PowerShell 中使用 2>$null 重定向 stderr:
jaf example.jaf 2>$null这将隐藏来自 libuv 的 C 堆栈错误信息。
规范文档
详细的 JAF 语言规范请参考 jaf.md。
更新日志
v0.4.1 (2026-02-19)
改进:
- 添加
--verbose选项,支持详细日志输出 - 在请求信息中显示实际发送的完整 URL(host 和 path)
- 优化输出文件名格式:
<original>.output.<yyyymmdd>.<seq>.jaf.txt(序号自动递增,3位数字) - 移除
example.jaf.txt,使用 examples/ 目录中的示例文件 - 更新 .gitignore,忽略所有测试和临时文件
- 所有日志消息改为中文输出
v0.4.0 (2026-02-19)
重大更新:
- 完全重构语法以符合 jaf.md 规范
- 修改
#@指令语法:#@ capture PATH VAR或#@ capture PATH VAR:TYPE#@ expect FIELD VALUE或#@ expect FIELD <TYPE>#@ assert CONDITION#@ ignore error(两个词)
- 修改
#>指令语法:#> record PATH(只记录,不参与逻辑控制)
- 移除旧的
#> capture/expect语法 - 更新 token 类型和 AST 类型定义
- 更新 tokenizer 和 parser 以支持新语法
改进:
- Output file 现在包含原始 jaf 文件的全部内容
- 在请求下一行添加
#>输出记录实际响应 - 更新 .gitignore 忽略所有临时和测试文件
- 更新 README 文档以反映最新语法
修复:
- 修复 parser 没有正确传递 tokens 的问题
- 修复 default method 没有被正确应用的问题
- 修复 formatter 没有初始化 outputLines 的问题
v0.3.0 (2026-02-19)
新增功能:
- 新增
@{_iso_timestamp_}内置变量,生成 ISO 8601 格式时间戳 - 改进错误提示,包含行号定位和修复建议
- 完善
--verbose选项,打印每个步骤的详细信息 - 完善
--no-stdout选项,抑制控制台输出但仍显示关键信息 - 完善
--clipboard选项,改进跨平台支持和错误提示
改进:
- 添加内置变量只读保护
- 改进变量替换错误提示
- 添加变量替换最佳实践文档
- 改进语法错误提示,包含彩色输出
修复:
- 修复变量替换规则文档说明
v0.2.0 (2025-12-15)
新增功能:
- 支持 Session 管理
- 支持变量类型转换
- 支持可选字段捕获
- 改进错误处理
改进:
- 优化性能
- 改进日志输出
v0.1.0 (2025-12-01)
初始发布:
- 基础 JAF 语言支持
- 变量管理
- HTTP 请求执行
- 响应断言
许可证
MIT
Q: 如何在 Linux 上使用 --clipboard 选项?
A: Linux 需要安装 xclip 或 xsel:
# Ubuntu/Debian
sudo apt-get install xclip
# Fedora/RHEL
sudo dnf install xclip
# Arch
sudo pacman -S xclip安装后即可使用:
jaf example.jaf --clipboardQ: 如何隐藏 C 堆栈错误信息?
A: 在 Windows 上,有时会看到来自 libuv 的 C 堆栈错误信息(如 "Assertion failed: !(handle->flags & UV_HANDLE_CLOSING)")。这些是 Node.js 内部错误,不影响 JAF 的功能。
要隐藏这些错误,在 PowerShell 中使用 2>$null 重定向:
jaf example.jaf --verbose 2>$null这将:
- 隐藏 C 堆栈错误信息
- 保留所有正常的 JAF 输出
- 不影响任何功能
更新日志
v0.4.5 (2026-02-19)
重要修复:
- 修复 Fast-Fail 机制:当请求失败且没有
#@ ignore error时,正确抛出异常停止执行 - 修复
#@ ignore error指令支持:现在可以正确忽略单个请求的错误并继续执行后续请求 - 修复绝对路径处理:将以
/开头的路径正确追加到 base URL 路径,而不是替换 - 修复 CLI URL 显示逻辑:确保 CLI 显示的 URL 与实际发送的 URL 一致
- 改进错误处理:即使在 fast-fail 情况下也会生成输出文件,保留部分执行结果
架构改进:
- 重构预请求指令(
#@)处理:现在指令与特定请求关联,而不是全局指令 - 改进 tokenizer:收集请求之前的
#@指令并关联到请求 - 改进 parser:正确解析请求关联的预请求指令
- 改进 CLI:在执行请求前正确处理预请求指令(如
#@ ignore error)
规范对齐:
- 绝对路径处理现在完全符合 jaf.md 规范:
/users→http://api.com/v1/users - 相对路径处理:
users→http://api.com/v1/users - 完整 URL:
https://other.com/api→ 原样使用 - 协议相对:
//cdn.com/file→ 使用 base URL 的协议
v0.4.4 (2026-02-19)
修复:
- 修复 verbose 输出中显示
undefined方法的问题 - 当请求未指定 method 时,正确显示默认方法或 GET
v0.4.3 (2026-02-19)
功能改进:
- 优化输出文件命名:序号不再使用前导零(如 1, 2, 3 而不是 001, 002, 003)
- 输出文件序号自动递增:每次运行时检查已存在的文件,自动使用下一个可用的序号
- 输出文件只使用 LF 换行符,确保跨平台兼容性
- 移除控制台输出中的 output file 内容显示,只显示文件路径和摘要
- 修复默认方法未正确应用的问题
- 修复 tokenizer 将未定义 method 设置为空字符串的问题
修复:
- 修复输出文件路径问题,确保只在输出目录中创建文件
- 修复错误处理中的模板字符串问题
v0.4.2 (2026-02-19)
安全改进:
- 新增敏感变量屏蔽功能:当输出环境变量或脚本变量时,如果变量名包含 "key"(不区分大小写),自动屏蔽变量值
- 屏蔽规则:短值(≤8字符)显示首尾字符,长值(>8字符)显示前4位和后4位
修复:
- 修复输出文件路径问题,确保只在输出目录中创建文件
- 修复错误处理中的模板字符串问题
v0.4.1 (2026-02-19)
新增功能:
- 新增
@{_iso_timestamp_}内置变量,生成 ISO 8601 格式时间戳 - 改进错误提示,包含行号定位和修复建议
- 将请求字段从
path改为uri以符合 jaf.md 规范 - 完善
--verbose选项,打印每个步骤的详细信息 - 完善
--no-stdout选项,抑制控制台输出但仍显示关键信息 - 完善
--clipboard选项,改进跨平台支持和错误提示
改进:
- 添加内置变量只读保护
- 改进变量替换错误提示
- 添加变量替换最佳实践文档
- 改进语法错误提示,包含彩色输出
修复:
- 修复变量替换规则文档说明
文档:
- 更新 README.md,包含所有新功能说明
- 添加变量替换最佳实践章节
- 添加常见问题解答
许可证
MIT
