Skip to content

feat(flyimg): add Flyimg built-in provider#2125

Open
bernhardberger wants to merge 1 commit intonuxt:mainfrom
bernhardberger:feat/flyimg-provider
Open

feat(flyimg): add Flyimg built-in provider#2125
bernhardberger wants to merge 1 commit intonuxt:mainfrom
bernhardberger:feat/flyimg-provider

Conversation

@bernhardberger
Copy link

@bernhardberger bernhardberger commented Feb 19, 2026

🔗 Linked issue

No pre-existing issue — this is a straightforward provider addition.

❓ Type of change

  • 📖 Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

This PR adds Flyimg as a built-in provider.

What is Flyimg?
Flyimg is an open-source, self-hosted image processing server built on top of ImageMagick and MozJPEG. It processes images on-the-fly via a URL-based API and caches results in Redis. It can be deployed via Docker and works as a drop-in image CDN for self-hosters.

Background
We've been running Flyimg in production for a while as our image processing layer. I wanted to contribute our provider implementation back to @nuxt/image so others can benefit from it without having to write a custom provider.

Disclaimer: I am not affiliated with flyimg.io or the Flyimg project in any way. This contribution is purely from the perspective of a long-time user sharing a production-tested implementation.

What's included

  • src/runtime/providers/flyimg.ts — provider implementation with full modifier support (resize, crop, gravity, format conversion, quality, watermarking, text overlays, background color, etc.)
  • docs/content/3.providers/flyimg.md — documentation page with setup instructions for both self-hosted and SaaS deployments, options table, and modifier reference
  • test/providers.ts + test/nuxt/providers.test.ts — unit tests for URL generation
  • playground/app/providers.ts + playground/nuxt.config.ts — playground entries for manual testing

Configuration

// nuxt.config.ts
export default defineNuxtConfig({
  image: {
    flyimg: {
      baseURL: 'https://your-flyimg-server.example.com',
      // Only needed for relative src paths — should be your site's public URL
      sourceURL: 'https://your-website.example.com',
    }
  }
})
<NuxtImg provider="flyimg" src="/my-image.jpg" width="800" height="600" />

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 19, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@nuxt/image@2125

commit: 4852993

@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new Flyimg image provider and integrates it across the project. Files added/updated: src/runtime/providers/flyimg.ts (new provider implementation and FlyimgOptions), src/provider.ts (BuiltInProviders updated), playground/nuxt.config.ts and playground/app/providers.ts (provider config and samples), docs/content/3.providers/flyimg.md (documentation), tests updated: test/providers.ts, test/nuxt/providers.test.ts (new provider tests), and test/e2e/snapshots/flyimg.json5 (snapshots). No existing public API signatures were removed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding Flyimg as a new built-in provider to the @nuxt/image module.
Description check ✅ Passed The description comprehensively explains the PR purpose, motivation, scope, and configuration, directly relating to the changeset which adds Flyimg provider implementation, documentation, tests, and playground entries.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
docs/content/3.providers/flyimg.md (1)

64-71: Document that fit="outside" is unsupported.

The provider implementation (Line 191–193 of flyimg.ts) emits a dev-time warning for fit="outside". Adding a row to this table noting it's unsupported would save users a debugging step.

📝 Suggested addition
 | `contain` / `inside` | Scale to fit within the target rectangle preserving aspect ratio (Flyimg default) |
 | `cover` | Crop to fill the rectangle (`c_1`) |
 | `fill` | Stretch to fill without preserving aspect ratio (`par_0`) |
+| `outside` | **Not supported** — ignored with a dev-time warning |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/content/3.providers/flyimg.md` around lines 64 - 71, Add a row to the
`fit` mapping table in docs/content/3.providers/flyimg.md noting that
fit="outside" is unsupported; reference the provider's dev-time warning in
flyimg.ts (the code path that emits the warning for `fit="outside"`) and state
"unsupported (dev-time warning emitted)" or similar so users know this option
isn't supported by Flyimg. Ensure the new row follows the existing table format
and aligns with the other entries (`Nuxt Image value` / `Flyimg behaviour`) so
the documentation clearly reflects the behavior implemented in the `flyimg.ts`
provider.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/runtime/providers/flyimg.ts`:
- Line 175: The local variable modifiers is declared as Record<string, unknown>
but is assigned boolean flags and then passed to operationsGenerator which
expects Partial<Record<string, string | number>>; change the modifiers
declaration to a compatible type (e.g., declare modifiers as
Partial<Record<string, string | number | boolean>>) so it accepts booleans, or
alternatively perform a targeted cast when calling operationsGenerator (cast
modifiers to Partial<Record<string, string | number>>) to satisfy the function
signature; update the declaration or the call involving modifiers and
operationsGenerator to use one of these fixes (refer to the modifiers variable
and the operationsGenerator(...) invocation).

