CommunityPesquisa e Análise de Dadosgithub.com

linqFR/ytn

Ferramentas portáteis compiláveis para JS: validação de esquemas, roteamento de fluxos de trabalho, construtor de consultas SQLite e em breve outras.

O que é ytn?

ytn (whY Try Not) é uma coleção de pacotes TypeScript portáteis e compiláveis para JavaScript, projetados para desenvolvedores que precisam de ferramentas leves e sem dependências externas. A suíte inclui `@ytrynot/dna`, um construtor de esquemas com API fluente similar ao Zod, que permite validação, transformação e compilação para bytecode serializável (DNA) – ideal para cenários onde o runtime de validação precisa ser portátil ou utilizado em ambientes sem TypeScript. O pacote `@ytrynot/schvalid` compila esquemas JSON Schema 2020-12 para DNA, oferecendo validadores ultra-rápidos com modos otimizados (validação booleana, análise com coleta de erros, híbrido e completo). Já o `@ytrynot/qb` é um construtor de consultas SQLite que gera SQL a partir de esquemas Zod ou DNA, com suporte a DDL, DML e operações avançadas como upserts e JOINs. Por fim, `@ytrynot/cli` permite criar roteadores de linha de comando com validação de parâmetros via DNA, geração automática de ajuda e compilação AOT. Todos os pacotes utilizam ESM puro, TypeScript estrito e zero dependências externas onde possível, funcionando perfeitamente em Node.js ou Bun. A documentação completa, receitas, comparações com Zod/AJV e guias técnicos estão disponíveis no repositório GitHub. Esta skill é especialmente útil para agentes de IA que precisam integrar validação de dados, geração de SQL ou construir CLIs com mínimo overhead.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add linqFR/ytn

Installed? Explore more Pesquisa e Análise de Dados skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

ytn — Guia de Uso dos Pacotes

ytn (whY Try Not) fornece pacotes TypeScript portáteis e compiláveis para JavaScript, para validação de esquemas, geração de SQL e roteamento de CLI. Todos os pacotes são ESM puro, TypeScript estrito e, onde possível, zero dependências externas.

Pacotes de Relance

PacotenpmInstalaçãoUse quando
@ytrynot/dna@ytrynot/dnanpm install @ytrynot/dnaVocê precisa de validação de esquema estilo Zod com bytecode serializável ou validadores compilados independentes
@ytrynot/schvalid@ytrynot/schvalidnpm install @ytrynot/schvalidVocê tem um JSON Schema 2020-12 e deseja validação ultra-rápida
@ytrynot/qb@ytrynot/qbnpm install @ytrynot/qbVocê precisa construir strings SQL para SQLite a partir de uma API fluente ou esquema Zod/DNA
@ytrynot/cli@ytrynot/clinpm install @ytrynot/cliVocê precisa de um roteador CLI com rotas validadas por DNA, ajuda automática e compilação AOT

@ytrynot/dna — Construtor de Esquemas + Runtime DNA

Instalação

npm install @ytrynot/dna

Defina um esquema (API fluente estilo Zod)

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

Validar / analisar

// Validação booleana (rápida, falha rápida)
user.validate({ name: "João", email: "[email protected]" }); // true

// Análise segura (sem lançar exceção)
const resultado = user.safeParse({ name: "J", email: "ruim" });
// { success: false, errors: [...] }

// Análise (lança exceção em erro)
const dados = user.parse({ name: "João", email: "[email protected]" });

Compilar para JavaScript independente (sem runtime necessário)

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

const schema = dna.object({ name: dna.string().min(2), age: dna.number() });
const bytecode = schema.toDna(); // bytecode DNA serializável

// Compila uma vez, reutiliza a função
const valida = validator(bytecode);
valida({ name: "João", age: 30 }); // true

const analisa = parser(bytecode);
analisa({ name: "João", age: 30 }); // { success: true, data: { name: "João", age: 30 } }

Modos de objeto

dna.object({ name: dna.string() })               // padrão — remove chaves desconhecidas
dna.strictObject({ name: dna.string() })          // estrito — rejeita chaves desconhecidas
dna.looseObject({ name: dna.string() })           // solto — passa chaves desconhecidas adiante

Tipos de esquema comuns

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("ativo")
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("tipo", [formaA, formaB])
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(ClasseUsuario)
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(esquemaRecursivo) }))
dna.brand<"MinhaMarca">()

Refinamentos

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

Transformações + pipe

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

Recursão

// Padrão 1: dna.lazy()
const EsquemaArvore: DnaLazy<Arvore> = dna.lazy(() =>
  dna.object({ valor: dna.number(), children: dna.array(EsquemaArvore) })
);

