Skip to content

Release January 20, 2026 - #2582

Open
github-actions[bot] wants to merge 251 commits into
mainfrom
changeset-release/from-canary
Open

Release January 20, 2026#2582
github-actions[bot] wants to merge 251 commits into
mainfrom
changeset-release/from-canary

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Automated release PR from canary branch

@vercel

vercel Bot commented Jan 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
graphcommerce-246 Ready Ready Preview Jul 20, 2026 9:25am
graphcommerce-247 Ready Ready Preview Jul 20, 2026 9:25am
graphcommerce-hygraph-dynamic-rows-ui Ready Ready Preview Jul 20, 2026 9:25am
graphcommerce-open-source Ready Ready Preview Jul 20, 2026 9:25am
graphcommerce-storyblok Ready Ready Preview Jul 20, 2026 9:25am

Request Review

paales and others added 30 commits July 8, 2026 16:58
Enabling Preview Mode in production requires typing the `secret` (it is only
pre-filled in dev), which dirties the field and persists it to sessionStorage.
`PreviewModeEnabled` then restored that secret via `setValue(shouldDirty)`, which
the all-fields `FormAutoSubmit` read as a user change and submitted the `update`
action — a `window.location` hop to `/api/preview` that 307-redirects back and
re-triggers the restore, looping forever.

Scope `FormAutoSubmit` to `previewData` (the only edited field) and exclude the
`secret` from `FormPersist` so the token is no longer stored in the browser.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Overrides the terminating link at the tail of the Apollo link chain (by
default the HttpLink to the Mesh backend). Because it runs after every
context-setting link (customer auth token, store, cache-id, headers), it
lets you route specific operations to a different transport while still
inheriting all request headers.