---

Nitpick comments:
In `@docs/content/3.providers/flyimg.md`:
- Around line 64-71: Add a row to the `fit` mapping table in
docs/content/3.providers/flyimg.md noting that fit="outside" is unsupported;
reference the provider's dev-time warning in flyimg.ts (the code path that emits
the warning for `fit="outside"`) and state "unsupported (dev-time warning
emitted)" or similar so users know this option isn't supported by Flyimg. Ensure
the new row follows the existing table format and aligns with the other entries
(`Nuxt Image value` / `Flyimg behaviour`) so the documentation clearly reflects
the behavior implemented in the `flyimg.ts` provider.

@bernhardberger bernhardberger force-pushed the feat/flyimg-provider branch 2 times, most recently from 5571b11 to d70c9be Compare February 19, 2026 09:58
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/runtime/providers/flyimg.ts (1)

106-108: Value mappers for color fields should guard against non-string input.

The mappers for background, textColor, and textBackground call value.startsWith(...) on a value typed as string, but modifiers is cast to Partial<Record<string, string | number>>, so a numeric value (e.g. background: 0) would reach the mapper and throw a TypeError at runtime.

🛡️ Proposed fix
-    background: (value: string) => value.startsWith('#') ? value.replace('#', '%23') : value,
-    textColor: (value: string) => value.startsWith('#') ? value.replace('#', '%23') : value,
-    textBackground: (value: string) => value.startsWith('#') ? value.replace('#', '%23') : value,
+    background: (value: string) => typeof value === 'string' && value.startsWith('#') ? value.replace('#', '%23') : value,
+    textColor: (value: string) => typeof value === 'string' && value.startsWith('#') ? value.replace('#', '%23') : value,
+    textBackground: (value: string) => typeof value === 'string' && value.startsWith('#') ? value.replace('#', '%23') : value,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/providers/flyimg.ts` around lines 106 - 108, The color mappers
background, textColor, and textBackground currently call value.startsWith(...)
but modifiers is Partial<Record<string, string | number>>, so non-string values
(e.g. numbers) will throw; update each mapper to first check typeof value ===
'string' before calling startsWith, and either convert non-string values to
strings or return them unchanged (e.g. if string and startsWith('#') replace '#'
with '%23', otherwise return value as-is) so numeric inputs no longer cause a
TypeError.
test/providers.ts (1)

36-242: Consider adding test fixtures for fit='cover' and fit='fill' mappings.

The current fixtures validate contain (no-op) and format conversion, but two additional non-trivial Flyimg flag mappings produced by the fit switch are untested:

  • fit: 'cover' → should emit c_1 (e.g. w_200,h_200,c_1)
  • fit: 'fill' → should emit par_0 (e.g. w_200,h_200,par_0)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/providers.ts` around lines 36 - 242, Add test fixtures covering
