Dev - #51
Merged
Merged
Conversation
…ated documentation and scripts
- context.ts: fix OPTIONAL_VAR_PATTERN so {{key|fallback}} message
templates split on the pipe correctly instead of swallowing the
whole placeholder into the key
- is-boolean.ts: anchor TRUE_PATTERN/FALSE_PATTERN alternatives so
substrings like "t" or "no" can't match anywhere in the input
(e.g. "October" was coercing to true)
- is-date.ts: fix PRECISION_INDEX so the "d" shorthand maps to the
same precision as "day" instead of colliding with "hours"/"hr"
- is-tuple.ts: check the coerced array (not the raw input) for the
array-type check, and enforce tuple arity so missing/extra
elements are rejected instead of silently accepted
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- is-date.ts: add abbreviated precision keys (yr/mo/d/hr/min/sec) to setPrecision(), and validate Date/number input with date-fns isValid() in coerceDateString() so invalid dates aren't silently coerced into bogus "NaN"-based strings - is-defined.ts: correct the JSDoc, which claimed null was rejected even though the code (and its test) only ever checked undefined - is-number.ts / is-integer.ts: actually reject bigint inputs that lose precision when coerced to number, instead of silently falling through and returning the rounded value - is-email.ts: wire up the previously-ignored ignoreMaxLength and domainSpecificValidation options instead of hardcoding ignore_max_length and leaving domain_specific_validation unset - is-time.ts: fix TIME_PATTERN so a dangling separator with no following digits (e.g. "12:30:") is no longer accepted Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- matches.ts: reset lastIndex before each test() call so a caller passing a global/sticky RegExp doesn't get nondeterministic pass/fail results across repeated validations of the same input - exists.ts: check the property descriptor using context.property (the actual key being validated) instead of input, which is always undefined at that point in the check — previously exists() rejected any key whose value is genuinely undefined, even though the key itself is present on the object Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- all-of.ts / one-of.ts: pass the combinator's own options (onFail, coerce) to the underlying validator() factory call, matching the other combinators (nullable, optional, required, pipe); previously these were silently dropped. Also drop the dead `context || options` fallback since context is always a real Context inside the rule fn. - utilities.ts: iif() now determines whether the "check" validator passed by whether it throws, not by whether it returns something other than undefined - some validators (e.g. isUndefined) legitimately return undefined on success, which previously made iif pick the "else" branch even though the check passed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds missing rejection-path, option, and edge-case tests across
nearly every rule file (coverage: 91.0% -> 99.5% statements,
88.8% -> 98.0% branches, 93.2% -> 98.1% functions), and fixes five
real bugs surfaced while writing/verifying those tests:
- is-tuple.ts: item label was suffixed with the index twice
("Point[0][0]" instead of "Point[0]")
- is-object.ts: additionalFields: 'error' crashed with "Cannot read
properties of undefined" instead of reporting a validation error,
because context.fail() was called with an undefined rule
- is-date.ts: the timezone suffix for offsets behind UTC (e.g. US
timezones) was just a bare "-" with no hours/minutes, due to an
operator-precedence mistake in the ternary building the string
- is-alpha.ts / is-alphanumeric.ts / is-base64.ts: stray unmatched
leading quote in the failure message
- is-iban.ts: whitelist/blacklist options were never forwarded to
the underlying validator, so they were silently ignored
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
src/factories.ts (a re-export of rules/index.ts) and src/helpers/object.utils.ts (omitKeys/pickKeys) were never imported from anywhere and not part of the public API surface - confirmed via grep and a clean dpdm dependency check. Removing them brings src/ statement coverage to 100%. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds `npm run bench` (benchmark/harness.ts + cli.ts): a duration-based
(not fixed-sample-count) throughput/memory benchmark runner with a
`-s`/`--rule` selector (comma-separated, case-insensitive, `*`
wildcard, "all"/"none"), colored output (ansi-colors), and one
benchmark file per rule under benchmark/rules/, auto-discovered.
Writing and profiling those benchmarks (node --cpu-prof) surfaced a
real, verified performance bug: nested validator calls throughout the
combinator layer (pipe, allOf, is-array/is-record/is-tuple/is-object's
item loops, one-of's branch loop) passed the parent context as the
*2nd* ("options") argument instead of the 3rd ("context") one. The
validator wrapper only skips creating a child Context when context
arrives in its own slot and the callee has no extra options - passing
it as "options" instead unconditionally forced a Context.extend() on
every nested call, even when nothing needed merging. Fixed all of
those call sites, and additionally:
- Context.extend() itself: replaced Object.entries()+destructuring
(allocates a discarded array of [key, value] pairs) with
Object.keys()+indexed access, and Object.setPrototypeOf() after
creation (one of the more expensive object operations in V8) with
Object.create() at creation time.
- is-array.ts/is-record.ts: the per-item "onFail" customization is now
set once on the reused item context instead of passed as a fresh
options object on every item, so those calls can take the
extend()-skipping fast path too.
Measured effect (isolated before/after benchmarks): lengthMin ~2.3x,
isObject with a 50-field schema ~6.4x, isObject nested one level
~1.9x, isArray(20 items) ~1.6x. No behavior change - full suite still
passes, including new tests for the label/prototype-inheritance edge
case this uncovered in is-object.ts.
Also fixes several typos found while reviewing rules to benchmark
them: wrong @validator tags in is-bigint.ts/is-instanceof.ts
(copy-pasted from other rules), "oerce"/"grater" typos, and "lover"
instead of "lower" in is-lt.ts/is-lte.ts's actual error messages
(updated the tests asserting on that text accordingly).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…y-rules
nullable.ts, optional.ts and required.ts all called their wrapped
rule as `nested(input, context)` - context in the 2nd ("options")
slot instead of the 3rd, the same needless-extend()-forcing mistake
already fixed elsewhere in this codebase for pipe/allOf/is-array/
is-record/is-tuple/is-object/one-of. Fixed all three.
Also fixes two more copy-pasted JSDoc blocks found while writing
their benchmarks: fixed.ts's whole doc comment (tag *and*
description) was copied from required.ts and described required's
behavior, not fixed's; nullable.ts's @validator tag said "optional".
Adds benchmarks for the rest of utility-rules: exists, fixed,
nullable, optional, required, getLength, stringReplace, stringSplit,
trim, trimStart, trimEnd.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ments Adds benchmark/rules/*.bench.ts for all 31 format-rules (isAlpha through matches), completing rule-category benchmark coverage. Also fixes two copy-paste JSDoc bugs found while writing the benchmarks: isMobilePhone's doc described email validation, and matches' doc described UUID coercion. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds resultsToMarkdown() to render the same results table as plain-text Markdown (no ANSI codes), and has the CLI write it to BENCHMARKS.md at the project root after every run. Also prints a one-line "✓ <case name>" as each case finishes, since a full run (~150+ cases) previously left the console silent for minutes. BENCHMARKS.md is gitignored since its numbers are machine-dependent and regenerated on every run. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
heapUsedPerOpBytes/rssPerOpBytes were computed by forcing a GC right before reading the "after" snapshot. Since the benchmarked fn's return value is always discarded, everything it allocated was garbage by that point, so the forced GC collected it right before we looked - making every case report ~0 bytes/op regardless of actual allocation (verified isObject's 50-field case, which clearly allocates, showed exactly 0). Removing that second forceGc() (keeping the one before the pass, for a clean baseline) surfaces real, sensible numbers, e.g. isObject ~1.8-3.7 KB/op vs isBoolean ~0.01-0.4 KB/op. BENCHMARKS.md regenerated with the fix. Also stop ignoring BENCHMARKS.md so it's tracked in git. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- forwardRef/iif (core/utilities.ts) still called nested validators as
nested(input, context) instead of nested(input, undefined, context) -
the wrong-slot perf/correctness regression already fixed everywhere
else in this project's rules, but missed here since these two live in
core, not rules/.
- isBigint silently accepted a plain number without coerce: true,
inconsistent with every sibling type-rule and with toBigint's own
{ coerce: true } default in src/index.ts; a non-integer number also
leaked a raw RangeError message instead of failing cleanly. Now
requires coerce for both number and string input, and always fails
with "Value must be a BigInt".
- isEmail: "must much" -> "must match" typo in its failure message.
- isMobilePhone/isSWIFT: "a valid a ..." double-article typo.
- isPassportNumber: unmatched closing paren in "... PassportNumber)".
- JSDoc fixes: isDate (claimed a date string validates without
coerce - it doesn't), isObject (didn't mention additionalFields/
caseInSensitive/detectCircular at all), nullable ("undefined of
null" typo), getLength (didn't mention Set/Map support), oneOf/pipe
(both had an empty JSDoc block).
Tests updated/added for the behavior changes; BENCHMARKS.md refreshed
from a full run after the fixes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds docs/api.md (the Validator shape, .silent(), pre-built instances
vs. factories, ExecutionOptions, error shape, composition patterns, and
how to write a custom rule) plus one page per rule category -
docs/api/{type,logical,utility,format}-rules.md - covering every
exported validator function with its signature, options, behavior, and
a verified usage example.
Every example was checked against the matching test/**/*.spec.ts
fixtures (or run directly against @browsery/validator where no spec
existed) rather than invented, which is how the bugs fixed in the
previous commit were found.
README.md is rewritten with an accurate feature summary, a verified
quick-start example, a script reference table, a benchmarking section,
and links into the new docs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.