Skip to content

chore: enforce lint, cleanup, cut v1.0.0#99

Draft
jan-kubica wants to merge 5 commits into
mainfrom
chore/v1-cleanup-and-release
Draft

chore: enforce lint, cleanup, cut v1.0.0#99
jan-kubica wants to merge 5 commits into
mainfrom
chore/v1-cleanup-and-release

Conversation

@jan-kubica
Copy link
Copy Markdown
Contributor

  • Drop dead imports map in package.json (built output uses relative paths; dev/test resolution uses tsconfig paths).
  • Expose parse? on the base Validator type so consumers no longer need an unsafe cast to detect parsable validators. Generic Validator<T> still narrows the return type.
  • Enable no-non-null-assertion in .oxlintrc.json (was silently off) and run oxlint --deny-warnings plus oxfmt --check in CI so both gates fail loudly.
  • Address the 127 resulting violations: weighted-sum loops migrated to for (const [i, w] of WEIGHTS.entries()), random-element pickers routed through new randomPick/randomChar helpers, and length-checked or required-capture cases carry an explicit // SAFETY: comment.
  • Reformat 22 drifted files with oxfmt.
  • Bump 0.0.11.0.0; refresh CHANGELOG.md and SECURITY.md supported-versions accordingly.

Test plan

  • bun run lint clean
  • bun run format:check clean
  • bun run typecheck clean
  • bun test green (4228 tests)
  • CHANGELOG.md entry for v1.0.0 present, SECURITY.md updated to 1.x

The package.json `imports` map referenced `./dist/_*/*.js` for
subpath imports. dist is built with relative paths (no `#util/*`
references survive bundling), and `bun test` resolves `#util/*`
via tsconfig paths, so the map was a no-op for both consumers and
local dev.

The Validator type previously used a conditional intersection
that exposed `parse: undefined` on the unparametrized form. That
forced consumers (and the parse-test discovery loop) to fall back
to ad-hoc `(value as { parse?: unknown }).parse` casts. Move
`parse?` onto the base shape with a widened
`ParsedIdentifier | null` return; the conditional intersection
still narrows it to the precise `TParsed` for typed validators.
Replace 127 non-null assertions across validators and scripts with
either narrowed accessors or `// SAFETY:` comments paired with a
local eslint disable. The most common patterns:

- Weighted-sum loops `for (let i = 0; i < weights.length; i++)`
  rewritten to `for (const [i, weight] of weights.entries())` so
  the weight is intrinsically narrowed.
- `arr[randomInt(0, arr.length - 1)]` and `str[randomInt(...)]`
  routed through new `randomPick(arr)` and `randomChar(str)`
  helpers in `_util/generate`, which throw on empty input
  instead of returning `undefined`.
- Length-checked accesses (`v[0]!` after `v.length === N`) and
  required regex captures (`match[1]!` after `if (match)`) keep
  the assertion but pick up an explicit `// SAFETY:` comment so
  the rule can be enabled globally without losing the audit trail.

Behavior is preserved: 4228 tests still pass.
The lint rule was silently disabled in `.oxlintrc.json`, so `bun
run lint` reported clean while 127 forbidden assertions sat in
source. Flip the rule to `error`, run `lint` with
`--deny-warnings`, add a `format:check` script, and run both
`bun run lint` and `bun run format:check` in CI so the gates fail
loudly on regressions.

oxfmt also touched 22 source/markdown files that had drifted from
the configured style. Those are reformatting-only changes.
The npm registry pins `^0.0.1` to exactly `0.0.1` (a long-standing
semver quirk for major 0/minor 0 ranges). Anyone who installed the
package at `^0.0.1` is stuck on the very first publish, even though
the codebase has had public-facing changes since. Skipping
straight to 1.0.0 frees consumers from that lock and gives later
releases a normal semver runway.

No public-API breakage compared to the pre-1.0 line; the changes
described in CHANGELOG are improvements on top of the same surface.
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request bumps the project version to 1.0.0 and enforces the no-non-null-assertion lint rule. Key changes include refactoring manual loops to use array iterators, adding explicit undefined checks, and introducing utility functions for random selection. The Validator interface was also updated to include an optional parse method. Review feedback identified a potential runtime crash in the Panama RUC validator due to an insufficient length check in the NT branch and noted an inaccurate safety comment regarding buffer length.

Comment thread src/pa/ruc.ts Outdated
Comment on lines +145 to +147
// NT designation (xxNT-xxx-xxx)
// SAFETY: NT branch requires len === 4 (checked above).
// eslint-disable-next-line no-non-null-assertion
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The NT branch in computeDV can crash if segments.length is 3. The safety comment is incorrect because the initial check at the start of the function (line 121) only ensures a length of 3 or 4. If the length is 3 and the second segment is "NT", segments[3] will be undefined, and accessing its length property on line 155 will throw a runtime error. You should explicitly check for the existence of the fourth segment to avoid a runtime error.

Suggested change
// NT designation (xxNT-xxx-xxx)
// SAFETY: NT branch requires len === 4 (checked above).
// eslint-disable-next-line no-non-null-assertion
const s3 = segments[3];
if (s3 === undefined) {
return null;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6045b06. You're right — the early guard at the top of computeDV only rejected len === 4 && segments[1] !== "NT", so a 3-segment input with segments[1] === "NT" slipped through and crashed on segments[3]!.length. Rather than adding a runtime s3 === undefined check inside the NT branch, I tightened the guard to make the NT branch and len === 4 exactly equivalent ((len === 4) !== (segments[1] === "NT") → return null), which lets the existing s3 assertion stay sound and updates the SAFETY note to match. Regression test added in __test__/pa.test.ts ("1-NT-100 DV00" → invalid).

CC on behalf of @jan-kubica

Comment thread src/pa/ruc.ts Outdated
s1 +
z(6 - s2.length) +
s2;
// SAFETY: juridical branch produces buf of length 20.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment stating that the juridical branch produces a buffer of length 20 is inaccurate. Based on the construction logic (lines 217-222), the length is typically around 11-12 characters. While index 5 is safe to access because the length is at least 11, the documentation should be corrected to avoid confusion.

Suggested change
// SAFETY: juridical branch produces buf of length 20.
// SAFETY: juridical branch produces buf of sufficient length (>= 11).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6045b06. You're right that "length 20" was wrong for the juridical buf — z(10 - s0.length) + s0 only collapses to 10 chars when s0.length <= 10, otherwise it's longer, and the s1/s2 parts vary the same way. Replaced the comment with the actual invariant used at the access site: s0 is zero-padded to at least 10 chars, so buf[3..6] are always defined regardless of the segment lengths.

CC on behalf of @jan-kubica

The early format guard only rejected 4-segment inputs whose second
segment was not "NT", but allowed a 3-segment input with "NT" in
position 1 (e.g. "1-NT-100"). The NT branch then accessed
`segments[3]` and called `.length` on `undefined`, crashing instead
of returning null/INVALID_FORMAT.

Tighten the guard so the NT branch and a 4-segment shape are
exactly equivalent, drop the misleading "len === 4 (checked above)"
SAFETY note in favour of one matching the new guard, and replace
the juridical "buf of length 20" SAFETY note with the actual
invariant: s0 is zero-padded to at least 10 chars, so buf[3..6]
are defined.

Add a regression test for the previously-crashing 3-segment NT
input.
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