fit='cover' and fit='fill' for the Flyimg provider: update the existing test
object entries that use args: ['/test.png', { width: 200, height: 200, fit: ...
}] and add expected flyimg url strings that include the Flyimg flags c_1 for
cover (e.g. include "w_200,h_200,c_1" in the flyimg url) and par_0 for fill
(e.g. include "w_200,h_200,par_0" in the flyimg url); ensure these entries
mirror the style of the existing fixtures (the flyimg key next to other
providers) so the test harness can validate the new mappings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/runtime/providers/flyimg.ts`:
- Around line 197-203: The inverted-default checks currently use strict ===
false which ignores numeric 0 values (e.g., strip: 0); update the checks for
strip, mozjpeg, preserveAspectRatio, and preserveNaturalSize so they detect
explicit “off” values including 0 — e.g., guard against null/undefined then
treat falsy 0/false as opt-out — and set the corresponding properties on the
modifiers object (modifiers.strip, modifiers.mozjpeg,
modifiers.preserveAspectRatio, modifiers.preserveNaturalSize) when the caller
explicitly disables them.

---

Nitpick comments:
In `@src/runtime/providers/flyimg.ts`:
- Around line 106-108: The color mappers background, textColor, and
textBackground currently call value.startsWith(...) but modifiers is
Partial<Record<string, string | number>>, so non-string values (e.g. numbers)
will throw; update each mapper to first check typeof value === 'string' before
calling startsWith, and either convert non-string values to strings or return
them unchanged (e.g. if string and startsWith('#') replace '#' with '%23',
otherwise return value as-is) so numeric inputs no longer cause a TypeError.

In `@test/providers.ts`:
- Around line 36-242: Add test fixtures covering fit='cover' and fit='fill' for
the Flyimg provider: update the existing test object entries that use args:
['/test.png', { width: 200, height: 200, fit: ... }] and add expected flyimg url
strings that include the Flyimg flags c_1 for cover (e.g. include
"w_200,h_200,c_1" in the flyimg url) and par_0 for fill (e.g. include
"w_200,h_200,par_0" in the flyimg url); ensure these entries mirror the style of
the existing fixtures (the flyimg key next to other providers) so the test
harness can validate the new mappings.

@bernhardberger bernhardberger force-pushed the feat/flyimg-provider branch 3 times, most recently from ce1fff1 to 6f5c0de Compare February 19, 2026 10:30
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/runtime/providers/flyimg.ts (1)

205-210: Consider a dev warning when src is relative but sourceURL is not configured.

When src is a relative path (e.g., /images/photo.jpg) and sourceURL is absent, imageUrl retains its leading /. The template literal then produces a double slash (upload/-//images/photo.jpg), which joinURL preserves verbatim, resulting in a malformed Flyimg URL. Flyimg also cannot fetch a relative path regardless. A targeted dev warning would surface this misconfiguration early.

💡 Proposed addition
 const imageUrl = !hasProtocol(src) && sourceURL
   ? joinURL(sourceURL, src)
   : src
+
+if (import.meta.dev && !hasProtocol(src) && !sourceURL) {
+  console.warn(
+    '[nuxt] [image] [flyimg] `src` is a relative path but `sourceURL` is not configured. '
+    + 'Flyimg requires an absolute image URL. Set `image.flyimg.sourceURL` in your nuxt.config.',
+  )
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/providers/flyimg.ts` around lines 205 - 210, When computing
imageUrl, add a developer warning if src is relative (detect via
hasProtocol(src) === false) while sourceURL is falsy so we avoid creating
malformed Flyimg URLs; specifically, in the same block that computes imageUrl
(symbols: src, hasProtocol, sourceURL, joinURL, imageUrl) emit a dev-only
console.warn or logger.warn indicating "relative src provided but sourceURL not
configured — Flyimg requires absolute URLs" and return or fall back safely
(e.g., skip processing or normalize) so callers know the configuration is
incorrect.
test/providers.ts (1)

36-242: Missing test cases for fit: 'cover' and fit: 'fill' — the two modes that actually emit extra Flyimg flags.

The current fixtures only exercise contain (which is a no-op) and jpeg format. Neither cover (c_1) nor fill (par_0) has a snapshot, so a regression in those switch branches would go undetected.

