Community研究与数据分析github.com

ytn — 便携式可编译JS工具包

一套便携、可编译为JS的TypeScript工具包:提供模式验证、工作流路由、SQLite查询构建器等功能。

ytn — 便携式可编译JS工具包 是什么?

ytn(whY Try Not)是一套专为开发者打造的TypeScript工具包,所有包均为纯ESM模块、严格TypeScript编写,并尽可能实现零外部依赖。核心包括:`@ytrynot/dna`——类似Zod的模式验证库,支持序列化字节码和独立编译验证器,可生成无运行时的纯JS验证函数;`@ytrynot/schvalid`——将JSON Schema 2020-12编译为DNA字节码,实现超快速验证;`@ytrynot/qb`——基于Zod或DNA模式的SQLite查询构建器,支持流畅API、自动DDL生成和完整的CRUD操作;`@ytrynot/cli`——基于DNA验证的CLI路由工具,支持自动帮助、AOT编译和完整的命令处理。这些工具特别适合需要高性能验证、可移植验证逻辑或类型安全CLI的Node.js项目,并且可以脱离运行时环境独立使用。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add linqFR/ytn

Installed? Explore more 研究与数据分析 skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

ytn — 包使用指南

ytn(whY Try Not)提供便携、可编译为 JavaScript 的 TypeScript 包,用于模式验证、SQL 生成和 CLI 路由。所有包均为纯 ESM、严格 TypeScript,并尽可能实现零外部依赖。

包概览

包名npm安装命令适用场景
@ytrynot/dna@ytrynot/dnanpm install @ytrynot/dna需要类似 Zod 的模式验证,且支持可序列化字节码或独立编译验证器
@ytrynot/schvalid@ytrynot/schvalidnpm install @ytrynot/schvalid拥有 JSON Schema 2020-12 并希望实现超快速验证
@ytrynot/qb@ytrynot/qbnpm install @ytrynot/qb需要通过流畅 API 或 Zod/DNA 模式构建 SQLite SQL 字符串
@ytrynot/cli@ytrynot/clinpm install @ytrynot/cli需要具有 DNA 验证、自动帮助和 AOT 编译的 CLI 路由器

@ytrynot/dna — 模式构建器 + DNA 运行时

安装

npm install @ytrynot/dna

定义模式(类似 Zod 的流畅 API)

import { dna } from "@ytrynot/dna";

const user = dna.object({
  name: dna.string().min(2),
  email: dna.string().email(),
  age: dna.number().int().min(0).optional(),
  role: dna.enum(["admin", "user"]).default("user"),
});

type User = dna.infer<typeof user>;
// { name: string; email: string; age?: number; role: "admin" | "user" }

验证 / 解析

// 布尔验证(快速,快速失败)
user.validate({ name: "John", email: "[email protected]" }); // true

// 安全解析(不抛出异常)
const result = user.safeParse({ name: "J", email: "bad" });
// { success: false, errors: [...] }

// 解析(错误时抛出异常)
const data = user.parse({ name: "John", email: "[email protected]" });

编译为独立 JS(无需运行时)

import { dna, validator, parser } from "@ytrynot/dna";

const schema = dna.object({ name: dna.string().min(2), age: dna.number() });
const bytecode = schema.toDna(); // 可序列化的 DNA 字节码

// 编译一次,重用函数
const validate = validator(bytecode);
validate({ name: "John", age: 30 }); // true

const parse = parser(bytecode);
parse({ name: "John", age: 30 }); // { success: true, data: { name: "John", age: 30 } }

对象模式

dna.object({ name: dna.string() })               // 标准模式 — 去除未知键
dna.strictObject({ name: dna.string() })          // 严格模式 — 拒绝未知键
dna.looseObject({ name: dna.string() })           // 宽松模式 — 允许未知键通过

常用模式类型

dna.string().min(1).max(100).email().uuid().url().regex(/^[a-z]+$/)
dna.number().int().positive().negative().min(0).max(100).multipleOf(2)
dna.boolean()
dna.date()
dna.bigint()
dna.literal("active")
dna.enum(["a", "b", "c"])
dna.array(dna.string()).min(1).max(10).nonempty()
dna.object({ ... })
dna.tuple([dna.string(), dna.number()])
dna.union([dna.string(), dna.number()])
dna.discriminatedUnion("type", [shapeA, shapeB])
dna.record(dna.string(), dna.boolean())
dna.map(dna.string(), dna.string())
dna.set(dna.string())
dna.optional()    // dna.string().optional()
dna.nullable()    // dna.string().nullable()
dna.default("fallback")
dna.catch("fallback")
dna.preprocess((v) => Number(v), dna.number())
dna.coerce.number().parse("123")  // 123
dna.instanceof(UserClass)
dna.templateLiteral(["https://", dna.string(), ".", dna.enum(["com", "net"])])
dna.function().input([dna.string()]).output(dna.number()).implement((s) => s.length)
dna.lazy(() => dna.object({ children: dna.array(recursiveSchema) }))
dna.brand<"MyBrand">()

