Repository navigation
Conversation
…et and preload data Pure functions, no React, no module state: decoCdn, vtexCatalog, vtexFileManager and shopify providers, createImageUrl, srcSet, parseSrcSet and preloadLink. A site builds one ImageUrl; v7's registerImageCdnDomain and registerImageQuality (module-wide state) do not come back. Golden-URL tests (2,111 cases) generated from each v8 storefront's current image helpers reproduce their URLs byte for byte. Intended changes: the next/image loader still on VTEX's file-manager ?width= resizer moves to the Thumbor route, and a mobile app's diverging hand copies follow its own site-loader copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig
This was referenced Oct 14, 2026
Brings in 6a273bc: the draft names and Studio's origins leave @decocms/blocks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig
…pify providers move to their packages
Product owner review: @decocms/blocks keeps framework code only.
- @decocms/blocks/image: decoCdn, createImageUrl, srcSet, parseSrcSet,
preloadLink and the generic types. No VTEX, Shopify or other platform
code, hosts or detection. decoCdn takes `sources` ("deco" | "all" | a
predicate), `stripAssetHosts`, `fit`, `legacyHosts`, `layout`. A provider's
format may return null to leave a source to the next provider.
- @decocms/apps-vtex/image: vtexCatalog, vtexCatalogFrame, vtexCatalogWebp,
vtexFileManager. @decocms/apps-shopify/image: shopifyCdn. Pure functions
over @decocms/blocks/image types.
- Every URL is what the sites' own helpers produce today: the golden
fixtures (split per package, regenerated from the sites' unchanged
helpers, edge cases added) have no changed case. Dropped from code: the
file-manager Thumbor switch for the loader still on the query resizer,
re-formatting already-resized CDN URLs, leading-only asset host stripping
as the default, rounding and re-encoding differences, and the mobile app
hand copies (not a consumer of these packages).
- tests/upstream-helpers.guardrail.test.ts: apps-* helper subpaths are pure
(no network, React, env reads or runtime imports) and bundle for a browser
and workerd. CLAUDE.md says where platform helpers live.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig
tlgimenes
added a commit
that referenced
this pull request
Oct 14, 2026
…draft names and Studio's origins Brings in the PO review: @decocms/blocks no longer exports the draft names, draftResponseHeaders or Studio's origins (#693), @decocms/blocks/image is platform-neutral with the VTEX and Shopify helpers in @decocms/apps-vtex/image and @decocms/apps-shopify/image (#694), StudioBridge takes its trusted origins from the binding (#695), and @decocms/tanstack owns its own copy (#696). CLAUDE.md conflict: tests/ (helper guardrails) and examples/ lines combined. @decocms/nextjs, in the same commit so every package type-checks: - src/draft.ts (DRAFT_PARAM/COOKIE/HEADER, draftResponseHeaders) and src/studio.ts (STUDIO_ORIGINS, STUDIO_FRAME_ANCESTORS, isStudioOrigin): the package's own copy, same values as @decocms/tanstack's, never imported from it. The proxy, drafts.ts, /server and config.test.ts use it; the root exports it. - StudioBridge (src/StudioBridge.ts): blocks' bridge with trustedOrigins set to STUDIO_ORIGINS, so the layout still passes only the site. - /image-loader stays platform-neutral; its next/image golden comparison moves to tests/nextjs-image-loader.golden.test.ts, which composes @decocms/apps-vtex/image with decoCdn as a Next storefront does: 1,212 cases from blocks' and apps-vtex's fixtures, none changed. exports.test.ts pins that the package depends on no @decocms/tanstack or @decocms/apps-*. - nextjs-binding-smoke: decoCdn({ fit: "contain" }) (blocks' default fit is now cover) and a check of the origins the bridge answers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig
tlgimenes
added a commit
that referenced
this pull request
Oct 14, 2026
Brings in the PO review down the stack: blocks without the draft names or Studio's origins (#693), the platform-neutral @decocms/blocks/image with the @decocms/apps-vtex/image and @decocms/apps-shopify/image helper subpaths (#694), StudioBridge's required trustedOrigins (#695), and each binding's own copy of the draft names and Studio's origins (#696, #697). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig
v7's getSrcSet, given no width, wrote two candidates with width=NaN
("… NaNw, … NaNw"), and an Image or Source without a width rendered
that srcset. srcSet returned undefined instead, so such an <img> lost its
srcset and sizes and a <source> got one width=undefined URL. Found on a
storefront's pages (12 <source> tags). URLs must stay byte for byte, so
srcSet reproduces v7: no special case for a missing width.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig
…ts src A Deco CDN URL pasted into content (https://assets.decocms.com/image?...&src=, or the decoims.com, deco-assets.edgedeco.com and deco-assets.decoazn.com endpoints) became src=image (or the whole /image URL without its query), which the CDN cannot serve. decoCdn now unwraps it (encoded or nested src included) and resizes the source it was made from. Default behaviour, approved by the product owner (2026-10-14). The golden fixture marks the 160 cases this changes (the two already-resized CDN sources, every config) with `before` and `reason`; the test asserts no other case changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig
c80dd33 also widened decoCdn's default match (sources "deco") to every Deco CDN endpoint, http and any host case. Those URLs passed through unchanged before and were served fine, so 168 of them changed outside the approved fix (e.g. https://deco-assets.edgedeco.com/image?…, an http:// or uppercase-host assets.decocms.com URL). match is back to the pre-fix prefixes; format still unwraps every Deco CDN endpoint, so the URLs that became src=image or src=<cdn>/image (domain, legacy hosts, sources "all", a predicate) are re-resized from their src. Differential fuzz against ea0a6bf over 9 configs x 10 hosts x 6 paths: 108 changed, all previously broken. Golden fixtures unchanged (160 approved cases). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig
The product owner approved "leading" as stripAssetHosts' default (2026-10-15), as v7's Image does since 7.71.4. "anywhere" stays an option. Golden fixtures: the 32 cases this changes (the source that only contains an asset URL, on the two configs that keep the default) are re-annotated as approved, each with its old URL and the reason; a test checks that the old URL is exactly what "anywhere" writes and that nothing else changed. The apps-vtex and apps-shopify fixtures don't change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZsmzpZ2zahRBoATBb2tBu
tlgimenes
added a commit
that referenced
this pull request
Oct 15, 2026
…ass no dev flag decoCdn strips only a leading asset host by default now (#694), so the migrator leaves stripAssetHosts out for every site. A site whose v7 image code stripped the asset hosts anywhere (7.x before 7.71.4, or such a copy) moves to "leading" too, as the PO approved (2026-10-15): the report's "image URLs" line counts and lists the URLs in .deco/blocks and src/ whose image URL changes (changedByLeadingStrip, checked against decoCdn itself). framework-code.md: Next.js sites pass no dev flag; createCMS reads NODE_ENV (PO decision 1), and isNextDev is gone from @decocms/nextjs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZsmzpZ2zahRBoATBb2tBu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZsmzpZ2zahRBoATBb2tBu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZsmzpZ2zahRBoATBb2tBu
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #693 (P2 of the v8 framework-packages plan). Reworked after the product owner's review:
@decocms/blockskeeps framework code only, and nothing that knows VTEX, Shopify or another commerce platform.What
@decocms/blocks/image(platform-neutral): pure functions, no React, no module state.decoCdn(options): the Deco image CDN (https://<domain>/image?fit=&width=…&src=). Options:domain,quality(tier),fit(defaultcover, as v7'sgetSrcSet/Image),legacyHosts(default["decoims.com"]on the Cloudflare CDNs),sources("deco"default,"all"for v7's "everything else goes through the CDN", or a predicate),stripAssetHosts("leading", the default since the PO's 2026-10-15 decision: v7 from 7.71.4; or"anywhere": v7 up to 7.71.3 and two storefronts' vendored helpers),layout("src-last"or"quality-last").createImageUrl({ providers }): oneImageUrlper site, withisOptimizableandhosts(). Providers are tried in order; a provider'sformatmay returnnullto leave a source to the next one.srcSet,parseSrcSet,preloadLink, and the generic types (ImageProvider,ImageUrl,ResizeOptions,Quality,Fit, …).srcSetwithout a width writes v7's twowidth=NaN … NaNwcandidates, as v7'sgetSrcSetdid for anImageorSourcewithout a width (a storefront's pages have 12 such<source>tags; before this, they lost their srcset).@decocms/apps-vtex/image: VTEX's URL formats as providers. They are pure helpers over the blocks types: no fetch, no React, no env reads, no runtime imports.vtexCatalog(): v7'sImagerule. VTEX hosts,{id}-{w}-{h ?? w}, URL re-serialised.vtexCatalogFrame({ frame? }): any host, a fixed frame or the call's box, rounded.vtexCatalogWebp({ quality? }): the next/image loaders' rule.{id}-{w}-auto/{file}.webp?…&quality=on VTEX's 1-10 scale.vtexFileManager({ via: "query" | "thumbor", quality? }).@decocms/apps-shopify/image:shopifyCdn(), v7's rule (width,heightwhen given,crop=center).A site lists the platform providers before
decoCdn. The bindings never depend on anapps-*package; sites wire the helper in. v7'sregisterImageCdnDomain/registerImageQualitydo not come back: they were module state. Their values aredecoCdn'sdomainandquality.Golden URLs: only the approved changes
The fixtures were regenerated by running each v8 storefront's own helpers, unchanged:
getOptimizedMediaUrl/getSrcSetvariantsThe corpus grew to cover edge cases: unencoded characters, an asset host inside another URL, already-resized CDN URLs, uppercase hosts, odd id suffixes, store-domain catalog URLs, and fractional widths. The fixtures are split per package:
@decocms/blocksdata:,""@decocms/apps-vtexdecoCdn@decocms/apps-shopifydecoCdnEvery case reproduces today's URL byte for byte, except 192 approved
@decocms/blockscases:https://images.example.com/https://assets.decocache.com/…, on the two configs that keepdecoCdn's default withsources: "all").Each of those carries
before(today's URL) andreason. The test asserts that no other case changed, and that each change-3beforeis exactly whatstripAssetHosts: "anywhere"still writes. Theapps-vtexandapps-shopifyfixtures have no changed case and still assert that none carries abefore. Where a helper throws today, the case is recorded as<throws>, and the provider still throws (v7's VTEX rule on a URL that only mentions a VTEX host in its query). The fixtures use no site names. The generator stays out of the repo because it reads the sites' paths.Approved changes (PO, 2026-10-14 and 2026-10-15)
The product owner approved 1 and 2 on 2026-10-14 ("the image fixes are good") and 3 on 2026-10-15:
File-manager images on the next/image loader that still uses VTEX's
?width=&aspect=trueresizer. That resizer has answered 500 for raster files since 2026-10-07. That store switches tovtexFileManager({ via: "thumbor" }), which the other loader already uses. This is a site change only; no package changes.An already-resized Deco CDN URL pasted into content (
https://assets.decocms.com/image?…, or thedecoims.com,deco-assets.edgedeco.comanddeco-assets.decoazn.comendpoints). Today it becomessrc=image, or the whole/imageURL without its query, and the CDN cannot serve either one.decoCdnnow re-resizes it from itssrcby default (c80dd33, narrowed in 6b7e700). That covers an encodedsrc, a nested CDN URL,httpand any host case. Resizing a result again gives the same URL. It applies only to the URLsdecoCdnalready resized (itsdomainand legacy hosts with the defaultsources: "deco"; every endpoint withsources: "all"or a predicate that takes it): a Deco CDN URL it passed through before, such as adeco-assets.edgedeco.comone undersources: "deco", still passes through unchanged, because it was served fine. Other URLs are unaffected, including a non-Deco/imagepath and an/imageURL with nosrc. A differential fuzz against the previous commit (9 configs, 10 hosts, 6 path shapes) finds 108 changed URLs, every one of themsrc=imageorsrc=<cdn>/imagebefore.stripAssetHostsdefaults to"leading"(afe430c). An asset host (assets.decocache.com/, the S3 bucket) is removed only where the source starts with it, as v7'sImagedoes since 7.71.4. Two storefronts' vendored helpers (and v7 up to 7.71.3) removed it anywhere, so a source that only contains an asset URL (https://other.host/https://assets.decocache.com/x.png) used to lose that part and now keeps it. Sources that start with an asset host, which is every one the storefronts' content has in the corpus, give the same URL either way."anywhere"stays an option. Follow-up in the migrator (feat: the v7-to-v8 migrator and skill use @decocms/tanstack and @decocms/nextjs #700): stop emitting"anywhere"and count and report the URLs this changes per site.Proposals for the PO (not decided, not in the code)
-W-autoid at its baked size. The app does not use these packages, so it is out of the golden suite. Proposal: point it at one helper.Nuvemshop's image helper stays in the store's own code (PO, 2026-10-14). There is no
apps-nuvemshoppackage.Checks
Decision 3 (afe430c, on top of #693's decisions 1–3 merged in d654d9a):
bun run check,bun run build,skills:checkandskills:readme:checkpass, and plain Node imports all 24 built exports (deco --helpruns).bun run test(2 GB): 2150 passed, 3 failed: DS-2 (fails on main too; fixed separately) and twoschema.test.tstimeouts under full-suite load (58/58 alone). Image tests 48/48 across the three packages; reverting the default fails three of them.Adversarial pass (6b7e700): c80dd33 had also widened
decoCdn's default match to every Deco CDN endpoint, which changed 168 URLs that used to pass through and were served fine; the match is back to the pre-fix sources, and a new unit test pins those pass-throughs.bun run checkandbun run buildpass; image tests 27/27; the golden fixtures are unchanged (160 approved cases). Full gates at the top of the stack (#700).Fix 2 (c80dd33):
bun run check,bun run build,skills:checkandskills:readme:checkpass.bun run test: 2133 passed, 2 failed: DS-2 (the known flake) and aschema.test.tstimeout under full-suite load, which passes 58/58 alone. The image tests pass 26/26, with 6 new fix-2 unit tests.Review pass (2026-10-14): the
srcSetfix above (ea0a6bf). Its golden markup cases (v7's own components, no width) are in #700'stests/tanstack-image-markup.golden.test.ts. Full gates at the top of the stack (#700).First round (before the PO review):
bun run checkpasses.bun run buildpasses and emitsdist/v8/image.*,apps-vtex/dist/image.*andapps-shopify/dist/image.*. Plain Node imports the built@decocms/apps-vtex/image.bun run test: 2124 passed, 4 failed:delivery.cli.test.ts, the known flake.cli-02,qs-01/in-03,saved-03/saved-11). Run alone, those files pass 179/179.bun run skills:checkandbun run skills:readme:checkpass.@decocms/blocks/imagebundles for a browser and for workerd with no Node built-ins and no React (esbuild).tests/upstream-helpers.guardrail.test.ts: everyapps-*helper subpath gets the same bundle check, plus a purity check (no network, React, env reads or runtime imports). Its comment records that the PO asked for helper subpaths inapps-*.CLAUDE.mdnow says where platform helpers live.For the PRs above (#696, #697, #700): the API changed:
vtexCatalog/vtexFileManager/shopifyare no longer in@decocms/blocks/image.parse/ParsedImageandproxyOtherHostsare gone (usesources: "all").@decocms/tanstack's defaultImageUrlcannot include VTEX/Shopify, because it must not depend onapps-*.🤖 Generated with Claude Code
https://claude.ai/code/session_016ZsmzpZ2zahRBoATBb2tBu