💡 Suggested additions to the image fixture array
// After the existing { args: ['/test.png', { width: 200, height: 200, fit: 'contain' }], ... } block:
+  {
+    args: ['/test.png', { width: 200, height: 200, fit: 'cover' }],
+    // ... other providers ...
+    flyimg: { url: 'https://demo.flyimg.io/upload/w_200,h_200,c_1/https://my-website.com/test.png' },
+  },
+  {
+    args: ['/test.png', { width: 200, height: 200, fit: 'fill' }],
+    // ... other providers ...
+    flyimg: { url: 'https://demo.flyimg.io/upload/w_200,h_200,par_0/https://my-website.com/test.png' },
+  },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/providers.ts` around lines 36 - 242, The test fixtures are missing cases
for fit: 'cover' and fit: 'fill' so changes to the Flyimg branch (which emits
extra flags) aren't validated; add two new entries in the same fixtures array
structure (objects with an args entry and per-provider expectations) targeting
the flyimg provider key: one with args ['/test.png', { width: 200, height: 200,
fit: 'cover' }] that expects a flyimg URL containing the c_1 flag (e.g.
upload/w_200,h_200,o_... including c_1) and one with args ['/test.png', { width:
200, height: 200, fit: 'fill' }] that expects a flyimg URL containing the par_0
flag; place these next to the existing contain/jpeg fixtures so the snapshot
tests will cover the Flyimg-specific branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/runtime/providers/flyimg.ts`:
- Around line 197-203: The current checks use strict === false which drops
numeric 0 values (Flyimg uses 0/1), so update the conditional checks for the
inverted-default flags (strip, mozjpeg, preserveAspectRatio,
preserveNaturalSize) to treat explicit false, numeric 0 and string "0" as
disabling values; e.g. for each of
strip/mozjpeg/preserveAspectRatio/preserveNaturalSize, replace the single strict
check with a compound check (value === false || value === 0 || value === '0')
and then set the corresponding
modifiers.strip/modifiers.mozjpeg/modifiers.preserveAspectRatio/modifiers.preserveNaturalSize
= false when that condition is met.

---

Nitpick comments:
In `@src/runtime/providers/flyimg.ts`:
- Around line 205-210: When computing imageUrl, add a developer warning if src
is relative (detect via hasProtocol(src) === false) while sourceURL is falsy so
we avoid creating malformed Flyimg URLs; specifically, in the same block that
computes imageUrl (symbols: src, hasProtocol, sourceURL, joinURL, imageUrl) emit
a dev-only console.warn or logger.warn indicating "relative src provided but
sourceURL not configured — Flyimg requires absolute URLs" and return or fall
back safely (e.g., skip processing or normalize) so callers know the
configuration is incorrect.

In `@test/providers.ts`:
- Around line 36-242: The test fixtures are missing cases for fit: 'cover' and
fit: 'fill' so changes to the Flyimg branch (which emits extra flags) aren't
validated; add two new entries in the same fixtures array structure (objects
with an args entry and per-provider expectations) targeting the flyimg provider
key: one with args ['/test.png', { width: 200, height: 200, fit: 'cover' }] that
expects a flyimg URL containing the c_1 flag (e.g. upload/w_200,h_200,o_...
including c_1) and one with args ['/test.png', { width: 200, height: 200, fit:
'fill' }] that expects a flyimg URL containing the par_0 flag; place these next
to the existing contain/jpeg fixtures so the snapshot tests will cover the
Flyimg-specific branches.

@bernhardberger bernhardberger marked this pull request as draft February 19, 2026 10:35
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
docs/content/3.providers/flyimg.md (1)

18-33: Consider adding an nginx merge_slashes off note to the self-hosted setup.

