feat: Standard Schema wrapper, JSR, pkg.pr.new, docs previews and a playground - #556
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #556 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 183 186 +3
Lines 2069 2069
Branches 612 613 +1
=========================================
Hits 2069 2069
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Tree-shaking report✅ No size regression. 1 grew, 1 new out of 156 exports.
What changed (2)
All exports (156)
How this is measuredEvery export is imported alone into an esbuild consumer bundle (minified, tree-shaken) built from the head and from the base of this pull request; the sizes are the resulting bundles, gzip is their gzipped size. 🔴 marks a regression: a pre-existing export that grew more than 20% and more than 256 B, or the bundle importing every pre-existing export growing more than 5%. 🟡 is growth under the threshold, 🟢 a decrease, ⚪ no change, 🆕 an export that does not exist on the base (never a regression), 🗑️ an export that was removed. An intentional increase is accepted with the |
`toStandardSchema(isValidCpf, { message, options })` wraps any `isValid*` utility
in a Standard Schema (the `~standard` interface), so the validators plug into
TanStack Form, tRPC, Hono, react-hook-form's standardSchemaResolver and whatever
else accepts the interface, next to Zod, Valibot and ArkType schemas.
The types of the specification are copied into the package as the specification
intends, so there is still no runtime dependency, and they are exported. The
wrapper is generic rather than one prebuilt schema per validator: a module
importing every validator would pull the dataset-backed ones into any bundle
that failed to tree-shake it, while this costs the validator handed to it and
nothing else, and covers options and future validators for free. Like the
validators it never throws. Available from the root and as the
`to-standard-schema` subpath.
…erate*
35 properties in 22 test files called generateCpf(), generatePhone(type) and the
like inside fc.property. Those utilities draw from Math.random(), which the seed
fast-check reports on a failure does not control, so a failing document could be
neither replayed nor shrunk; several were properties in name only
(fc.constant("landline") as the single arbitrary). They now draw the document
with fc.gen(): `const cpf = g(cpfs)`.
The arbitraries live where the project keeps its test arbitraries,
src/_internals/test/: cpfs, cnpjs, cnhs, pisNumbers, voterIds,
processosJuridicos, boletos and phones, built from fast-check primitives plus the
check-digit internals, in four files (the lint rule caps a file at ten imports).
A base that must not be one digit repeated gets its second character offset from
the first, so nothing is filtered. The generate* utilities keep their own tests.
The boleto assembly moves from generateBoleto into two internals shared with the
boletos arbitrary; no behavior change.
The weekly Update datasets workflow already rebuilds every generated table (Banco Central, CBO, CFOP, IBGE, CNAE, CONCLA, NCM) and opens a pull request; its description now comes from scripts/data-summary.ts: per table, how many entries were added and removed, with a sample of each, and a reminder to check the source when a table shrinks. The title stops naming IBGE/CONCLA only.
A Preview workflow builds the package and publishes it with pkg-pr-new (pinned to 0.0.88), so a change can be installed from its pull request before it is merged; the pkg.pr.new GitHub App posts the install command as a comment. The job needs no token and only contents: read, so it also runs for forks. It stays off until the app is installed and the PKG_PR_NEW repository variable is true.
jsr.json names the package, its files and its exports: the root plus one subpath per utility folder, mirrored from the npm subpaths by scripts/jsr.ts (npm run build:jsr; the Check workflow fails when it is stale). The public API has no slow types, so nothing had to change in src/. - Tests workflow, Deno job: `deno publish --dry-run` on every pull request. - Release workflow: a publish-jsr job publishes through OIDC (id-token: write, no token stored) when a release is created, off until the JSR package exists, the repository is linked to it and the JSR_PUBLISH variable is true. - release-please bumps the version in jsr.json together with package.json.
…rowser
A Playground page (en and pt-BR): pick any exported function, edit its arguments
as a JSON array and see the result. playground.js, loaded only on that page,
imports the ESM build of the latest release from jsDelivr (unpkg as fallback), so
there is no build step and the page runs what npm install gives. Arguments go
through JSON.parse, never eval; a Date is written as {"$date": "..."}; async
utilities are awaited and a rejection is shown, not thrown. `?fn=<name>` opens
on a given function.
The UMD build is not used: jsDelivr serves .cjs as application/node with nosniff,
which a browser refuses as a script. The pages join the sidebars, so build:site
gives them their shells and sitemap entries.
143df95 to
1de31c7
Compare
1de31c7 to
ab6c6ad
Compare
…equest vercel.json publishes docs/ as it is (no install, no build) as a Vercel preview deployment per pull request, with the URL posted as a comment by the Vercel app, forks included. cleanUrls serves /getting-started from getting-started.html, as GitHub Pages does, so the history-mode URLs behave the same. Production stays on GitHub Pages: deployments of main are turned off, every response carries X-Robots-Tag: noindex, and a deployment is skipped when nothing under docs/ changed.
…und out of the index
With two registries in the workflow, `publish` says too little: the jobs are now publish-npm and publish-jsr. The JSR job loses its JSR_PUBLISH gate and runs on every release; it needs the one-time JSR setup documented at the top of the workflow, and the npm job does not depend on it. The npm trusted publisher is bound to the workflow file and the environment, not to the job name, so the rename does not affect it.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
commit: |
… the branch's last deployment
…Angular and plain JavaScript
84ebe71 to
d715484
Compare
The templates of the document field guide were nineteen files in one folder, named by framework and part. They are now one folder per framework, so a framework's field, mask and form sit together. The base field the address guide reuses is generated there too, and is no longer kept in the repository, like the rest of what the generator writes.
It sat above the tabs on a negative margin, off to one side of them. The picker and the tabs are the two choices a guide offers, so they now share one strip: the picker first, then the tabs.
An Angular component is an element of its own, so a field's label, input and message were laid out inside it rather than by the form, and every Angular demo stood out from the React, Vue and vanilla ones next to it. Each of those components now steps aside with `display: contents`, and the element the runner mounts React and Vue into does the same, so the four demos of a guide are the same demo.
In front of the tabs it read as one of them, and a select on a tab bar has nowhere to sit. The tabs now start the strip and the document closes it, quieter than they are and on a line of its own when the screen is narrow. Two copies of these rules had also been left behind by a merge.
The demo went in as the first thing of its panel, so on the address and the state and city guides it landed above the line that says what is about to happen. It now goes between that line and the code, which is where the document field guide already had it.
The forms logged them, which is out of sight of the demo. They now show what the form holds, the values without their masks, formatted as JSON.
A select on its own says nothing about what it changes. It now comes with the question it answers, above the framework tabs, and the line that opens the page no longer repeats it.
A list of field names and a loop over it, to set four values. It now sets them, which is shorter than the machinery was.
…oking The effect that fills the form asked the resource for its value without asking whether it had one. In an error state that throws, which left change detection with an exception to report and the form saying it was still looking something up. It now checks first.
Its old name said what one schema is for rather than what the guide covers, which is the four ways the validators reach a schema.
Picking a state fetched 154 KB of cities whether or not anyone was going to pick one, and the states came with the page. Each list is now fetched when its own select is focused, which is the first moment it is about to be read, and the cities are tied to the state they were asked for, so picking another state puts them back to being fetched on demand.
Rewriting these to load on focus left them without the abort the other guides show. A controller now marks a load as no longer wanted when another state is picked or the component goes, and whether the cities are loading is read from the state they were asked for, so a load that is dropped cannot leave its message behind. Angular says where a resource does this by itself.
Merge order
This one goes last. It now carries the rewrite of the documentation from #572 (closed): the getting started and migration pages in plain language, and the utilities reference restructured into short per-utility sections and trimmed to what a caller needs. Every open feature PR (#558 through #570) adds a section to
docs/utilities.mdanddocs/pt-br/utilities.md, so merging this first would make each of them rewrite its docs; merging it last means one rebase here, carrying the new sections into the new format.What does this PR do?
Items 2 to 7 of the distribution plan, one commit each, plus the plan for item 8. No domain utility, validator or table is added and nothing breaks; the package root still has zero runtime dependencies.
feat: add toStandardSchematest: draw valid documents from fast-check arbitrariesgenerate*inside 35 properties; nothing publishedci(datasets)ci(preview)ci(jsr)jsr.json, dry run in CI, OIDC publication on releasedocsci(docs)2. Standard Schema
One generic wrapper instead of a prebuilt schema per validator: a module with ~40 schemas imports every validator, the dataset-backed ones included (
isValidNcm114 KB,isValidCbo119 KB), and relies on each consumer's bundler proving every top-level call pure. The wrapper costs the validator handed to it (1.1 KB alone), covers options and future validators, and only wraps; no validation logic is added. The spec's types are copied (@standard-schema/spec, MIT) astypealiases and exported. It never throws. Docs (en, pt-BR) show TanStack Form, Zod (refine), Valibot (check) and react-hook-form'sstandardSchemaResolver.3. fast-check: kept internal, not published
The first revision of this PR published 13 arbitraries as subpaths with fast-check as an optional peer dependency. They are gone: a second dependency story to explain, for something nobody asked for. What they were good for stays inside the test suite, where it fixes a real weakness.
35 properties in 22 test files called
generateCpf(),generatePhone(type)and the like insidefc.property. Those utilities draw fromMath.random(), which the seed fast-check reports on a failure does not control, so a failing document could be neither replayed nor shrunk; several were properties in name only (fc.constant("landline")as the single arbitrary). They now draw the document throughfc.gen():The arbitraries (
cpfs,cnpjs,cnhs,pisNumbers,voterIds,processosJuridicos,boletos,phones) live insrc/_internals/test/, next to the existingarbitraries.ts, in four files because the lint rule caps a file at ten imports. They are built from fast-check primitives plus the check-digit internals; a base that must not be one digit repeated gets its second character offset from the first, so nothing is filtered. Thegenerate*utilities keep their own tests, and CONTRIBUTING states the rule.package.json, the lockfile and the docs have no mention of fast-check outside the test suite.Refactor, no behavior change: the boleto assembly moves from
generateBoletointo two internals shared with theboletosarbitrary, tested with literals checked againstisValidBoleto.7. Dataset refresh
The weekly
Update datasetsworkflow already rebuilds every generated table and opens a pull request, so no monthly one is added. New:scripts/data-summary.tswrites the description (per table, entries added and removed with a sample, and a reminder to check the source when a table shrinks). CONTRIBUTING gains a Datasets section listing the tables with no generator, because their source is a law or regulation: area codes and service prefixes (Anatel), holidays, CNJ órgãos and tribunals, IBAN lengths, IBGE state codes, legal nature categories, CST/CSOSN, professional councils, the CPF região fiscal digits and the voter ID state codes.5. pkg.pr.new
Previewworkflow: builds and runspkg-pr-new@0.0.88 publish --compact.contents: readonly and no token, so it works for forks. The pkg.pr.new GitHub App is installed on the repository, so the job runs on every pull request, with no opt-in variable.4. JSR
jsr.json: name, license, publish list and exports (the root plus one subpath per utility folder, generated byscripts/jsr.ts/npm run build:jsr, stale file fails the Check workflow).deno publish --dry-runpasses as is, since the lint rules already require explicit return types on the public API.deno publish --dry-runon every pull request.publish-jsrjob next to the npm one (renamed frompublishtopublish-npmso the two read as a pair), OIDC (id-token: write), no stored token, reusing the pinnedsetup-denoaction. It runs on every release, with no opt-in variable.jsr.json's version withpackage.json(extra-files), so no version is edited by hand.brazilian-utilsand the packagebrazilian-utils, and link this repository under the package's Settings -> GitHub Actions. Until that exists thepublish-jsrjob fails (the npm publish is a separate job and is not affected).6. Examples
/examples(en and pt-BR): a CPF field that formats as you type and validates once complete, in one tab per framework (React, Angular, Vue, vanilla). Each tab shows one file ofdocs/snippets/, complete and copyable, and loads a live demo of that same file in a lazy iframe the first time the tab opens:docs/snippets/live/run.jscompiles the React, Vue and Angular files in the browser (Babel, the Vue SFC compiler and Angular's JIT compiler from a CDN), the vanilla one runs as is. The mask keeps the caret next to the digit being edited and turns a deleted separator into a deleted digit. Each field fits the form library most projects use, shown under its tab and run in a browser once: react-hook-form (a controlled component that takes the input's props), Angular Reactive Forms (aControlValueAccessor), VeeValidate (v-model). The demos use the site's own theme:docs/styles.cssnow holds the docsify theme and the site's styles, linked by every page shell instead of an inline<style>copied into each one. Checked in headless Chromium: eight editing scenarios on each demo, the four tabs in both languages; the React, Angular and Vue files type-check in strict mode.9. Documentation rewrite (from #572)
The getting started and migration pages rewritten in plain language, the utilities reference restructured into short sections (one sentence, a few bullets, an example, a source line) and trimmed to what a caller needs,
CONTRIBUTING.mddescribing that shape. ThetoStandardSchemasection follows it and sits at the end of the reference.Docs preview per pull request
vercel.jsonpublishesdocs/as it is (no install, no build) as a Vercel preview deployment for every pull request that touches it, forks included, with the URL posted as a comment.cleanUrlsserves/getting-startedfromgetting-started.html, as GitHub Pages does, so the history-mode URLs behave the same. Production stays on GitHub Pages: deployments ofmainare turned off (git.deploymentEnabled) and every response carriesX-Robots-Tag: noindex, so previews never compete with brazilian-utils.com.br. The file validates against Vercel's published schema. The home page still loads the README frommain, so a README change is the one thing a preview does not show. To enable: vercel.com -> Add New -> Project -> importbrazilian-utils/javascript; Vercel reads the file, nothing else to configure.8. MCP server and Agent Skill (plan only, nothing implemented)
@brazilian-utils/mcp, in its own repository (brazilian-utils/mcp). Putting it here means a monorepo (workspaces, two release-please packages, a second publish job) for a package with runtime dependencies (@modelcontextprotocol/sdk,zod), against this repository's zero-dependency rule and single-package release flow. It depends on the published@brazilian-utils/brazilian-utils, so it needs nothing from this repo's internals.validate_document(type: cpf, cnpj, cep, boleto, pix_key, pix_payload, nfe_key, phone, ie, pis, cnh, renavam, voter_id, license_plate, ... plusoptions),format_document,parse_document,generate_document(flagged as test data),get_document_info(boleto, Pix key and payload, NF-e key, IBAN, certidão),lookup_code(table: cfop, cnae, cbo, ncm, bank, legal_nature, municipality, state, area_code),get_holidays/is_business_day/add_business_days,get_address_by_cep(the only networked one, marked as such),number_to_words/currency_to_words.SKILL.mdin the same repo teaching when to call which tool and the library's conventions (alphanumeric CNPJ, never-throw, deprecated names), reusing the Context7 rules.Verification
npm run check,check:duplication,check:unused,check:api(report gainstoStandardSchemaand its types, 58 lines, additions only),check:lockfile,check:dependencies,check:vex,build(publint, attw),build:llms/build:site/build:jsrwithout drift,deno publish --dry-run, commitlint, actionlint and zizmor (no findings).toStandardSchemais the only new root export.One
featcommit (toStandardSchema), so release-please will propose 2.5.0.Checklist
npm test).docs/utilities.mdanddocs/pt-br/utilities.md.npm run checkpasses locally (format, lint, types).npm run build:llms.