Community연구 & 데이터 분석github.com

linqFR/ytn

이식 가능한 JS 컴파일 가능 도구: 스키마 검증, 워크플로우 라우팅, SQLite 쿼리 빌더 등

ytn란 무엇인가요?

ytn(whY Try Not)은 TypeScript 개발자를 위해 설계된 이식 가능한 JavaScript-컴파일 가능 패키지 모음입니다. 이 패키지 모음은 DNA(Dynamic Native Assertions) 스키마 검증, SQLite SQL 쿼리 빌더, CLI 라우터, JSON Schema 2020-12에서 DNA로의 컴파일러를 제공합니다. 모든 패키지는 순수 ESM, 엄격한 TypeScript이며, 가능한 한 외부 종속성이 없습니다. `@ytrynot/dna`는 Zod와 유사한 유창한 API를 제공하며, 직렬화 가능한 바이트코드로 컴파일하여 런타임 없이 사용할 수 있는 독립형 검증기/파서를 생성합니다. `@ytrynot/schvalid`는 JSON Schema 2020-12 스키마를 가져와 초고속 검증을 위한 DNA 바이트코드로 변환합니다. `@ytrynot/qb`는 Zod 또는 DNA 스키마에서 DDL(DATA Definition Language)을 생성하고, 유창한 API를 통해 SQLite SQL 문자열을 구성합니다. `@ytrynot/cli`는 DNA-검증된 라우트, 자동 도움말 생성, AOT 컴파일을 갖춘 CLI 라우터를 제공합니다. 이 패키지들은 AI 에이전트가 코드 작성, 스키마 검증, 워크플로우 라우팅을 위한 이식 가능하고 최적화된 도구가 필요할 때 특히 유용하며, Cursor, Codex, Claude Code와 같은 플랫폼에서 효과적으로 사용할 수 있습니다.

지원 대상~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에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

ytn — 패키지 사용 가이드

ytn(whY Try Not)은 스키마 검증, SQL 생성, CLI 라우팅을 위한 이식 가능한 JS-컴파일 가능 TypeScript 패키지를 제공합니다. 모든 패키지는 순수 ESM, 엄격한 TypeScript이며, 가능한 한 외부 종속성이 없습니다.

패키지 개요

패키지npm설치사용 시점
@ytrynot/dna@ytrynot/dnanpm install @ytrynot/dna직렬화 가능한 바이트코드 또는 독립형 컴파일 검증기가 포함된 Zod-유사 스키마 검증이 필요할 때
@ytrynot/schvalid@ytrynot/schvalidnpm install @ytrynot/schvalidJSON Schema 2020-12가 있고 초고속 검증이 필요할 때
@ytrynot/qb@ytrynot/qbnpm install @ytrynot/qb유창한 API 또는 Zod/DNA 스키마로 SQLite SQL 문자열을 빌드해야 할 때
@ytrynot/cli@ytrynot/clinpm install @ytrynot/cliDNA-검증된 라우트, 자동 도움말, 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">()

세부 규칙 (Refinement)

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

변환 + 파이프

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: 객체 게터 (자체 참조)
const NodeSchema = dna.object({
  value: dna.number(),
  get children() { return dna.array(NodeSchema); },
});

메타데이터

const field = dna.string().meta({ title: "Email", description: "User email" });
const field2 = dna.string().describe("User's full name");

추가 자료

  • README — 전체 API, 진입점, CLI Union, 외부 종속성
  • 레시피 — 15개 이상의 복사-붙여넣기 레시피 (객체 모드, 재귀, 파이프, 세부 규칙, 강제 변환, 템플릿 리터럴, 함수 스키마, 레코드/맵/셋, 기본값, 브랜딩, 메타데이터)
  • 기술 문서 — DNA opcode, 아키텍처, 코드 생성 내부
  • Zod 비교 — DNA 대 Zod 기능 비교
  • 외부 종속성 — 변환을 위한 외부 종속성 메커니즘
  • 직렬화 — DNA 직렬화 형식
  • Opcode 패턴 — opcode 사용 패턴

@ytrynot/schvalid — JSON Schema to 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 — 모든 스키마 소스, 메타데이터 키, 외래 키, 인덱스, 복합 PK
  • 방법: 고급 — 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: "Files to build" }),
}).meta({ cli: { routeId: "build" }, description: "Build the project" });

const deployRoute = dna.object({
  cmd: dna.literal("deploy"),
  target: dna.string().optional().meta({ description: "Deployment target" }),
}).meta({ cli: { routeId: "deploy" }, description: "Deploy the project" });

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

실행 (계층 1 — 동기 라우팅 + 검증)

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: [...] }

핸들러 + 포맷터 + process.exit를 사용한 전체 CLI

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

const handlers: ts.Handlers = {
  build: (payload) => ({ success: true, data: `Built ${payload.files?.length ?? 0} files` }),
  deploy: (payload) => ({ success: true, data: `Deployed to ${payload.target ?? "default"}` }),
};

const formatter: ts.FormatterFn = (result) => {
  if (result.success) return { exit: 0, message: String(result.data ?? "") };
  return { exit: 1, message: `Error: ${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") }).meta({
  cli: { routeId: "help" },
  description: "Show help",
});

const versionRoute = dna.looseObject({ cmd: dna.literal("version") }).meta({
  cli: { routeId: "version" },
  description: "Show version",
});

const contract = createContract({
  name: "mycli",
  description: "A demo CLI",
  targets: [buildRoute, deployRoute, helpRoute, versionRoute],
  cli: { positionals: ["cmd"], helpRoute: "help", versionRoute: "version" },
});

네 가지 계층

계층함수역할
1execute(contract, args)동기 라우팅 + 검증
2executeContract(contract, handlers)핸들러에 라우팅
3cliFactory(fn, formatter)출력 형식화
4fullCli(fn)process.argv 읽기, 출력, process.exit

추가 자료

  • README — 전체 API, AOT, getOpts, 오류 처리
  • AOT 가이드 — 종속성이 없는 독립형 CLI를 위해 계약을 독립형 빌드로 컴파일
  • 고급 오류 — 계층별 오류 처리

AI 에이전트를 위한 요약

이 패키지 모음은 언어 모델 에이전트가 이식 가능하고 최적화된 도구가 필요할 때 가장 유용합니다:

  • 코드 생성: .toDna()를 호출하여 스키마를 직렬화 가능한 바이트코드로 컴파일하고, validator()/parser()를 사용하여 독립형 검증기를 생성합니다. 런타임이나 추가 패키지 없이도 사용할 수 있습니다.
  • 워크플로우 라우팅: @ytrynot/cli는 계약에서 검증된 경로를 정의합니다. 자동 생성된 JSON 스키마는 도구 호출을 안내하고, 컴파일된 코드는 빠른 실행을 보장합니다.
  • SQL 생성: @ytrynot/qb는 SQL 문자열로 직접 변환되는 스키마를 정의합니다. 스키마 변경 시 SQL을 재생성하거나 유창한 API를 사용할 수 있습니다.
  • 이식성: 컴파일된 출력은 순수 JavaScript이므로 모든 런타임(Node.js, Deno, 브라우저)에서 작동합니다. 한 번 컴파일하면 어디서나 실행됩니다.

관련 스킬