When an absolute source image URL is embedded in the Flyimg request path (e.g., .../upload/w_300/https://cdn.example.com/img.jpg), merge_slashes is on by default in nginx, which compresses two or more forward slashes into one. This silently corrupts the embedded https:// to https:/, causing Flyimg to fail to fetch the source image. This is a confirmed problem for nginx users, fixed by adding merge_slashes off; to the nginx config.

A one-liner note in the Self-hosted section would prevent a frustrating deployment failure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/content/3.providers/flyimg.md` around lines 18 - 33, Add a brief note in
the "Self‑hosted" section warning nginx users that nginx's default merge_slashes
behavior will collapse the 'https://' embedded in Flyimg request paths (e.g.
paths containing baseURL/sourceURL + absolute image URLs) and instruct them to
set merge_slashes off in their nginx config to avoid corrupting embedded URLs so
Flyimg can fetch source images correctly; reference the flyimg configuration
keys baseURL and sourceURL when describing which requests are affected.
src/runtime/providers/flyimg.ts (1)

165-221: LGTM — fit mapping, inverted-defaults, and URL construction are correct.

The != null && !value guards for inverted-defaults now correctly handle both false and numeric 0. The joinURL construction avoids double-slash normalization by embedding the absolute imageUrl inside a template string passed as a single argument. The nginx merge_slashes concern is a known operational issue for all Flyimg users behind a proxy.

Worth noting in the docs: a known Flyimg issue shows that nginx's default merge_slashes on setting collapses // in request URIs, corrupting the embedded https:// in the image path — fixed by adding merge_slashes off; to the nginx config, though the Flyimg maintainer notes this is a reverse-proxy configuration issue rather than a Flyimg bug. Since users deploying Flyimg behind nginx will hit this silently, a brief note in the docs Setup section would prevent a hard-to-diagnose failure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/providers/flyimg.ts` around lines 165 - 221, Add a short note to
the docs Setup section explaining the nginx merge_slashes behavior that can
collapse the embedded "https://" in the Flyimg path and instruct users to set
merge_slashes off; when reverse-proxying Flyimg; reference the Flyimg maintainer
guidance and mention this affects requests built like the one produced by
joinURL() using imageUrl (the template path
`${processType}/${imageOptions}/${imageUrl}`), and also add a one-line inline
comment near the joinURL(...) return in the Flyimg provider implementation to
point maintainers to the docs note.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/content/3.providers/flyimg.md`:
- Around line 75-86: The example NuxtImg snippet is missing the provider
attribute so it uses the default provider instead of Flyimg; update the NuxtImg
component (NuxtImg) to include provider="flyimg" in its props so the example
explicitly targets the Flyimg provider and yields the documented behavior.

---

Nitpick comments:
In `@docs/content/3.providers/flyimg.md`:
- Around line 18-33: Add a brief note in the "Self‑hosted" section warning nginx
users that nginx's default merge_slashes behavior will collapse the 'https://'
embedded in Flyimg request paths (e.g. paths containing baseURL/sourceURL +
absolute image URLs) and instruct them to set merge_slashes off in their nginx
config to avoid corrupting embedded URLs so Flyimg can fetch source images
correctly; reference the flyimg configuration keys baseURL and sourceURL when
describing which requests are affected.

In `@src/runtime/providers/flyimg.ts`:
- Around line 165-221: Add a short note to the docs Setup section explaining the
nginx merge_slashes behavior that can collapse the embedded "https://" in the
Flyimg path and instruct users to set merge_slashes off; when reverse-proxying
Flyimg; reference the Flyimg maintainer guidance and mention this affects
requests built like the one produced by joinURL() using imageUrl (the template
path `${processType}/${imageOptions}/${imageUrl}`), and also add a one-line
inline comment near the joinURL(...) return in the Flyimg provider
implementation to point maintainers to the docs note.

@codecov-commenter
Copy link

codecov-commenter commented Feb 19, 2026

Codecov Report

❌ Patch coverage is 0.75758% with 131 lines in your changes missing coverage. Please review.
✅ Project coverage is 6.63%. Comparing base (67b40ac) to head (4852993).

Files with missing lines Patch % Lines
src/runtime/providers/flyimg.ts 0.76% 130 Missing ⚠️
src/provider.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            main   #2125      +/-   ##
========================================
- Coverage   6.83%   6.63%   -0.21%     
========================================
  Files         80      81       +1     
  Lines       3729    3861     +132     
  Branches     142     143       +1     
========================================
+ Hits         255     256       +1     
- Misses      3424    3555     +131     
  Partials      50      50              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 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.

@bernhardberger bernhardberger marked this pull request as ready for review February 19, 2026 11:04
@bernhardberger
Copy link
Author

fixed all the coderabbit nitpicks and the tests. ready for review now.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
test/nuxt/providers.test.ts (1)

722-741: Suggest adding unit tests for inverted-defaults and processType: 'path'.

The existing test loop covers dimension/format combos and fit: 'cover'/'fill'. However the following provider code paths have zero coverage per the Codecov report:

  • Inverted-defaults (strip, mozjpeg, preserveAspectRatio, preserveNaturalSize) — the isDisabled helper and each conditional at lines 202–205 are untested.
  • processType: 'path' — the path segment substitution is untested.
  • Absolute src without sourceURL — verifies the pass-through branch of the imageUrl resolution.
✅ Suggested additions inside the it('flyimg', …) block
+    // inverted-defaults: strip, mozjpeg, preserveAspectRatio, preserveNaturalSize
+    expect(flyimg().getImage('/test.png', { modifiers: { width: 200, strip: false }, ...providerOptions }, emptyContext))
+      .toMatchObject({ url: 'https://demo.flyimg.io/upload/w_200,st_0/https://my-website.com/test.png' })
+
+    expect(flyimg().getImage('/test.png', { modifiers: { width: 200, mozjpeg: false }, ...providerOptions }, emptyContext))
+      .toMatchObject({ url: 'https://demo.flyimg.io/upload/w_200,moz_0/https://my-website.com/test.png' })
+
+    expect(flyimg().getImage('/test.png', { modifiers: { width: 200, preserveNaturalSize: false }, ...providerOptions }, emptyContext))
+      .toMatchObject({ url: 'https://demo.flyimg.io/upload/w_200,pns_0/https://my-website.com/test.png' })
+
+    // processType: 'path'
+    expect(flyimg().getImage('/test.png', { modifiers: { width: 200 }, baseURL: 'https://demo.flyimg.io', sourceURL: 'https://my-website.com', processType: 'path' }, emptyContext))
+      .toMatchObject({ url: 'https://demo.flyimg.io/path/w_200/https://my-website.com/test.png' })
+
+    // absolute src — sourceURL is irrelevant
+    expect(flyimg().getImage('https://cdn.example.com/photo.jpg', { modifiers: { width: 200 }, baseURL: 'https://demo.flyimg.io' }, emptyContext))
+      .toMatchObject({ url: 'https://demo.flyimg.io/upload/w_200/https://cdn.example.com/photo.jpg' })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/nuxt/providers.test.ts` around lines 722 - 741, Tests for the flyimg
provider are missing coverage for inverted-defaults and path processing; add
unit cases that call flyimg().getImage(...) to exercise the isDisabled helper
and branches that toggle strip/mozjpeg/preserveAspectRatio/preserveNaturalSize
by passing explicit modifiers that invert defaults, add a test where provider
options include processType: 'path' to verify path-segment substitution
behavior, and add a test with an absolute src and no sourceURL to assert the
absolute URL is passed through unchanged; reference getImage, isDisabled,
processType and flyimg() when adding these assertions.
src/runtime/providers/flyimg.ts (1)

175-175: Redundant double type annotation — use one or the other.

The declaration and the as cast both assert the same type, making one redundant:

const modifiers: Partial<Record<string, string | number | boolean>> = { ...rest } as Partial<Record<string, string | number | boolean>>
♻️ Proposed fix
-    const modifiers: Partial<Record<string, string | number | boolean>> = { ...rest } as Partial<Record<string, string | number | boolean>>
+    const modifiers: Partial<Record<string, string | number | boolean>> = { ...rest }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/providers/flyimg.ts` at line 175, The declaration for modifiers
redundantly repeats the same type twice; update the declaration of modifiers
(the const named "modifiers" that spreads "rest") to use a single type assertion
by removing the unnecessary "as Partial<Record<string | number | boolean>>" cast
(or alternatively remove the explicit ": Partial<Record<string, number |
boolean>>" annotation) so only one typing remains and the value is still typed
correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/runtime/providers/flyimg.ts`:
- Around line 217-227: The current fallback imageOptions = operations || '-'
produces an invalid Flyimg segment; change the fallback to a valid default
option (e.g., replace '-' with a sensible option string like 'q_75' or
'w_400,q_75') or refactor so when operationsGenerator(modifiers) returns falsy
you omit the options segment entirely before calling joinURL; update the code
around operationsGenerator, imageOptions, and the joinURL call that builds the
return url (using processType, imageUrl, baseURL) so the produced path is a
valid Flyimg options segment (or excludes it) instead of a lone dash.

---

Nitpick comments:
In `@src/runtime/providers/flyimg.ts`:
- Line 175: The declaration for modifiers redundantly repeats the same type
twice; update the declaration of modifiers (the const named "modifiers" that
spreads "rest") to use a single type assertion by removing the unnecessary "as
Partial<Record<string | number | boolean>>" cast (or alternatively remove the
explicit ": Partial<Record<string, number | boolean>>" annotation) so only one
typing remains and the value is still typed correctly.