// Padrão 2: getters de objeto (autorreferência)
const EsquemaNo = dna.object({
  valor: dna.number(),
  get children() { return dna.array(EsquemaNo); },
});

Metadados

const campo = dna.string().meta({ title: "Email", description: "Email do usuário" });
const campo2 = dna.string().describe("Nome completo do usuário");

Leitura adicional

  • README — API completa, pontos de entrada, CLI Union, externos
  • Receitas — Mais de 15 receitas para copiar e colar (modos de objeto, recursão, pipe, refinamentos, coerção, template literals, esquemas de função, records/maps/sets, padrões, branding, metadados)
  • Documentação técnica — Opcodes DNA, arquitetura, detalhes internos de geração de código
  • Comparação com Zod — Paridade de recursos DNA vs Zod
  • Externos — Mecanismo de externos para transformações
  • Serialização — Formato de serialização DNA
  • Padrões de opcode — Padrões de uso de opcodes

@ytrynot/schvalid — Compilador JSON Schema para DNA

Instalação

npm install @ytrynot/schvalid

Converter JSON Schema para DNA + validar

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);

// Validação booleana rápida
const valida = validator(dna);
valida({ name: "João", age: 30 }); // true
valida({ name: "Jo" });            // false (minLength 3)

// Análise completa com coleta de erros
const analisa = parser(dna);
analisa({ name: "João", age: 30 }); // { success: true, data: { name: "João", age: 30 } }
analisa({ name: "Jo" });            // { success: false, errors: [...] }

Compilar uma vez, validar muitas vezes (API construtor)

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

const compilador = schvalid("validation");
const valida = compilador.compile(jsonSchema);
valida({ name: "João", age: 30 }); // true

// Quatro modos:
schvalid("validation"); // validador booleano (falha rápida)
schvalid("parser");     // analisador com coleta de erros + novo objeto de saída
schvalid("fast");       // híbrido: valida primeiro, analisa apenas em falha (data === input em sucesso)
schvalid("all");        // { validate, parse, parseFast } — todos compilados uma vez, instâncias compartilhadas

Recursos suportados do JSON Schema 2020-12

  • Tipos: string, number, integer, boolean, null, object, array
  • Restrições: properties, required, items, additionalProperties, minItems, maxItems, minLength, maxLength, minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, pattern, format, const, enum
  • Lógica: anyOf, allOf, oneOf, if/then/else, not
  • OpenAPI 3.1: discriminator
  • Referências: apenas $ref interna (sem URIs externos)

Limitações

  • Sem $ref externo (URIs HTTP, URNs, arquivos externos)
  • Sem formatos personalizados (apenas formatos padrão: email, uuid, uri, etc.)
  • Sem validação assíncrona, sem $data, sem coerção de tipo, sem injeção de padrão

Leitura adicional


@ytrynot/qb — Construtor de Consultas SQLite

Instalação

npm install @ytrynot/qb
# Dependências opcionais para DDL orientado a esquemas:
npm install zod        # para introspecção de esquema Zod v4
npm install @ytrynot/dna  # para introspecção de esquema DNA

Consultas DML fluentes

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

const sql = QB.table("usuarios", "u")
  .select(["u.id", "u.nome", "u.email"])
  .where(["u.ativo"])
  .orderBy("u.criado_em", "DESC")
  .limit(10)
  .toSQL();

-- SELECT u.id, u.nome, u.email FROM usuarios AS u WHERE u.ativo = @ativo ORDER BY u.criado_em DESC LIMIT 10

Definir uma tabela a partir de um esquema Zod

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

const EsquemaUsuario = z.object({
  id: z.string().uuid().meta({ pk: true }),
  email: z.string().email().meta({ unique: true }),
  nome: z.string(),
  idade: z.number().int(),
  criado_em: z.date().optional(),
});

const usuarios = QB.defTable("usuarios", EsquemaUsuario);

usuarios.createTable;
-- CREATE TABLE IF NOT EXISTS usuarios (
--   id TEXT PRIMARY KEY,
--   email TEXT UNIQUE NOT NULL,
--   nome TEXT NOT NULL,
--   idade INTEGER NOT NULL,
--   criado_em DATETIME
-- );

usuarios.getById;  -- SELECT * FROM usuarios WHERE id = @id
usuarios.insert;   -- INSERT INTO usuarios (id, email, nome, idade, criado_em) VALUES (@id, @email, @nome, @idade, @criado_em)
usuarios.update;   -- UPDATE usuarios SET email = @email, nome = @nome, idade = @idade, criado_em = @criado_em WHERE id = @id
usuarios.delete;   -- DELETE FROM usuarios WHERE id = @id
usuarios.upsert;   -- INSERT INTO usuarios (...) VALUES (...) ON CONFLICT(id) DO UPDATE SET ...

