{"_id":"@denisetiya/valdix","_rev":"3-24bdbeca4c3a10a7849be42499034fd8","name":"@denisetiya/valdix","dist-tags":{"latest":"1.1.1"},"versions":{"1.0.0":{"name":"@denisetiya/valdix","version":"1.0.0","keywords":["validation","schema","typescript","zod-like","i18n"],"author":{"name":"Deni Setiya"},"license":"MIT","_id":"@denisetiya/valdix@1.0.0","maintainers":[{"name":"denisetiya","email":"denisetiya009@gmail.com"}],"homepage":"https://github.com/denisetiya/Valdix#readme","bugs":{"url":"https://github.com/denisetiya/Valdix/issues"},"dist":{"shasum":"6db53bedbf951a3b1f189ef2114a1840eb8c3434","tarball":"https://registry.npmjs.org/@denisetiya/valdix/-/valdix-1.0.0.tgz","fileCount":71,"integrity":"sha512-5nf0SIS5SaM4qVopcyElqVnRDg5GJo17PnTTjXuz9dxd8FF3qH/zKlaqsWqOfQh3OUeEqGAQN7mEZZH1u/4tQQ==","signatures":[{"sig":"MEUCIBkYoTGZcEtDUUoKlvr01Orm/F1aPSS5Xb/kC3OvVhDqAiEA9ejfh6rDo7r4DXN0XrvfQDnYtXEX2+0Yl+Ag2iFdYMc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":337843},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js"}},"gitHead":"754613b251b93af8450c0b24fcd2c7bd057b1690","scripts":{"lint":"eslint \"src/**/*.ts\"","test":"pnpm run build && node --test tests/*.test.mjs","bench":"pnpm run build && node benchmarks/basic.bench.mjs","build":"pnpm run clean && tsc -p tsconfig.build.json","clean":"node ./scripts/clean.mjs","typecheck":"tsc -p tsconfig.build.json --noEmit"},"_npmUser":{"name":"denisetiya","email":"denisetiya009@gmail.com"},"repository":{"url":"git+https://github.com/denisetiya/Valdix.git","type":"git"},"_npmVersion":"11.8.0","description":"Zero-dependency high-performance schema validation with multilingual errors.","directories":{},"sideEffects":false,"_nodeVersion":"24.13.1","_hasShrinkwrap":false,"devDependencies":{"eslint":"10.0.2","@eslint/js":"10.0.1","typescript":"5.9.3","typescript-eslint":"8.56.1"},"_npmOperationalInternal":{"tmp":"tmp/valdix_1.0.0_1771981915465_0.3911628188903702","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@denisetiya/valdix","version":"1.0.1","keywords":["validation","schema","typescript","zod-like","i18n"],"author":{"name":"Deni Setiya"},"license":"MIT","_id":"@denisetiya/valdix@1.0.1","maintainers":[{"name":"denisetiya","email":"denisetiya009@gmail.com"}],"homepage":"https://github.com/denisetiya/Valdix#readme","bugs":{"url":"https://github.com/denisetiya/Valdix/issues"},"dist":{"shasum":"1114d65e847fbdfd888222d0fa131601f5b3fc60","tarball":"https://registry.npmjs.org/@denisetiya/valdix/-/valdix-1.0.1.tgz","fileCount":71,"integrity":"sha512-o5LNrKrXuNmLfvba1oFNr5RWPWjKOg2/h4oDBomvRIMnK+h7UYeWXPQQ50bLQ2rPK+FqeoPfbh/vUnAGi3clAg==","signatures":[{"sig":"MEUCIGnZl0+SjMSsAzLGgdDhhLEWudxdjowkAzHfDU7krPAQAiEAgDuOGq6Z/m004F4uNayS+CdkREWwzKcD1x5CYln4z2k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":337999},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js"}},"gitHead":"754613b251b93af8450c0b24fcd2c7bd057b1690","scripts":{"lint":"eslint \"src/**/*.ts\"","test":"pnpm run build && node --test tests/*.test.mjs","bench":"pnpm run build && node benchmarks/basic.bench.mjs","build":"pnpm run clean && tsc -p tsconfig.build.json","clean":"node ./scripts/clean.mjs","typecheck":"tsc -p tsconfig.build.json --noEmit"},"_npmUser":{"name":"denisetiya","email":"denisetiya009@gmail.com"},"repository":{"url":"git+https://github.com/denisetiya/Valdix.git","type":"git"},"_npmVersion":"11.8.0","description":"Zero-dependency high-performance schema validation with multilingual errors.","directories":{},"sideEffects":false,"_nodeVersion":"24.13.1","_hasShrinkwrap":false,"devDependencies":{"eslint":"10.0.2","@eslint/js":"10.0.1","typescript":"5.9.3","typescript-eslint":"8.56.1"},"_npmOperationalInternal":{"tmp":"tmp/valdix_1.0.1_1772070538305_0.8502973258195496","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@denisetiya/valdix","version":"1.1.1","description":"Zero-dependency TypeScript-first schema validation with 17 built-in locales and casual end-user-ready error messages. About 1KB gzipped.","type":"module","main":"./dist/cjs/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/cjs/index.cjs"}},"sideEffects":false,"scripts":{"build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.build.json","build:cjs":"tsc -p tsconfig.cjs.json && node scripts/postbuild-cjs.mjs","clean":"node ./scripts/clean.mjs","typecheck":"tsc -p tsconfig.build.json --noEmit","test":"npm run test:esm && npm run test:cjs","test:esm":"npm run test:unit && npm run test:e2e","test:unit":"node --test tests/valdix.test.mjs tests/valdix-v2.test.mjs tests/v0.4.test.mjs tests/v0.6-locales.test.mjs tests/v0.7.test.mjs tests/function.test.mjs tests/tree-shake.test.mjs","test:cjs":"node tests/cjs.test.cjs","test:e2e":"node --test tests/e2e.test.mjs","bench":"node benchmark/run.mjs","bench:compare":"node benchmark/compare.mjs","size":"node scripts/size.mjs","docs:dev":"npm --prefix docs run dev","docs:build":"npm --prefix docs run build","docs:preview":"npm --prefix docs run preview"},"keywords":["validation","schema","typescript","zod","i18n","multilingual","zero-dependency","standard-schema"],"author":{"name":"Deni Setiya"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/denisetiya/valdix.git"},"devDependencies":{"typescript":"7.0.2"},"_id":"@denisetiya/valdix@1.1.1","gitHead":"d5abe2112bd3c70861ff189bccfcfc1a8b5fe7b6","bugs":{"url":"https://github.com/denisetiya/valdix/issues"},"homepage":"https://github.com/denisetiya/valdix#readme","_nodeVersion":"22.23.2","_npmVersion":"10.9.8","dist":{"integrity":"sha512-216V6A3A/8/KwnI4hYFrIvyk/tPE/lrwALHSuKo61AEbwTm9c2zCdhYiCLIpUFDTdIVlCqbvei5DU9CKpjdB1A==","shasum":"89cafb1cb104565338ccbb1b86fc4ffd6adf21ec","tarball":"https://registry.npmjs.org/@denisetiya/valdix/-/valdix-1.1.1.tgz","fileCount":168,"unpackedSize":621476,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCqlNKOXUZuiHbqAPN/0hxQSC4fUpQdrZ6eY5uY0/XTZQIhAP7/M6gphX2V5oeTw87THw2mwSn/uWsJFZ/LXbHrqa4X"}]},"_npmUser":{"name":"denisetiya","email":"denisetiya009@gmail.com"},"directories":{},"maintainers":[{"name":"denisetiya","email":"denisetiya009@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/valdix_1.1.1_1789352195274_0.3582338862670087"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-25T01:11:55.305Z","modified":"2026-09-14T02:16:35.589Z","1.0.0":"2026-02-25T01:11:55.608Z","1.0.1":"2026-02-26T01:48:58.444Z","1.1.1":"2026-09-14T02:16:35.430Z"},"bugs":{"url":"https://github.com/denisetiya/valdix/issues"},"author":{"name":"Deni Setiya"},"license":"MIT","homepage":"https://github.com/denisetiya/valdix#readme","keywords":["validation","schema","typescript","zod","i18n","multilingual","zero-dependency","standard-schema"],"repository":{"type":"git","url":"git+https://github.com/denisetiya/valdix.git"},"description":"Zero-dependency TypeScript-first schema validation with 17 built-in locales and casual end-user-ready error messages. About 1KB gzipped.","maintainers":[{"name":"denisetiya","email":"denisetiya009@gmail.com"}],"readme":"# Valdix\n\n> Schema validation with messages your users can read. Zero dependencies, TypeScript-first, 17 locales, about 1 KB gzipped.\n\n```ts\nimport v from \"@denisetiya/valdix\"\n\nconst Signup = v.object({\n  name: v.string().min(3),\n  email: v.string().email(),\n  age: v.number().min(18),\n})\n\ntype Signup = v.Infer<typeof Signup>\n// → { name: string; email: string; age: number }\n\n// Switch language. Form errors now read in Indonesian\nv.useLang(\"id\")\nconst r = Signup.safeParse({ name: \"\", email: \"bad\", age: 15 })\n// r.errors[0].message === \"name minimal 3 karakter\"\n// r.errors[1].message === \"Alamat email tidak valid\"\n// r.errors[2].message === \"age minimal 18\"\n```\n\n## Why Valdix?\n\n| | Zod | Valdix |\n|---|---|---|\n| TypeScript inference | yes | yes, full |\n| Zero deps | yes | yes |\n| ESM-first + CJS | partial | yes, both |\n| Multi-language | DIY | 17 locales built-in |\n| Casual, user-ready error messages | no | end-user tone |\n| Smart \"required\" behavior (`\"\"` → \"tidak boleh kosong\") | no | yes |\n| safeParse output | Complex ZodError | Simple `[{ path, message, code }]` |\n| Bundle size (gzipped) | ~13 KB | ~1 KB |\n| Locales | DIY | 17 built-in |\n\n## 17 Built-in Locales\n\n```ts\nv.useLang(\"id\")  // Bahasa Indonesia\nv.useLang(\"jp\")  // 日本語\nv.useLang(\"zh\")  // 简体中文\nv.useLang(\"zh-TW\") // 繁體中文\nv.useLang(\"ko\")  // 한국어\nv.useLang(\"fr\")  // Français\nv.useLang(\"pt\")  // Português\nv.useLang(\"nl\")  // Nederlands\nv.useLang(\"es\")  // Español\nv.useLang(\"de\")  // Deutsch\nv.useLang(\"ar\")  // العربية\nv.useLang(\"ru\")  // Русский\nv.useLang(\"it\")  // Italiano\nv.useLang(\"vi\")  // Tiếng Việt\nv.useLang(\"hi\")  // हिन्दी\nv.useLang(\"th\")  // ไทย\nv.useLang(\"en\")  // English (default)\n```\n\nPer-parse override, custom locales, and custom error maps are supported.\n\n## Smart \"required\" behavior\n\nA missing object field reports `required`. String schemas also treat empty input as missing, so forms show \"tidak boleh kosong\" instead of a type error or a min-length error:\n\n```ts\nv.useLang(\"id\")\nconst s = v.string().min(3)\n\ns.safeParse(\"\")        // → \"tidak boleh kosong\"\ns.safeParse(null)      // → \"tidak boleh kosong\"\ns.safeParse(undefined) // → \"tidak boleh kosong\"\ns.safeParse(\"hi\")      // → \"minimal 3 karakter\"\ns.safeParse(\"hello\")   // → valid\n```\n\n## API\n\n### Primitives\n\n```ts\n// String\nv.string()\nv.string().min(3).max(50).length(10)\nv.string().email().url().uuid()\nv.string().trim().lowercase().uppercase()\nv.string().startsWith(\"api_\").endsWith(\"_v1\").includes(\"foo\")\nv.string().regex(/^\\d+$/)\n\n// Character-class validators (v0.4)\nv.string().alpha()      // letters only (a-z, A-Z, Unicode)\nv.string().numeric()    // digits only (0-9)\nv.string().symbol()     // non-alphanumeric only\nv.string().phone()      // phone number\nv.string().e164()       // strict E.164 (+6281234567890)\nv.string().jwt()        // three base64url segments\nv.string().mac()        // AA:BB:CC:DD:EE:FF\nv.string().semver()     // 1.2.3 with prerelease/build\nv.string().creditCard() // Luhn check\nv.string().imei()       // 15-digit IMEI with Luhn check\nv.string().hash(\"sha256\") // hex digest (md5/sha1/sha256/sha512)\nv.string().hex()        // hex with optional 0x\nv.string().base64url()  // base64url-encoded\nv.string().lowercaseCheck() // must be lowercase\nv.string().uppercaseCheck() // must be uppercase\nv.string().normalized() // must be NFC-normalized\n\n// String formats\nv.string().ip()        // IPv4/IPv6\nv.string().ip(4)       // IPv4 only\nv.string().cidr()\nv.string().base64()\nv.string().cuid().cuid2().ulid().nanoid().emoji()\nv.string().datetime().date().time()\nv.string().nonempty()  // 1 char or more\n\n// Number\nv.number().min(0).max(100).int().positive().multipleOf(5)\nv.number().lt(50).gt(0).safe().finite()\nv.number().nan()        // accept NaN (rejected by default)\nv.number().step(5)      // multiple of 5 from 0\n\n// Other primitives\nv.boolean()\nv.date().min(new Date(\"2024-01-01\")).max(new Date(\"2024-12-31\"))\nv.bigint().min(0n).max(100n)\nv.literal(\"admin\")\nv.enum([\"a\", \"b\", \"c\"])\nv.nativeEnum(MyEnum)\nv.file().maxSize(1024 * 1024).mime(\"image/png\", /^image\\//)\nv.templateLiteral([\"Hello, \", v.string().min(1), \"!\"])\nv.custom((x): x is string => typeof x === \"string\")\n```\n\n### Objects\n\n```ts\nconst User = v.object({\n  name: v.string(),\n  age: v.number().optional(),\n  role: v.enum([\"admin\", \"user\"]).default(\"user\"),\n})\n\nUser.parse({ name: \"Eki\" })\n// → { name: \"Eki\", age: undefined, role: \"user\" }\n\n// Utilities\nUser.partial()           // all fields optional\nUser.required()          // all fields required\nUser.required([\"name\"])  // only these required\nUser.pick([\"name\"])      // { name: string }\nUser.omit([\"age\"])       // { name: string, role: ... }\nUser.strict()            // rejects unknown keys\nUser.passthrough()       // keeps unknown keys\nUser.catchall(v.string())// unknown keys validated as string\nUser.keyof()             // enum of shape keys\nUser.readonly()          // type-level Readonly<>\nUser.extend({ role: v.string() })\nUser.merge(other)\n```\n\n### Arrays & Tuples\n\n```ts\nv.array(v.string()).min(1).max(10)\nv.array(v.string()).nonempty()\nv.array(v.string()).unique()\nv.tuple(v.string(), v.number())\nv.tuple(v.string()).restItems(v.number()) // [\"a\", 1, 2]\n```\n\n### Records\n\n```ts\nv.record(v.string(), v.number())\nv.record(v.string(), v.number()).min(1).max(10)\nv.record(v.string(), v.number()).nonempty()\n```\n\n### Sets & Maps\n\n```ts\nv.set(v.string())\nv.map(v.string(), v.number())\n```\n\n### Unions & Intersections\n\n```ts\nv.union([v.string(), v.number()])\nv.string().or(v.number())   // same\n\n// Intersection: 2 or more schemas\nv.intersection(A, B)        // A & B\nv.intersection(A, B, C)     // A & B & C (variadic)\nA.and(B).and(C)             // chainable equivalent\n```\n\n### Discriminated Union\n\n```ts\nconst Event = v.discriminatedUnion(\"type\", {\n  click:    v.object({ type: v.literal(\"click\"),    x: v.number(), y: v.number() }),\n  keypress: v.object({ type: v.literal(\"keypress\"), key: v.string() }),\n})\n```\n\n### Modifiers\n\n```ts\nv.string().optional()       // string | undefined\nv.string().nullable()       // string | null\nv.string().nullish()        // string | null | undefined\nv.string().default(\"n/a\")   // undefined → \"n/a\" at parse time\nv.string().catch(\"fallback\")// on fail, return fallback\nv.string().brand(\"UserId\")  // branded type\nv.string().readonly()       // Readonly<T>\n```\n\n### Refinements & SuperRefine\n\n```ts\nv.string().refine(s => s.length > 0, \"Must be non-empty\")\n\nv.string().superRefine((val, ctx) => {\n  if (val.length < 8) ctx.addIssue({ code: \"custom\", message: \"Min 8 chars\" });\n  if (!/[A-Z]/.test(val)) ctx.addIssue({ code: \"custom\", message: \"Need uppercase\" });\n})\n\nv.string().refine(async s => await checkServer(s))  // async\n```\n\n### Transform & Pipe\n\n```ts\nconst Num = v.string().transform(s => parseInt(s))\nNum.parse(\"42\")  // → 42 (type: number)\n\nconst Slug = v.string().trim().lowercase().pipe(v.string().min(1))\n```\n\n### Coercion & Preprocess\n\n```ts\nv.coerce.string()  // input → String(input)\nv.coerce.number()  // input → Number(input)\nv.coerce.boolean()\nv.coerce.bigint()\nv.coerce.date()\n\nv.preprocess(v => JSON.parse(v), v.object({ x: v.number() }))\n```\n\n### Recursive\n\n```ts\ntype Category = { name: string; children?: Category[] }\nconst Category = v.lazy(() => v.object({\n  name: v.string(),\n  children: v.array(Category).optional(),\n}))\n```\n\n### Safe Parse Output\n\n```ts\nconst r = schema.safeParse(input)\n// r.success === true  → r.data\n// r.success === false → r.errors: [{ path, message, code, field, description }]\n// (map r.errors directly in your UI)\n```\n\n### Custom Error Messages\n\n```ts\n// Per-rule\nv.string().min(3, \"Minimal 3 karakter ya\")\nv.string().email(\"Email gak valid\")\nv.string().regex(/^\\d+$/, \"Harus angka\")\n\n// {{field}} interpolation\nv.object({\n  userName: v.string().min(3, \"{{field}} minimal 3 karakter\"),\n})\n// → \"user name minimal 3 karakter\"\n\n// Global error map\nv.setErrorMap((issue, ctx) => `[${issue.code}] ${ctx.defaultError}`)\n\n// Field-level description (UI hint)\nv.string().email().describe(\"Email address\")\n// → issue.description === \"Email address\", issue.field === \"email\"\n```\n\n### Custom Locales\n\n```ts\nv.registerLocale(\"fr\", {\n  required: () => \"Ce champ est requis\",\n  too_small: (i) => `Doit contenir au moins ${i.minimum} caractères`,\n  // ... other codes\n})\n```\n\n### JSON Schema\n\n```ts\nv.object({ name: v.string().min(1), age: v.number().int() }).toJSONSchema()\n// → { type: \"object\", properties: { name: { type: \"string\", minLength: 1 }, age: { type: \"integer\" } }, required: [\"name\", \"age\"] }\n```\n\n### Standard Schema\n\n```ts\nconst schema = v.object({ name: v.string() })\nconst std = schema[\"~standard\"]()\nconst r = std.validate({ name: \"x\" })\n// r is { value: ... } or { issues: [...] }\n```\n\n## Performance\n\nValdix is small (2.8 KB raw, 958 B gzipped; Zod is about 13 KB gzipped). On this repo's dev machine (Ryzen 5 6600H, Node 24), Valdix 0.6.0 leads Zod 3.25 on email, numbers, objects, and arrays, and ties on plain strings. See `benchmark/README.md` for the method, the scripts (`npm run bench`, `npm run bench:compare`), and the full measured tables. Re-run on your hardware before repeating any speed claim.\n\n## Setup\n\n```bash\nnpm install @denisetiya/valdix\n```\n\n## Scripts\n\n```bash\nnpm run build       # ESM + CJS\nnpm run typecheck   # tsc --noEmit\nnpm test            # unit + e2e + CJS tests\nnpm run bench       # micro-benchmark\nnpm run size        # bundle size\nnpm run docs:dev    # docs site (Astro)\nnpm run docs:build  # build docs to docs/dist\n```\n\n## Docs\n\n`docs/` is an Astro site with 6 pages: home, quick start, API, i18n, benchmark, and Zod migration. Markdown guides live in `docs/src/pages/`. Docs dependencies are independent from the library (the library stays zero-dependency).\n\n```bash\nnpm --prefix docs install  # once: install docs dependencies\nnpm run docs:dev           # local preview\nnpm run docs:build         # static output in docs/dist\n```\n\n## Release process\n\nThe package is published to npm via the Release GitHub Actions workflow. Trigger it manually from the Actions tab.\n\n### Steps\n\n1. Go to Actions → Release → Run workflow\n2. Pick the version bump:\n   - patch: bug fixes, perf (0.6.0 → 0.6.1)\n   - minor: new features, locales (0.6.0 → 0.7.0)\n   - major: breaking changes (0.6.0 → 1.0.0)\n3. Optional: add a prerelease tag (e.g. `beta`, `rc.1`)\n4. Optional: enable dry run to verify build + version bump without publishing\n5. Click Run workflow\n\nThe workflow will:\n- Run typecheck + 229 unit tests + 15 e2e tests + 8 CJS tests\n- Run the examples as a consumer smoke test\n- Build ESM + CJS + types, plus the docs site\n- Verify the new version isn't already on npm\n- Bump `package.json` and stamp a CHANGELOG entry\n- Publish to npm\n- Smoke-test the published package in a clean install\n- Commit the version bump + tag, push to `main`\n- Create a GitHub release with auto-generated notes\n\n### First-time setup\n\nAdd the `NPM_TOKEN` secret to the repo:\n\n```bash\n# Get an automation token from https://www.npmjs.com/settings/~/tokens\n# (Use \"Automation\" type for publish)\ngh secret set NPM_TOKEN\n# paste token when prompted\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}