In `@test/nuxt/providers.test.ts`:
- Around line 722-741: Tests for the flyimg provider are missing coverage for
inverted-defaults and path processing; add unit cases that call
flyimg().getImage(...) to exercise the isDisabled helper and branches that
toggle strip/mozjpeg/preserveAspectRatio/preserveNaturalSize by passing explicit
modifiers that invert defaults, add a test where provider options include
processType: 'path' to verify path-segment substitution behavior, and add a test
with an absolute src and no sourceURL to assert the absolute URL is passed
through unchanged; reference getImage, isDisabled, processType and flyimg() when
adding these assertions.

Comment on lines +217 to +227
// --- Build Flyimg URL --------------------------------------------------
const operations = operationsGenerator(modifiers as Partial<Record<string, string | number>>)
const imageOptions = operations || '-'

// Construct the path manually so that an absolute imageUrl is treated as a
// literal path segment rather than a new base by ufo's joinURL.
// Note: if reverse-proxied via nginx, set `merge_slashes off` to prevent
// nginx from collapsing the `https://` embedded in this path. See docs.
return {
url: joinURL(baseURL || '/', `${processType}/${imageOptions}/${imageUrl}`),
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Flyimg URL format image_options dash parameter support

💡 Result:

Flyimg’s URL format is:

https://<server>/<process-type>/<image_options>/<path_to_image> (e.g. /upload/w_500/...). Image options are comma-separated transformations, and each option is encoded as <key>_<value> (e.g. w_300,h_200,q_85,o_webp). A dash (-) is not a supported separator/“parameter” format for image_options—use underscores between key/value and commas between options. [1], [2]

Sources
[1] Flyimg docs – “URL Options” https://docs.flyimg.io/url-options/
[2] Klutch.sh guide – “Deploying Flyimg” https://docs.klutch.sh/guides/open-source-software/flyimg/


🌐 Web query:

Flyimg upload dash empty options segment https://demo.flyimg.io

💡 Result:

Flyimg’s upload endpoint requires a non-empty “image options” path segment:

https://demo.flyimg.io/upload/{image_options}/{source_image_url} [1][2]

So a URL like:

https://demo.flyimg.io/upload//https://example.com/img.jpg

fails because the {image_options} segment is empty.

Use at least one option, e.g.:

  • https://demo.flyimg.io/upload/w_800/https://flyimg.io/demo-images/Citroen-DS.jpg [1][2]
  • https://demo.flyimg.io/upload/q_90/https://flyimg.io/demo-images/Citroen-DS.jpg [2]

If you tell me the exact URL you tried (and the exact error text), I can point out the precise fix.


🌐 Web query:

Flyimg option parser behavior underscore key_value parameters

💡 Result:

In Flyimg, the URL “image options” segment is parsed as a list of option tokens (transformations/settings):

  • Multiple options are separated by options_separator (default comma ,, configurable in config/parameters.yml). (docs.flyimg.io)
  • Each option token is written as <key>_<value> (underscore-delimited “key_value”), e.g. w_250,q_50 or c_1. (docs.flyimg.io)
  • Many boolean-ish options are expressed by passing ..._1 (e.g. c_1, rf_1). (docs.flyimg.io)

Practical implication: _ is the key/value delimiter inside each token, while options_separator only controls how tokens are split from each other. (docs.flyimg.io)

Citations:


Replace the fallback - with a valid default option or refactor to handle no-modifier requests differently.

The code at line 219 uses operations || '-' to provide a fallback when no modifiers are provided. However, Flyimg's URL structure requires the image_options segment to contain valid key_value pairs separated by commas (e.g., w_500,q_90). A bare dash has no underscore and is not a recognized option format in Flyimg—it will not be parsed correctly and will produce invalid requests.

Additionally, Flyimg's documentation and tests confirm that a completely empty image_options segment fails (e.g., /upload//https://...). When no modifiers are provided, the code should either:

  1. Provide a sensible default option (e.g., q_75 for quality, or w_400 for a standard width).
  2. Document what behavior Flyimg exhibits with this malformed option and why it is acceptable.
  3. Refactor to omit the options segment entirely if Flyimg supports a direct format like /upload/https://... (verify with Flyimg maintainers if uncertain).

Without a fix, requests without modifiers will generate invalid Flyimg URLs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/providers/flyimg.ts` around lines 217 - 227, The current fallback
imageOptions = operations || '-' produces an invalid Flyimg segment; change the
fallback to a valid default option (e.g., replace '-' with a sensible option
string like 'q_75' or 'w_400,q_75') or refactor so when
operationsGenerator(modifiers) returns falsy you omit the options segment
entirely before calling joinURL; update the code around operationsGenerator,
imageOptions, and the joinURL call that builds the return url (using
processType, imageUrl, baseURL) so the produced path is a valid Flyimg options
segment (or excludes it) instead of a lone dash.

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