Definir uma tabela a partir de um esquema DNA

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

const EsquemaUsuario = dna.object({
  id: dna.string().uuid().meta({ pk: true }),
  email: dna.string().email().meta({ unique: true }),
  nome: dna.string(),
  idade: dna.int(),
  criado_em: dna.date().optional(),
});

const usuarios = QB.defTable("usuarios", EsquemaUsuario);
-- Mesma saída SQL da versão Zod

Consultas personalizadas via .req / .q

usuarios.req.select("id", "nome").where("id").toSQL();
-- SELECT id, nome FROM usuarios WHERE id = @id

usuarios.req.upsert("email", "nome").toSQL();
-- INSERT INTO usuarios (email, nome) VALUES (@email, @nome)
-- ON CONFLICT(id, email) DO UPDATE SET nome = excluded.nome

Executar com um driver

import { DatabaseSync } from "node:sqlite";

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

const stmt = db.prepare(usuarios.insert);
stmt.run({ id: crypto.randomUUID(), email: "[email protected]", nome: "Alice", idade: 30, criado_em: null });

Chaves de metadados para DDL

ChaveEfeito
pk: truePRIMARY KEY
pkauto: truePRIMARY KEY AUTOINCREMENT
unique: trueUNIQUE
default: valorDEFAULT valor
fk: { table, column }REFERENCES tabela(coluna)

Leitura adicional

  • Início Rápido — Tutorial completo, CRUD ponta a ponta
  • Como fazer: Consultas — SELECT, INSERT, UPDATE, DELETE, UPSERT, WHERE, JOINs, ordenação, limites, paginação, busca textual, GROUP BY
  • Como fazer: DDL — Todas as fontes de esquema, chaves de metadados, chaves estrangeiras, índices, PK composta
  • Como fazer: Avançado — EXISTS, CASE WHEN, subconsultas, funções de janela, PragmaBuilder
  • Referência de recursos — Inventário completo de métodos, matriz de versão SQLite

@ytrynot/cli — Roteador CLI Validado por DNA

Instalação

npm install @ytrynot/cli

Definir rotas como esquemas DNA

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

const rotaBuild = dna.object({
  cmd: dna.literal("build"),
  arquivos: dna.array(dna.string()).optional().meta({ description: "Arquivos para compilar" }),
}).meta({ cli: { routeId: "build" }, description: "Compilar o projeto" });

const rotaDeploy = dna.object({
  cmd: dna.literal("deploy"),
  alvo: dna.string().optional().meta({ description: "Alvo de implantação" }),
}).meta({ cli: { routeId: "deploy" }, description: "Implantar o projeto" });

const contrato = createContract({
  name: "meucli",
  description: "Um CLI de demonstração",
  targets: [rotaBuild, rotaDeploy],
  cli: { positionals: ["cmd", "arquivos"] },
});

Executar (camada 1 — roteamento síncrono + validação)

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

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

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

CLI completo com handlers + formatador + process.exit

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

const handlers: ts.Handlers = {
  build: (payload) => ({ success: true, data: `Compilados ${payload.arquivos?.length ?? 0} arquivos` }),
  deploy: (payload) => ({ success: true, data: `Implantado em ${payload.alvo ?? "padrão"}` }),
};

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

const run = fullCli(cliFactory(executeContract(contrato, handlers), formatter));
await run(); // lê process.argv.slice(2), imprime, sai

Adicionar --help / --version

import { Version } from "@ytrynot/cli";

const rotaHelp = dna.looseObject({ cmd: dna.literal("help") }).meta({
  cli: { routeId: "help" }, description: "Mostra ajuda"
});
const rotaVersion = dna.looseObject({ cmd: dna.literal("version") }).meta({
  cli: { routeId: "version" }, description: "Mostra versão"
});

const handlers: ts.Handlers = {
  help: () => ({
    success: true,
    data: contrato.cli.help({
      name: "meucli",
      headers: { commands: "Comandos" },
      labels: { description: "Descrição" },
    }),
  }),
  version: () => ({ success: true, data: Version }),
};

Saída com cores

import { TTY } from "@ytrynot/cli";

console.log(TTY.blue("texto"));
console.log(TTY.green("sucesso"));
console.log(TTY.red("erro"));

Leitura adicional

  • README — API completa, geração de ajuda, aninhamento, subcomandos
  • Guia — Guia detalhado com exemplos completos
  • Exemplos — Exemplos completos de CLIs

Habilidades Relacionadas