@ amritk/ 验证
一个发电机表面超过Mjst的种类,守卫,验证者,胁迫者,修理者和剖析器.
概览
@amritk/validation 是前门 由于一个JSON Schema(2020-12草案),它喷出
包含任意运行时间切入点的一组一致类型脚本文件
要求——从赤裸裸的型号,通过布林警卫和完整的出错报告,到
胁迫者,修理者 和全剖析器.
在下面,这是 两个 发电机引擎,不是一台:验证器、强制器和
修理机引擎,以及解析器和类型引擎。 他们分别作为
@amritk/generate-validators 财务报告和审定财务报表 @amritk/generate-parsers 直到此软件包
吸收了它们 保留它们都是故意的 他们真的不同
生成值模式的代码: 以
检查它,而且速度是它的多倍;验证员保存通行证
分开,因此可以报告什么是错的。 把它们折叠成一个发射器
这意味着放弃其中之一财产.
使得一个输出目录成为可能的是,两者都获得 TypeScript 类型
从同 @amritk/helpers/generate-type-definition,所以该类型被宣布
一次 输出中的每一个功能都涉及到它.
安装
bun add @amritk/validation
使用量
import { generate } from '@amritk/validation'
import type { JSONSchema } from 'json-schema-typed/draft-2020-12'
const schema: JSONSchema = {
type: 'object',
properties: {
retries: { type: 'integer', minimum: 0, maximum: 10, default: 3 },
name: { type: 'string', minLength: 3 },
},
required: ['retries', 'name'],
}
const files = await generate(schema, 'Config', {
modes: ['types', 'guard', 'validate', 'coerce', 'repair', 'parse'],
})
for (const file of files) await Bun.write(`./generated/${file.filename}`, file.content)
模式
每种模式对 这会对我的文件造成多大影响
有人告诉我。它们会编曲: 要求多个功能,然后您会得到多个功能
一种类型, 每一个判断一个价值 它以同样的判断 validateX, (中文(简体) ),
因此,对于一个文件是否有效,没有两人可以持不同意见.
| 模式 | 函数 | 返回值 | 告诉你 | 停下来 |
|---|---|---|---|---|
types | —— 说 | —— 说 | —— 说 | —— 说 |
guard | isX(input): input is X | —— 说 | 布尔 | 第一个问题 |
validate | validateX(input) | —— 说 | 每个错误 | 结束 |
check | checkX(input) | —— 说 | 第一个错误 | 第一个问题 |
coerce | coerceX(input) | 被胁迫 | 每个错误 | 结束 |
repair | repairX(input) | 已修复 | 维修 财务报告和审定财务报表 错误 | 结束 |
parse | parseX(input) | 已修复 | 无 | 从不失败 |
parseStrict | parseX(input) | 给定的 | 丢出第一个 | 第一个问题 |
只验证、 快速失败或找到每个错误 . guard 失败的快取:
它在第一件事上停止错误, 没有构建错误对象, 它在无效时
投入比报告便宜得多. validate 是另一半: 每一个
错误,每个都带有 JSON 指针,关键词拒绝了该值,并且
关键词本身的值. 到达 guard 当答案是树枝
validate 当一个人类或API客户端必须被告知如何修复. check 坐着
双方: checkX 返回相同结果类型 validateX,仅持有
错误 validateX 我会先报到 再走不远了.
粗而修之,有误而有误也. coerceX 财务报告和审定财务报表 repairX 手执
返回该值 财务报告和审定财务报表 诊断; parseX 手只返回值和
从不失败。 强制移动一个已经正确但写入
类型错误; 修复还替换了一个值 :
不能被强迫, 并报告验证人自己的错误 作为修复.
也就是说 repairX 自动修正工具,而不是更严格的验证器。 它接受
文档 validateX 拒绝( 缺少所需的名称获得一个; 未知)
enum 值成为第一个成员),只接受 valid 带空
repairs 给出准确的判决 coerceX 给。 一个空的 repairs 独自一人
不够:文件也无一可修复
valid: false。拒绝错误的配置,使用 coerceX交出一个固定的,
使用情况 repairX。 。 。 .
修复大型文件. repairX(input, options?) 选择
对象( 一个赤色的字符串仍然读作基础路径) :
| 选项 | 默认 | 它会做什么 |
|---|---|---|
path | '' | 贾森 每个报告路径的指针根植于 |
inPlace | false | 在输入上指定和删除,而不是复制容器 |
coerce | true | 移动错误类型所写的scalar("5" 下级 integer修复前;关闭后,按计划价值修复并报告 |
refs | true | 后续实例 $refs 位于参考计划选择的位置(以下) |
skipRefs | [] | 指针前缀( e)'#/components/'其目标 根计划已经走; 仍然解决,不再走 |
onRef | —— 说 | (value, path, typeId) => boolean | void,每个选择 $ref· ; false 别管它了 |
maxDepth | 512 | 所报告位置的深度(maxDepth而不是走着 |
maxWork | 10_000_000 | 在行走停止前参观的集装箱(maxWork(中文(简体) ) |
memo | false | 在每个计划上行走每个对象——用于带有共享或循环子树的内相图 |
finite | false | 拒绝 NaN 和数报的无限 |
plainObjects | false | 只接受普通对象,并删除自己的对象 __proto__ 键 |
当计划中的每个定义都是从 type, (中文(简体) ), enum, (中文(简体) ), const, (中文(简体) ),
properties, (中文(简体) ), required, (中文(简体) ), additionalProperties, (中文(简体) ), patternProperties, (中文(简体) ), items, (中文(简体) ),
$ref, (中文(简体) ), if编号 :then编号 :else 财务报告和审定财务报表 allOf - 附加说明,和分级
例如 minLength, (中文(简体) ), pattern, (中文(简体) ), minimum, (中文(简体) ), anyOf, (中文(简体) ), oneOf 或者说 not, (中文(简体) ),
被问及验证人自己对位置的测试.. repairX 是一个
单次通行证:检查并修复每个位置的位置,一个 if 实值
根据实际值评估,将构建缺失的必需对象
与所需子女(在递归定义上达到极限深度)
错误直接进入一个阵列,只有当某物被构建时它们才会被构建路径
报告。 有效文件本身不会被复制。 在24 MB 上
捆绑的 OpenAPI 文档远远低于百毫秒(bun --conditions development scripts/bench-repair-walk.ts) (中文(简体) ). 其它关键字保存
验证和修复环路,这符合 path, (中文(简体) ), inPlace 财务报告和审定财务报表 coerce 财务报告和审定财务报表
扔出只步选项(skipRefs, (中文(简体) ), onRef, (中文(简体) ), maxDepth, (中文(简体) ), maxWork, (中文(简体) ),
memo, (中文(简体) ), finite, (中文(简体) ), plainObjects而不是悄悄地放下警卫.
两份计划说明指导它. "x-repair": "drop" ——和a false 计划..
删除一个失败位置, 而不是写入默认: 未知的密钥
additionalProperties: false,在 items: false页:1
滴入标记属性. "x-repair": "ref" 打开参考计划
实例 $ref 如下: 在写职位上
{ if: { type: object, required: [$ref] }, then: Reference, else: T },则
$ref 在根文档中以 JSON 指针解决(拥有属性)
目标作为该位置的类型加以检查和修复
目标自身路径出错. 每个目标都走过一次 每一个类型,链
周期;未解决的周期报告为 unresolvedRef
从未创造过.
parseX 赞同 coerceX。 。 。 . 无论何处 coerceX 接受文档
强迫 parseX 返回非常相同的值——通过联盟,a allOf,一个
if页:1 $ref,包括复发——它只修复什么 coerceX 将
已经拒绝了。 一个差别测试针 比随机计数。 为了到那里,
一个自有树的定义 anyOf编号 :oneOf编号 :allOf编号 :if编号 :not 携带一个
准确测试( e)matchesX和强迫行走coerceXInput在它的前面
修补分解器 这样一个定义都包含着它们 A 类
没有梳理器的图案 完全照着它常做的来,一个和它们一起
发布更多 — 大约是大型 OpenAPI 文档的两倍的解析器代码
对每个定义的准确意见都花了多少时间 A 类 strict 或者说
stripUnknown 解析器没有 coerceX 同意,并且保持不变.
parse 财务报告和审定财务报表 parseStrict 在两个合同下是同一个函数名称,所以
要求两者都是错误而不是沉默的选择.
离开Ajv
coerceX 用于替换 Ajv 编译为 { allErrors: true, coerceTypes: true },每个计数都更好:答案、速度和
输入的安全.
它只接受计划所接受的. 任何东西 coerceX 手还原为
无效, validateX 刚刚接受, 和差别模糊 随机
计划 anyOf, (中文(简体) ), oneOf, (中文(简体) ), allOf 财务报告和审定财务报表 if编号 :then编号 :else 检查每个
被接受的文件反对Ajv 不胁迫。 如果两者不同,它
目的为:
-
null从来没有被胁迫无论朝哪个方向,都是不同的 你会遇到的最多: 在一个真实的配置中, 几乎每个Ajv的案件 已接受,并coerceX没有。 Ajv 读到null作为"", (中文(简体) ),0或者说false(单位:千美元) 字符串、数字或布尔字段,并读出nullA级null通常表示"没有设定",而将其变成一个真正的值会抹去这一点. 给 转到写入的验证器,验证器拒绝.切换前, 请检查您的呼叫者对一个被拒绝的文档做了什么 。 如果一个 将拒绝视为缺席(返回)
undefined,倒回到默认, 单人null在一个街区的深处 让整个街区消失 错误。 Ajv的胁迫曾经隐藏这一点. 要么让计划说null实值 允许( e)type: ['string', 'null']),丢出nulls 在验证前, 或有 调用者显示错误而不是丢弃值. -
只有干净的数字成为数字.
" ", (中文(简体) )," 1 ", (中文(简体) ),"0x10", (中文(简体) ),"Infinity"财务报告和审定财务报表"1."拒绝。 Ajv把它们变成0, (中文(简体) ),1, (中文(简体) ),16一个值 JSON 无法坚守,而且1。 。 。 ."007"财务报告和审定财务报表"1e3"仍然被胁迫. -
在工会中,一个已经匹配的分会获胜. Ajv 胁迫 首个会取值的分支. 鉴于
anyOf: [{ type: 'string' }, { const: false }],Ajv转弯false输入"false"· ;coerceX保留false。 。 。 . 当没有分支匹配成文时,每个分支会以自己的方式强制值 并且只有在接受结果的每一个分支都同意的情况下,才会得到结果. 两种不同的读取让验证者单独报告值,所以 分行的顺序是永远不变的. -
一个失败的分支 留下什么. Ajv 强迫到位 尝试每个分支,所以在
oneOf: [{ type: 'integer' }, { type: 'string' }]转了0输入"0"在尝试字符串分支时,看到两个匹配 拒绝有效文件.coerceX接受0原文如上. -
强制令每个应用者. 斯卡拉人被胁迫通过
$ref, (中文(简体) ),allOf(每个子母依次),if编号 :then编号 :else(转入)then当 数值匹配if, (中文(简体) ),else和连结的分枝.
它从来不触及输入. Ajv 强迫设置, 所以一个打电话者保持它的
文档必须先复制它. coerceX 返回共享的新值
所有它没有触及, 或输入本身 当不需要强迫.
投入必须生存的地方更快. 首先反对Ajv克隆,所以
两者都留下打电话者的证件(bun run bench:validators:coerce,或
bench:validators:coerce:node:
| 计划 | 运行时间 | 有效输入 | 需要强制 | 不能强迫 |
|---|---|---|---|---|
| 小( 4个字段) | 阿宝 | 5.4× (中文(简体) ) | 1.7× (中文(简体) ) | 1.5× (中文(简体) ) |
| 小( 4个字段) | 节点 | 9.9× (简体中文) | 2.1× (中文(简体) ) | 2.2× (中文(简体) ) |
| 顺序( 已识别 + 数组) | 阿宝 | 11× (中文(简体) ) | 1.6× (中文(简体) ) | 1.9× (中文(简体) ) |
| 顺序( 已识别 + 数组) | 节点 | 8.4× (中文(简体) ) | 1.8× (中文(简体) ) | 2.2× (中文(简体) ) |
| 平发 | 阿宝 | 19× (中文(简体) ) | 1.4× (中文(简体) ) | 3.0× (中文(简体) ) |
| 平发 | 节点 | 36× (中文(简体) ) | 1.6× (中文(简体) ) | 3.1× (中文(简体) ) |
配置(通过 $ref(中文(简体) ) | 阿宝 | 7.2× (中文(简体) ) | 1.7× (中文(简体) ) | 3.7× (中文(简体) ) |
配置(通过 $ref(中文(简体) ) | 节点 | 5.2× (中文(简体) ) | 1.5× (中文(简体) ) | 4.6× (中文(简体) ) |
每个细胞的速度是多少倍 coerceX 比Ajv在同一输入,
每台发动机在自己的过程中定时(Bun 1.3.11,Node 22.22,Linux x64). A 类
有效文件是常见的,也是最大的空白: coerceX 用这个回答
isX 直接把输入拿回来,不要走. isX 正在建设
无事——没有错误,没有路径——在第一次失败时止步,回答:
$ref 与目标自己的 isX; 它召唤 validateX 只是为了一个计划
折为常. 在有效输入时, Ajv 重写任何内容, 因此它可以
并且没有克隆人的时间; coerceX 还在前面的这些
计划,用1.2-3.3xx, 但不是每个: 在一个庞大的 工会重的文件中
呼叫者让Ajv胁迫到位,Ajv可以轻而易举地领先. 这个
上面的空白是克隆人,所以它是真实的 在那里呼叫者不能让Ajv
变异其入.
配置大小写是形状 --coerce 已存在, 用于: 每个可强制的平板
在工会内部 $ref,每个工会分支都通过
指定函数,而不是每个调用时建立的关闭.
错误不同 一种方式值得知道 如果你的关键诊断
path + keyword + params页:1 additionalProperties 出错点 地点
未申报的密钥 (单位:千美元)/readme/example_requestsAjv 指向对象的地方
拿着/readme) (中文(简体) ). params.additionalProperty 将两个键都命名.
API 密码
generate(rootSchema, rootTypeName, options?)
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
rootSchema | JSONSchema | —— 说 | 取根相会. $ref 财务报告和审定财务报表 $dynamicRef 递归解决. |
rootTypeName | string | —— 说 | 根类型的名称(例如:. "Config") (中文(简体) ). |
options.modes | Mode[] | ['types', 'guard', 'validate'] | 哪个切入点可以发射. 默认为只读;任何重写文档的都是选入. |
options.typeSuffix | string | '' | 每页后缀 $ref- 来源类型名称。 根名无相. |
options.schemas | Record<string, unknown> | —— 说 | 您已经装入的文档, 由 URI 键入 a $ref 叫他们名字. |
options.unknownKeys | 'count-keys' | 'count-enumerable' | 'count-keys' | 闭合物体的快道如何证明它没有未申报的密钥. |
options.formats | 'all' | string[] | —— 说 | 字符串 formats 验证符执行。 未落叶 format 一个注释. |
options.branchErrors | boolean | false | 解释失败 anyOf 编号 : oneOf 和它明显意义上的分枝, 或者它接受的类型 当没有分枝取值的种类. |
options.stripUnknown | boolean | false | 构建每个解析结果 . |
options.readonly | boolean | false | 释放 readonly 类型成员. |
options.caseInsensitive | boolean | false | 将错误的字符串规范到一个 enum 成员它匹配. |
options.helpersMode | 'package' | 'embedded' | 'package' | 解析器的一半得到运行时间帮助的地方. |
options.helpersImportPrefix | string | './' | 释放的帮助导入描述符的前缀, 何时 helpersMode 实值 'embedded'。 。 。 . |
options.extensions | SchemaExtensions | —— 说 | Schema 扩展,传递到剖析器的一半. |
options.importExt | 'js' | 'ts' | 'js' | 每次发射的相对导入的扩展 . 'ts' 释放上盘路径, 因此输出在节点类型剥离下运行, 没有构建步骤 . |
options.logWarnings | boolean | false | 打印解析器半导体对策略的警告, 例如它无法执行的关键字 . |
回返情况: Promise<GeneratedFile[]> 地点 GeneratedFile = { filename: string; content: string }。 。 。 .
套件还出口 ALL_MODES (每个模式但 parseStrict来电的
和类型 Mode, (中文(简体) ), GenerateOptions, (中文(简体) ), GeneratedFile 财务报告和审定财务报表
ImportExtension。 。 。 .
输出布局
config.ts the type, plus isX / validateX / coerceX / repairX
config.parse.ts parseX, importing the type from ./config.js
validation-result.ts the shared error and result types, and the runtime helpers
formats.ts the format checks, only when `formats` enforces one the schema names
_helpers/ the parser's runtime helpers, only when `helpersMode` is 'embedded'
index.ts a barrel over all of it
该类型生活在 config.ts 因为这是读者寻找的地方。 这个
解析器半个移动到 config.parse.ts 因为这两个生成器都命名了文件
在计划之后,他们中的一个必须让路。 只有一个
export type Config 在输出时,无论您要求的模式组合
测试套房通过汇编图案,在一系列图案中声称
结果是在这个repo自己的旗帜下.
请求不使用验证器模式, 且没有冲突, 所以解析器
既保留它自己的文件名, 也保留它已经编写过的文件类型, 也不保留
validation-result.ts 完全被释放出来 —— 仅一个解析的建筑没有
可导入其17 KiB 错误类型和运行时间帮助器.
更快吗?
比它包装的两个引擎? 不,它不应该是。 (Than Ajv,是的——见
离开Ajv. ) (中文(简体) ). 询问此软件包为一个模式, 它会发出 准确
字节 拥有该模式的引擎释放出——测试套接字针
模式,通过指纹,而不是读取输出。 无法使用同一代码
运行速度不同 所以这里没有运行时间 也没有
已经做了. bun run bench 测量它反正,三角洲徘徊在两边
运行之间为零,即进程级别差异在出现时的外观
无根本差别.
改变的只是冷淡的一面,只有你所希望的方向:
| (两个引擎都直接) | 之后 | |
|---|---|---|
| 整个矩阵的代码 | 2.9 毫秒(秒) | 2.7 毫秒(秒) |
| 已发射字节 | 46.0基贝 | 45.8 基贝 |
| 文件 | 第7条 | 6个 |
声明 Order | 2个 | 1个 |
| 每模式代码源 | —— 说 | 等于或略低 |
| 每模式字节 | —— 说 | 不变 |
重要的是中间的最后一行 达到每个模式 曾经的意思是自己运行发电机 和运送两棵树一棵 计划,包括两个同类型的声明——结构 可互换,但两件事要保持步入正轨. 现在有一个.
相关软件包
@amritk/helpers共享类型定义和倒置表@amritk/mjst——命令行接口
许可证
麻省理工学院