{"_id":"@codeandmoney/belikto","_rev":"2-2c44698881114c13a3455ffec21eaaf4","name":"@codeandmoney/belikto","dist-tags":{"latest":"0.0.0-rc.1"},"versions":{"0.0.0-rc.0":{"name":"@codeandmoney/belikto","version":"0.0.0-rc.0","license":"MIT","_id":"@codeandmoney/belikto@0.0.0-rc.0","maintainers":[{"name":"alexvyber","email":"alexvyber@gmail.com"}],"dist":{"shasum":"1b206c46b4ed69c34b49ce77170297faa23312a8","tarball":"https://registry.npmjs.org/@codeandmoney/belikto/-/belikto-0.0.0-rc.0.tgz","fileCount":128,"integrity":"sha512-2DCMlBhRPVbz+drpGJiQQG3P2KfdsUvvKxKjGMlYI2xKtcXCvCwUwSTeOrYrEQXBcBgLWQL3w6Id3XHLvqKNyQ==","signatures":[{"sig":"MEYCIQDF6dEiWaVrcCr/vhfLHI5bYjsKwBF9Jx3TnzcyP3VktAIhAKTz/2UFtb6bBe6x1F1yGgJeBtGGZMbVS9ZqJBNYfwwA","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":347555},"shasum":"1b206c46b4ed69c34b49ce77170297faa23312a8","exports":{".":"./src/exports.ts","./belikto-to-code":"./tools/exports.ts"},"scripts":{"dev":"bun run --watch exports.ts","lint":"bun lint","test":"bun test --concurrent","tojson":"bun run tools/tojson.ts","coverage":"bun task test --coverage=coverage && bun coverage coverage --lcov --output=coverage.lcov","gen-docs":"bun run --allow-write=docs tools/generate_mappings.ts","validate":"bun run tools/validate.ts","playground":"bun run tools/playground.ts"},"_npmUser":{"name":"alexvyber","email":"alexvyber@gmail.com"},"_integrity":"sha512-2DCMlBhRPVbz+drpGJiQQG3P2KfdsUvvKxKjGMlYI2xKtcXCvCwUwSTeOrYrEQXBcBgLWQL3w6Id3XHLvqKNyQ==","_npmVersion":"10.8.3","description":"Convert Valibot schemas to/from a stable serialized AST","directories":{},"_nodeVersion":"24.3.0","dependencies":{"valibot":"^1.1.0","prettier":"^3.6.2","fast-check":"^4.3.0","drizzle-orm":"^0.44.6","drizzle-zod":"^0.8.3","@biomejs/biome":"^2.2.5","@biomejs/js-api":"^3.0.0","drizzle-valibot":"^0.4.2","semantic-release":"^24.2.9","@biomejs/wasm-nodejs":"^2.2.5","@standard-schema/spec":"^1.0.0","@traversable/valibot-test":"^0.0.20"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"bun-types":"^1.3.0","@types/bun":"^1.3.0","typescript":"^5.9.3","@types/node":"^24.7.2"},"_npmOperationalInternal":{"tmp":"tmp/belikto_0.0.0-rc.0_1760448177751_0.14667839332612265","host":"s3://npm-registry-packages-npm-production"}},"0.0.0-rc.1":{"name":"@codeandmoney/belikto","version":"0.0.0-rc.1","description":"Convert Valibot schemas to/from a stable serialized AST","license":"MIT","exports":{".":"./src/exports.ts","./belikto-to-code":"./tools/exports.ts"},"dependencies":{"@biomejs/biome":"^2.3.7","@biomejs/js-api":"^4.0.0","@biomejs/wasm-nodejs":"^2.3.7","@standard-schema/spec":"^1.0.0","@traversable/valibot-test":"^0.0.20","drizzle-orm":"^0.44.7","drizzle-valibot":"^0.4.2","drizzle-zod":"^0.8.3","fast-check":"^4.3.0","prettier":"^3.6.2","semantic-release":"^25.0.2","valibot":"^1.1.0"},"devDependencies":{"@types/bun":"^1.3.3","@traversable/registry":"^0.0.49","@traversable/valibot-types":"^0.0.18","fast-check":"^4.3.0","valibot":"^1.1.0","@types/node":"^24.10.1","typescript":"^5.9.3"},"publishConfig":{"access":"public"},"scripts":{"dev":"bun run --watch exports.ts","test":"bun test --concurrent","coverage":"bun task test --coverage=coverage && bun coverage coverage --lcov --output=coverage.lcov","lint":"bun lint","tojson":"bun run tools/tojson.ts","validate":"bun run tools/validate.ts","gen-docs":"bun run --allow-write=docs tools/generate_mappings.ts","playground":"bun run tools/playground.ts"},"gitHead":"4c38feeb2bcf9e9b7e5bcabfc8c637804beefadd","_id":"@codeandmoney/belikto@0.0.0-rc.1","_nodeVersion":"24.9.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-Us/NoilrhkLgLsxBLWMKG/NeNzjhQWwtkSeA1OJPuomqx5yvOJO60+YgyeLHteg56pIapcd0dUpoflXupJfzew==","shasum":"138f225008f2390ad9c09b97c3c702f0d9a9d619","tarball":"https://registry.npmjs.org/@codeandmoney/belikto/-/belikto-0.0.0-rc.1.tgz","fileCount":129,"unpackedSize":474422,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCuHpfZbBocTPpJ8OZQYgZ28ELwzlHVRlU+FeWVY6NzNgIgYbAXlvMiNQUa8tm1S6r+ccz/mriTrjEQ+HRILNb3K+c="}]},"_npmUser":{"name":"alexvyber","email":"alexvyber@gmail.com"},"directories":{},"maintainers":[{"name":"alexvyber","email":"alexvyber@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/belikto_0.0.0-rc.1_1763825854941_0.4598574739906367"},"_hasShrinkwrap":false}},"time":{"created":"2025-10-14T13:22:57.640Z","modified":"2025-11-22T15:37:35.380Z","0.0.0-rc.0":"2025-10-14T13:22:57.930Z","0.0.0-rc.1":"2025-11-22T15:37:35.145Z"},"license":"MIT","description":"Convert Valibot schemas to/from a stable serialized AST","maintainers":[{"name":"alexvyber","email":"alexvyber@gmail.com"}],"readme":"# belikto\n\nSerialize / deserialize Valibot schemas, including \"to code\" for dep-free tree-shaking.\n\n\n[![npm](https://img.shields.io/npm/v/belikto)](https://www.npmjs.com/package/belikto)\n[![Tests](https://github.com/codeandmoney/belikto/actions/workflows/test-release.yaml/badge.svg)](https://github.com/codeandmoney/belikto/actions/workflows/test-release.yaml)\n[![coverage](https://img.shields.io/codecov/c/github/codeandmoney/belikto)](https://codecov.io/gh/codeandmoney/belikto)\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)\n[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)\n\n## Quick Start\n\n```ts\n// On JSR, it's @valibot/valibot and @codeandmoney/belikto\nimport * as v from \"valibot\";\nimport * as belikto from \"belikto\";\n\nconst LoginSchema = v.object( { email: v.string(), password: v.string() } );\n\nconst serialized = belikto.fromValibot( LoginSchema );\nconst NewLoginSchema = belikto.toValibot( serialized );\n\n// { email: 'jane@example.com', password: '12345678' }\nconst parsed = v.parse( NewLoginSchema, { email: \"hello@example.com\", password: \"password\" } );\n\n// Write this to a file to benefit from dep-free tree-shaking\nconst code = belikto.toCode( serialized );\n// \"v.object({email:v.string(),password:v.string()});\"\n```\n\n**See [CLI Usage](#cli) section for quick no-code usage.**\n\n#### Important Notes\n\n- Obviously **transforms and callbacks** can't be serialized. You should store these elsewhere and apply after deserialization.\n\n- To avoid requiring `belikto` as a (non-dev) dependency and pulling in the entire `valibot` library, use `toCode` instead of `toValibot` and write\n  the results to a file. This way you'll still benefit tree-shaking. See [CLI](#cli) below for a code-free way to scan a directory and auto-write modules for\n  each detected schema.\n\n- This is a new project (mid September, 2025) - please let us know if anything doesn't work as expected.\n\n## Motivation\n\nI use [`drizzle-valibot`](http://npmjs.com/package/drizzle-valibot) to create validation schemas (valibot) from my database schemas (drizzle-orm / postgresql),\nwhich works great, but, sometimes I want them on the client side too without bundling `drizzle` and `pg`. This way, we can use a small script to create dep-free\nversions of those schemas.\n\n## CLI\n\n### belikto\n\nScan files for supported exports (valibot schema or drizzle tables) and generate static code to recreate them that works with tree-shaking.\n\nSuggested usage: put it in your `package.json`:\n\n```json\n{ \"scripts\": { \"schema:gen\": \"belikto --include 'src/db/schema/*.ts' --outDir src/db/valibot/generated\" } }\n```\n\n`belikto --help` for all options. You can also import `\"belikto/belikto\"` and run it programatically. Formatters like `prettier`, `biome` or\n`deno` will be used if found.\n\nAlternative ways to run:\n\n```bash\n$ npx -p belikto belikto\n$ deno run --allow-read --allow-write jsr:@codeandmoney/belikto/belikto\n```\n\nSample input:\n\n```ts\nimport { integer, pgTable, text } from \"drizzle-orm/pg-core\";\nexport const users = pgTable( \"users\", { id: integer().primaryKey() /* ... */ } );\n```\n\nSample output:\n\n```ts\nexport const usersSelect = v.object( { name: v.string() /*...*/ } );\nexport const usersInsert = v.object( { id: v.pipe( v.number() /*...*/ ) } );\nexport const usersUpdate = v.object( { name: v.optiona( v.string() ) /*...*/ } );\nexport type UsersSelect = v.InferOutput<typeof usersSelect>;\nexport type UsersInsert = v.InferInput<typeof usersInsert>;\nexport type UsersUpdate = v.InferInput<typeof usersUpdate>;\n```\n\nIf you don't like this opinionated output structure for drizzle tables, simply use `drizzle-valibot` yourself and export the structure you like. **Or use the\nprogramatic API** (by simply importing `\"belikto/belikto\"`).\n\nWe also suggest to auto-load in watch-mode with e.g. `.vscode/tasks.json`:\n\n```json\n{\n  \"version\": \"2.0.0\",\n  \"tasks\": [\n    {\n      \"label\": \"schema watch\",\n      \"command\": \"npm\",\n      \"args\": [\"run\", \"schema:gen\", \"--watch\"],\n      \"isBackground\": true,\n      \"type\": \"shell\",\n      \"runOptions\": { \"runOn\": \"folderOpen\" },\n      \"presentation\": { \"echo\": true, \"reveal\": \"always\", \"focus\": false, \"panel\": \"shared\", \"showReuseMessage\": true, \"clear\": false }\n    }\n  ]\n}\n```\n\nFAQ:\n\n- **Should I commit generated files to git/vcs?**\n\n  Totally up to you. No problem to generate at build time, but also nice to have them for clear, easy viewing in your repo.\n\n### Other Miscellaneous things we might not keep\n\nIs anything here useful? Anything else you'd like?\n\nConvert a serialized AST (read from stdin) to JSON Schema:\n\n```\necho\n'{\"kind\":\"schema\",\"vendor\":\"valibot\",\"version\":1,\"format\":1,\"node\":{\"type\":\"object\",\"entries\":{\"a\":{\"type\":\"string\"}},\"policy\":\"strict\"}}'\\\n| deno task tojson\n```\n\nOutputs a JSON Schema for the data shape.\n\n~~Generate Valibot builder code from a serialized AST (read from stdin):~~\n\n```\n// Removed for now. If you'd find this useful, please open an issue. echo\n'{\"kind\":\"schema\",\"vendor\":\"valibot\",\"version\":1,\"format\":1,\"node\":{\"type\":\"object\",\"entries\":{\"email\":{\"type\":\"string\"},\"password\":{\"type\":\"string\"}}}}'\\\n| deno task tocode\n```\n\nOutputs:\n\n```\nv.object({email:v.string(),password:v.string()});\n```\n\n## API\n\n- `fromValibot(schema: v.BaseSchema): SerializedSchema`\n  - Encodes a Valibot schema to a JSON‑serializable AST with `{ kind, vendor, version, format, node }`.\n- `toValibot(data: SerializedSchema): v.BaseSchema`\n  - Decodes the AST back to a Valibot schema.\n- `isSerializedSchema(x: unknown): x is SerializedSchema`\n  - Runtime type guard for the AST envelope.\n- `serializedSchemaJson`\n  - JSON Schema for the AST envelope and node variants (useful to validate serialized payloads).\n- `toJsonSchema(serialized: SerializedSchema): JsonSchema`\n  - Best‑effort conversion from our AST to JSON Schema (Draft 2020‑12) for data validation.\n- `fromJsonSchema(json: JsonSchemaLike): SerializedSchema`\n  - Basic, lossy converter from a subset of JSON Schema → our AST (strings/numbers/booleans/literals/arrays/objects/enums/unions/tuples/sets/maps\n    approximations).\n- `toCode(serialized: SerializedSchema): string`\n  - Emits concise Valibot builder code for the given AST (no imports), ending with a semicolon. Intended for code‑gen/export; format it as you like.\n\n## Module Structure\n\n- Each Valibot schema kind is implemented in its own module under `src/types/`. For example: `string.ts`, `number.ts`, `object.ts`, `enum.ts`, and\n  `picklist.ts`. This keeps detection/encode/decode/codegen/JSON‑Schema logic focused and easy to maintain. When adding support for a new schema, prefer\n  creating `src/types/<kind>.ts` and export it via `src/types/index.ts`.\n\n### Supported nodes and flags (AST)\n\n- `string` with:\n  - lengths: `minLength`, `maxLength`, exact `length`\n  - patterns: `pattern` (+ `patternFlags`), `startsWith`, `endsWith`\n  - formats/validators: `email`, `rfcEmail`, `url`, `uuid`, `ip`, `ipv4`, `ipv6`, `hexColor`, `slug`, `digits`, `emoji`, `hexadecimal`, `creditCard`, `imei`,\n    `mac`, `mac48`, `mac64`, `base64`, ids `ulid`, `nanoid`, `cuid2`, ISO time/date variants `isoDate`, `isoDateTime`, `isoTime`, `isoTimeSecond`,\n    `isoTimestamp`, `isoWeek`\n  - counters: `minGraphemes`, `maxGraphemes`, `minWords`, `maxWords`\n  - transforms: `trim`, `trimStart`, `trimEnd`, `toUpperCase`, `toLowerCase`, `normalize`\n- `number` with `min`, `max`, `gt`, `lt`, `integer`, `safeInteger`, `multipleOf`, `finite`\n- `boolean`, `literal`\n- `array` with `item` + `minLength`, `maxLength`, `length`\n- `object` with `entries`, `optionalKeys` hint, `policy` (`loose`/`strict`), `rest`, `minEntries`, `maxEntries`\n- `optional`, `nullable`, `nullish`\n- `union`, `tuple` (+ `rest`), `record`\n- `enum` with `values`\n- `picklist` with `values` (string options)\n- `set` with `value`, `minSize`, `maxSize`\n- `map` with `key`, `value`, `minSize`, `maxSize`\n- `date`, `file` (`minSize`, `maxSize`, `mimeTypes`), `blob` (`minSize`, `maxSize`, `mimeTypes`)\n\n## Notes\n\n- The AST is independent of Valibot internals and versioned (`format: 1`).\n- Some validators don’t map cleanly to JSON Schema and are approximated (e.g., word counts, ISO formats, IDs) using patterns.\n- Complex constructs (custom transforms/effects) are intentionally unsupported and fail fast on `fromValibot`.\n- `fromJsonSchema` is intentionally minimal and lossy; prefer authoring schemas in Valibot and using `fromValibot` as the source of truth.\n\n### JSON Schema conversion\n\nThis was never a main goal for the project especially since other, mature tools exist for this purpose (i.e.\n[`@valibot/to-json-schema`](https://www.npmjs.com/package/@valibot/to-json-schema) and\n[`json-schema-to-valibot`](https://www.npmjs.com/package/json-schema-to-valibot), however, the AI offered to implement it and I said why not :) Let us know if\nyou find it useful.\n\n- `toJsonSchema` converts:\n  - Strings to string schemas, mapping common formats and adding regexes for selected validators (see notes).\n    - IDs approximated: `ulid`, `nanoid`, `cuid2` via patterns.\n    - Validators approximated: `creditCard`, `imei`, `mac`, `mac48`, `mac64`, `base64` via patterns.\n  - Numbers, booleans, arrays, objects, tuples, enums, unions, sets/maps (approximate), records (as additionalProperties), date/file/blob as strings (binary for\n    file/blob).\n  - Union of literals becomes an `enum`.\n- `fromJsonSchema` converts back a subset:\n  - `type` string/number/integer/boolean, `const` (`literal`), `enum`, `array`/`object`, `tuple` (`prefixItems`), `union` (`anyOf`), and `anyOf` of constants →\n    `picklist` (all strings) or `enum` (mixed types).\n  - Recognizes string format/email/uri/uuid/ipv4/ipv6, and common patterns produced by `toJsonSchema` for startsWith/endsWith, `hexColor`, `slug`, `digits`,\n    `hexadecimal`, ids (`ulid`, `nanoid`, `cuid2`) and sets flags accordingly.\n\n### Compatibility mapping (selected)\n\n| Valibot/AST                | toJsonSchema                          | fromJsonSchema back |\n| -------------------------- | ------------------------------------- | ------------------- |\n| string.email               | type: string, format: email           | email: true         |\n| string.url                 | type: string, format: uri             | url: true           |\n| string.uuid                | type: string, format: uuid            | uuid: true          |\n| string.ipv4/ipv6           | format: ipv4/ipv6                     | ipv4/ipv6: true     |\n| string.ip                  | anyOf [ipv4, ipv6]                    | ip: true            |\n| string.startsWith/endsWith | pattern/allOf anchored                | starts/ends: true   |\n| string.hexColor            | regex                                 | hexColor: true      |\n| string.slug                | regex                                 | slug: true          |\n| string.digits/hexadecimal  | regex                                 | digits/hexadecimal  |\n| ulid/nanoid/cuid2          | regex                                 | flags: true         |\n| creditCard/imei/mac/...    | regex                                 | flags: true         |\n| number min/max/gt/lt       | min/max/exclusiveMin/Max              | fields restored     |\n| array min/max/len          | minItems/maxItems                     | fields restored     |\n| object min/max entries     | minProperties/maxProperties           | fields restored     |\n| union of literals          | enum                                  | enum node           |\n| enum values                | enum                                  | enum node           |\n| set/map                    | array uniqueItems / object additional | approximated        |\n| tuple/rest                 | prefixItems (+ items/rest)            | fields restored     |\n| date                       | string (format: date-time)            | approximated        |\n| file/blob                  | string binary (+ mediaType)           | approximated        |\n\n## Creation Notes\n\nThis was \"vibe-coded\" (with AI) over a weekend. I set up minimalist structure with a test case for how I wanted the code to work, and some empty functions with\nsignatures. I then asked OpenAI Codex to complete the code.\n\nCodex did so, and consistently gave some great suggestions on what to do next, and I kept saying yes to see where it would go. Eventually then I moved on to\nprompts for cleanup, refactoring, project structure, etc. The CLI tool was written by hand.\n\nPlease do bring any weird issues to our attention, and feel free to request clearer docs, examples, etc. Working on that next.\n\n## Relevant issues / discussions on valibot repo:\n\n- [Issue #30: Allow schema serialization](https://github.com/fabian-hiller/valibot/issues/30)\n- [Discussion #733: Can you generate a schema from the Reflection API?](https://github.com/fabian-hiller/valibot/discussions/733)\n\n## Development\n\nSee CONTRIBUTING.md for project layout, test naming, and workflow conventions.\n\n## License\n\nMIT\n\n```\n```\n\n```\n```\n","readmeFilename":"README.md"}