细化规则

dna.string().refine((s) => s.length > 3, "太短");
dna.array(dna.string()).superRefine((val, ctx) => {
  if (val.length > 10) ctx.addIssue({ code: "too_big", maximum: 10, message: "太多" });
});

转换 + 管道

const schema = dna.string().transform((s) => s.length).pipe(dna.number());
schema.parse("hello"); // 5

递归

// 模式 1:dna.lazy()
const TreeSchema: DnaLazy<Tree> = dna.lazy(() =>
  dna.object({ value: dna.number(), children: dna.array(TreeSchema) })
);

// 模式 2:对象 getter(自引用)
const NodeSchema = dna.object({
  value: dna.number(),
  get children() { return dna.array(NodeSchema); },
});

元数据

const field = dna.string().meta({ title: "邮箱", description: "用户邮箱" });
const field2 = dna.string().describe("用户全名");

进一步阅读

  • README — 完整 API、入口点、CLI Union、外部依赖
  • 配方库 — 15+ 可复制粘贴的配方(对象模式、递归、管道、细化规则、强制转换、模板字面量、函数模式、映射/集合、默认值、品牌化、元数据)
  • 技术文档 — DNA 操作码、架构、代码生成内部
  • Zod 对比 — DNA 与 Zod 功能对比
  • 外部依赖 — 用于转换的外部机制
  • 序列化 — DNA 序列化格式
  • 操作码模式 — 操作码使用模式

@ytrynot/schvalid — JSON Schema 到 DNA 编译器

安装

npm install @ytrynot/schvalid

将 JSON Schema 转换为 DNA 并验证

import { jschemaToDna, validator, parser } from "@ytrynot/schvalid";

const jsonSchema = {
  type: "object",
  properties: {
    name: { type: "string", minLength: 3 },
    age: { type: "number", minimum: 0 },
  },
  required: ["name"],
};

const dna = jschemaToDna(jsonSchema);

// 快速布尔验证
const validate = validator(dna);
validate({ name: "John", age: 30 }); // true
validate({ name: "Jo" });            // false (minLength 3)

// 完整解析并收集错误
const parse = parser(dna);
parse({ name: "John", age: 30 }); // { success: true, data: { name: "John", age: 30 } }
parse({ name: "Jo" });            // { success: false, errors: [...] }

编译一次,多次验证(构建器 API)

import { schvalid } from "@ytrynot/schvalid";

const compiler = schvalid("validation");
const validate = compiler.compile(jsonSchema);
validate({ name: "John", age: 30 }); // true

// 四种模式:
schvalid("validation"); // 布尔验证器(快速失败)
schvalid("parser");     // 解析器(收集错误 + 新输出对象)
schvalid("fast");       // 混合模式:先验证,失败时再解析(成功时 data === input)
schvalid("all");        // { validate, parse, parseFast } — 一次编译,共享实例

支持的 JSON Schema 2020-12 特性

  • 类型:string、number、integer、boolean、null、object、array
  • 约束:properties、required、items、additionalProperties、minItems、maxItems、minLength、maxLength、minimum、maximum、exclusiveMinimum、exclusiveMaximum、multipleOf、pattern、format、const、enum
  • 逻辑:anyOf、allOf、oneOf、if/then/else、not
  • OpenAPI 3.1:discriminator
  • 引用:仅支持内部 $ref(不支持外部 URI)

局限性

  • 不支持外部 $ref(HTTP URI、URN、外部文件)
  • 不支持自定义格式(仅支持标准格式:email、uuid、uri 等)
  • 不支持异步验证、$data、类型强制转换、默认值注入

进一步阅读

  • README — 完整 API、模式、性能
  • AJV 对比 — schvalid 与 AJV 功能逐项对比

@ytrynot/qb — SQLite 查询构建器

安装

npm install @ytrynot/qb
# 可选的用于模式驱动 DDL 的对等依赖:
npm install zod        # 用于 Zod v4 模式自省
npm install @ytrynot/dna  # 用于 DNA 模式自省

流畅 DML 查询

import { QueryBuilder as QB } from "@ytrynot/qb";

const sql = QB.table("users", "u")
  .select(["u.id", "u.name", "u.email"])
  .where(["u.active"])
  .orderBy("u.created_at", "DESC")
  .limit(10)
  .toSQL();

// SELECT u.id, u.name, u.email FROM users AS u WHERE u.active = @active ORDER BY u.created_at DESC LIMIT 10

从 Zod 模式定义表

import { z } from "zod";
import { QueryBuilder as QB } from "@ytrynot/qb";

const UserSchema = z.object({
  id: z.string().uuid().meta({ pk: true }),
  email: z.string().email().meta({ unique: true }),
  name: z.string(),
  age: z.number().int(),
  created_at: z.date().optional(),
});

const users = QB.defTable("users", UserSchema);

users.createTable;
// CREATE TABLE IF NOT EXISTS users (
//   id TEXT PRIMARY KEY,
//   email TEXT UNIQUE NOT NULL,
//   name TEXT NOT NULL,
//   age INTEGER NOT NULL,
//   created_at DATETIME
// );

users.getById;  // SELECT * FROM users WHERE id = @id
users.insert;   // INSERT INTO users (id, email, name, age, created_at) VALUES (@id, @email, @name, @age, @created_at)
users.update;   // UPDATE users SET email = @email, name = @name, age = @age, created_at = @created_at WHERE id = @id
users.delete;   // DELETE FROM users WHERE id = @id
users.upsert;   // INSERT INTO users (...) VALUES (...) ON CONFLICT(id) DO UPDATE SET ...

从 DNA 模式定义表

import { dna } from "@ytrynot/dna";
import { QueryBuilder as QB } from "@ytrynot/qb";

const UserSchema = dna.object({
  id: dna.string().uuid().meta({ pk: true }),
  email: dna.string().email().meta({ unique: true }),
  name: dna.string(),
  age: dna.int(),
  created_at: dna.date().optional(),
});

const users = QB.defTable("users", UserSchema);
// 与 Zod 版本相同的 SQL 输出

通过 .req / .q 自定义查询

users.req.select("id", "name").where("id").toSQL();
// SELECT id, name FROM users WHERE id = @id

users.req.upsert("email", "name").toSQL();
// INSERT INTO users (email, name) VALUES (@email, @name)
// ON CONFLICT(id, email) DO UPDATE SET name = excluded.name

使用驱动程序执行

import { DatabaseSync } from "node:sqlite";

const db = new DatabaseSync("app.db");
db.exec(QB.enableForeignKeys()); // PRAGMA foreign_keys = ON
db.exec(users.createTable);

const stmt = db.prepare(users.insert);
stmt.run({ id: crypto.randomUUID(), email: "[email protected]", name: "Alice", age: 30, created_at: null });

DDL 元数据键

键效果
pk: truePRIMARY KEY
pkauto: truePRIMARY KEY AUTOINCREMENT
unique: trueUNIQUE
default: valueDEFAULT value
fk: { table, column }REFERENCES table(column)

进一步阅读

  • 快速入门 — 完整教程,端到端 CRUD
  • 查询指南 — SELECT、INSERT、UPDATE、DELETE、UPSERT、WHERE、JOIN、排序、限制、分页、文本搜索、GROUP BY
  • DDL 指南 — 所有模式源、元数据键、外键、索引、复合主键
  • 高级指南 — EXISTS、CASE WHEN、子查询、窗口函数、PragmaBuilder
  • 功能参考 — 完整方法清单,SQLite 版本矩阵

@ytrynot/cli — DNA 验证的 CLI 路由器

安装

npm install @ytrynot/cli

将路由定义为 DNA 模式

import { dna } from "@ytrynot/dna";
import { createContract, execute } from "@ytrynot/cli";

const buildRoute = dna.object({
  cmd: dna.literal("build"),
  files: dna.array(dna.string()).optional().meta({ description: "要构建的文件" }),
}).meta({ cli: { routeId: "build" }, description: "构建项目" });

const deployRoute = dna.object({
  cmd: dna.literal("deploy"),
  target: dna.string().optional().meta({ description: "部署目标" }),
}).meta({ cli: { routeId: "deploy" }, description: "部署项目" });

const contract = createContract({
  name: "mycli",
  description: "一个演示 CLI",
  targets: [buildRoute, deployRoute],
  cli: { positionals: ["cmd", "files"] },
});

运行(第一层 — 同步路由 + 验证)

const result = execute(contract, ["build", "a.ts", "b.ts"]);
// { success: true, route: "build", payload: { cmd: "build", files: ["a.ts", "b.ts"] } }

execute(contract, ["deploy", "--target", "prod"]);
// { success: true, route: "deploy", payload: { cmd: "deploy", target: "prod" } }

execute(contract, ["unknown"]);
// { success: false, errors: [...] }

完整 CLI(处理函数 + 格式化器 + process.exit)

import { executeContract, cliFactory, fullCli } from "@ytrynot/cli";
import type { ts } from "@ytrynot/cli";

const handlers: ts.Handlers = {
  build: (payload) => ({ success: true, data: `构建了 ${payload.files?.length ?? 0} 个文件` }),
  deploy: (payload) => ({ success: true, data: `部署到 ${payload.target ?? "default"}` }),
};

const formatter: ts.FormatterFn = (result) => {
  if (result.success) return { exit: 0, message: String(result.data ?? "") };
  return { exit: 1, message: `错误:${result.error}` };
};

const run = fullCli(cliFactory(executeContract(contract, handlers), formatter));
await run(); // 读取 process.argv.slice(2),打印,退出

添加 --help / --version

const helpRoute = dna.looseObject({ cmd: dna.literal("help") })

相关技能