Skip to content

Speed up the hot paths, no behaviour change - #617

Merged
hyanmandian merged 16 commits into
claude/bundle-shrinkfrom
claude/perf-hot-paths
Sep 29, 2026
Merged

hyanmandian merged 16 commits into
claude/bundle-shrinkfrom
claude/perf-hot-paths

Conversation

@hyanmandian

@hyanmandian hyanmandian commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Third PR of the stack (base: the bundle PR). Runtime performance of the hot paths with zero behaviour change: a differential check compared every export of this branch with #615 on 12.9M calls (valid, masked, hostile strings, numbers, null, null-prototype objects, a throwing toString, holidays across dates and states) and found 0 differences.

Biggest wins (ops/s, #615 → this PR, Node, same machine, best of 5)

function before after speedup
getCodeByMunicipalityName 9.4K 4.86M 517x
getCid10 51K 8.0M 158x
getMunicipalities() 289 31K 108x
getMunicipalitiesByAreaCode 72K 2.57M 35x
getMunicipalityByCode 97K 2.90M 30x
getStateCodeByName 0.23M 6.83M 29x
isBusinessDay 1.23M 17.4M 14x
isHoliday 0.94M 8.6M 9x
getBankByCode 3.8M 18.9M 5x
capitalize 0.28M 0.92M 3.3x
isValidPixPayload, getPixPayloadInfo ~2x
getCpfInfo 4.4M 6.7M 1.5x

What changed

  • Holidays: isHoliday/isBusinessDay read the memoized days instead of rebuilding Date objects per call; the business-day walk reuses one Date (these exports also get 218 to 258 B smaller).
  • Lookups: lazily built indexes instead of per-call scans (municipality names, state names, CID-10 offsets, banks by code and ISPB, area codes), through a shared _internals/read-cached.
  • capitalize: default word sets cached (the linear word scan itself is in Validation round: fix every audited edge case, docs and convention drift #615).
  • Pix: CRC-16 with a lazily built 256-entry table; TLV parsed without regexes.
  • getCpfInfo caches the states of each fiscal region; getCities() builds its cached list with toSorted.

Size

Speed never bought bytes on shared helpers: fast paths in the sanitizers, toStringSafe, removeAccents and the mask helper were measured (up to 3x on functions already above 5M ops/s, for +40 to +290 B on 130+ exports) and left out. 185 of the 198 exports are within 15 B of #616 (isValidCpf 500 B and isValidCnpj 796 B unchanged). The 13 that grow are the ones that gain algorithmically: isValidPixPayload/getPixPayloadInfo +323 B, getMunicipality/getCodeByMunicipalityName +257 B, generatePixPayload +250 B, getMunicipalitiesByAreaCode +134 B, getCpfInfo +123 B, getBankByCode/getBankByIspb +114 B, getMunicipalities +82 B, getStateCodeByName +60 B, getCid10 +48 B, getBanks +28 B. Whole package: 1,420,542 B / 300,083 gzip (#616: 1,419,421 / 299,486; main: 1,889,707 / 335,145).

Gates

vp check, tests with 100% coverage (also under TZ=America/Sao_Paulo and TZ=UTC), build, jscpd, knip, check:api, commitlint, Stryker 100% on the changed files.

@vercel

vercel Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
brazilian-utils Ready Ready Preview Sep 29, 2026 11:07am UTC

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 3e75c4bb-278e-4eb7-b4d4-368c3fdf8020

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@hyanmandian hyanmandian added the tree-shaking: accepted The size increase reported by the tree-shaking check is intentional label Sep 29, 2026
@pkg-pr-new

pkg-pr-new Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@brazilian-utils/brazilian-utils@617

commit: 2b562e0

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Tree-shaking report

✅ No size regression. 16 grew, 8 shrank out of 198 exports.

Base Head Δ
Pre-existing exports, all imported 1386.2 KB 1387.2 KB (gzip 293.0 KB) +1.1 KB (+0.1%)
Full import 1386.2 KB 1387.2 KB (gzip 293.0 KB) +1.1 KB (+0.1%)
Exports 198 198 0

What changed (24)

Export Base Head Δ gzip
🟡 getPixPayloadInfo 4.8 KB 5.1 KB +323 B (+6.5%) 2.3 KB
🟡 isValidPixPayload 4.3 KB 4.7 KB +323 B (+7.3%) 2.1 KB
🟢 addBusinessDays 9.5 KB 9.2 KB -258 B (-2.7%) 3.2 KB
🟢 differenceInBusinessDays 9.3 KB 9.1 KB -258 B (-2.7%) 3.2 KB
🟢 subBusinessDays 9.6 KB 9.3 KB -258 B (-2.6%) 3.3 KB
🟡 getCodeByMunicipalityName 76.0 KB 76.3 KB +257 B (+0.3%) 29.6 KB
🟡 getMunicipality 76.9 KB 77.2 KB +257 B (+0.3%) 30.1 KB
🟢 isHoliday 8.6 KB 8.4 KB -252 B (-2.8%) 2.9 KB
🟡 generatePixPayload 4.7 KB 5.0 KB +250 B (+5.2%) 2.2 KB
🟢 isBusinessDay 8.8 KB 8.6 KB -218 B (-2.4%) 3.0 KB
🟡 getMunicipalitiesByAreaCode 90.3 KB 90.4 KB +134 B (+0.1%) 33.0 KB
🟡 getCpfInfo 994 B 1.1 KB +123 B (+12.4%) 683 B
🟡 getBankByCode 26.5 KB 26.6 KB +114 B (+0.4%) 7.9 KB
🟡 getBankByIspb 26.4 KB 26.5 KB +114 B (+0.4%) 7.8 KB
🟡 getMunicipalities 76.2 KB 76.3 KB +82 B (+0.1%) 29.5 KB
🟡 getStateCodeByName 2.3 KB 2.4 KB +60 B (+2.5%) 691 B
🟡 getCid10 721.5 KB 721.6 KB +48 B (+0.0%) 113.5 KB
🟡 getBanks 26.2 KB 26.2 KB +28 B (+0.1%) 7.6 KB
🟢 getNfeKeyInfo 2.2 KB 2.2 KB -14 B (-0.6%) 1.2 KB
🟢 isValidNfeKey 1.6 KB 1.6 KB -14 B (-0.8%) 956 B
Show the other 4
Export Base Head Δ gzip
🟡 getCities 64.2 KB 64.2 KB +4 B (+0.0%) 26.4 KB
🟡 isValidCid10 6.7 KB 6.7 KB +4 B (+0.1%) 2.4 KB
🟢 capitalize 2.5 KB 2.5 KB -3 B (-0.1%) 1.3 KB
🟡 getHolidays 8.4 KB 8.4 KB +2 B (+0.0%) 2.9 KB
All exports (198)
Export Base Head Δ gzip
⚪ GetAddressInfoByCepError 112 B 112 B 0 B 124 B
⚪ GetAddressInfoByCepNotFoundError 205 B 205 B 0 B 145 B
⚪ GetAddressInfoByCepServiceError 204 B 204 B 0 B 143 B
⚪ GetAddressInfoByCepValidationError 203 B 203 B 0 B 144 B
⚪ GetCepInfoByAddressError 112 B 112 B 0 B 124 B
⚪ GetCepInfoByAddressNotFoundError 205 B 205 B 0 B 145 B
⚪ GetCepInfoByAddressValidationError 203 B 203 B 0 B 144 B
🟢 addBusinessDays 9.5 KB 9.2 KB -258 B (-2.7%) 3.2 KB
🟢 capitalize 2.5 KB 2.5 KB -3 B (-0.1%) 1.3 KB
⚪ convertCurrencyToWords 2.0 KB 2.0 KB 0 B 1.0 KB
⚪ convertDateToWords 2.4 KB 2.4 KB 0 B 1.2 KB
⚪ convertLicensePlateToMercosul 632 B 632 B 0 B 425 B
⚪ convertNumberToWords 1.6 KB 1.6 KB 0 B 817 B
🟢 differenceInBusinessDays 9.3 KB 9.1 KB -258 B (-2.7%) 3.2 KB
⚪ formatBoleto 668 B 668 B 0 B 407 B
⚪ formatCEP 489 B 489 B 0 B 348 B
⚪ formatCNPJ 639 B 639 B 0 B 430 B
⚪ formatCPF 540 B 540 B 0 B 376 B
⚪ formatCaepf 506 B 506 B 0 B 358 B
⚪ formatCbo 491 B 491 B 0 B 350 B
⚪ formatCei 503 B 503 B 0 B 356 B
⚪ formatCep 489 B 489 B 0 B 348 B
⚪ formatCertidao 532 B 532 B 0 B 361 B
⚪ formatCest 493 B 493 B 0 B 350 B
⚪ formatCfop 489 B 489 B 0 B 349 B
⚪ formatCid10 478 B 478 B 0 B 339 B
⚪ formatCnae 493 B 493 B 0 B 352 B
⚪ formatCnh 536 B 536 B 0 B 374 B
⚪ formatCno 507 B 507 B 0 B 357 B
⚪ formatCnpj 639 B 639 B 0 B 430 B
⚪ formatCns 506 B 506 B 0 B 352 B
⚪ formatCpf 540 B 540 B 0 B 376 B
⚪ formatCurrency 1023 B 1023 B 0 B 613 B
⚪ formatIban 267 B 267 B 0 B 228 B
⚪ formatIsbn 15.3 KB 15.3 KB 0 B 5.5 KB
⚪ formatLegalNature 489 B 489 B 0 B 349 B
⚪ formatLicensePlate 361 B 361 B 0 B 270 B
⚪ formatNbs 496 B 496 B 0 B 353 B
⚪ formatNcm 494 B 494 B 0 B 352 B
⚪ formatNfeKey 724 B 724 B 0 B 461 B
⚪ formatPassport 187 B 187 B 0 B 174 B
⚪ formatPhone 2.6 KB 2.6 KB 0 B 1.2 KB
⚪ formatPis 540 B 540 B 0 B 377 B
⚪ formatProcessoJuridico 509 B 509 B 0 B 357 B
⚪ formatSuframa 499 B 499 B 0 B 354 B
⚪ formatVoterId 542 B 542 B 0 B 377 B
⚪ generateBoleto 1.4 KB 1.4 KB 0 B 779 B
⚪ generateCNPJ 843 B 843 B 0 B 524 B
⚪ generateCPF 694 B 694 B 0 B 456 B
⚪ generateCep 1.3 KB 1.3 KB 0 B 471 B
⚪ generateCnh 565 B 565 B 0 B 343 B
⚪ generateCnpj 843 B 843 B 0 B 524 B
⚪ generateCpf 694 B 694 B 0 B 456 B
⚪ generateLegalNature 5.0 KB 5.0 KB 0 B 1.6 KB
⚪ generateLicensePlate 308 B 308 B 0 B 243 B
⚪ generatePassport 226 B 226 B 0 B 191 B
⚪ generatePhone 670 B 670 B 0 B 419 B
⚪ generatePis 357 B 357 B 0 B 273 B
🟡 generatePixPayload 4.7 KB 5.0 KB +250 B (+5.2%) 2.2 KB
⚪ generateProcessoJuridico 821 B 821 B 0 B 523 B
⚪ generateRenavam 378 B 378 B 0 B 284 B
⚪ generateSuframa 451 B 451 B 0 B 335 B
⚪ generateVoterId 922 B 922 B 0 B 587 B
⚪ getAddressInfoByCep 5.3 KB 5.3 KB 0 B 2.1 KB
⚪ getAreaCodeByMunicipalityCode 23.6 KB 23.6 KB 0 B 5.6 KB
⚪ getAreaCodeInfo 3.1 KB 3.1 KB 0 B 989 B
⚪ getAreaCodesByState 814 B 814 B 0 B 463 B
🟡 getBankByCode 26.5 KB 26.6 KB +114 B (+0.4%) 7.9 KB
🟡 getBankByIspb 26.4 KB 26.5 KB +114 B (+0.4%) 7.8 KB
🟡 getBanks 26.2 KB 26.2 KB +28 B (+0.1%) 7.6 KB
⚪ getBoletoInfo 2.7 KB 2.7 KB 0 B 1.4 KB
⚪ getCbo 80.6 KB 80.6 KB 0 B 24.1 KB
⚪ getCepInfoByAddress 2.2 KB 2.2 KB 0 B 1.1 KB
⚪ getCertidaoInfo 1.0 KB 1.0 KB 0 B 594 B
⚪ getCest 102.3 KB 102.3 KB 0 B 24.1 KB
⚪ getCfop 56.9 KB 56.9 KB 0 B 5.1 KB
🟡 getCid10 721.5 KB 721.6 KB +48 B (+0.0%) 113.5 KB
🟡 getCities 64.2 KB 64.2 KB +4 B (+0.0%) 26.4 KB
⚪ getClassTrib 50.0 KB 50.0 KB 0 B 9.0 KB
⚪ getCnae 66.8 KB 66.8 KB 0 B 17.7 KB
⚪ getCnpjInfo 964 B 964 B 0 B 542 B
🟡 getCodeByMunicipalityName 76.0 KB 76.3 KB +257 B (+0.3%) 29.6 KB
🟡 getCpfInfo 994 B 1.1 KB +123 B (+12.4%) 683 B
⚪ getCstIbsCbs 963 B 963 B 0 B 534 B
⚪ getFormatLicensePlate 420 B 420 B 0 B 301 B
⚪ getGtinInfo 821 B 821 B 0 B 559 B
🟡 getHolidays 8.4 KB 8.4 KB +2 B (+0.0%) 2.9 KB
⚪ getIbanInfo 752 B 752 B 0 B 492 B
⚪ getIsbnInfo 15.1 KB 15.1 KB 0 B 5.4 KB
⚪ getLegalNature 5.7 KB 5.7 KB 0 B 1.9 KB
⚪ getLegalNatures 5.1 KB 5.1 KB 0 B 1.6 KB
⚪ getLegalNaturesByCategory 5.6 KB 5.6 KB 0 B 1.9 KB
🟡 getMunicipalities 76.2 KB 76.3 KB +82 B (+0.1%) 29.5 KB
🟡 getMunicipalitiesByAreaCode 90.3 KB 90.4 KB +134 B (+0.1%) 33.0 KB
🟡 getMunicipality 76.9 KB 77.2 KB +257 B (+0.3%) 30.1 KB
⚪ getMunicipalityByCode 76.3 KB 76.3 KB 0 B 29.6 KB
⚪ getNbs 51.1 KB 51.1 KB 0 B 11.3 KB
🟢 getNfeKeyInfo 2.2 KB 2.2 KB -14 B (-0.6%) 1.2 KB
⚪ getNfseKeyInfo 2.6 KB 2.6 KB 0 B 1.2 KB
⚪ getPixKeyInfo 2.7 KB 2.7 KB 0 B 1.3 KB
🟡 getPixPayloadInfo 4.8 KB 5.1 KB +323 B (+6.5%) 2.3 KB
⚪ getRegions 255 B 255 B 0 B 165 B
⚪ getServiceItem 25.5 KB 25.5 KB 0 B 8.2 KB
⚪ getStateByCep 3.6 KB 3.6 KB 0 B 1.0 KB
⚪ getStateByIbgeCode 2.4 KB 2.4 KB 0 B 700 B
⚪ getStateCapital 955 B 955 B 0 B 638 B
🟡 getStateCodeByName 2.3 KB 2.4 KB +60 B (+2.5%) 691 B
⚪ getStateNameByCode 2.2 KB 2.2 KB 0 B 594 B
⚪ getStates 2.2 KB 2.2 KB 0 B 539 B
⚪ getStatesByRegion 2.3 KB 2.3 KB 0 B 602 B
⚪ getTimezoneByState 755 B 755 B 0 B 342 B
🟢 isBusinessDay 8.8 KB 8.6 KB -218 B (-2.4%) 3.0 KB
🟢 isHoliday 8.6 KB 8.4 KB -252 B (-2.8%) 2.9 KB
⚪ isValidBankAccount 5.1 KB 5.1 KB 0 B 1.9 KB
⚪ isValidBoleto 1.8 KB 1.8 KB 0 B 970 B
⚪ isValidCEP 165 B 165 B 0 B 165 B
⚪ isValidCNPJ 796 B 796 B 0 B 447 B
⚪ isValidCPF 500 B 500 B 0 B 334 B
⚪ isValidCaepf 782 B 782 B 0 B 479 B
⚪ isValidCbo 6.7 KB 6.7 KB 0 B 1.7 KB
⚪ isValidCei 714 B 714 B 0 B 472 B
⚪ isValidCep 165 B 165 B 0 B 165 B
⚪ isValidCertidao 758 B 758 B 0 B 462 B
⚪ isValidCest 3.6 KB 3.6 KB 0 B 856 B
⚪ isValidCfop 2.0 KB 2.0 KB 0 B 700 B
🟡 isValidCid10 6.7 KB 6.7 KB +4 B (+0.1%) 2.4 KB
⚪ isValidClassTrib 1.8 KB 1.8 KB 0 B 610 B
⚪ isValidCnae 4.6 KB 4.6 KB 0 B 1.9 KB
⚪ isValidCnh 612 B 612 B 0 B 372 B
⚪ isValidCno 718 B 718 B 0 B 474 B
⚪ isValidCnpj 796 B 796 B 0 B 447 B
⚪ isValidCns 746 B 746 B 0 B 475 B
⚪ isValidCpf 500 B 500 B 0 B 334 B
⚪ isValidCreditCard 555 B 555 B 0 B 389 B
⚪ isValidCsosn 330 B 330 B 0 B 262 B
⚪ isValidCst 978 B 978 B 0 B 549 B
⚪ isValidCstIbsCbs 890 B 890 B 0 B 504 B
⚪ isValidEmail 244 B 244 B 0 B 198 B
⚪ isValidGtin 432 B 432 B 0 B 315 B
⚪ isValidIE 5.2 KB 5.2 KB 0 B 1.8 KB
⚪ isValidIban 531 B 531 B 0 B 376 B
⚪ isValidIe 5.2 KB 5.2 KB 0 B 1.8 KB
⚪ isValidIsbn 422 B 422 B 0 B 328 B
⚪ isValidLandlinePhone 890 B 890 B 0 B 561 B
⚪ isValidLegalNature 918 B 918 B 0 B 554 B
⚪ isValidLicensePlate 372 B 372 B 0 B 280 B
⚪ isValidMobilePhone 923 B 923 B 0 B 590 B
⚪ isValidNbs 3.5 KB 3.5 KB 0 B 1.3 KB
⚪ isValidNcm 29.6 KB 29.6 KB 0 B 9.0 KB
🟢 isValidNfeKey 1.6 KB 1.6 KB -14 B (-0.8%) 956 B
⚪ isValidNfseKey 2.2 KB 2.2 KB 0 B 1.1 KB
⚪ isValidPIS 418 B 418 B 0 B 315 B
⚪ isValidPassport 197 B 197 B 0 B 186 B
⚪ isValidPhone 1.9 KB 1.9 KB 0 B 963 B
⚪ isValidPis 418 B 418 B 0 B 315 B
⚪ isValidPixKey 2.5 KB 2.5 KB 0 B 1.2 KB
🟡 isValidPixPayload 4.3 KB 4.7 KB +323 B (+7.3%) 2.1 KB
⚪ isValidProcessoJuridico 711 B 711 B 0 B 432 B
⚪ isValidRegistroProfissional 1.3 KB 1.3 KB 0 B 787 B
⚪ isValidRenavam 518 B 518 B 0 B 358 B
⚪ isValidServiceItem 1.1 KB 1.1 KB 0 B 570 B
⚪ isValidServicePhone 1.0 KB 1.0 KB 0 B 573 B
⚪ isValidSuframa 611 B 611 B 0 B 418 B
⚪ isValidVin 898 B 898 B 0 B 600 B
⚪ isValidVoterId 728 B 728 B 0 B 433 B
⚪ parseBoleto 247 B 247 B 0 B 215 B
⚪ parseCaepf 199 B 199 B 0 B 183 B
⚪ parseCbo 198 B 198 B 0 B 182 B
⚪ parseCei 199 B 199 B 0 B 183 B
⚪ parseCep 198 B 198 B 0 B 182 B
⚪ parseCertidao 199 B 199 B 0 B 183 B
⚪ parseCest 198 B 198 B 0 B 182 B
⚪ parseCfop 198 B 198 B 0 B 182 B
⚪ parseCid10 177 B 177 B 0 B 170 B
⚪ parseCnae 198 B 198 B 0 B 182 B
⚪ parseCnh 199 B 199 B 0 B 183 B
⚪ parseCno 199 B 199 B 0 B 183 B
⚪ parseCnpj 298 B 298 B 0 B 240 B
⚪ parseCns 199 B 199 B 0 B 184 B
⚪ parseCpf 199 B 199 B 0 B 183 B
⚪ parseCurrency 610 B 610 B 0 B 409 B
⚪ parseIban 227 B 227 B 0 B 202 B
⚪ parseIsbn 150 B 150 B 0 B 150 B
⚪ parseLegalNature 198 B 198 B 0 B 182 B
⚪ parseLicensePlate 177 B 177 B 0 B 169 B
⚪ parseNbs 198 B 198 B 0 B 181 B
⚪ parseNcm 198 B 198 B 0 B 182 B
⚪ parseNfeKey 412 B 412 B 0 B 312 B
⚪ parseNfseKey 253 B 253 B 0 B 219 B
⚪ parsePassport 177 B 177 B 0 B 169 B
⚪ parsePhone 497 B 497 B 0 B 341 B
⚪ parsePis 199 B 199 B 0 B 183 B
⚪ parseProcessoJuridico 199 B 199 B 0 B 182 B
⚪ parseSuframa 198 B 198 B 0 B 181 B
⚪ parseVoterId 199 B 199 B 0 B 183 B
⚪ removeAccents 99 B 99 B 0 B 119 B
🟢 subBusinessDays 9.6 KB 9.3 KB -258 B (-2.6%) 3.3 KB
⚪ toStandardSchema 324 B 324 B 0 B 254 B
How this is measured

Every 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 tree-shaking: accepted label.

@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (e883225) to head (2b562e0).

Additional details and impacted files
@@                  Coverage Diff                   @@
##           claude/bundle-shrink      #617   +/-   ##
======================================================
  Coverage                100.00%   100.00%           
======================================================
  Files                       255       261    +6     
  Lines                      2558      2640   +82     
  Branches                    745       752    +7     
======================================================
+ Hits                       2558      2640   +82     
Flag Coverage Δ
node 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@vercel

vercel Bot commented Sep 29, 2026

Copy link
Copy Markdown

Deployment failed for project brazilian-utils with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/hyan-mandians-projects?upgradeToPro=build-rate-limit

@hyanmandian hyanmandian removed the tree-shaking: accepted The size increase reported by the tree-shaking check is intentional label Sep 29, 2026
… dates

isHoliday, isBusinessDay, addBusinessDays, subBusinessDays and differenceInBusinessDays
compared against freshly built Date objects on every call. They now read the memoized
year, month and day directly, and the business day walks validate their options once.

ops/sec (Mops/s, before -> after): isHoliday 0.94 -> 8.6, isBusinessDay 1.23 -> 17.4.
…m per call

Municipality name, state name, bank code and bank ISPB lookups scanned (and re-normalized) their
tables on every call; getCid10 walked the 2,046 categories per lookup; getMunicipalitiesByAreaCode
filtered every municipality of the states. Each now reads a cache built on first use. getBanks
copies with a literal instead of Object.assign.

ops/sec (before -> after): getCodeByMunicipalityName 9.4K -> 4.9M, getCid10 51K -> 8.0M,
getStateCodeByName 0.23M -> 6.8M, getMunicipalitiesByAreaCode 72K -> 2.6M,
getBankByCode 3.8M -> 18.9M, getBankByIspb 2.6M -> 17.9M, getMunicipalities() 289 -> 31K.
The default lower and upper case sets were rebuilt on every call and are now built once, and
case folding uses toUpperCase/toLowerCase, which give the same result as the pt-BR locale (only
tr, az and lt tailor case mapping).

ops/sec (before -> after): capitalize 0.28M -> 0.92M.
crc16Ccitt looped over the bits of every byte and built a TextEncoder per call; it now uses a
256 entry table built on first use and reads ASCII text straight from the string, keeping the
UTF-8 path for anything else. parseTlv reads the two digit id and length with char codes
instead of slicing and testing two regexes per field.

ops/sec (before -> after): isValidPixPayload 1.18M -> 1.9M, generatePixPayload 0.55M -> 0.85M,
getPixPayloadInfo 0.78M -> 0.97M.
getCpfInfo filtered the 27 state codes on every call; the states of each fiscal region are now
computed once and copied.

ops/sec (before -> after): getCpfInfo 4.4M -> 6.7M.
eachLocalDay built two Date objects per day walked; it now updates one Date with setTime and
builds only the local noon candidate it yields.

ops/sec (before -> after): addBusinessDays +250 (on top of the memo change) 16K -> 18K,
differenceInBusinessDays 7.1K -> 8.2K.
getCities() copies a cached, sorted list on every call. Since the names are unpacked from the packed
tables, the list built with flatMap and an in place sort was copied at 0.45M ops/s; the same list
built with toSorted is copied at 1.9M ops/s, the speed it had before the tables were packed.
…f the public modules

readBusinessDayRules, isBusinessDayUnderRules, BusinessDayRules, readMemoizedHolidays and
MemoizedHoliday were exported from src/is-business-day and src/get-holidays, which are build
entries, so they leaked into the declarations of those two subpaths. Each now lives in its own
_internals folder (the holiday memo with computeHolidays and its cache), with a test of its own.
The declarations of the subpaths are back to the public API.
The text said no Date is ever mutated, which stopped being literally true when the walk began
reusing one private scratch Date. It now says that no Date a caller sees is mutated and that the
walk reads the scratch Date through its UTC getters. The paragraph is rewrapped to the 100 column
width of the rest of the block.
…ric already does

sanitizeToAlphanumeric already upper cases its result, so the toUpperCase after it never changed
the key. The behavior is identical.
… with toSorted, like getCities

getCities and getMunicipalities built their cached sorted list with different calls. Both use
toSorted now; the ops per second of getMunicipalities() are unchanged (74k before and after).
… internal

The lazy table of category offsets sat in getCid10.ts next to the lazy table of subcategories it is
built from. It now has its own _internals folder and test, and getCid10 only reads it.
…lone

Reordering or splicing the states of one result must not reach the next call nor another region, and
two results never share an array.
The 100% mutation threshold failed on the fast paths this branch adds. Most are caches
and shortcuts whose slow path returns the very same value, so their mutants are
equivalent and carry a disable directive with the reason: the lookup and word set caches
and the UTF-8 pass and typed array bound of crc16Ccitt. New tests kill the rest:
crc16Ccitt checks the UTF-8 bytes of U+0080, parseTlv rejects an id whose digit is one
code off the range, and capitalize reads the default upper case words on its first call.

No behavior changes.
…llable mutants

The lookup caches checked `if (!index)` behind a Stryker directive, which also silenced the mutant
that turns the check into `if (false)`, a crash that the tests do kill. The caches of
getCodeByMunicipalityName, getCpfInfo and getMunicipalitiesByAreaCode now read through a new
readCached internal, `cache.get(key) ?? store(...)`, which has a test of its own (each key built
once, the same value handed back). getStateCodeByName builds its index with `??=` and a builder,
and findBank keeps one lazily built index per key. The five directives are gone and Stryker still
scores 100% on each file.

crc16Ccitt builds its table with Uint16Array.from, which drops the loop bound directive, and leaves
the ASCII pass through one early return, which leaves one directive where there were three, all on
the check whose `true` variant only sends an ASCII string down the UTF-8 pass.

The capitalize test for the default upper case words no longer carries the order it depends on in
its title. Only the first test that reaches the lazily built default set can see an emptied list, so
the directive on that array now says so. Stryker scores 100% on capitalize with the test in another
position and under shuffled test order.

No behavior changes.
The lookup caches and the tables they read add up to 0.3 KB to the dataset utils, so the size
table is measured again with node scripts/tree-shaking.ts on this branch.
@hyanmandian
hyanmandian added this pull request to stack #618 September 29, 2026 12:49
@hyanmandian
hyanmandian merged commit d294baa into main Sep 29, 2026
30 checks passed

This branch was successfully deployed

1 active deployment
Preview — 2b562e09 Deployed Sep 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant