Skip to content

Fix falsy option arguments skipping validation - #91

Merged
11bit merged 2 commits into
mainfrom
fix-falsy-values-skipping-validation
Aug 24, 2026
Merged

Fix falsy option arguments skipping validation#91
11bit merged 2 commits into
mainfrom
fix-falsy-values-skipping-validation

Conversation

@11bit

@11bit 11bit commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Third and last finding from the audit that produced #89 and #90: optional arguments guarded with if (value) and rendered with value || "", so any falsy value was treated as "not provided" — including invalid ones.

watermarkSize.build({ wms: { width: null, height: 100 } })
// before: "wms::100"   (silently accepted)
// after:  throws "watermark_size.width is not a number"

Swept across 11 modules: resize, size, watermark, watermarkSize, trim, unsharpMasking, pngOptions, jpegOptions, colorize, monochrome, gravity.

What changes

  • Invalid falsy values now raise. null, "" and out-of-range 0 reach their guards instead of slipping past them.
  • Explicit 0 is rendered rather than dropped. { resize: { resizing_type: "fit", width: 0, height: 100 } } now builds rs:fit:0:100 instead of rs:fit::100. Both forms mean the same thing to imgproxy — 0 and an omitted argument are both "calculate this dimension" — so no URL changes meaning. This is unlike Fix gradient dropping optional arguments set to 0 #89, where the dropped 0 genuinely differed from the default.
  • colorize.color and monochrome.color are now validated as hex colors. Neither had any type validation at all; they now match trim.color and gradient.color.
  • gravity offset checks use presence, so { type: "sm", x_offset: 0 } correctly raises gravity.type is invalid — previously only a non-zero offset triggered that check.

Bug found while sweeping

jpeg_options threw "progressive must be true if optimize_scans is true" whenever optimize_scans was merely present. With the truthiness guard that was masked; converting it to a presence check exposed it, so the cross-check is now tied to optimize_scans actually being enabled. { progressive: false, optimize_scans: false } is valid and now builds jpgo:false::::false.

Second commit: Gravity is now an exclusive union (minor)

The gravity builder's cross-variant checks needed six @ts-expect-error suppressions, because fields like x_offset or class_names exist on only one member of the Gravity union. Instead of suppressing, the union is now exclusive: each variant types the other variants' fields as ?: never.

  • Invalid combinations such as { type: "sm", x_offset: 0 } or { type: "no", x: 0.5 } are now compile-time errors for TypeScript users. They have always thrown at runtime, so any code this breaks was already broken — it just failed later.
  • All six suppressions in src/options/gravity.ts are removed; runtime behavior is unchanged.
  • Ships as a minor changeset (type-level breaking only), separate from the patch changeset for the validation fixes.

Verification

  • tsc --noEmit clean, eslint src tests clean
  • vitest run → 97 files / 1114 tests passing (19 new, at least one per swept module)
  • No existing test needed changing for the validation fixes, which suggests the truthiness behavior was never deliberate. The exclusive-union commit adds one @ts-expect-error to an existing test — the stricter union now flags y: 0.5 on a non-fp gravity independently of x, which previously slipped past the excess-property check.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_014gcu8oHhTciugXNPUYNoqK

11bit and others added 2 commits August 21, 2026 11:52
Optional arguments were guarded with `if (value)` and rendered with
`value || ""`, so falsy but invalid values such as `null` or an empty
color skipped validation and were silently rendered as omitted
arguments. Check for `undefined` instead, matching `gradient` and
`progressive_blur`.

Also validate `colorize.color` and `monochrome.color` as hex colors, and
tie the `jpeg_options` progressive cross-check to `optimize_scans` being
enabled rather than merely present.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8Aa3xeVUCaxKtQJDzZhMo
Fields belonging to one gravity variant are now typed as forbidden
(`?: never`) on the other variants, so invalid combinations such as
`{type: "sm", x_offset: 0}` fail to compile instead of only throwing
at runtime. This also makes cross-variant property access legal on the
union, removing all six `@ts-expect-error` suppressions from the
gravity builder. Runtime behavior is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014gcu8oHhTciugXNPUYNoqK
@11bit
11bit merged commit 4f9ad1b into main Aug 24, 2026
2 checks passed
@11bit
11bit deleted the fix-falsy-values-skipping-validation branch August 24, 2026 12:49
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