feat(provider): add TruoCloud provider - #2304
Conversation
TruoCloud serves images from https://img.truo.cloud/i/<pid>/<path>, with the transformation as query parameters, so the provider is a `baseURL` and a key map. Three details are not obvious from the URL shape and each has a test: - booleans travel as `1`. `createOperationsGenerator` stringifies `true` as 'true', which the service accepts, but every other builder of this contract emits `1` — and two spellings of one request are two CDN cache entries for the same image. - a `src` that is already a TruoCloud URL is rewritten in place rather than wrapped again. Nuxt re-runs the provider over whatever src it is given, and a partially migrated site hands it URLs that already point at the CDN; wrapping twice produces a URL that works and costs twice. - parameters come out sorted, and commas stay literal. The transformation engine does not decode %2C, so an escaped crop=60,30,0,0 is ignored and the image comes back uncropped, with a 200. Merging the two vocabularies is why the key map is a named const: a URL carries wire names (`w`) while modifiers carry standard ones (`width`), so one side is translated before the merge or `?w=200&w=800` comes out meaning whichever the service reads first. Registered in src/provider.ts, the playground, the provider test table and the docs. 174 tests pass; eslint clean.
commit: |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds the TruoCloud image provider with modifier and format mappings, path encoding, query canonicalization, existing-URL handling, and merged modifiers. Registers the provider as built in. Adds URL fixture and Nuxt provider tests. Adds provider documentation, homepage listing, and playground examples with demo configuration. Estimated code review effort: 3 (Moderate) | ~25 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/runtime/providers/truocloud.ts`:
- Around line 164-166: Update the direct-source path handling around the
existing `path` assignment and `encodePath` so valid `%HH` percent escapes are
preserved instead of being encoded again, while still encoding unescaped path
characters and retaining the verbatim `existing.path` behavior. Add a regression
fixture covering a source path such as `/assets/a%20b.png` and verify the
generated URL references the original encoded asset.
- Around line 158-160: Update the getImage baseURL handling in the TruoCloud
provider to require a PID-qualified baseURL at runtime, or replace the current
default with a valid /i/<pid> endpoint. Ensure generated URLs always target the
required PID-qualified delivery path when callers omit configuration.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d7334603-f194-4937-8f94-897dcb62a9e1
📒 Files selected for processing (8)
docs/content/3.providers/truocloud.mddocs/content/index.mdplayground/app/providers.tsplayground/nuxt.config.tssrc/provider.tssrc/runtime/providers/truocloud.tstest/nuxt/providers.test.tstest/providers.ts
| export default defineProvider<TruoCloudOptions>({ | ||
| getImage: (src, { modifiers, baseURL = 'https://img.truo.cloud' }) => { | ||
| const existing = unwrap(src, baseURL) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Require a PID-qualified baseURL.
The default value produces https://img.truo.cloud/<path>. It does not produce the required /i/<pid>/<path> endpoint. A caller that selects this provider without configuration gets an invalid delivery URL.
Require baseURL at runtime, or provide a valid PID-qualified default.
Proposed fix
export default defineProvider<TruoCloudOptions>({
- getImage: (src, { modifiers, baseURL = 'https://img.truo.cloud' }) => {
+ getImage: (src, { modifiers, baseURL }) => {
+ if (!baseURL) {
+ throw new Error('The TruoCloud provider requires a baseURL such as https://img.truo.cloud/i/<pid>.')
+ }
const existing = unwrap(src, baseURL)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export default defineProvider<TruoCloudOptions>({ | |
| getImage: (src, { modifiers, baseURL = 'https://img.truo.cloud' }) => { | |
| const existing = unwrap(src, baseURL) | |
| export default defineProvider<TruoCloudOptions>({ | |
| getImage: (src, { modifiers, baseURL }) => { | |
| if (!baseURL) { | |
| throw new Error('The TruoCloud provider requires a baseURL such as https://img.truo.cloud/i/<pid>.') | |
| } | |
| const existing = unwrap(src, baseURL) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/runtime/providers/truocloud.ts` around lines 158 - 160, Update the
getImage baseURL handling in the TruoCloud provider to require a PID-qualified
baseURL at runtime, or replace the current default with a valid /i/<pid>
endpoint. Ensure generated URLs always target the required PID-qualified
delivery path when callers omit configuration.
| // An already-encoded path is reused verbatim; encoding it again would turn | ||
| // `%20` into `%2520`. | ||
| const path = existing ? existing.path : encodePath(src) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Preserve valid percent escapes for direct source paths.
A direct source such as /assets/a%20b.png does not enter existing. encodePath changes it to assets/a%2520b.png. The generated URL then requests a different asset.
Preserve existing %HH escapes when encoding direct source segments. Add a regression fixture for an encoded path.
Proposed fix
.map(segment =>
encodeURIComponent(segment).replace(
/[!'()*]/g,
c => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,
- ),
+ ).replace(/%25([0-9A-Fa-f]{2})/g, (_, hex: string) => `%${hex.toUpperCase()}`),
)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/runtime/providers/truocloud.ts` around lines 164 - 166, Update the
direct-source path handling around the existing `path` assignment and
`encodePath` so valid `%HH` percent escapes are preserved instead of being
encoded again, while still encoding unescaped path characters and retaining the
verbatim `existing.path` behavior. Add a regression fixture covering a source
path such as `/assets/a%20b.png` and verify the generated URL references the
original encoded asset.
Adding a playground entry creates a new snapshot case in both e2e suites, and without the file they fail on a mismatch against nothing. Generated by running the real browser tests, not written by hand — the snapshot records the URLs Chromium actually requested, and guessing them is how it would drift. It also happens to be the clearest proof the provider works end to end: the requests it captured are the canonical, sorted form.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2304 +/- ##
=======================================
Coverage 32.61% 32.61%
=======================================
Files 7 7
Lines 371 371
Branches 131 131
=======================================
Hits 121 121
Misses 194 194
Partials 56 56 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Adds a provider for TruoCloud.
Images are served from
https://img.truo.cloud/i/<pid>/<path>with thetransformation as query parameters, so the configuration is a single
baseURLholding the endpoint the console publishes:
No API key: the delivery contract is public by design, and signing — when a
tenant turns it on — happens server-side.
Three details that are not obvious, each with a test
Booleans travel as
1.createOperationsGeneratorstringifiestrueas'true', which the service accepts, but every other builder of this contractemits
1— and two spellings of one request are two CDN cache entries for thesame image.
A
srcthat is already a TruoCloud URL is rewritten, not wrapped again.Nuxt re-runs the provider over whatever
srcit is given, and a partiallymigrated site hands it URLs that already point at the CDN. Wrapping twice
produces
/i/<pid>/https%3A//img.truo.cloud/i/<pid>/…— a URL that works, coststwice and is unreadable in a bug report.
Merging the two vocabularies is why the key map is a named const rather than
inline: the URL carries wire names (
w) while modifiers carry standard ones(
width), so one side has to be translated before the merge or?w=200&w=800comes out meaning whichever the service reads first.
Commas stay literal. The transformation engine does not decode
%2C, so anescaped
crop=60,30,0,0is ignored — the image comes back uncropped, with a200.
Registered
src/provider.ts,playground/app/providers.ts,playground/nuxt.config.ts,the six rows in
test/providers.ts, a block intest/nuxt/providers.test.ts,docs/content/3.providers/truocloud.mdand the index. Purely additive.Verified
nuxt prepare playground && vitest run test/unit test/nuxt— 174 passed(including
provider-coverage, which enforces the playground entry and thetable row)
eslint .cleanformat: 'auto'one really doesnegotiate avif rather than just changing a header
The provider is also run against a shared fixture of URL vectors generated by
the service itself, which is how the TypeScript, PHP and server-side builders of
this contract are kept byte-identical. That fixture found two of the three
issues above while this file was being written.