Skip to content

💥 Support minLength and maxLength on iterator - #7183

Open
dubzzz wants to merge 2 commits into
next-v4_9_0from
claude/iterator-length-constraints-s31iw5
Open

💥 Support minLength and maxLength on iterator#7183
dubzzz wants to merge 2 commits into
next-v4_9_0from
claude/iterator-length-constraints-s31iw5

Conversation

@dubzzz

@dubzzz dubzzz commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Description

AI-agent disclosure: this PR was authored by an automated agent (Claude Code) and has not been line-by-line reviewed by a human before submission.

From an end-user point of view, fc.iterator can now generate finite iterators, and its constraints let users pick exactly which world they want to live in:

  • minLength? (default: 0) — minimal number of items yielded before completing. Number.POSITIVE_INFINITY is accepted and forces every generated iterator to be a never-ending one.
  • maxLength? (default: Number.POSITIVE_INFINITY) — maximal number of items yielded before completing. Any finite value makes every generated iterator finite.
  • noDefaultInfinity? (default: false) — moves the default maxLength to Number.MAX_SAFE_INTEGER, i.e. "only finite iterators, I don't care about the exact cap". An explicit maxLength: Number.POSITIVE_INFINITY still wins over it.
  • size? — caps the number of items yielded by finite iterators through the exact same helper as fc.array (maxGeneratedLengthFromSizeForArbitrary), maxLength staying the hard upper bound. size: 'max' goes up to Number.MAX_SAFE_INTEGER (iterators are lazy, so they are not tied to the 2³²−1 bound of arrays).

Breaking change (major, changeset included): by default fc.iterator(arb) no longer produces only never-ending iterators. Most generated iterators are now finite — lengths drawn in [0, 10] with the default size: 'small' — while a never-ending one still shows up from time to time (the target length is drawn with one extra slot above maxGeneratedLength; landing on it means never-ending, a 1-in-12 chance with defaults). Users relying on the previous always-infinite behavior migrate with fc.iterator(arb, { minLength: Number.POSITIVE_INFINITY }). As a consequence the return type also moves from Arbitrary<IteratorObject<T, never>> to Arbitrary<IteratorObject<T, undefined>>, and a fully-consumed iterator now prints without the trailing /*…*/ marker.

Why this design: the infinity handling deliberately mirrors the fc.double idiom (min/max accepting infinity, plus noDefaultInfinity to opt out of the infinite default), so the API stays consistent with existing fast-check conventions rather than introducing a new mode flag. The finite lengths reuse the array size machinery so size, baseSize and defaultSizeToMaxWhenMaxSpecified behave as everywhere else — with one deliberate edge case: an infinite maxLength is treated as "max not specified" for defaultSizeToMaxWhenMaxSpecified, since there is no uniform draw over an unbounded range. The drawn target length is decided once per generated value (before cloning), so all clones of a value share the same length and reproducibility by seed is preserved.

This PR is focused on that single concern; the snapshot and documentation updates it carries are direct consequences of the new default behavior. Tests added: constraint-resolution and validation cases on iterator (defaults, noDefaultInfinity, explicit finite/infinite bounds, size incl. 'max', throwing combinations), and generation-behavior cases on IteratorArbitrary (stops exactly at the drawn length, extra-slot draw producing never-ending iterators, no length draw when forced infinite, completed-iterator printing) plus finite-only and never-ending-only integration assertions — all of which fail without the implementation. Existing tests were migrated to the forced-infinite configuration to keep covering the previous behavior. NoRegression snapshots and the auto-generated docs examples were refreshed with the dedicated scripts.

Checklist

Don't delete this checklist and make sure you do the following before opening the PR

  • I have a full understanding of every line in this PR — whether the code was hand-written, AI-generated, copied from external sources or produced by any other tool
  • I flagged the impact of my change (minor / patch / major) either by running pnpm run bump or by following the instructions from the changeset bot
  • I kept this PR focused on a single concern and did not bundle unrelated changes
  • I followed the gitmoji specification for the name of the PR, including the package scope (e.g. 🐛(vitest) Something...) when the change targets a package other than fast-check
  • I added relevant tests and they would have failed without my PR (when applicable)

🤖 Generated with Claude Code

https://claude.ai/code/session_01FVQi7vQRR6HQEgp8mJhQVQ


Generated by Claude Code

claude added 2 commits July 26, 2026 10:43
Add support for minLength and maxLength constraints on the iterator
arbitrary. By default, generated iterators are mostly finite with
lengths computed the same way as for arrays (driven by size), while
never-ending iterators still get produced from time to time as the
default maxLength is Number.POSITIVE_INFINITY.

Mirroring the fc.double idiom for infinity handling:
- noDefaultInfinity: true moves the default maxLength to
  Number.MAX_SAFE_INTEGER so only finite iterators get produced
- an explicit finite maxLength also rules out never-ending iterators
- minLength: Number.POSITIVE_INFINITY forces only never-ending ones

The return type moves from IteratorObject<T, never> to
IteratorObject<T, undefined> as generated iterators may now complete.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVQi7vQRR6HQEgp8mJhQVQ
Internal test helpers and specs use fc.iterator as a never-ending
source of seeds and shrink paths. Now that iterator defaults to mostly
finite iterators, request never-ending ones explicitly through
minLength: Number.POSITIVE_INFINITY.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVQi7vQRR6HQEgp8mJhQVQ
@changeset-bot

changeset-bot Bot commented Jul 26, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: eb7cb79

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
fast-check Major
@fast-check/ava Patch
@fast-check/jest Patch
@fast-check/vitest Patch
@fast-check/worker Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 26, 2026

Copy link
Copy Markdown
@fast-check/ava

npm i https://pkg.pr.new/@fast-check/ava@7183

fast-check

npm i https://pkg.pr.new/fast-check@7183

@fast-check/jest

npm i https://pkg.pr.new/@fast-check/jest@7183

@fast-check/packaged

npm i https://pkg.pr.new/@fast-check/packaged@7183

@fast-check/poisoning

npm i https://pkg.pr.new/@fast-check/poisoning@7183

@fast-check/vitest

npm i https://pkg.pr.new/@fast-check/vitest@7183

@fast-check/worker

npm i https://pkg.pr.new/@fast-check/worker@7183

commit: eb7cb79

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.

2 participants