Community研究與資料分析github.com

linqFR/ytn

可移植、可編譯為 JavaScript 的工具集:包含 schema 驗證、工作流程路由、SQLite 查詢建構器,以及更多功能。

ytn 是什麼?

ytn (whY Try Not) 是一個純 ESM、嚴謹 TypeScript、且盡可能零外部依賴的 TypeScript 套件集合,為開發者提供可移植且可編譯為 JavaScript 的實用工具。其核心套件包含:@ytrynot/dna —— 類似 Zod 的 schema 建構器,支援序列化為可攜帶的 DNA 字節碼,並可編譯為無需執行時依賴的獨立驗證器或解析器;@ytrynot/schvalid —— 可將 JSON Schema 2020-12 編譯為 DNA 格式以實現超快速驗證;@ytrynot/qb —— 提供流暢 API,可從 Zod 或 DNA schema 建構 SQLite SQL 字串;@ytrynot/cli —— CLI 路由工具,支援 DNA 驗證、自動說明文件生成與提前編譯 (AOT)。這些套件適合用於需要高效能、可序列化且無外部依賴的 schema 驗證、資料庫查詢建構或命令列工具開發的場景,可在 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 ?? "預設"}` }),
};

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") }

相關技能