Motivating case: file uploads. File/Blob variables must be sent as a
multipart request (apollo-upload-client's UploadHttpLink), which the default
HttpLink cannot serialize. Prepending such a link via `links` terminated it
before the auth/header links ran, dropping the customer token from multipart
requests. Supplying the upload-aware split as terminatingLink keeps it at the
tail so uploads inherit the token like any other operation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…xt-bodyparser

fix(graphql-mesh): disable Next bodyParser so multipart uploads survive the mesh
…port

findOriginalSource only matched an export specifier's `exported` field, which
SWC leaves null for a non-aliased re-export like `export { Image }` — the name
lives in `orig`, and `exported` is only set by an aliased `export { X as Y }`.
Components re-exported without an alias (e.g. `Image` from @graphcommerce/image)
were therefore unreachable by plugins and failed with "Plugin target not found".
Fall back to `orig` when `exported` is absent.

dist/index.js is committed and consumed directly (no repo build step), so the
compiled output is updated to match the src change; prepack rebuilds it cleanly
on the next release.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…/executor-http FormData mismatch

customFetch used globalThis.fetch (undici) inside Next while
@graphql-tools/executor-http builds multipart bodies with FormData from
@whatwg-node/fetch. In next dev (turbopack) @whatwg-node/fetch evaluates
during next.config.ts loading, before any __NEXT global exists, so its
Next.js detection fails and it exports ponyfills — undici then stringifies
the ponyfill FormData body to the literal '[object Object]' (Magento:
"Unable to parse the request."). Always use @whatwg-node/fetch's own
fetch so fetch and FormData come from the same implementation family.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ata-fetch-mismatch

fix(graphql-mesh): multipart uploads broken in next dev — customFetch/executor-http FormData mismatch
…cept-non-aliased-reexports

fix(next-config): intercept components exported via non-aliased re-export
…-terminating-link

feat(graphql): add terminatingLink prop to GraphQLProvider
…erceptors

export * from './X.original' does not re-export default (ES semantics), so an
intercepted module silently lost its default export. GraphQL Mesh resolves
customFetch via `exported.default || exported`, so a plugin on
@graphcommerce/graphql-mesh/customFetch handed Mesh the module namespace
instead of the fetch function, breaking schema introspection during
gc-mesh build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rd-default-export

fix(next-config): forward the original module's default export in interceptors
…nite-reload

fix(ecommerce-ui): stop Preview Mode infinite reload in production
…a GC_MAGENTO_ENDPOINT_SERVER

When the runtime environment sets GC_MAGENTO_ENDPOINT_SERVER, customFetch
rewrites every request matching the origin of GC_MAGENTO_ENDPOINT to the
internal origin (GraphQL and REST) and adds X-Forwarded-Proto: https. This
keeps frontend<->Magento traffic inside the cluster instead of hairpinning
over the public load balancer. Runtime-only by design: build-time schema
introspection and SSG keep using the public endpoint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e errors to the customer inside checkout / Added card icon for the method
A `<video>` paints nothing until it has buffered enough for its first
frame, and nothing at all when autoplay is blocked (iOS Low Power Mode).
An autoplaying video banner therefore starts out black, and can stay
black. `Asset` now takes a `poster`, rendered as `<video poster>`.

Storyblok's `type: asset` has no room for a poster, so `assetWithPoster()`
reads the convention of an asset value carrying an extra `poster` key:

  { "fieldtype": "asset", "filename": "…", "poster": { "filename": "…" } }

Keeping the poster beside the asset rather than nesting both under a
wrapper is what makes a custom field type storing that shape a drop-in
for a plain asset field: existing content stays valid and
`value.filename` keeps working for consumers that ignore the poster. The
narrowing is unavoidable regardless — Storyblok has no JSONSchema for
custom field types, so its type generator emits `unknown` for them.

`@graphcommerce/image` gains `imageUrl()`, which builds an optimized URL
outside of a React tree for the spots that need a bare URL rather than an
`<Image>` — `<video poster>`, `background-image`, `og:image`. It routes
through the configured loader, so the bytes are served and cached by the
deployment instead of every visitor hitting the origin host; that matters
when the origin meters bandwidth. Width snaps up to the nearest
configured size, since the optimizer 400s on anything outside
`imageSizes`/`deviceSizes`.
Improve Braintree checkout flow and make 3D Secure optional
…ndpoint

feat(graphql-mesh): route Magento traffic over an internal network via GC_MAGENTO_ENDPOINT_SERVER
…Poster

Each asset field in the example now goes through `assetWithPoster()`
instead of reading `blok.asset` directly, and forwards the resulting
`poster` to `<Asset>`.

This makes the choice of field type a content decision rather than a code
one: a plain `type: asset` and a custom field storing the poster
convention both narrow through the same call, so a project can switch any
field between the two — in either direction — without touching a
component. Fields that never hold a video simply resolve `poster` to
undefined.
`HeroBanner` rendered a `<video>` itself, from a raw URL. That meant it
could only ever hold a video, and left nowhere to hang a poster — the one
component where a poster matters most, being a full-bleed autoplaying
video above the fold.

It now takes `asset?: React.ReactNode` and renders it, exactly as its
sibling SpecialBanner already does. Positioning stays with the banner:
whatever is passed gets stretched to fill via `& img, & video`.

  - <HeroBanner videoSrc={asset.filename} … />
  + <HeroBanner asset={<Asset asset={asset} poster={poster} />} … />

Taking a node rather than data also keeps next-ui free of any CMS: the
Storyblok and Hygraph examples each pass their own `<Asset>`, both of
which already render images and video. Importing a CMS `Asset` into
next-ui would have inverted the dependency — storyblok-ui and hygraph-ui
depend on next-ui, not the other way round.

Non-breaking: `videoSrc` stays as a deprecated fallback that renders a
bare autoplaying video (keeping the HeroBanner-video class), and the
`animated`/`video` class parts stay exported. The only behavioural change
on the videoSrc path is the loss of the scroll parallax, along with the
framer-motion, useScrollY and clientSize machinery it required.
…et-poster

Feature/storyblok asset poster
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.

5 participants