From d2c208bb1d4482a50fea0c7ba5ba84c8fc9ac891 Mon Sep 17 00:00:00 2001 From: schplitt Date: Fri, 10 Oct 2025 23:11:29 +0200 Subject: [PATCH 01/13] chore: update package metadata for spur and add missing fields --- package.json | 23 +++- packages/spur/package.json | 27 ++++- pnpm-lock.yaml | 230 +++++++++++++++++++++++++------------ 3 files changed, 196 insertions(+), 84 deletions(-) diff --git a/package.json b/package.json index a3bd05d..23d0a82 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,23 @@ "version": "0.0.0", "packageManager": "pnpm@10.15.1", "description": "Monorepo for Spur", - "author": "", - "license": "ISC", - "keywords": [], + "author": "schplitt", + "license": "MIT", + "homepage": "https://github.com/schplitt/spur#readme", + "repository": { + "type": "git", + "url": "https://github.com/schplitt/spur.git" + }, + "bugs": { + "url": "https://github.com/schplitt/spur/issues" + }, + "keywords": [ + "spur", + "typescript", + "schema", + "validation", + "monorepo" + ], "engines": { "node": ">=20.0.0" }, @@ -16,6 +30,9 @@ "release": "bumpp -r", "prerelease": "eslint . && tsc --noEmit && vitest run" }, + "dependencies": { + "bumpp": "^10.3.1" + }, "devDependencies": { "@antfu/eslint-config": "^5.2.2", "changelogithub": "^13.16.0", diff --git a/packages/spur/package.json b/packages/spur/package.json index 33d543e..22ba9d8 100644 --- a/packages/spur/package.json +++ b/packages/spur/package.json @@ -3,10 +3,27 @@ "type": "module", "version": "0.0.0", "packageManager": "pnpm@10.15.1", - "description": "Monorepo for Spur", - "author": "", - "license": "ISC", - "keywords": [], + "description": "Lightweight TypeScript-first schema validation library with Zod-like API", + "author": "schplitt", + "license": "MIT", + "homepage": "https://github.com/schplitt/spur/tree/main/packages/spur#readme", + "repository": { + "type": "git", + "url": "https://github.com/schplitt/spur.git", + "directory": "packages/spur" + }, + "bugs": { + "url": "https://github.com/schplitt/spur/issues" + }, + "keywords": [ + "typescript", + "validation", + "schema", + "parser", + "data-validation", + "spur", + "zod-alternative" + ], "exports": { ".": { "types": "./dist/index.d.ts", @@ -28,7 +45,7 @@ "dist" ], "engines": { - "node": ">=24.0.0" + "node": ">=20.0.0" }, "scripts": { "typecheck": "vitest --typecheck", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 572f008..cf19e5c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: importers: .: + dependencies: + bumpp: + specifier: ^10.3.1 + version: 10.3.1 devDependencies: '@antfu/eslint-config': specifier: ^5.2.2 @@ -486,8 +490,8 @@ packages: '@jridgewell/trace-mapping@0.3.30': resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} - '@napi-rs/wasm-runtime@1.0.5': - resolution: {integrity: sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==} + '@napi-rs/wasm-runtime@1.0.7': + resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -501,8 +505,8 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@oxc-project/types@0.92.0': - resolution: {integrity: sha512-PDLfCbwgXjGdTBxzcuDOUxJYNBl6P8dOp3eDKWw54dYvqONan9rwGDRQU0zrkdEMiItfXQQUOI17uOcMX5Zm7A==} + '@oxc-project/types@0.94.0': + resolution: {integrity: sha512-+UgQT/4o59cZfH6Cp7G0hwmqEQ0wE+AdIwhikdwnhWI9Dp8CgSY081+Q3O67/wq3VJu8mgUEB93J9EHHn70fOw==} '@pkgr/core@0.2.9': resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} @@ -511,85 +515,85 @@ packages: '@quansync/fs@0.1.5': resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} - '@rolldown/binding-android-arm64@1.0.0-beta.40': - resolution: {integrity: sha512-9Ii9phC7QU6Lb+ncMfG1Xlosq0NBB1N/4sw+EGZ3y0BBWGy02TOb5ghWZalphAKv9rn1goqo5WkBjyd2YvsLmA==} + '@rolldown/binding-android-arm64@1.0.0-beta.42': + resolution: {integrity: sha512-W5ZKF3TP3bOWuBfotAGp+UGjxOkGV7jRmIRbBA7NFjggx7Oi6vOmGDqpHEIX7kDCiry1cnIsWQaxNvWbMdkvzQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-beta.40': - resolution: {integrity: sha512-5O6d0y2tBQTL+ecQY3qXIwSnF1/Zik8q7LZMKeyF+VJ9l194d0IdMhl2zUF0cqWbYHuF4Pnxplk4OhurPQ/Z9Q==} + '@rolldown/binding-darwin-arm64@1.0.0-beta.42': + resolution: {integrity: sha512-abw/wtgJA8OCgaTlL+xJxnN/Z01BwV1rfzIp5Hh9x+IIO6xOBfPsQ0nzi0+rWx3TyZ9FZXyC7bbC+5NpQ9EaXQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-beta.40': - resolution: {integrity: sha512-izB9jygt3miPQbOTZfSu5K51isUplqa8ysByOKQqcJHgrBWmbTU8TM9eouv6tRmBR0kjcEcID9xhmA1CeZ1VIg==} + '@rolldown/binding-darwin-x64@1.0.0-beta.42': + resolution: {integrity: sha512-Y/UrZIRVr8CvXVEB88t6PeC46r1K9/QdPEo2ASE/b/KBEyXIx+QbM6kv9QfQVWU2Atly2+SVsQzxQsIvuk3lZQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-beta.40': - resolution: {integrity: sha512-2fdpEpKT+wwP0vig9dqxu+toTeWmVSjo3psJQVDeLJ51rO+GXcCJ1IkCXjhMKVEevNtZS7B8T8Z2vvmRV9MAdA==} + '@rolldown/binding-freebsd-x64@1.0.0-beta.42': + resolution: {integrity: sha512-zRM0oOk7BZiy6DoWBvdV4hyEg+j6+WcBZIMHVirMEZRu8hd18kZdJkg+bjVMfCEhwpWeFUfBfZ1qcaZ5UdYzlQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.40': - resolution: {integrity: sha512-HP2lo78OWULN+8TewpLbS9PS00jh0CaF04tA2u8z2I+6QgVgrYOYKvX+T0hlO5smgso4+qb3YchzumWJl3yCPQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.42': + resolution: {integrity: sha512-6RjFaC52QNwo7ilU8C5H7swbGlgfTkG9pudXwzr3VYyT18s0C9gLg3mvc7OMPIGqNxnQ0M5lU8j6aQCk2DTRVg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.40': - resolution: {integrity: sha512-ng00gfr9BhA2NPAOU5RWAlTiL+JcwAD+L+4yUD1sbBy6tgHdLiNBOvKtHISIF9RM9/eQeS0tAiWOYZGIH9JMew==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.42': + resolution: {integrity: sha512-LMYHM5Sf6ROq+VUwHMDVX2IAuEsWTv4SnlFEedBnMGpvRuQ14lCmD4m5Q8sjyAQCgyha9oghdGoK8AEg1sXZKg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.40': - resolution: {integrity: sha512-mF0R1l9kLcaag/9cLEiYYdNZ4v1uuX4jklSDZ1s6vJE4RB3LirUney0FavdVRwCJ5sDvfvsPgXgtBXWYr2M2tQ==} + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.42': + resolution: {integrity: sha512-/bNTYb9aKNhzdbPn3O4MK2aLv55AlrkUKPE4KNfBYjkoZUfDr4jWp7gsSlvTc5A/99V1RCm9axvt616ZzeXGyA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.40': - resolution: {integrity: sha512-+wi08S7wT5iLPHRZb0USrS6n+T6m+yY++dePYedE5uvKIpWCJJioFTaRtWjpm0V6dVNLcq2OukrvfdlGtH9Wgg==} + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.42': + resolution: {integrity: sha512-n/SLa4h342oyeGykZdch7Y3GNCNliRPL4k5wkeZ/5eQZs+c6/ZG1SHCJQoy7bZcmxiMyaXs9HoFmv1PEKrZgWg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-musl@1.0.0-beta.40': - resolution: {integrity: sha512-W5qBGAemUocIBKCcOsDjlV9GUt28qhl/+M6etWBeLS5gQK0J6XDg0YVzfOQdvq57ZGjYNP0NvhYzqhOOnEx+4g==} + '@rolldown/binding-linux-x64-musl@1.0.0-beta.42': + resolution: {integrity: sha512-4PSd46sFzqpLHSGdaSViAb1mk55sCUMpJg+X8ittXaVocQsV3QLG/uydSH8RyL0ngHX5fy3D70LcCzlB15AgHw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-openharmony-arm64@1.0.0-beta.40': - resolution: {integrity: sha512-vJwoDehtt+yqj2zacq1AqNc2uE/oh7mnRGqAUbuldV6pgvU01OSQUJ7Zu+35hTopnjFoDNN6mIezkYlGAv5RFA==} + '@rolldown/binding-openharmony-arm64@1.0.0-beta.42': + resolution: {integrity: sha512-BmWoeJJyeZXmZBcfoxG6J9+rl2G7eO47qdTkAzEegj4n3aC6CBIHOuDcbE8BvhZaEjQR0nh0nJrtEDlt65Q7Sw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-beta.40': - resolution: {integrity: sha512-Oj3YyqVUPurr1FlMpEE/bJmMC+VWAWPM/SGUfklO5KUX97bk5Q/733nPg4RykK8q8/TluJoQYvRc05vL/B74dw==} + '@rolldown/binding-wasm32-wasi@1.0.0-beta.42': + resolution: {integrity: sha512-2Ft32F7uiDTrGZUKws6CLNTlvTWHC33l4vpXrzUucf9rYtUThAdPCOt89Pmn13tNX6AulxjGEP2R0nZjTSW3eQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.40': - resolution: {integrity: sha512-0ZtO6yN8XjVoFfN4HDWQj4nDu3ndMybr7jIM00DJqOmc+yFhly7rdOy7fNR9Sky3leCpBtsXfepVqRmVpYKPVA==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.42': + resolution: {integrity: sha512-hC1kShXW/z221eG+WzQMN06KepvPbMBknF0iGR3VMYJLOe9gwnSTfGxFT5hf8XrPv7CEZqTWRd0GQpkSHRbGsw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.40': - resolution: {integrity: sha512-BPl1inoJXPpIe38Ja46E4y11vXlJyuleo+9Rmu//pYL5fIDYJkXUj/oAXqjSuwLcssrcwnuPgzvzvlz9++cr3w==} + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.42': + resolution: {integrity: sha512-AICBYromawouGjj+GS33369E8Vwhy6UwhQEhQ5evfS8jPCsyVvoICJatbDGDGH01dwtVGLD5eDFzPicUOVpe4g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.40': - resolution: {integrity: sha512-UguA4ltbAk+nbwHRxqaUP/etpTbR0HjyNlsu4Zjbh/ytNbFsbw8CA4tEBkwDyjgI5NIPea6xY11zpl7R2/ddVA==} + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.42': + resolution: {integrity: sha512-XpZ0M+tjoEiSc9c+uZR7FCnOI0uxDRNs1elGOMjeB0pUP1QmvVbZGYNsyLbLoP4u7e3VQN8rie1OQ8/mB6rcJg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -597,8 +601,8 @@ packages: '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} - '@rolldown/pluginutils@1.0.0-beta.40': - resolution: {integrity: sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w==} + '@rolldown/pluginutils@1.0.0-beta.42': + resolution: {integrity: sha512-N7pQzk9CyE7q0bBN/q0J8s6Db279r5kUZc6d7/wWRe9/zXqC52HQovVyu6iXPIDY4BEzzgbVLhVFXrOuGJ22ZQ==} '@rollup/rollup-android-arm-eabi@4.50.0': resolution: {integrity: sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==} @@ -902,6 +906,10 @@ packages: resolution: {integrity: sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==} engines: {node: '>=14'} + ansis@4.2.0: + resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + engines: {node: '>=14'} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -913,6 +921,9 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + args-tokenizer@0.3.0: + resolution: {integrity: sha512-xXAd7G2Mll5W8uo37GETpQ2VrE84M181Z7ugHFGQnJZ50M2mbOv0osSZ9VsSgPfJQ+LVG0prSi0th+ELMsno7Q==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -953,6 +964,11 @@ packages: resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==} engines: {node: '>=18.20'} + bumpp@10.3.1: + resolution: {integrity: sha512-cOKPRFCWvHcYPJQAHN6V7Jp/wAfnyqQRXQ+2fgWIL6Gao20rpu7xQ1cGGo1APOfmbQmmHngEPg9Fy7nJ3giRkQ==} + engines: {node: '>=18'} + hasBin: true + bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -973,6 +989,14 @@ packages: magicast: optional: true + c12@3.3.0: + resolution: {integrity: sha512-K9ZkuyeJQeqLEyqldbYLG3wjqwpw4BVaAqvmxq3GYKK0b1A/yYQdIcJxkzAOWcNVWhJpRXAPfZFueekiY/L8Dw==} + peerDependencies: + magicast: ^0.3.5 + peerDependenciesMeta: + magicast: + optional: true + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1647,6 +1671,9 @@ packages: resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1962,6 +1989,9 @@ packages: perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + perfect-debounce@2.0.0: + resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2070,8 +2100,8 @@ packages: vue-tsc: optional: true - rolldown@1.0.0-beta.40: - resolution: {integrity: sha512-VqEHbKpOgTPmQrZ4fVn4eshDQS/6g/fRpNE7cFSJY+eQLDZn4B9X61J6L+hnlt1u2uRI+pF7r1USs6S5fuWCvw==} + rolldown@1.0.0-beta.42: + resolution: {integrity: sha512-xaPcckj+BbJhYLsv8gOqezc8EdMcKKe/gk8v47B0KPvgABDrQ0qmNPAiT/gh9n9Foe0bUkEv2qzj42uU5q1WRg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -2184,6 +2214,10 @@ packages: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + tinypool@1.1.1: resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} @@ -2849,7 +2883,7 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@napi-rs/wasm-runtime@1.0.5': + '@napi-rs/wasm-runtime@1.0.7': dependencies: '@emnapi/core': 1.5.0 '@emnapi/runtime': 1.5.0 @@ -2868,7 +2902,7 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@oxc-project/types@0.92.0': {} + '@oxc-project/types@0.94.0': {} '@pkgr/core@0.2.9': {} @@ -2876,53 +2910,53 @@ snapshots: dependencies: quansync: 0.2.11 - '@rolldown/binding-android-arm64@1.0.0-beta.40': + '@rolldown/binding-android-arm64@1.0.0-beta.42': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-beta.40': + '@rolldown/binding-darwin-arm64@1.0.0-beta.42': optional: true - '@rolldown/binding-darwin-x64@1.0.0-beta.40': + '@rolldown/binding-darwin-x64@1.0.0-beta.42': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.40': + '@rolldown/binding-freebsd-x64@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.40': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.40': + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.40': + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.40': + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.42': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-beta.40': + '@rolldown/binding-linux-x64-musl@1.0.0-beta.42': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-beta.40': + '@rolldown/binding-openharmony-arm64@1.0.0-beta.42': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.40': + '@rolldown/binding-wasm32-wasi@1.0.0-beta.42': dependencies: - '@napi-rs/wasm-runtime': 1.0.5 + '@napi-rs/wasm-runtime': 1.0.7 optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.40': + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.42': optional: true - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.40': + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.42': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.40': + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.42': optional: true '@rolldown/pluginutils@1.0.0-beta.27': {} - '@rolldown/pluginutils@1.0.0-beta.40': {} + '@rolldown/pluginutils@1.0.0-beta.42': {} '@rollup/rollup-android-arm-eabi@4.50.0': optional: true @@ -3260,6 +3294,8 @@ snapshots: ansis@4.1.0: {} + ansis@4.2.0: {} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 @@ -3269,6 +3305,8 @@ snapshots: argparse@2.0.1: {} + args-tokenizer@0.3.0: {} + assertion-error@2.0.1: {} ast-kit@2.1.2: @@ -3306,6 +3344,22 @@ snapshots: builtin-modules@5.0.0: {} + bumpp@10.3.1: + dependencies: + ansis: 4.2.0 + args-tokenizer: 0.3.0 + c12: 3.3.0 + cac: 6.7.14 + escalade: 3.2.0 + jsonc-parser: 3.3.1 + package-manager-detector: 1.3.0 + semver: 7.7.2 + tinyexec: 1.0.1 + tinyglobby: 0.2.15 + yaml: 2.8.1 + transitivePeerDependencies: + - magicast + bundle-name@4.1.0: dependencies: run-applescript: 7.0.0 @@ -3340,6 +3394,21 @@ snapshots: pkg-types: 2.3.0 rc9: 2.1.2 + c12@3.3.0: + dependencies: + chokidar: 4.0.3 + confbox: 0.2.2 + defu: 6.1.4 + dotenv: 17.2.2 + exsolve: 1.0.7 + giget: 2.0.0 + jiti: 2.5.1 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 2.0.0 + pkg-types: 2.3.0 + rc9: 2.1.2 + cac@6.7.14: {} callsites@3.1.0: {} @@ -4050,6 +4119,8 @@ snapshots: espree: 9.6.1 semver: 7.7.2 + jsonc-parser@3.3.1: {} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -4541,6 +4612,8 @@ snapshots: perfect-debounce@1.0.0: {} + perfect-debounce@2.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -4622,7 +4695,7 @@ snapshots: reusify@1.1.0: {} - rolldown-plugin-dts@0.15.10(rolldown@1.0.0-beta.40)(typescript@5.9.2): + rolldown-plugin-dts@0.15.10(rolldown@1.0.0-beta.42)(typescript@5.9.2): dependencies: '@babel/generator': 7.28.3 '@babel/parser': 7.28.4 @@ -4632,33 +4705,33 @@ snapshots: debug: 4.4.1 dts-resolver: 2.1.2 get-tsconfig: 4.10.1 - rolldown: 1.0.0-beta.40 + rolldown: 1.0.0-beta.42 optionalDependencies: typescript: 5.9.2 transitivePeerDependencies: - oxc-resolver - supports-color - rolldown@1.0.0-beta.40: + rolldown@1.0.0-beta.42: dependencies: - '@oxc-project/types': 0.92.0 - '@rolldown/pluginutils': 1.0.0-beta.40 - ansis: 4.1.0 + '@oxc-project/types': 0.94.0 + '@rolldown/pluginutils': 1.0.0-beta.42 + ansis: 4.2.0 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.40 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.40 - '@rolldown/binding-darwin-x64': 1.0.0-beta.40 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.40 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.40 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.40 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.40 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.40 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.40 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.40 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.40 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.40 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.40 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.40 + '@rolldown/binding-android-arm64': 1.0.0-beta.42 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.42 + '@rolldown/binding-darwin-x64': 1.0.0-beta.42 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.42 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.42 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.42 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.42 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.42 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.42 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.42 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.42 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.42 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.42 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.42 rollup@4.50.0: dependencies: @@ -4774,6 +4847,11 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + tinypool@1.1.1: {} tinyrainbow@2.0.0: {} @@ -4808,8 +4886,8 @@ snapshots: diff: 8.0.2 empathic: 2.0.0 hookable: 5.5.3 - rolldown: 1.0.0-beta.40 - rolldown-plugin-dts: 0.15.10(rolldown@1.0.0-beta.40)(typescript@5.9.2) + rolldown: 1.0.0-beta.42 + rolldown-plugin-dts: 0.15.10(rolldown@1.0.0-beta.42)(typescript@5.9.2) semver: 7.7.2 tinyexec: 1.0.1 tinyglobby: 0.2.14 From 9f23299a76fb40b20188d7f81b27f88a93cde2f6 Mon Sep 17 00:00:00 2001 From: schplitt Date: Fri, 10 Oct 2025 23:11:35 +0200 Subject: [PATCH 02/13] fix(release): reorder changelog generation and publish steps in workflow --- .github/workflows/release.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d0abae2..5b9a02b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,10 +28,6 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - run: pnpx changelogithub - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Build run: pnpm -r build @@ -41,5 +37,9 @@ jobs: - name: Update npm run: npm install -g npm@latest - - name: Publish to NPM - run: pnpm -r publish --access public --no-git-checks + - run: pnpx changelogithub + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish spur to NPM + run: pnpm -r publish --access public --no-git-checks --filter ./packages/spur From c0fd3a8240a4294d5a320654a0b32bfae5f83681 Mon Sep 17 00:00:00 2001 From: schplitt Date: Sun, 16 Nov 2025 19:08:45 +0100 Subject: [PATCH 03/13] docs: update README and package.json for clarity and new features --- README.md | 55 +++++++++++++++++++++++++++++--------- package.json | 2 +- packages/spur/README.md | 49 +++++++++++++++++++++++++++++++++ packages/spur/package.json | 10 +++++-- 4 files changed, 100 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 145dbaa..03ece07 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,24 @@ -## Spur +# Spur -Spur is a lightweight TypeScript schema validation library with a familiar, Zod-inspired API. It keeps bundle size tiny by loading constraint logic asynchronously only when a schema is built, so you get a fluent, chainable experience without paying for unused checks. +Lightweight TypeScript schema validation library with a Zod-like API that keeps bundle sizes minimal. -> Note: Spur is in early development with no official release yet. The API is not stable and may change in future builds. +> **⚠️ Development Status:** Spur is in early development with no stable release yet. The API may change in future builds. -### Why Spur -- **Tiny bundles** – constraint logic is lazy-loaded on first build, so unused checks never ship. -- **Ergonomic chains** – compose schemas with a straightforward, Zod-like API. -- **Heuristic insights** – built-in scoring highlights the most plausible schema branch and pinpoints exactly what failed, without losing data when branches overlap. +## Why Spur -### Runtime modes -- **`safeParse` (lazy)** – call `schema.safeParse(input)` and Spur will build the schema and run validation in one step. This keeps bundles minimal thanks to on-demand dynamic imports. -- **`build()` (eager)** – build the schema once to load the required checks up front, then reuse the returned evaluator for hot paths. -- **`spur/inline` (fully bundled)** – import from `spur/inline` when throughput beats bundle size; all checks ship together, so there are no dynamic imports at runtime. +- **Tiny bundles** – constraint logic lazy-loads on first build, so unused checks never ship +- **Zod-like API** – familiar, chainable schema composition +- **Heuristic insights** – built-in scoring highlights the most plausible schema branch and pinpoints failures +- **Performance options** – choose between minimal bundles or maximum throughput + +## Runtime Modes + +- **`safeParse` (lazy)** – validates with dynamic imports for minimal bundles +- **`build()` (eager)** – preload checks upfront for reuse in hot paths +- **`spur/inline`** – all checks bundled together for maximum throughput + +## Quick Start -### Quick taste ```ts import { number } from 'spur' @@ -29,4 +33,29 @@ else { } ``` -For more details, explore the `packages/spur` directory or run the playground in `packages/playground`. +## Project Structure + +This is a monorepo containing: + +- **`packages/spur`** – the main validation library +- **`packages/bench`** – performance benchmarks + +## Development + +```bash +# Install dependencies +pnpm install + +# Run tests +pnpm -r test + +# Lint +pnpm lint + +# Release spur package +pnpm release:spur +``` + +## License + +MIT diff --git a/package.json b/package.json index 23d0a82..57a424d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "spur-monorepo", "type": "module", "version": "0.0.0", - "packageManager": "pnpm@10.15.1", + "packageManager": "pnpm@10.22.0", "description": "Monorepo for Spur", "author": "schplitt", "license": "MIT", diff --git a/packages/spur/README.md b/packages/spur/README.md index e69de29..a524945 100644 --- a/packages/spur/README.md +++ b/packages/spur/README.md @@ -0,0 +1,49 @@ +# Spur + +Lightweight TypeScript schema validation library with a Zod-like API that keeps bundle sizes minimal. + +## Features + +- **Minimal runtime code** – constraint logic loads asynchronously on first build, so unused checks never ship +- **Zod-like API** – familiar, chainable schema composition +- **Inline mode** – import from `spur/inline` for performance-critical paths; all checks bundle together with no dynamic imports + +## Installation + +```bash +npm install spur +``` + +## Quick Start + +```ts +import * as s from 'spur' + +const userSchema = s.object({ + name: s.string().min(1), + age: s.number().min(0).max(130), +}) + +const report = await userSchema.safeParse(input) + +if (report.passed) { + console.log(report.value) +} +else { + console.log(report.issues) +} +``` + +## Runtime Modes + +- **`safeParse` (lazy)** – validates on-demand with dynamic imports for minimal bundles +- **`build()` (eager)** – preload checks upfront for reuse in hot paths +- **`spur/inline`** – all checks bundled together for maximum throughput + +## Development Status + +⚠️ **Spur is in active development with no stable release yet. Expect breaking changes.** + +## License + +MIT diff --git a/packages/spur/package.json b/packages/spur/package.json index 22ba9d8..3e9dbf7 100644 --- a/packages/spur/package.json +++ b/packages/spur/package.json @@ -22,7 +22,10 @@ "parser", "data-validation", "spur", - "zod-alternative" + "zod-alternative", + "valibot-alternative", + "lightweight", + "arktype-alternative" ], "exports": { ".": { @@ -42,6 +45,8 @@ "module": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ + "LICENSE", + "README.md", "dist" ], "engines": { @@ -50,7 +55,8 @@ "scripts": { "typecheck": "vitest --typecheck", "test": "vitest run --typecheck", - "build": "tsdown" + "build": "tsdown", + "lint": "eslint ." }, "dependencies": { "@standard-schema/spec": "^1.0.0" From 10ddd1b1181e86b0f11be62cce09992bb339b4e5 Mon Sep 17 00:00:00 2001 From: schplitt Date: Sun, 16 Nov 2025 19:08:58 +0100 Subject: [PATCH 04/13] chore: add MIT License file --- packages/spur/LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 packages/spur/LICENSE diff --git a/packages/spur/LICENSE b/packages/spur/LICENSE new file mode 100644 index 0000000..30cc34a --- /dev/null +++ b/packages/spur/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 schplitt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file From 6f3978630bb3d36ea460f866b4231b995c626410 Mon Sep 17 00:00:00 2001 From: schplitt Date: Sun, 16 Nov 2025 19:09:03 +0100 Subject: [PATCH 05/13] chore: update release workflow for npm registry and changelog generation --- .github/workflows/release.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5b9a02b..fa56a03 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,6 +24,7 @@ jobs: uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 24 + registry-url: 'https://registry.npmjs.org' - name: Install dependencies run: pnpm install --frozen-lockfile @@ -37,9 +38,11 @@ jobs: - name: Update npm run: npm install -g npm@latest - - run: pnpx changelogithub + - run: pnpm dlx changelogithub env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Publish spur to NPM - run: pnpm -r publish --access public --no-git-checks --filter ./packages/spur + run: pnpm --filter ./packages/spur publish --access public --no-git-checks + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From a88f63e33f4cb4c7fa686d80facba45cf3211cf5 Mon Sep 17 00:00:00 2001 From: schplitt Date: Sun, 16 Nov 2025 19:09:12 +0100 Subject: [PATCH 06/13] lockfile --- pnpm-lock.yaml | 147 +++++++++++++++++++++++++------------------------ 1 file changed, 76 insertions(+), 71 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1865b1f..0974701 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -427,8 +427,8 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@oxc-project/types@0.95.0': - resolution: {integrity: sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ==} + '@oxc-project/types@0.97.0': + resolution: {integrity: sha512-lxmZK4xFrdvU0yZiDwgVQTCvh2gHWBJCBk5ALsrtsBWhs0uDIi+FTOnXRQeQfs304imdvTdaakT/lqwQ8hkOXQ==} '@pkgr/core@0.2.9': resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} @@ -437,91 +437,91 @@ packages: '@quansync/fs@0.1.5': resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} - '@rolldown/binding-android-arm64@1.0.0-beta.44': - resolution: {integrity: sha512-g9ejDOehJFhxC1DIXQuZQ9bKv4lRDioOTL42cJjFjqKPl1L7DVb9QQQE1FxokGEIMr6FezLipxwnzOXWe7DNPg==} + '@rolldown/binding-android-arm64@1.0.0-beta.50': + resolution: {integrity: sha512-XlEkrOIHLyGT3avOgzfTFSjG+f+dZMw+/qd+Y3HLN86wlndrB/gSimrJCk4gOhr1XtRtEKfszpadI3Md4Z4/Ag==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-beta.44': - resolution: {integrity: sha512-PxAW1PXLPmCzfhfKIS53kwpjLGTUdIfX4Ht+l9mj05C3lYCGaGowcNsYi2rdxWH24vSTmeK+ajDNRmmmrK0M7g==} + '@rolldown/binding-darwin-arm64@1.0.0-beta.50': + resolution: {integrity: sha512-+JRqKJhoFlt5r9q+DecAGPLZ5PxeLva+wCMtAuoFMWPoZzgcYrr599KQ+Ix0jwll4B4HGP43avu9My8KtSOR+w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-beta.44': - resolution: {integrity: sha512-/CtQqs1oO9uSb5Ju60rZvsdjE7Pzn8EK2ISAdl2jedjMzeD/4neNyCbwyJOAPzU+GIQTZVyrFZJX+t7HXR1R/g==} + '@rolldown/binding-darwin-x64@1.0.0-beta.50': + resolution: {integrity: sha512-fFXDjXnuX7/gQZQm/1FoivVtRcyAzdjSik7Eo+9iwPQ9EgtA5/nB2+jmbzaKtMGG3q+BnZbdKHCtOacmNrkIDA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-beta.44': - resolution: {integrity: sha512-V5Q5W9c4+2GJ4QabmjmVV6alY97zhC/MZBaLkDtHwGy3qwzbM4DYgXUbun/0a8AH5hGhuU27tUIlYz6ZBlvgOA==} + '@rolldown/binding-freebsd-x64@1.0.0-beta.50': + resolution: {integrity: sha512-F1b6vARy49tjmT/hbloplzgJS7GIvwWZqt+tAHEstCh0JIh9sa8FAMVqEmYxDviqKBaAI8iVvUREm/Kh/PD26Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.44': - resolution: {integrity: sha512-X6adjkHeFqKsTU0FXdNN9HY4LDozPqIfHcnXovE5RkYLWIjMWuc489mIZ6iyhrMbCqMUla9IOsh5dvXSGT9o9A==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.50': + resolution: {integrity: sha512-U6cR76N8T8M6lHj7EZrQ3xunLPxSvYYxA8vJsBKZiFZkT8YV4kjgCO3KwMJL0NOjQCPGKyiXO07U+KmJzdPGRw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.44': - resolution: {integrity: sha512-kRRKGZI4DXWa6ANFr3dLA85aSVkwPdgXaRjfanwY84tfc3LncDiIjyWCb042e3ckPzYhHSZ3LmisO+cdOIYL6Q==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.50': + resolution: {integrity: sha512-ONgyjofCrrE3bnh5GZb8EINSFyR/hmwTzZ7oVuyUB170lboza1VMCnb8jgE6MsyyRgHYmN8Lb59i3NKGrxrYjw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.44': - resolution: {integrity: sha512-hMtiN9xX1NhxXBa2U3Up4XkVcsVp2h73yYtMDY59z9CDLEZLrik9RVLhBL5QtoX4zZKJ8HZKJtWuGYvtmkCbIQ==} + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.50': + resolution: {integrity: sha512-L0zRdH2oDPkmB+wvuTl+dJbXCsx62SkqcEqdM+79LOcB+PxbAxxjzHU14BuZIQdXcAVDzfpMfaHWzZuwhhBTcw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.44': - resolution: {integrity: sha512-rd1LzbpXQuR8MTG43JB9VyXDjG7ogSJbIkBpZEHJ8oMKzL6j47kQT5BpIXrg3b5UVygW9QCI2fpFdMocT5Kudg==} + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.50': + resolution: {integrity: sha512-gyoI8o/TGpQd3OzkJnh1M2kxy1Bisg8qJ5Gci0sXm9yLFzEXIFdtc4EAzepxGvrT2ri99ar5rdsmNG0zP0SbIg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-musl@1.0.0-beta.44': - resolution: {integrity: sha512-qI2IiPqmPRW25exXkuQr3TlweCDc05YvvbSDRPCuPsWkwb70dTiSoXn8iFxT4PWqTi71wWHg1Wyta9PlVhX5VA==} + '@rolldown/binding-linux-x64-musl@1.0.0-beta.50': + resolution: {integrity: sha512-zti8A7M+xFDpKlghpcCAzyOi+e5nfUl3QhU023ce5NCgUxRG5zGP2GR9LTydQ1rnIPwZUVBWd4o7NjZDaQxaXA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - '@rolldown/binding-openharmony-arm64@1.0.0-beta.44': - resolution: {integrity: sha512-+vHvEc1pL5iJRFlldLC8mjm6P4Qciyfh2bh5ZI6yxDQKbYhCHRKNURaKz1mFcwxhVL5YMYsLyaqM3qizVif9MQ==} + '@rolldown/binding-openharmony-arm64@1.0.0-beta.50': + resolution: {integrity: sha512-eZUssog7qljrrRU9Mi0eqYEPm3Ch0UwB+qlWPMKSUXHNqhm3TvDZarJQdTevGEfu3EHAXJvBIe0YFYr0TPVaMA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-beta.44': - resolution: {integrity: sha512-XSgLxRrtFj6RpTeMYmmQDAwHjKseYGKUn5LPiIdW4Cq+f5SBSStL2ToBDxkbdxKPEbCZptnLPQ/nfKcAxrC8Xg==} + '@rolldown/binding-wasm32-wasi@1.0.0-beta.50': + resolution: {integrity: sha512-nmCN0nIdeUnmgeDXiQ+2HU6FT162o+rxnF7WMkBm4M5Ds8qTU7Dzv2Wrf22bo4ftnlrb2hKK6FSwAJSAe2FWLg==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.44': - resolution: {integrity: sha512-cF1LJdDIX02cJrFrX3wwQ6IzFM7I74BYeKFkzdcIA4QZ0+2WA7/NsKIgjvrunupepWb1Y6PFWdRlHSaz5AW1Wg==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.50': + resolution: {integrity: sha512-7kcNLi7Ua59JTTLvbe1dYb028QEPaJPJQHqkmSZ5q3tJueUeb6yjRtx8mw4uIqgWZcnQHAR3PrLN4XRJxvgIkA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.44': - resolution: {integrity: sha512-5uaJonDafhHiMn+iEh7qUp3QQ4Gihv3lEOxKfN8Vwadpy0e+5o28DWI42DpJ9YBYMrVy4JOWJ/3etB/sptpUwA==} + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.50': + resolution: {integrity: sha512-lL70VTNvSCdSZkDPPVMwWn/M2yQiYvSoXw9hTLgdIWdUfC3g72UaruezusR6ceRuwHCY1Ayu2LtKqXkBO5LIwg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.44': - resolution: {integrity: sha512-vsqhWAFJkkmgfBN/lkLCWTXF1PuPhMjfnAyru48KvF7mVh2+K7WkKYHezF3Fjz4X/mPScOcIv+g6cf6wnI6eWg==} + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.50': + resolution: {integrity: sha512-4qU4x5DXWB4JPjyTne/wBNPqkbQU8J45bl21geERBKtEittleonioACBL1R0PsBu0Aq21SwMK5a9zdBkWSlQtQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-beta.44': - resolution: {integrity: sha512-g6eW7Zwnr2c5RADIoqziHoVs6b3W5QTQ4+qbpfjbkMJ9x+8Og211VW/oot2dj9dVwaK/UyC6Yo+02gV+wWQVNg==} + '@rolldown/pluginutils@1.0.0-beta.50': + resolution: {integrity: sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==} '@rollup/rollup-android-arm-eabi@4.50.0': resolution: {integrity: sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==} @@ -822,6 +822,9 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + args-tokenizer@0.3.0: + resolution: {integrity: sha512-xXAd7G2Mll5W8uo37GETpQ2VrE84M181Z7ugHFGQnJZ50M2mbOv0osSZ9VsSgPfJQ+LVG0prSi0th+ELMsno7Q==} + arktype@2.1.23: resolution: {integrity: sha512-tyxNWX6xJVMb2EPJJ3OjgQS1G/vIeQRrZuY4DeBNQmh8n7geS+czgbauQWB6Pr+RXiOO8ChEey44XdmxsqGmfQ==} @@ -1979,8 +1982,8 @@ packages: vue-tsc: optional: true - rolldown@1.0.0-beta.44: - resolution: {integrity: sha512-gcqgyCi3g93Fhr49PKvymE8PoaGS0sf6ajQrsYaQ8o5de6aUEbD6rJZiJbhOfpcqOnycgsAsUNPYri1h25NgsQ==} + rolldown@1.0.0-beta.50: + resolution: {integrity: sha512-JFULvCNl/anKn99eKjOSEubi0lLmNqQDAjyEMME2T4CwezUDL0i6t1O9xZsu2OMehPnV2caNefWpGF+8TnzB6A==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -2688,7 +2691,7 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@oxc-project/types@0.95.0': {} + '@oxc-project/types@0.97.0': {} '@pkgr/core@0.2.9': {} @@ -2696,51 +2699,51 @@ snapshots: dependencies: quansync: 0.2.11 - '@rolldown/binding-android-arm64@1.0.0-beta.44': + '@rolldown/binding-android-arm64@1.0.0-beta.50': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-beta.44': + '@rolldown/binding-darwin-arm64@1.0.0-beta.50': optional: true - '@rolldown/binding-darwin-x64@1.0.0-beta.44': + '@rolldown/binding-darwin-x64@1.0.0-beta.50': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.44': + '@rolldown/binding-freebsd-x64@1.0.0-beta.50': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.44': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.50': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.44': + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.50': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.44': + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.50': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.44': + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.50': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-beta.44': + '@rolldown/binding-linux-x64-musl@1.0.0-beta.50': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-beta.44': + '@rolldown/binding-openharmony-arm64@1.0.0-beta.50': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.44': + '@rolldown/binding-wasm32-wasi@1.0.0-beta.50': dependencies: '@napi-rs/wasm-runtime': 1.0.7 optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.44': + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.50': optional: true - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.44': + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.50': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.44': + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.50': optional: true - '@rolldown/pluginutils@1.0.0-beta.44': {} + '@rolldown/pluginutils@1.0.0-beta.50': {} '@rollup/rollup-android-arm-eabi@4.50.0': optional: true @@ -3056,6 +3059,8 @@ snapshots: argparse@2.0.1: {} + args-tokenizer@0.3.0: {} + arktype@2.1.23: dependencies: '@ark/regex': 0.0.0 @@ -4436,7 +4441,7 @@ snapshots: reusify@1.1.0: {} - rolldown-plugin-dts@0.15.10(rolldown@1.0.0-beta.44)(typescript@5.9.2): + rolldown-plugin-dts@0.15.10(rolldown@1.0.0-beta.50)(typescript@5.9.2): dependencies: '@babel/generator': 7.28.3 '@babel/parser': 7.28.4 @@ -4446,32 +4451,32 @@ snapshots: debug: 4.4.1 dts-resolver: 2.1.2 get-tsconfig: 4.10.1 - rolldown: 1.0.0-beta.44 + rolldown: 1.0.0-beta.50 optionalDependencies: typescript: 5.9.2 transitivePeerDependencies: - oxc-resolver - supports-color - rolldown@1.0.0-beta.44: + rolldown@1.0.0-beta.50: dependencies: - '@oxc-project/types': 0.95.0 - '@rolldown/pluginutils': 1.0.0-beta.44 + '@oxc-project/types': 0.97.0 + '@rolldown/pluginutils': 1.0.0-beta.50 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.44 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.44 - '@rolldown/binding-darwin-x64': 1.0.0-beta.44 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.44 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.44 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.44 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.44 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.44 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.44 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.44 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.44 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.44 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.44 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.44 + '@rolldown/binding-android-arm64': 1.0.0-beta.50 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.50 + '@rolldown/binding-darwin-x64': 1.0.0-beta.50 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.50 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.50 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.50 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.50 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.50 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.50 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.50 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.50 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.50 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.50 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.50 rollup@4.50.0: dependencies: @@ -4624,8 +4629,8 @@ snapshots: diff: 8.0.2 empathic: 2.0.0 hookable: 5.5.3 - rolldown: 1.0.0-beta.44 - rolldown-plugin-dts: 0.15.10(rolldown@1.0.0-beta.44)(typescript@5.9.2) + rolldown: 1.0.0-beta.50 + rolldown-plugin-dts: 0.15.10(rolldown@1.0.0-beta.50)(typescript@5.9.2) semver: 7.7.2 tinyexec: 1.0.1 tinyglobby: 0.2.14 From ec3687c5f6062f0f7dfe7199174f515ad313299b Mon Sep 17 00:00:00 2001 From: schplitt Date: Sun, 16 Nov 2025 19:23:49 +0100 Subject: [PATCH 07/13] chore: renamed spur to narro --- .github/copilot-instructions.md | 2 +- .github/workflows/release.yml | 4 +- Pitfalls.md | 26 ++++++++++ README.md | 16 +++--- package.json | 12 ++--- packages/bench/README.md | 16 +++--- packages/bench/bench/complex-backend.bench.ts | 24 ++++----- .../bench/bench/complex-frontend.bench.ts | 24 ++++----- packages/bench/format-results.mjs | 2 +- packages/bench/package.json | 6 +-- packages/spur/README.md | 12 ++--- packages/spur/package.json | 12 ++--- .../src/__tests__/leitplanken/array.test.ts | 4 +- .../src/__tests__/leitplanken/literal.test.ts | 6 +-- .../src/__tests__/leitplanken/object.test.ts | 50 +++++++++---------- .../src/__tests__/leitplanken/string.test.ts | 10 ++-- .../src/__tests__/leitplanken/union.test.ts | 8 +-- pnpm-lock.yaml | 6 +-- 18 files changed, 133 insertions(+), 107 deletions(-) create mode 100644 Pitfalls.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index e416f9b..fa169be 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,4 +1,4 @@ -# Spur Copilot Instructions +# Narro Copilot Instructions ## Rules diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fa56a03..ecb7ae0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,7 +42,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Publish spur to NPM - run: pnpm --filter ./packages/spur publish --access public --no-git-checks + - name: Publish narro to NPM + run: pnpm --filter ./packages/narro publish --access public --no-git-checks env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/Pitfalls.md b/Pitfalls.md new file mode 100644 index 0000000..adac637 --- /dev/null +++ b/Pitfalls.md @@ -0,0 +1,26 @@ +# Typescript Pitfalls + +Here are some of the typescript pitfalls i have encountered while working on Narro. +This would make a good blog post. + +## 1. Type bailouts + +Typescript will sometimes bail out on types if they are actually never used. +If we have a generic that passes its generic to another generic. AND we try to later infer that type, but have not used it inside an object or so internally, typescript will bail out and just use the default type. + +For this reason there is the `~types` property where we "store" the type information. At runtime this will never be used, this is only to hint typescript to not bail out on those types (specifically options as input and output are always used). + +## 2. Unexpected type widening + +Typescript will try to infer generic types based on the usage. This can lead to types having inferred (generic) types having the types inserted from generic used above. +I encountered this with the ObjectEntries where each entry was a BuildableSchema. This unknown is then passed to to the actual Schema used. +So a StringSchema constructed via `string()` would become StringSchema instead of StringSchema. + +To fix this we need to give string() an overload signature with NO generics where we just return StringSchema. +This way typescript will first try to match the non-generic signature and only if we actually pass generics it will use the generic signature. + +### AI + +ChaGPT Codex model was used to help development. What is very supprising is that even complex typescript behaiviours would be awnsered correctly,e even some i didnt know. +It correclty helped point out errors and fix complex type s that was previoulsly never possible. It could also go very hendands of nd develop schaemas correctly on its own. very intriguing and scary. +Though it still needed all the right points of what was necessary. leting it go off on its won never really resulted ion anything useful diff --git a/README.md b/README.md index 03ece07..0a26f76 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Spur +# Narro Lightweight TypeScript schema validation library with a Zod-like API that keeps bundle sizes minimal. -> **⚠️ Development Status:** Spur is in early development with no stable release yet. The API may change in future builds. +> **⚠️ Development Status:** Narro is in early development with no stable release yet. The API may change in future builds. -## Why Spur +## Why Narro - **Tiny bundles** – constraint logic lazy-loads on first build, so unused checks never ship - **Zod-like API** – familiar, chainable schema composition @@ -15,12 +15,12 @@ Lightweight TypeScript schema validation library with a Zod-like API that keeps - **`safeParse` (lazy)** – validates with dynamic imports for minimal bundles - **`build()` (eager)** – preload checks upfront for reuse in hot paths -- **`spur/inline`** – all checks bundled together for maximum throughput +- **`narro/inline`** – all checks bundled together for maximum throughput ## Quick Start ```ts -import { number } from 'spur' +import { number } from 'narro' const ageSchema = number().min(0).max(130) const report = await ageSchema.safeParse(input) @@ -37,7 +37,7 @@ else { This is a monorepo containing: -- **`packages/spur`** – the main validation library +- **`packages/narro`** – the main validation library - **`packages/bench`** – performance benchmarks ## Development @@ -52,8 +52,8 @@ pnpm -r test # Lint pnpm lint -# Release spur package -pnpm release:spur +# Release narro package +pnpm release:narro ``` ## License diff --git a/package.json b/package.json index 57a424d..1ae65a1 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,21 @@ { - "name": "spur-monorepo", + "name": "narro-monorepo", "type": "module", "version": "0.0.0", "packageManager": "pnpm@10.22.0", - "description": "Monorepo for Spur", + "description": "Monorepo for Narro", "author": "schplitt", "license": "MIT", - "homepage": "https://github.com/schplitt/spur#readme", + "homepage": "https://github.com/schplitt/narro#readme", "repository": { "type": "git", - "url": "https://github.com/schplitt/spur.git" + "url": "https://github.com/schplitt/narro.git" }, "bugs": { - "url": "https://github.com/schplitt/spur/issues" + "url": "https://github.com/schplitt/narro/issues" }, "keywords": [ - "spur", + "narro", "typescript", "schema", "validation", diff --git a/packages/bench/README.md b/packages/bench/README.md index 132e32e..f10907d 100644 --- a/packages/bench/README.md +++ b/packages/bench/README.md @@ -1,6 +1,6 @@ -# Spur Benchmarks +# Narro Benchmarks -Performance benchmarks comparing Spur with other popular validation libraries: Zod, Valibot, and ArkType. +Performance benchmarks comparing Narro with other popular validation libraries: Zod, Valibot, and ArkType. ## Benchmark Suites @@ -32,13 +32,13 @@ pnpm bench ## Benchmark Variants -Each suite tests 4 Spur configurations + 3 competitors: +Each suite tests 4 Narro configurations + 3 competitors: -### Spur Variants -1. **spur unbuild async** - Schema built on each validation call -2. **spur async** - Pre-built schema (recommended for production) -3. **spur inline unbuild** - Inline export, built on each call -4. **spur inline** - Inline export, pre-built +### Narro Variants +1. **narro unbuild async** - Schema built on each validation call +2. **narro async** - Pre-built schema (recommended for production) +3. **narro inline unbuild** - Inline export, built on each call +4. **narro inline** - Inline export, pre-built ### Competitors - **zod** - Synchronous validation diff --git a/packages/bench/bench/complex-backend.bench.ts b/packages/bench/bench/complex-backend.bench.ts index 9303c77..bb24eb8 100644 --- a/packages/bench/bench/complex-backend.bench.ts +++ b/packages/bench/bench/complex-backend.bench.ts @@ -1,6 +1,6 @@ import { type } from 'arktype' -import { array, literal, number, object, string, union } from 'spur' -import { array as arrayInline, literal as literalInline, number as numberInline, object as objectInline, string as stringInline, union as unionInline } from 'spur/inline' +import { array, literal, number, object, string, union } from 'narro' +import { array as arrayInline, literal as literalInline, number as numberInline, object as objectInline, string as stringInline, union as unionInline } from 'narro/inline' import * as v from 'valibot' import { bench, describe } from 'vitest' import { z } from 'zod' @@ -105,7 +105,7 @@ const invalidOrder = { tags: ['priority', 'gift-wrap'], } -// Spur async schema +// Narro async schema const spurAsyncUnbuild = object({ customer: object({ id: string().minLength(10), @@ -194,7 +194,7 @@ const spurInlineUnbuild = objectInline({ tags: arrayInline(stringInline()).optional(), }) -// Spur inline schema +// Narro inline schema const spurInlineBuilt = await objectInline({ customer: objectInline({ id: stringInline().minLength(10), @@ -374,16 +374,16 @@ const arkTypeSchema = type({ }) describe('complex backend: valid parse', () => { - bench('spur unbuild async valid', async () => { + bench('narro unbuild async valid', async () => { await spurAsyncUnbuild.safeParse(validOrder) }) - bench('spur async valid', () => { + bench('narro async valid', () => { spurAsyncBuilt.safeParse(validOrder) }) - bench('spur inline unbuild valid', async () => { + bench('narro inline unbuild valid', async () => { await spurInlineUnbuild.safeParse(validOrder) }) - bench('spur inline valid', () => { + bench('narro inline valid', () => { spurInlineBuilt.safeParse(validOrder) }) bench('zod valid', () => { @@ -398,16 +398,16 @@ describe('complex backend: valid parse', () => { }) describe('complex backend: invalid parse', () => { - bench('spur unbuild async invalid', async () => { + bench('narro unbuild async invalid', async () => { await spurAsyncUnbuild.safeParse(invalidOrder) }) - bench('spur async invalid', () => { + bench('narro async invalid', () => { spurAsyncBuilt.safeParse(invalidOrder) }) - bench('spur inline unbuild invalid', async () => { + bench('narro inline unbuild invalid', async () => { await spurInlineUnbuild.safeParse(invalidOrder) }) - bench('spur inline invalid', () => { + bench('narro inline invalid', () => { spurInlineBuilt.safeParse(invalidOrder) }) bench('zod invalid', () => { diff --git a/packages/bench/bench/complex-frontend.bench.ts b/packages/bench/bench/complex-frontend.bench.ts index befd5d0..f668ca5 100644 --- a/packages/bench/bench/complex-frontend.bench.ts +++ b/packages/bench/bench/complex-frontend.bench.ts @@ -1,7 +1,7 @@ import { type } from 'arktype' -import { array, boolean, literal, object, string, union } from 'spur' +import { array, boolean, literal, object, string, union } from 'narro' -import { array as arrayInline, boolean as booleanInline, literal as literalInline, object as objectInline, string as stringInline, union as unionInline } from 'spur/inline' +import { array as arrayInline, boolean as booleanInline, literal as literalInline, object as objectInline, string as stringInline, union as unionInline } from 'narro/inline' import * as v from 'valibot' import { bench, describe } from 'vitest' import { z } from 'zod' @@ -87,7 +87,7 @@ const invalidFormData = { referralCode: null, } -// Spur async schema +// Narro async schema const spurAsyncUnbuild = object({ account: object({ username: string().minLength(3).maxLength(30), @@ -208,7 +208,7 @@ const spurInlineUnbuild = objectInline({ referralCode: stringInline().nullable(), }) -// Spur inline schema +// Narro inline schema const spurInlineBuilt = await objectInline({ account: objectInline({ username: stringInline().minLength(3).maxLength(30), @@ -374,17 +374,17 @@ const arkTypeSchema = type({ }) describe('complex frontend: valid parse', () => { - bench('spur async unbuild valid', async () => { + bench('narro async unbuild valid', async () => { await spurAsyncUnbuild.safeParse(validFormData) }) - bench('spur async valid', () => { + bench('narro async valid', () => { spurAsyncBuilt.safeParse(validFormData) }) - bench('spur inline unbuild valid', async () => { + bench('narro inline unbuild valid', async () => { await spurInlineUnbuild.safeParse(validFormData) }) - bench('spur inline valid', () => { + bench('narro inline valid', () => { spurInlineBuilt.safeParse(validFormData) }) bench('zod valid', () => { @@ -399,16 +399,16 @@ describe('complex frontend: valid parse', () => { }) describe('complex frontend: invalid parse', () => { - bench('spur unbuild async invalid', async () => { + bench('narro unbuild async invalid', async () => { await spurAsyncUnbuild.safeParse(invalidFormData) }) - bench('spur async invalid', () => { + bench('narro async invalid', () => { spurAsyncBuilt.safeParse(invalidFormData) }) - bench('spur inline unbuild invalid', async () => { + bench('narro inline unbuild invalid', async () => { await spurInlineUnbuild.safeParse(invalidFormData) }) - bench('spur inline invalid', () => { + bench('narro inline invalid', () => { spurInlineBuilt.safeParse(invalidFormData) }) bench('zod invalid', () => { diff --git a/packages/bench/format-results.mjs b/packages/bench/format-results.mjs index 410af26..5b106c1 100644 --- a/packages/bench/format-results.mjs +++ b/packages/bench/format-results.mjs @@ -34,7 +34,7 @@ try { } } - let markdown = '_Performance comparison of Spur vs Zod, Valibot, and ArkType_\n\n' + let markdown = '_Performance comparison of Narro vs Zod, Valibot, and ArkType_\n\n' // Build lookup for main results const mainMap = new Map() diff --git a/packages/bench/package.json b/packages/bench/package.json index c868e56..02e7b9b 100644 --- a/packages/bench/package.json +++ b/packages/bench/package.json @@ -3,12 +3,12 @@ "type": "module", "version": "0.0.0", "packageManager": "pnpm@10.15.1", - "description": "Benchmark suite comparing Spur with Zod and Valibot", + "description": "Benchmark suite comparing Narro with Zod and Valibot", "author": "schplitt", "license": "MIT", "keywords": [ "benchmark", - "spur", + "narro", "zod", "valibot" ], @@ -19,7 +19,7 @@ }, "dependencies": { "arktype": "^2.1.23", - "spur": "workspace:*", + "narro": "workspace:*", "valibot": "^1.1.0", "zod": "^4.1.11" }, diff --git a/packages/spur/README.md b/packages/spur/README.md index a524945..e4ff986 100644 --- a/packages/spur/README.md +++ b/packages/spur/README.md @@ -1,4 +1,4 @@ -# Spur +# Narro Lightweight TypeScript schema validation library with a Zod-like API that keeps bundle sizes minimal. @@ -6,18 +6,18 @@ Lightweight TypeScript schema validation library with a Zod-like API that keeps - **Minimal runtime code** – constraint logic loads asynchronously on first build, so unused checks never ship - **Zod-like API** – familiar, chainable schema composition -- **Inline mode** – import from `spur/inline` for performance-critical paths; all checks bundle together with no dynamic imports +- **Inline mode** – import from `narro/inline` for performance-critical paths; all checks bundle together with no dynamic imports ## Installation ```bash -npm install spur +npm install narro ``` ## Quick Start ```ts -import * as s from 'spur' +import * as s from 'narro' const userSchema = s.object({ name: s.string().min(1), @@ -38,11 +38,11 @@ else { - **`safeParse` (lazy)** – validates on-demand with dynamic imports for minimal bundles - **`build()` (eager)** – preload checks upfront for reuse in hot paths -- **`spur/inline`** – all checks bundled together for maximum throughput +- **`narro/inline`** – all checks bundled together for maximum throughput ## Development Status -⚠️ **Spur is in active development with no stable release yet. Expect breaking changes.** +⚠️ **Narro is in active development with no stable release yet. Expect breaking changes.** ## License diff --git a/packages/spur/package.json b/packages/spur/package.json index 3e9dbf7..1a6b4b6 100644 --- a/packages/spur/package.json +++ b/packages/spur/package.json @@ -1,19 +1,19 @@ { - "name": "spur", + "name": "narro", "type": "module", "version": "0.0.0", "packageManager": "pnpm@10.15.1", "description": "Lightweight TypeScript-first schema validation library with Zod-like API", "author": "schplitt", "license": "MIT", - "homepage": "https://github.com/schplitt/spur/tree/main/packages/spur#readme", + "homepage": "https://github.com/schplitt/narro/tree/main/packages/narro#readme", "repository": { "type": "git", - "url": "https://github.com/schplitt/spur.git", - "directory": "packages/spur" + "url": "https://github.com/schplitt/narro.git", + "directory": "packages/narro" }, "bugs": { - "url": "https://github.com/schplitt/spur/issues" + "url": "https://github.com/schplitt/narro/issues" }, "keywords": [ "typescript", @@ -21,7 +21,7 @@ "schema", "parser", "data-validation", - "spur", + "narro", "zod-alternative", "valibot-alternative", "lightweight", diff --git a/packages/spur/src/__tests__/leitplanken/array.test.ts b/packages/spur/src/__tests__/leitplanken/array.test.ts index 17d0585..8cd0f0f 100644 --- a/packages/spur/src/__tests__/leitplanken/array.test.ts +++ b/packages/spur/src/__tests__/leitplanken/array.test.ts @@ -73,9 +73,9 @@ describe('array schema', () => { expect(undefinedReport.success).toBe(true) expect(undefinedReport.data).toBeUndefined() - const definedReport = await schema.safeParse(['spur']) + const definedReport = await schema.safeParse(['narro']) expect(definedReport.success).toBe(true) - expect(definedReport.data).toEqual(['spur']) + expect(definedReport.data).toEqual(['narro']) }) it('supports nullable modifier', async () => { diff --git a/packages/spur/src/__tests__/leitplanken/literal.test.ts b/packages/spur/src/__tests__/leitplanken/literal.test.ts index 060ff05..93c2f87 100644 --- a/packages/spur/src/__tests__/leitplanken/literal.test.ts +++ b/packages/spur/src/__tests__/leitplanken/literal.test.ts @@ -4,13 +4,13 @@ import { literal } from '../../index' describe('literal schema', () => { it('accepts matching string literal', async () => { - const schema = literal('spur') + const schema = literal('narro') - await expect(schema.parse('spur')).resolves.toBe('spur') + await expect(schema.parse('narro')).resolves.toBe('narro') }) it('rejects non matching values', async () => { - const schema = literal('spur') + const schema = literal('narro') const report = await schema.safeParse('other') expect(report.success).toBe(false) diff --git a/packages/spur/src/__tests__/leitplanken/object.test.ts b/packages/spur/src/__tests__/leitplanken/object.test.ts index 722629a..b07cf34 100644 --- a/packages/spur/src/__tests__/leitplanken/object.test.ts +++ b/packages/spur/src/__tests__/leitplanken/object.test.ts @@ -33,8 +33,8 @@ describe('object schema', () => { name: string(), }).optional() - const value = await schema.parse({ name: 'Spur' }) - expect(value).toEqual({ name: 'Spur' }) + const value = await schema.parse({ name: 'Narro' }) + expect(value).toEqual({ name: 'Narro' }) const optionalResult = await schema.safeParse(undefined) expect(optionalResult.success).toBe(true) @@ -139,8 +139,8 @@ describe('object schema', () => { nickname: string().optional(), }) - await expect(schema.parse({ name: 'Spur' })).resolves.toEqual({ name: 'Spur' }) - await expect(schema.parse({ name: 'Spur', nickname: 'S' })).resolves.toEqual({ name: 'Spur', nickname: 'S' }) + await expect(schema.parse({ name: 'Narro' })).resolves.toEqual({ name: 'Narro' }) + await expect(schema.parse({ name: 'Narro', nickname: 'S' })).resolves.toEqual({ name: 'Narro', nickname: 'S' }) }) it('allows for a complex nested object schema', async () => { @@ -279,7 +279,7 @@ describe('object schema', () => { label: string(), }).strict() - const result = await schema.safeParse({ label: 'Spur', alias: 'Team' }) + const result = await schema.safeParse({ label: 'Narro', alias: 'Team' }) expect(result.success).toBe(false) expect('data' in result).toBe(false) @@ -290,10 +290,10 @@ describe('object schema', () => { label: string(), }).strict() - const result = await schema.safeParse({ label: 'Spur' }) + const result = await schema.safeParse({ label: 'Narro' }) expect(result.success).toBe(true) - expect(result.data).toEqual({ label: 'Spur' }) + expect(result.data).toEqual({ label: 'Narro' }) }) it('passthrough retains unknown keys', async () => { @@ -301,10 +301,10 @@ describe('object schema', () => { label: string(), }).passthrough() - const result = await schema.safeParse({ label: 'Spur', alias: 'Team' }) + const result = await schema.safeParse({ label: 'Narro', alias: 'Team' }) expect(result.success).toBe(true) - expect(result.data).toEqual({ label: 'Spur', alias: 'Team' }) + expect(result.data).toEqual({ label: 'Narro', alias: 'Team' }) }) it('strip removes unknown keys', async () => { @@ -312,10 +312,10 @@ describe('object schema', () => { label: string(), }).strip() - const result = await schema.safeParse({ label: 'Spur', alias: 'Team' }) + const result = await schema.safeParse({ label: 'Narro', alias: 'Team' }) expect(result.success).toBe(true) - expect(result.data).toEqual({ label: 'Spur' }) + expect(result.data).toEqual({ label: 'Narro' }) expect(Object.prototype.hasOwnProperty.call(result.data, 'alias')).toBe(false) }) }) @@ -364,10 +364,10 @@ describe('object schema', () => { name: string().optional(), }) - const result = await schema.safeParse({ name: 'Spur' }) + const result = await schema.safeParse({ name: 'Narro' }) expect(result.success).toBe(true) - expect(result.data).toEqual({ name: 'Spur' }) + expect(result.data).toEqual({ name: 'Narro' }) }) it('fails when property has invalid value', async () => { @@ -418,10 +418,10 @@ describe('object schema', () => { name: string().exactOptional(), }) - const result = await schema.safeParse({ name: 'Spur' }) + const result = await schema.safeParse({ name: 'Narro' }) expect(result.success).toBe(true) - expect(result.data).toEqual({ name: 'Spur' }) + expect(result.data).toEqual({ name: 'Narro' }) }) it('fails when property has invalid value', async () => { @@ -472,10 +472,10 @@ describe('object schema', () => { name: string().undefinable(), }) - const result = await schema.safeParse({ name: 'Spur' }) + const result = await schema.safeParse({ name: 'Narro' }) expect(result.success).toBe(true) - expect(result.data).toEqual({ name: 'Spur' }) + expect(result.data).toEqual({ name: 'Narro' }) }) it('fails when property has invalid value', async () => { @@ -536,10 +536,10 @@ describe('object schema', () => { name: string().nullable(), }) - const result = await schema.safeParse({ name: 'Spur' }) + const result = await schema.safeParse({ name: 'Narro' }) expect(result.success).toBe(true) - expect(result.data).toEqual({ name: 'Spur' }) + expect(result.data).toEqual({ name: 'Narro' }) }) it('fails when property has invalid value', async () => { @@ -591,10 +591,10 @@ describe('object schema', () => { name: string().nullish(), }) - const result = await schema.safeParse({ name: 'Spur' }) + const result = await schema.safeParse({ name: 'Narro' }) expect(result.success).toBe(true) - expect(result.data).toEqual({ name: 'Spur' }) + expect(result.data).toEqual({ name: 'Narro' }) }) it('fails when property has invalid value', async () => { @@ -644,10 +644,10 @@ describe('object schema', () => { name: string(), }) - const result = await schema.safeParse({ name: 'Spur' }) + const result = await schema.safeParse({ name: 'Narro' }) expect(result.success).toBe(true) - expect(result.data).toEqual({ name: 'Spur' }) + expect(result.data).toEqual({ name: 'Narro' }) }) it('fails when property has invalid value', async () => { @@ -700,10 +700,10 @@ describe('object schema', () => { name: string().default('Default'), }) - const result = await schema.safeParse({ name: 'Spur' }) + const result = await schema.safeParse({ name: 'Narro' }) expect(result.success).toBe(true) - expect(result.data).toEqual({ name: 'Spur' }) + expect(result.data).toEqual({ name: 'Narro' }) }) it('fails when property has invalid value', async () => { diff --git a/packages/spur/src/__tests__/leitplanken/string.test.ts b/packages/spur/src/__tests__/leitplanken/string.test.ts index 353bff2..1ce029f 100644 --- a/packages/spur/src/__tests__/leitplanken/string.test.ts +++ b/packages/spur/src/__tests__/leitplanken/string.test.ts @@ -6,9 +6,9 @@ describe('string schema', () => { it('accepts strings that satisfy length constraints', async () => { const schema = string().minLength(2).maxLength(5) - const value = await schema.parse('spur') + const value = await schema.parse('narro') - expect(value).toBe('spur') + expect(value).toBe('narro') }) it('rejects strings that are too short', async () => { @@ -40,11 +40,11 @@ describe('string schema', () => { }) it('validates startsWith constraint', async () => { - const schema = string().startsWith('spur') + const schema = string().startsWith('narro') - await expect(schema.parse('spur-app')).resolves.toBe('spur-app') + await expect(schema.parse('narro-app')).resolves.toBe('narro-app') - const report = await schema.safeParse('app-spur') + const report = await schema.safeParse('app-narro') expect(report.success).toBe(false) }) diff --git a/packages/spur/src/__tests__/leitplanken/union.test.ts b/packages/spur/src/__tests__/leitplanken/union.test.ts index 7f96084..fd52ae4 100644 --- a/packages/spur/src/__tests__/leitplanken/union.test.ts +++ b/packages/spur/src/__tests__/leitplanken/union.test.ts @@ -9,7 +9,7 @@ describe('union schema', () => { const schema = union([number(), string()]) await expect(schema.parse(42)).resolves.toBe(42) - await expect(schema.parse('spur')).resolves.toBe('spur') + await expect(schema.parse('narro')).resolves.toBe('narro') }) it('rejects values outside all branches', async () => { @@ -50,7 +50,7 @@ describe('union schema', () => { }) await expect(schema.parse(5)).resolves.toBe(10) - await expect(schema.parse('spur')).resolves.toBe('SPUR') + await expect(schema.parse('narro')).resolves.toBe('SPUR') }) describe('union optionality modifiers', () => { @@ -69,9 +69,9 @@ describe('union schema', () => { expect(undefinedReport.success).toBe(true) expect(undefinedReport.data).toBeUndefined() - const definedReport = await schema.safeParse('spur') + const definedReport = await schema.safeParse('narro') expect(definedReport.success).toBe(true) - expect(definedReport.data).toBe('spur') + expect(definedReport.data).toBe('narro') }) it('supports nullable modifier', async () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0974701..05b88f1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,7 +27,7 @@ importers: arktype: specifier: ^2.1.23 version: 2.1.23 - spur: + narro: specifier: workspace:* version: link:../spur valibot: @@ -4622,7 +4622,7 @@ snapshots: tsdown@0.14.2(typescript@5.9.2): dependencies: - ansis: 4.1.0 + ansis: 4.2.0 cac: 6.7.14 chokidar: 4.0.3 debug: 4.4.1 @@ -4633,7 +4633,7 @@ snapshots: rolldown-plugin-dts: 0.15.10(rolldown@1.0.0-beta.50)(typescript@5.9.2) semver: 7.7.2 tinyexec: 1.0.1 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 tree-kill: 1.2.2 unconfig: 7.3.3 optionalDependencies: From a29524008d50aafcd515f911ac46bdae66ba0a9e Mon Sep 17 00:00:00 2001 From: schplitt Date: Sun, 16 Nov 2025 19:24:20 +0100 Subject: [PATCH 08/13] remove --- Pitfalls.md | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 Pitfalls.md diff --git a/Pitfalls.md b/Pitfalls.md deleted file mode 100644 index adac637..0000000 --- a/Pitfalls.md +++ /dev/null @@ -1,26 +0,0 @@ -# Typescript Pitfalls - -Here are some of the typescript pitfalls i have encountered while working on Narro. -This would make a good blog post. - -## 1. Type bailouts - -Typescript will sometimes bail out on types if they are actually never used. -If we have a generic that passes its generic to another generic. AND we try to later infer that type, but have not used it inside an object or so internally, typescript will bail out and just use the default type. - -For this reason there is the `~types` property where we "store" the type information. At runtime this will never be used, this is only to hint typescript to not bail out on those types (specifically options as input and output are always used). - -## 2. Unexpected type widening - -Typescript will try to infer generic types based on the usage. This can lead to types having inferred (generic) types having the types inserted from generic used above. -I encountered this with the ObjectEntries where each entry was a BuildableSchema. This unknown is then passed to to the actual Schema used. -So a StringSchema constructed via `string()` would become StringSchema instead of StringSchema. - -To fix this we need to give string() an overload signature with NO generics where we just return StringSchema. -This way typescript will first try to match the non-generic signature and only if we actually pass generics it will use the generic signature. - -### AI - -ChaGPT Codex model was used to help development. What is very supprising is that even complex typescript behaiviours would be awnsered correctly,e even some i didnt know. -It correclty helped point out errors and fix complex type s that was previoulsly never possible. It could also go very hendands of nd develop schaemas correctly on its own. very intriguing and scary. -Though it still needed all the right points of what was necessary. leting it go off on its won never really resulted ion anything useful From 7fbbefbcff623dcc86a95e9d7922980923747357 Mon Sep 17 00:00:00 2001 From: schplitt Date: Sun, 16 Nov 2025 19:25:12 +0100 Subject: [PATCH 09/13] move/rename --- packages/{spur => narro}/LICENSE | 0 packages/{spur => narro}/README.md | 0 packages/{spur => narro}/package.json | 0 packages/{spur => narro}/src/__tests__/leitplanken/array.test.ts | 0 .../{spur => narro}/src/__tests__/leitplanken/boolean.test.ts | 0 .../{spur => narro}/src/__tests__/leitplanken/edge-cases.test.ts | 0 packages/{spur => narro}/src/__tests__/leitplanken/enum.test.ts | 0 .../{spur => narro}/src/__tests__/leitplanken/literal.test.ts | 0 packages/{spur => narro}/src/__tests__/leitplanken/null.test.ts | 0 packages/{spur => narro}/src/__tests__/leitplanken/number.test.ts | 0 packages/{spur => narro}/src/__tests__/leitplanken/object.test.ts | 0 packages/{spur => narro}/src/__tests__/leitplanken/string.test.ts | 0 .../{spur => narro}/src/__tests__/leitplanken/undefined.test.ts | 0 packages/{spur => narro}/src/__tests__/leitplanken/union.test.ts | 0 packages/{spur => narro}/src/__tests__/types/array.test-d.ts | 0 packages/{spur => narro}/src/__tests__/types/boolean.test-d.ts | 0 packages/{spur => narro}/src/__tests__/types/edge-cases.test-d.ts | 0 packages/{spur => narro}/src/__tests__/types/enum.test-d.ts | 0 packages/{spur => narro}/src/__tests__/types/literal.test-d.ts | 0 packages/{spur => narro}/src/__tests__/types/number.test-d.ts | 0 packages/{spur => narro}/src/__tests__/types/object.test-d.ts | 0 packages/{spur => narro}/src/__tests__/types/string.test-d.ts | 0 packages/{spur => narro}/src/__tests__/types/union.test-d.ts | 0 packages/{spur => narro}/src/build/arrayBuild.ts | 0 packages/{spur => narro}/src/build/build.ts | 0 packages/{spur => narro}/src/build/objectBuild.ts | 0 packages/{spur => narro}/src/build/unionBuild.ts | 0 packages/{spur => narro}/src/build/utils.ts | 0 packages/{spur => narro}/src/index.ts | 0 packages/{spur => narro}/src/inline.ts | 0 packages/{spur => narro}/src/options/arrayOptions.ts | 0 packages/{spur => narro}/src/options/objectOptions.ts | 0 packages/{spur => narro}/src/options/options.ts | 0 packages/{spur => narro}/src/options/utils.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/_shared/length.ts | 0 .../src/leitplanken => narro/src/schemas}/_shared/maxLength.ts | 0 .../src/leitplanken => narro/src/schemas}/_shared/minLength.ts | 0 .../src/schemas}/_shared/optionality/defaulted.ts | 0 .../src/schemas}/_shared/optionality/exactOptional.ts | 0 .../src/schemas}/_shared/optionality/nullable.ts | 0 .../src/schemas}/_shared/optionality/nullish.ts | 0 .../src/schemas}/_shared/optionality/optional.ts | 0 .../src/schemas}/_shared/optionality/required.ts | 0 .../src/schemas}/_shared/optionality/undefinable.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/array/array.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/array/index.ts | 0 .../src/leitplanken => narro/src/schemas}/boolean/boolean.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/boolean/index.ts | 0 packages/{spur/src/leitplanken => narro/src/schemas}/enum/enum.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/enum/index.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/literal/index.ts | 0 .../src/leitplanken => narro/src/schemas}/literal/literal.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/null/index.ts | 0 packages/{spur/src/leitplanken => narro/src/schemas}/null/null.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/number/index.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/number/max.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/number/min.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/number/number.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/object/index.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/object/object.ts | 0 .../src/leitplanken => narro/src/schemas}/string/endsWith.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/string/index.ts | 0 .../src/leitplanken => narro/src/schemas}/string/startsWith.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/string/string.ts | 0 .../src/leitplanken => narro/src/schemas}/undefined/index.ts | 0 .../src/leitplanken => narro/src/schemas}/undefined/undefined.ts | 0 .../{spur/src/leitplanken => narro/src/schemas}/union/index.ts | 0 packages/{spur => narro}/src/types/report.ts | 0 packages/{spur => narro}/src/types/schema.ts | 0 packages/{spur => narro}/src/types/utils.ts | 0 packages/{spur => narro}/tsconfig.json | 0 packages/{spur => narro}/tsdown.config.ts | 0 packages/{spur => narro}/vitest.config.ts | 0 73 files changed, 0 insertions(+), 0 deletions(-) rename packages/{spur => narro}/LICENSE (100%) rename packages/{spur => narro}/README.md (100%) rename packages/{spur => narro}/package.json (100%) rename packages/{spur => narro}/src/__tests__/leitplanken/array.test.ts (100%) rename packages/{spur => narro}/src/__tests__/leitplanken/boolean.test.ts (100%) rename packages/{spur => narro}/src/__tests__/leitplanken/edge-cases.test.ts (100%) rename packages/{spur => narro}/src/__tests__/leitplanken/enum.test.ts (100%) rename packages/{spur => narro}/src/__tests__/leitplanken/literal.test.ts (100%) rename packages/{spur => narro}/src/__tests__/leitplanken/null.test.ts (100%) rename packages/{spur => narro}/src/__tests__/leitplanken/number.test.ts (100%) rename packages/{spur => narro}/src/__tests__/leitplanken/object.test.ts (100%) rename packages/{spur => narro}/src/__tests__/leitplanken/string.test.ts (100%) rename packages/{spur => narro}/src/__tests__/leitplanken/undefined.test.ts (100%) rename packages/{spur => narro}/src/__tests__/leitplanken/union.test.ts (100%) rename packages/{spur => narro}/src/__tests__/types/array.test-d.ts (100%) rename packages/{spur => narro}/src/__tests__/types/boolean.test-d.ts (100%) rename packages/{spur => narro}/src/__tests__/types/edge-cases.test-d.ts (100%) rename packages/{spur => narro}/src/__tests__/types/enum.test-d.ts (100%) rename packages/{spur => narro}/src/__tests__/types/literal.test-d.ts (100%) rename packages/{spur => narro}/src/__tests__/types/number.test-d.ts (100%) rename packages/{spur => narro}/src/__tests__/types/object.test-d.ts (100%) rename packages/{spur => narro}/src/__tests__/types/string.test-d.ts (100%) rename packages/{spur => narro}/src/__tests__/types/union.test-d.ts (100%) rename packages/{spur => narro}/src/build/arrayBuild.ts (100%) rename packages/{spur => narro}/src/build/build.ts (100%) rename packages/{spur => narro}/src/build/objectBuild.ts (100%) rename packages/{spur => narro}/src/build/unionBuild.ts (100%) rename packages/{spur => narro}/src/build/utils.ts (100%) rename packages/{spur => narro}/src/index.ts (100%) rename packages/{spur => narro}/src/inline.ts (100%) rename packages/{spur => narro}/src/options/arrayOptions.ts (100%) rename packages/{spur => narro}/src/options/objectOptions.ts (100%) rename packages/{spur => narro}/src/options/options.ts (100%) rename packages/{spur => narro}/src/options/utils.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/_shared/length.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/_shared/maxLength.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/_shared/minLength.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/_shared/optionality/defaulted.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/_shared/optionality/exactOptional.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/_shared/optionality/nullable.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/_shared/optionality/nullish.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/_shared/optionality/optional.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/_shared/optionality/required.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/_shared/optionality/undefinable.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/array/array.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/array/index.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/boolean/boolean.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/boolean/index.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/enum/enum.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/enum/index.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/literal/index.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/literal/literal.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/null/index.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/null/null.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/number/index.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/number/max.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/number/min.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/number/number.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/object/index.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/object/object.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/string/endsWith.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/string/index.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/string/startsWith.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/string/string.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/undefined/index.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/undefined/undefined.ts (100%) rename packages/{spur/src/leitplanken => narro/src/schemas}/union/index.ts (100%) rename packages/{spur => narro}/src/types/report.ts (100%) rename packages/{spur => narro}/src/types/schema.ts (100%) rename packages/{spur => narro}/src/types/utils.ts (100%) rename packages/{spur => narro}/tsconfig.json (100%) rename packages/{spur => narro}/tsdown.config.ts (100%) rename packages/{spur => narro}/vitest.config.ts (100%) diff --git a/packages/spur/LICENSE b/packages/narro/LICENSE similarity index 100% rename from packages/spur/LICENSE rename to packages/narro/LICENSE diff --git a/packages/spur/README.md b/packages/narro/README.md similarity index 100% rename from packages/spur/README.md rename to packages/narro/README.md diff --git a/packages/spur/package.json b/packages/narro/package.json similarity index 100% rename from packages/spur/package.json rename to packages/narro/package.json diff --git a/packages/spur/src/__tests__/leitplanken/array.test.ts b/packages/narro/src/__tests__/leitplanken/array.test.ts similarity index 100% rename from packages/spur/src/__tests__/leitplanken/array.test.ts rename to packages/narro/src/__tests__/leitplanken/array.test.ts diff --git a/packages/spur/src/__tests__/leitplanken/boolean.test.ts b/packages/narro/src/__tests__/leitplanken/boolean.test.ts similarity index 100% rename from packages/spur/src/__tests__/leitplanken/boolean.test.ts rename to packages/narro/src/__tests__/leitplanken/boolean.test.ts diff --git a/packages/spur/src/__tests__/leitplanken/edge-cases.test.ts b/packages/narro/src/__tests__/leitplanken/edge-cases.test.ts similarity index 100% rename from packages/spur/src/__tests__/leitplanken/edge-cases.test.ts rename to packages/narro/src/__tests__/leitplanken/edge-cases.test.ts diff --git a/packages/spur/src/__tests__/leitplanken/enum.test.ts b/packages/narro/src/__tests__/leitplanken/enum.test.ts similarity index 100% rename from packages/spur/src/__tests__/leitplanken/enum.test.ts rename to packages/narro/src/__tests__/leitplanken/enum.test.ts diff --git a/packages/spur/src/__tests__/leitplanken/literal.test.ts b/packages/narro/src/__tests__/leitplanken/literal.test.ts similarity index 100% rename from packages/spur/src/__tests__/leitplanken/literal.test.ts rename to packages/narro/src/__tests__/leitplanken/literal.test.ts diff --git a/packages/spur/src/__tests__/leitplanken/null.test.ts b/packages/narro/src/__tests__/leitplanken/null.test.ts similarity index 100% rename from packages/spur/src/__tests__/leitplanken/null.test.ts rename to packages/narro/src/__tests__/leitplanken/null.test.ts diff --git a/packages/spur/src/__tests__/leitplanken/number.test.ts b/packages/narro/src/__tests__/leitplanken/number.test.ts similarity index 100% rename from packages/spur/src/__tests__/leitplanken/number.test.ts rename to packages/narro/src/__tests__/leitplanken/number.test.ts diff --git a/packages/spur/src/__tests__/leitplanken/object.test.ts b/packages/narro/src/__tests__/leitplanken/object.test.ts similarity index 100% rename from packages/spur/src/__tests__/leitplanken/object.test.ts rename to packages/narro/src/__tests__/leitplanken/object.test.ts diff --git a/packages/spur/src/__tests__/leitplanken/string.test.ts b/packages/narro/src/__tests__/leitplanken/string.test.ts similarity index 100% rename from packages/spur/src/__tests__/leitplanken/string.test.ts rename to packages/narro/src/__tests__/leitplanken/string.test.ts diff --git a/packages/spur/src/__tests__/leitplanken/undefined.test.ts b/packages/narro/src/__tests__/leitplanken/undefined.test.ts similarity index 100% rename from packages/spur/src/__tests__/leitplanken/undefined.test.ts rename to packages/narro/src/__tests__/leitplanken/undefined.test.ts diff --git a/packages/spur/src/__tests__/leitplanken/union.test.ts b/packages/narro/src/__tests__/leitplanken/union.test.ts similarity index 100% rename from packages/spur/src/__tests__/leitplanken/union.test.ts rename to packages/narro/src/__tests__/leitplanken/union.test.ts diff --git a/packages/spur/src/__tests__/types/array.test-d.ts b/packages/narro/src/__tests__/types/array.test-d.ts similarity index 100% rename from packages/spur/src/__tests__/types/array.test-d.ts rename to packages/narro/src/__tests__/types/array.test-d.ts diff --git a/packages/spur/src/__tests__/types/boolean.test-d.ts b/packages/narro/src/__tests__/types/boolean.test-d.ts similarity index 100% rename from packages/spur/src/__tests__/types/boolean.test-d.ts rename to packages/narro/src/__tests__/types/boolean.test-d.ts diff --git a/packages/spur/src/__tests__/types/edge-cases.test-d.ts b/packages/narro/src/__tests__/types/edge-cases.test-d.ts similarity index 100% rename from packages/spur/src/__tests__/types/edge-cases.test-d.ts rename to packages/narro/src/__tests__/types/edge-cases.test-d.ts diff --git a/packages/spur/src/__tests__/types/enum.test-d.ts b/packages/narro/src/__tests__/types/enum.test-d.ts similarity index 100% rename from packages/spur/src/__tests__/types/enum.test-d.ts rename to packages/narro/src/__tests__/types/enum.test-d.ts diff --git a/packages/spur/src/__tests__/types/literal.test-d.ts b/packages/narro/src/__tests__/types/literal.test-d.ts similarity index 100% rename from packages/spur/src/__tests__/types/literal.test-d.ts rename to packages/narro/src/__tests__/types/literal.test-d.ts diff --git a/packages/spur/src/__tests__/types/number.test-d.ts b/packages/narro/src/__tests__/types/number.test-d.ts similarity index 100% rename from packages/spur/src/__tests__/types/number.test-d.ts rename to packages/narro/src/__tests__/types/number.test-d.ts diff --git a/packages/spur/src/__tests__/types/object.test-d.ts b/packages/narro/src/__tests__/types/object.test-d.ts similarity index 100% rename from packages/spur/src/__tests__/types/object.test-d.ts rename to packages/narro/src/__tests__/types/object.test-d.ts diff --git a/packages/spur/src/__tests__/types/string.test-d.ts b/packages/narro/src/__tests__/types/string.test-d.ts similarity index 100% rename from packages/spur/src/__tests__/types/string.test-d.ts rename to packages/narro/src/__tests__/types/string.test-d.ts diff --git a/packages/spur/src/__tests__/types/union.test-d.ts b/packages/narro/src/__tests__/types/union.test-d.ts similarity index 100% rename from packages/spur/src/__tests__/types/union.test-d.ts rename to packages/narro/src/__tests__/types/union.test-d.ts diff --git a/packages/spur/src/build/arrayBuild.ts b/packages/narro/src/build/arrayBuild.ts similarity index 100% rename from packages/spur/src/build/arrayBuild.ts rename to packages/narro/src/build/arrayBuild.ts diff --git a/packages/spur/src/build/build.ts b/packages/narro/src/build/build.ts similarity index 100% rename from packages/spur/src/build/build.ts rename to packages/narro/src/build/build.ts diff --git a/packages/spur/src/build/objectBuild.ts b/packages/narro/src/build/objectBuild.ts similarity index 100% rename from packages/spur/src/build/objectBuild.ts rename to packages/narro/src/build/objectBuild.ts diff --git a/packages/spur/src/build/unionBuild.ts b/packages/narro/src/build/unionBuild.ts similarity index 100% rename from packages/spur/src/build/unionBuild.ts rename to packages/narro/src/build/unionBuild.ts diff --git a/packages/spur/src/build/utils.ts b/packages/narro/src/build/utils.ts similarity index 100% rename from packages/spur/src/build/utils.ts rename to packages/narro/src/build/utils.ts diff --git a/packages/spur/src/index.ts b/packages/narro/src/index.ts similarity index 100% rename from packages/spur/src/index.ts rename to packages/narro/src/index.ts diff --git a/packages/spur/src/inline.ts b/packages/narro/src/inline.ts similarity index 100% rename from packages/spur/src/inline.ts rename to packages/narro/src/inline.ts diff --git a/packages/spur/src/options/arrayOptions.ts b/packages/narro/src/options/arrayOptions.ts similarity index 100% rename from packages/spur/src/options/arrayOptions.ts rename to packages/narro/src/options/arrayOptions.ts diff --git a/packages/spur/src/options/objectOptions.ts b/packages/narro/src/options/objectOptions.ts similarity index 100% rename from packages/spur/src/options/objectOptions.ts rename to packages/narro/src/options/objectOptions.ts diff --git a/packages/spur/src/options/options.ts b/packages/narro/src/options/options.ts similarity index 100% rename from packages/spur/src/options/options.ts rename to packages/narro/src/options/options.ts diff --git a/packages/spur/src/options/utils.ts b/packages/narro/src/options/utils.ts similarity index 100% rename from packages/spur/src/options/utils.ts rename to packages/narro/src/options/utils.ts diff --git a/packages/spur/src/leitplanken/_shared/length.ts b/packages/narro/src/schemas/_shared/length.ts similarity index 100% rename from packages/spur/src/leitplanken/_shared/length.ts rename to packages/narro/src/schemas/_shared/length.ts diff --git a/packages/spur/src/leitplanken/_shared/maxLength.ts b/packages/narro/src/schemas/_shared/maxLength.ts similarity index 100% rename from packages/spur/src/leitplanken/_shared/maxLength.ts rename to packages/narro/src/schemas/_shared/maxLength.ts diff --git a/packages/spur/src/leitplanken/_shared/minLength.ts b/packages/narro/src/schemas/_shared/minLength.ts similarity index 100% rename from packages/spur/src/leitplanken/_shared/minLength.ts rename to packages/narro/src/schemas/_shared/minLength.ts diff --git a/packages/spur/src/leitplanken/_shared/optionality/defaulted.ts b/packages/narro/src/schemas/_shared/optionality/defaulted.ts similarity index 100% rename from packages/spur/src/leitplanken/_shared/optionality/defaulted.ts rename to packages/narro/src/schemas/_shared/optionality/defaulted.ts diff --git a/packages/spur/src/leitplanken/_shared/optionality/exactOptional.ts b/packages/narro/src/schemas/_shared/optionality/exactOptional.ts similarity index 100% rename from packages/spur/src/leitplanken/_shared/optionality/exactOptional.ts rename to packages/narro/src/schemas/_shared/optionality/exactOptional.ts diff --git a/packages/spur/src/leitplanken/_shared/optionality/nullable.ts b/packages/narro/src/schemas/_shared/optionality/nullable.ts similarity index 100% rename from packages/spur/src/leitplanken/_shared/optionality/nullable.ts rename to packages/narro/src/schemas/_shared/optionality/nullable.ts diff --git a/packages/spur/src/leitplanken/_shared/optionality/nullish.ts b/packages/narro/src/schemas/_shared/optionality/nullish.ts similarity index 100% rename from packages/spur/src/leitplanken/_shared/optionality/nullish.ts rename to packages/narro/src/schemas/_shared/optionality/nullish.ts diff --git a/packages/spur/src/leitplanken/_shared/optionality/optional.ts b/packages/narro/src/schemas/_shared/optionality/optional.ts similarity index 100% rename from packages/spur/src/leitplanken/_shared/optionality/optional.ts rename to packages/narro/src/schemas/_shared/optionality/optional.ts diff --git a/packages/spur/src/leitplanken/_shared/optionality/required.ts b/packages/narro/src/schemas/_shared/optionality/required.ts similarity index 100% rename from packages/spur/src/leitplanken/_shared/optionality/required.ts rename to packages/narro/src/schemas/_shared/optionality/required.ts diff --git a/packages/spur/src/leitplanken/_shared/optionality/undefinable.ts b/packages/narro/src/schemas/_shared/optionality/undefinable.ts similarity index 100% rename from packages/spur/src/leitplanken/_shared/optionality/undefinable.ts rename to packages/narro/src/schemas/_shared/optionality/undefinable.ts diff --git a/packages/spur/src/leitplanken/array/array.ts b/packages/narro/src/schemas/array/array.ts similarity index 100% rename from packages/spur/src/leitplanken/array/array.ts rename to packages/narro/src/schemas/array/array.ts diff --git a/packages/spur/src/leitplanken/array/index.ts b/packages/narro/src/schemas/array/index.ts similarity index 100% rename from packages/spur/src/leitplanken/array/index.ts rename to packages/narro/src/schemas/array/index.ts diff --git a/packages/spur/src/leitplanken/boolean/boolean.ts b/packages/narro/src/schemas/boolean/boolean.ts similarity index 100% rename from packages/spur/src/leitplanken/boolean/boolean.ts rename to packages/narro/src/schemas/boolean/boolean.ts diff --git a/packages/spur/src/leitplanken/boolean/index.ts b/packages/narro/src/schemas/boolean/index.ts similarity index 100% rename from packages/spur/src/leitplanken/boolean/index.ts rename to packages/narro/src/schemas/boolean/index.ts diff --git a/packages/spur/src/leitplanken/enum/enum.ts b/packages/narro/src/schemas/enum/enum.ts similarity index 100% rename from packages/spur/src/leitplanken/enum/enum.ts rename to packages/narro/src/schemas/enum/enum.ts diff --git a/packages/spur/src/leitplanken/enum/index.ts b/packages/narro/src/schemas/enum/index.ts similarity index 100% rename from packages/spur/src/leitplanken/enum/index.ts rename to packages/narro/src/schemas/enum/index.ts diff --git a/packages/spur/src/leitplanken/literal/index.ts b/packages/narro/src/schemas/literal/index.ts similarity index 100% rename from packages/spur/src/leitplanken/literal/index.ts rename to packages/narro/src/schemas/literal/index.ts diff --git a/packages/spur/src/leitplanken/literal/literal.ts b/packages/narro/src/schemas/literal/literal.ts similarity index 100% rename from packages/spur/src/leitplanken/literal/literal.ts rename to packages/narro/src/schemas/literal/literal.ts diff --git a/packages/spur/src/leitplanken/null/index.ts b/packages/narro/src/schemas/null/index.ts similarity index 100% rename from packages/spur/src/leitplanken/null/index.ts rename to packages/narro/src/schemas/null/index.ts diff --git a/packages/spur/src/leitplanken/null/null.ts b/packages/narro/src/schemas/null/null.ts similarity index 100% rename from packages/spur/src/leitplanken/null/null.ts rename to packages/narro/src/schemas/null/null.ts diff --git a/packages/spur/src/leitplanken/number/index.ts b/packages/narro/src/schemas/number/index.ts similarity index 100% rename from packages/spur/src/leitplanken/number/index.ts rename to packages/narro/src/schemas/number/index.ts diff --git a/packages/spur/src/leitplanken/number/max.ts b/packages/narro/src/schemas/number/max.ts similarity index 100% rename from packages/spur/src/leitplanken/number/max.ts rename to packages/narro/src/schemas/number/max.ts diff --git a/packages/spur/src/leitplanken/number/min.ts b/packages/narro/src/schemas/number/min.ts similarity index 100% rename from packages/spur/src/leitplanken/number/min.ts rename to packages/narro/src/schemas/number/min.ts diff --git a/packages/spur/src/leitplanken/number/number.ts b/packages/narro/src/schemas/number/number.ts similarity index 100% rename from packages/spur/src/leitplanken/number/number.ts rename to packages/narro/src/schemas/number/number.ts diff --git a/packages/spur/src/leitplanken/object/index.ts b/packages/narro/src/schemas/object/index.ts similarity index 100% rename from packages/spur/src/leitplanken/object/index.ts rename to packages/narro/src/schemas/object/index.ts diff --git a/packages/spur/src/leitplanken/object/object.ts b/packages/narro/src/schemas/object/object.ts similarity index 100% rename from packages/spur/src/leitplanken/object/object.ts rename to packages/narro/src/schemas/object/object.ts diff --git a/packages/spur/src/leitplanken/string/endsWith.ts b/packages/narro/src/schemas/string/endsWith.ts similarity index 100% rename from packages/spur/src/leitplanken/string/endsWith.ts rename to packages/narro/src/schemas/string/endsWith.ts diff --git a/packages/spur/src/leitplanken/string/index.ts b/packages/narro/src/schemas/string/index.ts similarity index 100% rename from packages/spur/src/leitplanken/string/index.ts rename to packages/narro/src/schemas/string/index.ts diff --git a/packages/spur/src/leitplanken/string/startsWith.ts b/packages/narro/src/schemas/string/startsWith.ts similarity index 100% rename from packages/spur/src/leitplanken/string/startsWith.ts rename to packages/narro/src/schemas/string/startsWith.ts diff --git a/packages/spur/src/leitplanken/string/string.ts b/packages/narro/src/schemas/string/string.ts similarity index 100% rename from packages/spur/src/leitplanken/string/string.ts rename to packages/narro/src/schemas/string/string.ts diff --git a/packages/spur/src/leitplanken/undefined/index.ts b/packages/narro/src/schemas/undefined/index.ts similarity index 100% rename from packages/spur/src/leitplanken/undefined/index.ts rename to packages/narro/src/schemas/undefined/index.ts diff --git a/packages/spur/src/leitplanken/undefined/undefined.ts b/packages/narro/src/schemas/undefined/undefined.ts similarity index 100% rename from packages/spur/src/leitplanken/undefined/undefined.ts rename to packages/narro/src/schemas/undefined/undefined.ts diff --git a/packages/spur/src/leitplanken/union/index.ts b/packages/narro/src/schemas/union/index.ts similarity index 100% rename from packages/spur/src/leitplanken/union/index.ts rename to packages/narro/src/schemas/union/index.ts diff --git a/packages/spur/src/types/report.ts b/packages/narro/src/types/report.ts similarity index 100% rename from packages/spur/src/types/report.ts rename to packages/narro/src/types/report.ts diff --git a/packages/spur/src/types/schema.ts b/packages/narro/src/types/schema.ts similarity index 100% rename from packages/spur/src/types/schema.ts rename to packages/narro/src/types/schema.ts diff --git a/packages/spur/src/types/utils.ts b/packages/narro/src/types/utils.ts similarity index 100% rename from packages/spur/src/types/utils.ts rename to packages/narro/src/types/utils.ts diff --git a/packages/spur/tsconfig.json b/packages/narro/tsconfig.json similarity index 100% rename from packages/spur/tsconfig.json rename to packages/narro/tsconfig.json diff --git a/packages/spur/tsdown.config.ts b/packages/narro/tsdown.config.ts similarity index 100% rename from packages/spur/tsdown.config.ts rename to packages/narro/tsdown.config.ts diff --git a/packages/spur/vitest.config.ts b/packages/narro/vitest.config.ts similarity index 100% rename from packages/spur/vitest.config.ts rename to packages/narro/vitest.config.ts From 8e18c98cc64fa3b8d64d9036a395e59e7670a89a Mon Sep 17 00:00:00 2001 From: schplitt Date: Sun, 16 Nov 2025 19:30:16 +0100 Subject: [PATCH 10/13] rename to narro --- .vscode/settings.json | 2 +- .../src/__tests__/leitplanken/array.test.ts | 8 +++---- .../__tests__/leitplanken/edge-cases.test.ts | 4 ++-- .../src/__tests__/leitplanken/object.test.ts | 2 +- .../src/__tests__/leitplanken/union.test.ts | 6 ++--- .../narro/src/__tests__/types/array.test-d.ts | 16 +++++++------- .../src/__tests__/types/boolean.test-d.ts | 2 +- .../src/__tests__/types/edge-cases.test-d.ts | 16 +++++++------- .../narro/src/__tests__/types/enum.test-d.ts | 2 +- .../src/__tests__/types/literal.test-d.ts | 2 +- .../src/__tests__/types/number.test-d.ts | 2 +- .../src/__tests__/types/object.test-d.ts | 16 +++++++------- .../src/__tests__/types/string.test-d.ts | 2 +- .../narro/src/__tests__/types/union.test-d.ts | 12 +++++----- packages/narro/src/build/objectBuild.ts | 10 ++++----- packages/narro/src/build/unionBuild.ts | 2 +- packages/narro/src/index.ts | 22 +++++++++---------- 17 files changed, 63 insertions(+), 63 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 860b3c6..a525483 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -50,6 +50,6 @@ ], "cSpell.words": [ "Checkables", - "leitplanken" + "narro" ] } diff --git a/packages/narro/src/__tests__/leitplanken/array.test.ts b/packages/narro/src/__tests__/leitplanken/array.test.ts index 8cd0f0f..d90910f 100644 --- a/packages/narro/src/__tests__/leitplanken/array.test.ts +++ b/packages/narro/src/__tests__/leitplanken/array.test.ts @@ -1,9 +1,9 @@ import { describe, expect, it } from 'vitest' -import { array } from '../../leitplanken/array' -import { number } from '../../leitplanken/number' -import { object } from '../../leitplanken/object' -import { string } from '../../leitplanken/string' +import { array } from '../../schemas/array' +import { number } from '../../schemas/number' +import { object } from '../../schemas/object' +import { string } from '../../schemas/string' describe('array schema', () => { it('parses arrays when every element matches the child schema', async () => { diff --git a/packages/narro/src/__tests__/leitplanken/edge-cases.test.ts b/packages/narro/src/__tests__/leitplanken/edge-cases.test.ts index 3b692b6..8f2a0d6 100644 --- a/packages/narro/src/__tests__/leitplanken/edge-cases.test.ts +++ b/packages/narro/src/__tests__/leitplanken/edge-cases.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest' import { boolean, enum as enumSchema, literal, null as nullSchema, number, string, undefined as undefinedSchema, union } from '../../index' -import { array } from '../../leitplanken/array' -import { object } from '../../leitplanken/object' +import { array } from '../../schemas/array' +import { object } from '../../schemas/object' function createPlatformSchema() { return object({ diff --git a/packages/narro/src/__tests__/leitplanken/object.test.ts b/packages/narro/src/__tests__/leitplanken/object.test.ts index b07cf34..79735bf 100644 --- a/packages/narro/src/__tests__/leitplanken/object.test.ts +++ b/packages/narro/src/__tests__/leitplanken/object.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest' import { _null, _undefined, literal, number, string, union } from '../../index' -import { object } from '../../leitplanken/object' +import { object } from '../../schemas/object' describe('object schema', () => { it('accepts objects matching nested schemas', async () => { diff --git a/packages/narro/src/__tests__/leitplanken/union.test.ts b/packages/narro/src/__tests__/leitplanken/union.test.ts index fd52ae4..1841418 100644 --- a/packages/narro/src/__tests__/leitplanken/union.test.ts +++ b/packages/narro/src/__tests__/leitplanken/union.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest' import { literal, number, string, union } from '../../index' -import { array } from '../../leitplanken/array' -import { object } from '../../leitplanken/object' +import { array } from '../../schemas/array' +import { object } from '../../schemas/object' describe('union schema', () => { it('accepts values from any branch', async () => { @@ -50,7 +50,7 @@ describe('union schema', () => { }) await expect(schema.parse(5)).resolves.toBe(10) - await expect(schema.parse('narro')).resolves.toBe('SPUR') + await expect(schema.parse('narro')).resolves.toBe('NARRO') }) describe('union optionality modifiers', () => { diff --git a/packages/narro/src/__tests__/types/array.test-d.ts b/packages/narro/src/__tests__/types/array.test-d.ts index 1cef4df..295dac1 100644 --- a/packages/narro/src/__tests__/types/array.test-d.ts +++ b/packages/narro/src/__tests__/types/array.test-d.ts @@ -2,14 +2,14 @@ import type { InferInput, InferOutput } from '../../types/utils' import { describe, expectTypeOf, it } from 'vitest' -import { array } from '../../leitplanken/array' -import { boolean } from '../../leitplanken/boolean' -import { enum_ } from '../../leitplanken/enum' -import { literal } from '../../leitplanken/literal' -import { number } from '../../leitplanken/number' -import { object } from '../../leitplanken/object' -import { string } from '../../leitplanken/string' -import { union } from '../../leitplanken/union' +import { array } from '../../schemas/array' +import { boolean } from '../../schemas/boolean' +import { enum_ } from '../../schemas/enum' +import { literal } from '../../schemas/literal' +import { number } from '../../schemas/number' +import { object } from '../../schemas/object' +import { string } from '../../schemas/string' +import { union } from '../../schemas/union' describe('arraySchema - basic types', () => { it('array of strings', () => { diff --git a/packages/narro/src/__tests__/types/boolean.test-d.ts b/packages/narro/src/__tests__/types/boolean.test-d.ts index 5165fd3..6bb471a 100644 --- a/packages/narro/src/__tests__/types/boolean.test-d.ts +++ b/packages/narro/src/__tests__/types/boolean.test-d.ts @@ -2,7 +2,7 @@ import type { InferInput, InferOutput } from '../../types/utils' import { describe, expectTypeOf, it } from 'vitest' -import { boolean } from '../../leitplanken/boolean' +import { boolean } from '../../schemas/boolean' describe('booleanSchema - basic types', () => { it('basic boolean schema', () => { diff --git a/packages/narro/src/__tests__/types/edge-cases.test-d.ts b/packages/narro/src/__tests__/types/edge-cases.test-d.ts index d41a209..e1a0f77 100644 --- a/packages/narro/src/__tests__/types/edge-cases.test-d.ts +++ b/packages/narro/src/__tests__/types/edge-cases.test-d.ts @@ -2,14 +2,14 @@ import type { InferInput, InferOutput } from '../../types/utils' import { describe, expectTypeOf, it } from 'vitest' -import { array } from '../../leitplanken/array' -import { boolean } from '../../leitplanken/boolean' -import { enum_ } from '../../leitplanken/enum' -import { literal } from '../../leitplanken/literal' -import { number } from '../../leitplanken/number' -import { object } from '../../leitplanken/object' -import { string } from '../../leitplanken/string' -import { union } from '../../leitplanken/union' +import { array } from '../../schemas/array' +import { boolean } from '../../schemas/boolean' +import { enum_ } from '../../schemas/enum' +import { literal } from '../../schemas/literal' +import { number } from '../../schemas/number' +import { object } from '../../schemas/object' +import { string } from '../../schemas/string' +import { union } from '../../schemas/union' describe('edge cases - extreme type complexity', () => { it('maximum nesting depth', () => { diff --git a/packages/narro/src/__tests__/types/enum.test-d.ts b/packages/narro/src/__tests__/types/enum.test-d.ts index 0a5c6c5..cf46413 100644 --- a/packages/narro/src/__tests__/types/enum.test-d.ts +++ b/packages/narro/src/__tests__/types/enum.test-d.ts @@ -1,6 +1,6 @@ import type { InferInput, InferOutput } from '../../types/utils' import { describe, expectTypeOf, it } from 'vitest' -import { enum_ } from '../../leitplanken/enum' +import { enum_ } from '../../schemas/enum' describe('enumSchema - basic types', () => { it('string enum schema', () => { diff --git a/packages/narro/src/__tests__/types/literal.test-d.ts b/packages/narro/src/__tests__/types/literal.test-d.ts index bc8f952..6427112 100644 --- a/packages/narro/src/__tests__/types/literal.test-d.ts +++ b/packages/narro/src/__tests__/types/literal.test-d.ts @@ -2,7 +2,7 @@ import type { InferInput, InferOutput } from '../../types/utils' import { describe, expectTypeOf, it } from 'vitest' -import { literal } from '../../leitplanken/literal' +import { literal } from '../../schemas/literal' describe('literalSchema - basic types', () => { it('string literal schema', () => { diff --git a/packages/narro/src/__tests__/types/number.test-d.ts b/packages/narro/src/__tests__/types/number.test-d.ts index d5d2b4e..05f04ba 100644 --- a/packages/narro/src/__tests__/types/number.test-d.ts +++ b/packages/narro/src/__tests__/types/number.test-d.ts @@ -2,7 +2,7 @@ import type { InferInput, InferOutput } from '../../types/utils' import { describe, expectTypeOf, it } from 'vitest' -import { number } from '../../leitplanken/number' +import { number } from '../../schemas/number' describe('numberSchema - basic types', () => { it('basic number schema', () => { diff --git a/packages/narro/src/__tests__/types/object.test-d.ts b/packages/narro/src/__tests__/types/object.test-d.ts index dc78f7e..7bac064 100644 --- a/packages/narro/src/__tests__/types/object.test-d.ts +++ b/packages/narro/src/__tests__/types/object.test-d.ts @@ -2,14 +2,14 @@ import type { InferInput, InferOutput } from '../../types/utils' import { describe, expectTypeOf, it } from 'vitest' -import { array } from '../../leitplanken/array' -import { boolean } from '../../leitplanken/boolean' -import { enum_ } from '../../leitplanken/enum' -import { literal } from '../../leitplanken/literal' -import { number } from '../../leitplanken/number' -import { object } from '../../leitplanken/object' -import { string } from '../../leitplanken/string' -import { union } from '../../leitplanken/union' +import { array } from '../../schemas/array' +import { boolean } from '../../schemas/boolean' +import { enum_ } from '../../schemas/enum' +import { literal } from '../../schemas/literal' +import { number } from '../../schemas/number' +import { object } from '../../schemas/object' +import { string } from '../../schemas/string' +import { union } from '../../schemas/union' describe('objectSchema - basic types', () => { it('empty object', () => { diff --git a/packages/narro/src/__tests__/types/string.test-d.ts b/packages/narro/src/__tests__/types/string.test-d.ts index f8e094b..53601ee 100644 --- a/packages/narro/src/__tests__/types/string.test-d.ts +++ b/packages/narro/src/__tests__/types/string.test-d.ts @@ -2,7 +2,7 @@ import type { InferInput, InferOutput } from '../../types/utils' import { describe, expectTypeOf, it } from 'vitest' -import { string } from '../../leitplanken/string' +import { string } from '../../schemas/string' describe('stringSchema - basic types', () => { it('basic string schema', () => { diff --git a/packages/narro/src/__tests__/types/union.test-d.ts b/packages/narro/src/__tests__/types/union.test-d.ts index 97e73c1..4c5aaee 100644 --- a/packages/narro/src/__tests__/types/union.test-d.ts +++ b/packages/narro/src/__tests__/types/union.test-d.ts @@ -2,12 +2,12 @@ import type { InferInput, InferOutput } from '../../types/utils' import { describe, expectTypeOf, it } from 'vitest' -import { boolean } from '../../leitplanken/boolean' -import { enum_ } from '../../leitplanken/enum' -import { literal } from '../../leitplanken/literal' -import { number } from '../../leitplanken/number' -import { string } from '../../leitplanken/string' -import { union } from '../../leitplanken/union' +import { boolean } from '../../schemas/boolean' +import { enum_ } from '../../schemas/enum' +import { literal } from '../../schemas/literal' +import { number } from '../../schemas/number' +import { string } from '../../schemas/string' +import { union } from '../../schemas/union' describe('unionSchema - basic types', () => { it('union of string and number', () => { diff --git a/packages/narro/src/build/objectBuild.ts b/packages/narro/src/build/objectBuild.ts index 223ee48..63fafe4 100644 --- a/packages/narro/src/build/objectBuild.ts +++ b/packages/narro/src/build/objectBuild.ts @@ -1,11 +1,11 @@ -import type { ObjectEntries } from '../leitplanken/object' import type { ObjectShape } from '../options/objectOptions' +import type { ObjectEntries } from '../schemas/object' import type { SchemaReport, SchemaReportFailure } from '../types/report' import type { BranchCheckable, BranchCheckableImport, EvaluableSchema, SourceCheckable, SourceCheckableImport } from '../types/schema' -import { exactOptionalSymbol } from '../leitplanken/_shared/optionality/exactOptional' -import { nullishSymbol } from '../leitplanken/_shared/optionality/nullish' -import { undefinableSymbol } from '../leitplanken/_shared/optionality/undefinable' -import { undefinedSymbol } from '../leitplanken/undefined/undefined' +import { exactOptionalSymbol } from '../schemas/_shared/optionality/exactOptional' +import { nullishSymbol } from '../schemas/_shared/optionality/nullish' +import { undefinableSymbol } from '../schemas/_shared/optionality/undefinable' +import { undefinedSymbol } from '../schemas/undefined/undefined' import { flattenUnionReportCandidates, mergeOptionality, selectPreferredUnionReport } from './utils' interface ObjectSchemas { diff --git a/packages/narro/src/build/unionBuild.ts b/packages/narro/src/build/unionBuild.ts index e321de7..67a3209 100644 --- a/packages/narro/src/build/unionBuild.ts +++ b/packages/narro/src/build/unionBuild.ts @@ -119,4 +119,4 @@ export function buildUnionSchema( // - unionBuild.ts: consolidate unionReports recursively into a flat array, reselect best report after consolidation, and add a comment for future feature where we ensure metadata continues to track original branches. // - unionBuild.ts: when a promoted report fails downstream (e.g. object checkIds), iterate promoted + stored union reports to find the highest scoring passing candidate, demote failing ones, and update unionReports accordingly. // - objectBuild.ts: extend checkIds handling to walk the selected union report set, run checkIds against each candidate, adjust scores, remove invalid data, and bubble updated unionReports back into the chosen result. -// - tests/__tests__/leitplanken: add unionBuild.test.ts covering union report consolidation edge cases and object schema interactions; add targeted object schema tests for optionality/union promotion scenarios. +// - tests/__tests__/narro: add unionBuild.test.ts covering union report consolidation edge cases and object schema interactions; add targeted object schema tests for optionality/union promotion scenarios. diff --git a/packages/narro/src/index.ts b/packages/narro/src/index.ts index 20334ef..4ccc512 100644 --- a/packages/narro/src/index.ts +++ b/packages/narro/src/index.ts @@ -1,11 +1,11 @@ -export * from './leitplanken/array' -export * from './leitplanken/array' -export * from './leitplanken/boolean' -export * from './leitplanken/enum' -export * from './leitplanken/literal' -export * from './leitplanken/null' -export * from './leitplanken/number' -export * from './leitplanken/object' -export * from './leitplanken/string' -export * from './leitplanken/undefined' -export * from './leitplanken/union' +export * from './schemas/array' +export * from './schemas/array' +export * from './schemas/boolean' +export * from './schemas/enum' +export * from './schemas/literal' +export * from './schemas/null' +export * from './schemas/number' +export * from './schemas/object' +export * from './schemas/string' +export * from './schemas/undefined' +export * from './schemas/union' From c63ad3e9e1394157a4463a52d1cafc70e7feaae6 Mon Sep 17 00:00:00 2001 From: schplitt Date: Sun, 16 Nov 2025 19:30:22 +0100 Subject: [PATCH 11/13] update --- pnpm-lock.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05b88f1..550d2ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,7 +29,7 @@ importers: version: 2.1.23 narro: specifier: workspace:* - version: link:../spur + version: link:../narro valibot: specifier: ^1.1.0 version: 1.1.0(typescript@5.9.2) @@ -41,7 +41,7 @@ importers: specifier: ^3.2.4 version: 3.2.4(@types/debug@4.1.12)(jiti@2.5.1)(yaml@2.8.1) - packages/spur: + packages/narro: dependencies: '@standard-schema/spec': specifier: ^1.0.0 From a1b6ac7e66f4af9ad865d6926687a2ee872cdb45 Mon Sep 17 00:00:00 2001 From: schplitt Date: Sun, 16 Nov 2025 19:32:40 +0100 Subject: [PATCH 12/13] rename --- packages/bench/bench/complex-backend.bench.ts | 24 +++++++++---------- .../bench/bench/complex-frontend.bench.ts | 24 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/bench/bench/complex-backend.bench.ts b/packages/bench/bench/complex-backend.bench.ts index bb24eb8..92de5fa 100644 --- a/packages/bench/bench/complex-backend.bench.ts +++ b/packages/bench/bench/complex-backend.bench.ts @@ -106,7 +106,7 @@ const invalidOrder = { } // Narro async schema -const spurAsyncUnbuild = object({ +const narroAsyncUnbuild = object({ customer: object({ id: string().minLength(10), email: string().minLength(5), @@ -149,9 +149,9 @@ const spurAsyncUnbuild = object({ tags: array(string()).optional(), }) -const spurAsyncBuilt = await spurAsyncUnbuild.build() +const narroAsyncBuilt = await narroAsyncUnbuild.build() -const spurInlineUnbuild = objectInline({ +const narroInlineUnbuild = objectInline({ customer: objectInline({ id: stringInline().minLength(10), email: stringInline().minLength(5), @@ -195,7 +195,7 @@ const spurInlineUnbuild = objectInline({ }) // Narro inline schema -const spurInlineBuilt = await objectInline({ +const narroInlineBuilt = await objectInline({ customer: objectInline({ id: stringInline().minLength(10), email: stringInline().minLength(5), @@ -375,16 +375,16 @@ const arkTypeSchema = type({ describe('complex backend: valid parse', () => { bench('narro unbuild async valid', async () => { - await spurAsyncUnbuild.safeParse(validOrder) + await narroAsyncUnbuild.safeParse(validOrder) }) bench('narro async valid', () => { - spurAsyncBuilt.safeParse(validOrder) + narroAsyncBuilt.safeParse(validOrder) }) bench('narro inline unbuild valid', async () => { - await spurInlineUnbuild.safeParse(validOrder) + await narroInlineUnbuild.safeParse(validOrder) }) bench('narro inline valid', () => { - spurInlineBuilt.safeParse(validOrder) + narroInlineBuilt.safeParse(validOrder) }) bench('zod valid', () => { zodSchema.safeParse(validOrder) @@ -399,16 +399,16 @@ describe('complex backend: valid parse', () => { describe('complex backend: invalid parse', () => { bench('narro unbuild async invalid', async () => { - await spurAsyncUnbuild.safeParse(invalidOrder) + await narroAsyncUnbuild.safeParse(invalidOrder) }) bench('narro async invalid', () => { - spurAsyncBuilt.safeParse(invalidOrder) + narroAsyncBuilt.safeParse(invalidOrder) }) bench('narro inline unbuild invalid', async () => { - await spurInlineUnbuild.safeParse(invalidOrder) + await narroInlineUnbuild.safeParse(invalidOrder) }) bench('narro inline invalid', () => { - spurInlineBuilt.safeParse(invalidOrder) + narroInlineBuilt.safeParse(invalidOrder) }) bench('zod invalid', () => { zodSchema.safeParse(invalidOrder) diff --git a/packages/bench/bench/complex-frontend.bench.ts b/packages/bench/bench/complex-frontend.bench.ts index f668ca5..66032f5 100644 --- a/packages/bench/bench/complex-frontend.bench.ts +++ b/packages/bench/bench/complex-frontend.bench.ts @@ -88,7 +88,7 @@ const invalidFormData = { } // Narro async schema -const spurAsyncUnbuild = object({ +const narroAsyncUnbuild = object({ account: object({ username: string().minLength(3).maxLength(30), email: string().minLength(5).maxLength(100), @@ -128,7 +128,7 @@ const spurAsyncUnbuild = object({ referralCode: string().nullable(), }) -const spurAsyncBuilt = await object({ +const narroAsyncBuilt = await object({ account: object({ username: string().minLength(3).maxLength(30), email: string().minLength(5).maxLength(100), @@ -168,7 +168,7 @@ const spurAsyncBuilt = await object({ referralCode: string().nullable(), }).build() -const spurInlineUnbuild = objectInline({ +const narroInlineUnbuild = objectInline({ account: objectInline({ username: stringInline().minLength(3).maxLength(30), email: stringInline().minLength(5).maxLength(100), @@ -209,7 +209,7 @@ const spurInlineUnbuild = objectInline({ }) // Narro inline schema -const spurInlineBuilt = await objectInline({ +const narroInlineBuilt = await objectInline({ account: objectInline({ username: stringInline().minLength(3).maxLength(30), email: stringInline().minLength(5).maxLength(100), @@ -375,17 +375,17 @@ const arkTypeSchema = type({ describe('complex frontend: valid parse', () => { bench('narro async unbuild valid', async () => { - await spurAsyncUnbuild.safeParse(validFormData) + await narroAsyncUnbuild.safeParse(validFormData) }) bench('narro async valid', () => { - spurAsyncBuilt.safeParse(validFormData) + narroAsyncBuilt.safeParse(validFormData) }) bench('narro inline unbuild valid', async () => { - await spurInlineUnbuild.safeParse(validFormData) + await narroInlineUnbuild.safeParse(validFormData) }) bench('narro inline valid', () => { - spurInlineBuilt.safeParse(validFormData) + narroInlineBuilt.safeParse(validFormData) }) bench('zod valid', () => { zodSchema.safeParse(validFormData) @@ -400,16 +400,16 @@ describe('complex frontend: valid parse', () => { describe('complex frontend: invalid parse', () => { bench('narro unbuild async invalid', async () => { - await spurAsyncUnbuild.safeParse(invalidFormData) + await narroAsyncUnbuild.safeParse(invalidFormData) }) bench('narro async invalid', () => { - spurAsyncBuilt.safeParse(invalidFormData) + narroAsyncBuilt.safeParse(invalidFormData) }) bench('narro inline unbuild invalid', async () => { - await spurInlineUnbuild.safeParse(invalidFormData) + await narroInlineUnbuild.safeParse(invalidFormData) }) bench('narro inline invalid', () => { - spurInlineBuilt.safeParse(invalidFormData) + narroInlineBuilt.safeParse(invalidFormData) }) bench('zod invalid', () => { zodSchema.safeParse(invalidFormData) From 1642fc5ea17def782464c7d6cfe6b9b0fe92a7e7 Mon Sep 17 00:00:00 2001 From: schplitt Date: Sun, 16 Nov 2025 19:39:58 +0100 Subject: [PATCH 13/13] fix: update Quick Start example to use correct import and report handling --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0a26f76..d8df6b5 100644 --- a/README.md +++ b/README.md @@ -20,16 +20,16 @@ Lightweight TypeScript schema validation library with a Zod-like API that keeps ## Quick Start ```ts -import { number } from 'narro' +import * as n from 'narro' -const ageSchema = number().min(0).max(130) +const ageSchema = n.number().min(0).max(130) const report = await ageSchema.safeParse(input) -if (report.passed) { +if (report.success) { // use report.value } else { - console.log(report) // heuristics explain the most likely mismatch + // see why validation failed } ```