Skip to content

feat!: v0.2.0 — reshape ParseIssue and strengthen ParseResult types#59

Merged
roottool merged 9 commits intomainfrom
feat/v0.2.0
Mar 1, 2026
Merged

feat!: v0.2.0 — reshape ParseIssue and strengthen ParseResult types#59
roottool merged 9 commits intomainfrom
feat/v0.2.0

Conversation

@roottool
Copy link
Owner

@roottool roottool commented Mar 1, 2026

Description

Implements all breaking changes planned for v0.2.0, focused on making ParseIssue and ParseResult more precise and honest.

  • Remove ParseIssue.path (was always readonly [] — carried no information)
  • Add ParseIssue.key: string (required, identifies the offending FormData key)
  • Narrow ParseResult failure issues to a non-empty tuple [ParseIssue, ...ParseIssue[]]
  • Export SuccessResult and FailureResult named utility types
  • Update all documentation, examples, and skill references to match

Type of Change

  • Refactoring (non-breaking change improving code structure)
  • Bugfix (non-breaking change fixing an issue)
  • Security fix (non-breaking change addressing a security issue)
  • Documentation (changes to documentation only)
  • Tests (adding or updating tests)
  • Tooling / CI (changes to build tools, CI configuration)

Note: Despite the "refactoring" checkbox, this PR contains breaking changes to the public API under the v0.x versioning policy (minor bump = effective major).

Boundary Checklist (Required for Implementation Changes)

  • No interpretation
    • Key names are treated as opaque strings
    • No structural inference ([], ., brackets, paths, etc.)
  • No silent behavior
    • No merging, overwriting, autofixing, or implicit resolution
    • All boundary violations are reported explicitly
  • Boundary respected
    • No validation, coercion, schema, framework conventions, or business logic

📘 Boundary rules & non-goals:
https://github.com/roottool/safe-formdata/blob/main/AGENTS.md

Security & API Stability

Security Impact

  • No impact on security (no changes to forbidden keys, prototype safety)
  • Reviewed against security rules in AGENTS.md

API Contract

  • No changes to public API (parse function signature, type definitions)
  • If API change: Is this a breaking change? Yes — see migration guide below and CHANGELOG.md

Breaking changes:

  1. ParseIssue.path removed — callers referencing issue.path must delete those references
  2. ParseIssue.key: string added (required) — type definitions referencing ParseIssue must be updated
  3. issues: [ParseIssue, ...ParseIssue[]] on failure — explicit type annotations may need updating

Versioning

  • Change is compatible with current v0.x versioning policy
  • If breaking change proposed: Justification for major version bump
    • Under the 0.x policy, this minor bump is treated as an effective major. Breaking changes are documented in CHANGELOG.md.

Testing

Automated Checks

  • TypeScript type checking passes (bun run check:type:source)
  • All tests pass (bun run test)
  • Build succeeds (bun run build)

Additional Verification

  • Existing tests updated to assert issue.key (removed issue.path assertions)
  • boundary-validator skill run against all changes; findings resolved

Automated Review (Optional)

boundary-validator was run as part of this PR. All findings were resolved:

  • Added explicit ParseIssue[] type annotation to issues array in parse.ts
  • Added comment explaining the destructuring pattern used to avoid type assertion
  • Fixed JSDoc example: if (result.data)if (result.data !== null)
  • Fixed invalid_key description: "unsafe characters" → "empty or not a string"

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

roottool and others added 9 commits March 2, 2026 01:28
BREAKING CHANGE: ParseIssue.path (always-empty array for external format
compatibility) is removed. It conveyed no information and implied false
Standard Schema compliance.

BREAKING CHANGE: ParseIssue.key type changes from optional `unknown` to
required `string`. FormData keys are always strings per spec; the previous
type was misleading.

ParseIssue is now: { code: IssueCode; key: string }

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reflect removal of path field and key type change (unknown → string)
in AGENTS.md, README.md, and all boundary-validator skill references.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BREAKING CHANGE: ParseResult failure branch issues type changes from
ParseIssue[] to [ParseIssue, ...ParseIssue[]], reflecting the invariant
that at least one issue always exists when data is null.

Uses destructuring at the return site to let TypeScript infer the
non-empty tuple type without a type assertion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Derived from ParseResult via Extract, providing named aliases for
each branch of the discriminated union without changing ParseResult itself.

  SuccessResult = Extract<ParseResult, { data: Record<string, string | File> }>
  FailureResult = Extract<ParseResult, { data: null }>

FailureResult.issues is [ParseIssue, ...ParseIssue[]], reflecting the
non-empty tuple introduced in the previous commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous description claimed keys with characters outside [a-zA-Z0-9_-]
were rejected, but the implementation only rejects empty strings and
non-string keys. Keys are treated as opaque strings per design.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Object.create(null) returns any; using a type annotation instead of
a cast (as) lets TypeScript verify assignability rather than silence it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add explicit `ParseIssue[]` type annotation to `issues` array (was inferred as `never[]`)
- Add comment explaining destructuring pattern used to avoid type assertion
- Fix JSDoc example: `if (result.data)` → `if (result.data !== null)`
- Fix JSDoc description: "unsafe characters" → "empty or not a string" for invalid_key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ParseResult type in api-contract

- Add SuccessResult/FailureResult utility types to Public API section
- Update ParseResult failure branch: ParseIssue[] → [ParseIssue, ...ParseIssue[]]
- Update constraint description to mention "non-empty tuple"
- Add checklist item for SuccessResult/FailureResult derivation rule
- Update Last updated date

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Document breaking changes with before/after examples and migration steps:
- ParseIssue.path removal
- ParseIssue.key addition (required string)
- issues type narrowed to non-empty tuple [ParseIssue, ...ParseIssue[]]

Use [Unreleased] header to align with planned workflow automation
that will auto-rewrite it to [v{version}] at release time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@roottool roottool self-assigned this Mar 1, 2026
@roottool roottool merged commit dcdd746 into main Mar 1, 2026
11 checks passed
@roottool roottool deleted the feat/v0.2.0 branch March 1, 2026 17:25
@codecov
Copy link

codecov bot commented Mar 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (07dffcc) to head (4a201ca).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main       #59   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            3         3           
  Lines           22        23    +1     
  Branches         6         5    -1     
=========================================
+ Hits            22        23    +1     
🚀 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.

@roottool roottool added the breaking change Introduces a backward-incompatible API or behavior change label Mar 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Introduces a backward-incompatible API or behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant