Skip to content

Releases: graphcommerce-org/graphcommerce

Release 11.0.0-canary.47

Pre-release

Choose a tag to compare

@github-actions github-actions released this 11 Aug 19:55

@graphcommerce/[email protected]

Patch Changes

  • #2659 e7fe659 - Support the Magento 2.4.9 guest-order lookup, which renamed the guestOrder argument type OrderInformationInputGuestOrderInformationInput and swapped the postcode lookup field for lastname. The GuestOrder query and the guest order form now use the new type/field, and schema-249 re-declares guestOrder(input: GuestOrderInformationInput!) so operations keep validating on older backends via the mesh version shim. (@paales)

@graphcommerce/[email protected]

Patch Changes

  • #2659 8623e57 - Schema defintions for 246,247 and 248 (@paales)

  • #2659 e7fe659 - Support the Magento 2.4.9 guest-order lookup, which renamed the guestOrder argument type OrderInformationInputGuestOrderInformationInput and swapped the postcode lookup field for lastname. The GuestOrder query and the guest order form now use the new type/field, and schema-249 re-declares guestOrder(input: GuestOrderInformationInput!) so operations keep validating on older backends via the mesh version shim. (@paales)

  • #2659 5cead7c - Make every field the schema-<version> backfills add to an existing type nullable. These folders are layered onto an older Magento backend that does not have the field, so it can never produce a value: a non-null declaration made codegen emit a required property (breaking getStoreConfig in algolia-products with "Type 'StoreConfig' is missing the following properties … and 15 more" on a 2.4.7 backend) and, worse, would nullify the whole parent object at runtime because a non-null field resolving to null propagates upward. Brand-new types keep their original nullability. (@paales)

Release 11.0.0-canary.46

Pre-release

Choose a tag to compare

@github-actions github-actions released this 11 Aug 08:17

@graphcommerce/[email protected]

Patch Changes

  • #2668 b822462 - Fix two bugs in toDate:

    • ISO datetime strings with a timezone offset (e.g. 2024-01-15T10:30:00-05:00) were being corrupted into Invalid Date, because replace(/-/g, '/') was applied to every string instead of only to plain YYYY-MM-DD dates
    • Strings in Magento's DATETIME_SLASH_PHP_FORMAT (d/m/Y H:i:s) were misparsed as MM/DD/YYYY by the native Date constructor, silently producing the wrong date or undefined (@hsngdz)

Release 11.0.0-canary.45

Pre-release

Choose a tag to compare

@github-actions github-actions released this 07 Aug 12:21

@graphcommerce/[email protected]

Major Changes

  • #2626 e2c9f9b - Breaking change — requires Node.js 22 or newer. This kicks off GraphCommerce 11.

    Node.js 20 hit end-of-life in April 2026, so the framework no longer supports it. engines.node is now >=22.0.0 <27.0.0 across the root and all example storefronts — Node 22 (Maintenance LTS) is the new minimum, Node 24 (Active LTS) is recommended, and Node 26 (current) is also accepted.

    • CI: release-canary, release-main and pr-analysis now run on Node 24. periodic-build matrix changed from [20, 22] to [22, 24], and actions/setup-node bumped from v3 to v4.
    • .gitpod.yml bootstrap moved from nvm install 18 to nvm install 24.
    • Getting-started docs and the three example READMEs now point at Node 22/24.

    See docs/upgrading/graphcommerce-10-to-11.md for the migration steps. (@paales)

Release 10.1.0-canary.44

Pre-release

Choose a tag to compare

@github-actions github-actions released this 07 Aug 12:02

@graphcommerce/[email protected]

Patch Changes

  • #2662 54e167f - Publishing content now pushes a renew signal through the Next.js incremental cache, so every server invalidates at once instead of each polling cdn/spaces/me on a 60 second interval — storyblok.cacheVersionTtl therefore defaults to 3600 as a failsafe. (@paales)

@graphcommerce/[email protected]

Patch Changes

  • #2662 33d0940 - Cut the number of Storyblok CDN requests a storefront makes: the pinned cache-version now actually advances, unknown slugs are answered from a cached slug index instead of one request each, and getStoryblokStaticPaths() no longer downloads story bodies. (@paales)

  • #2662 54e167f - Publishing content now pushes a renew signal through the Next.js incremental cache, so every server invalidates at once instead of each polling cdn/spaces/me on a 60 second interval — storyblok.cacheVersionTtl therefore defaults to 3600 as a failsafe. (@paales)

Release 10.1.0-canary.43

Pre-release

Choose a tag to compare

@github-actions github-actions released this 07 Aug 10:01

@graphcommerce/[email protected]

Patch Changes

  • #2667 6ee8488 - Give useClearCurrentCartId() and useCartLock()'s lock/unlock a stable function identity, so consumers can list them in a useEffect dependency array without the effect re-running on every render.

    useClearCurrentCartId returned a bare arrow function and useCartLock defined lock/unlock inline, while the neighbouring useAssignCurrentCartId already returned a useCallback. useClearCurrentCartId now uses useCallback with the Apollo cache as its only dependency (matching useAssignCurrentCartId), and lock/unlock use useEventCallback because they read values that legitimately change (currentCartId, the router query state and the Apollo client) — useEventCallback keeps the identity stable while always reading the latest values, so no stale closure is introduced. (@paales)

@graphcommerce/[email protected]

Patch Changes

  • #2667 6ee8488 - Give useClearCurrentCartId() and useCartLock()'s lock/unlock a stable function identity, so consumers can list them in a useEffect dependency array without the effect re-running on every render.

    useClearCurrentCartId returned a bare arrow function and useCartLock defined lock/unlock inline, while the neighbouring useAssignCurrentCartId already returned a useCallback. useClearCurrentCartId now uses useCallback with the Apollo cache as its only dependency (matching useAssignCurrentCartId), and lock/unlock use useEventCallback because they read values that legitimately change (currentCartId, the router query state and the Apollo client) — useEventCallback keeps the identity stable while always reading the latest values, so no stale closure is introduced. (@paales)

Release 10.1.0-canary.42

Pre-release

Choose a tag to compare

@github-actions github-actions released this 06 Aug 12:36

@graphcommerce/[email protected]

Minor Changes

  • #2653 18ef8ef - Adds Google Places address autocomplete to customer and checkout address forms. Selected addresses populate GraphCommerce address fields and Magento regions, with automatic fallback to manual entry when Google Maps is unavailable. (@hsngdz)

Release 10.1.0-canary.41

Pre-release

Choose a tag to compare

@github-actions github-actions released this 06 Aug 12:27

@graphcommerce/[email protected]

Patch Changes

  • #2660 84e23e2 - Redirect the Magento frontend account URLs that end up in transactional emails to their GraphCommerce equivalents.

    Magento renders links from base_link_url, which on a headless setup points at the GraphCommerce storefront. Every stock email template contains a customer/account/ link ("Sign in to your account"), and gated Magento routes 302 to customer/account/login — none of which GraphCommerce serves, so customers landed on a 404.

    withGraphCommerce now adds permanent redirects for /customer/account, /customer/account/index, /customer/account/login, /customer/account/create and /sales/order/history. They are exact matches on purpose: a redirect wins over a filesystem route, and /customer/account/confirm and /customer/account/createPassword are real pages in the examples — the two that carry Magento's confirmation key and reset rp_token. A /customer/account/:path* catch-all would make both unreachable.

    This restores the /customer/account redirect that was dropped as collateral in "Remove redirects for /product/$type/[url] routes". (@paales)

Release 10.1.0-canary.40

Pre-release

Choose a tag to compare

@github-actions github-actions released this 06 Aug 12:22

@graphcommerce/[email protected]

Patch Changes

  • #2661 c849720 - Resolve story multilinks against the target's current slug instead of the frozen cached_url. cached_url is a snapshot taken when the referencing story was last published, so renaming the target left every link to it pointing at a 404 until an editor re-published each referencing story. multilinkHref now prefers story.full_slug — populated from the links map the CDN resolves on every read, since sbParams always sends resolve_links: 'story' — which makes story links self-healing across renames. Other link types are unaffected. (@paales)

Release 10.1.0-canary.39

Pre-release

Choose a tag to compare

@github-actions github-actions released this 20 Jul 09:20

@graphcommerce/[email protected]

Minor Changes

  • #2652 cd21a97 - Let a Storyblok video asset show a poster.

    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) — so an autoplaying video banner starts out black, and can stay black. Asset now takes a poster prop, rendered as <video poster>.

    Storyblok's own type: asset has no room for a poster, so assetWithPoster() is added to read the convention of an asset value carrying an extra poster key:

    {
      "fieldtype": "asset",
      "id": 1,
      "filename": "",
      "poster": { "filename": "" },
    }

    Keeping the poster beside the asset rather than nesting both under a wrapper means a custom field type storing that shape is 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 — Storyblok has no JSONSchema for custom field types, so its type generator emits unknown for them.

    const { asset, poster } = assetWithPoster(blok.asset)
    return asset && <Asset asset={asset} poster={poster} />

    @graphcommerce/image gains imageUrl(src, { width, quality }), which builds an optimized URL outside of a React tree — for the places that need a bare URL string rather than an <Image>, such as <video poster>, a CSS background-image or an og:image. It routes through the configured loader exactly like <Image> does, so the bytes are served and cached by your own deployment rather than fetched from the origin host by every visitor, which matters when the origin meters bandwidth. width is snapped up to the nearest configured size, since the optimizer rejects any width outside imageSizes/deviceSizes. (@bramvanderholst)

@graphcommerce/[email protected]

Minor Changes

  • #2652 132d1e1 - HeroBanner gains an asset?: React.ReactNode prop, and videoSrc is deprecated.

    The banner rendered a <video> itself, from a raw URL — so it could only ever hold a video, and left nowhere to hang a poster, the one place a poster matters most (a full-bleed autoplaying video above the fold). It now takes a node and renders it, the way its sibling SpecialBanner already does; positioning stays with the banner, stretching whatever is passed to fill via & img, & video.

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

    Taking a node rather than a source 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.

    videoSrc still works but is deprecated: when set (and asset is not) it renders a bare autoplaying video, keeping the HeroBanner-video class. The only behavioural change on that path is that the scroll parallax is gone, along with the framer-motion, useScrollY and clientSize machinery it needed. Migrate to asset to regain images, posters, and control over how the media is rendered. (@bramvanderholst)

@graphcommerce/[email protected]

Minor Changes

  • #2652 cd21a97 - Let a Storyblok video asset show a poster.

    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) — so an autoplaying video banner starts out black, and can stay black. Asset now takes a poster prop, rendered as <video poster>.

    Storyblok's own type: asset has no room for a poster, so assetWithPoster() is added to read the convention of an asset value carrying an extra poster key:

    {
      "fieldtype": "asset",
      "id": 1,
      "filename": "",
      "poster": { "filename": "" },
    }

    Keeping the poster beside the asset rather than nesting both under a wrapper means a custom field type storing that shape is 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 — Storyblok has no JSONSchema for custom field types, so its type generator emits unknown for them.

    const { asset, poster } = assetWithPoster(blok.asset)
    return asset && <Asset asset={asset} poster={poster} />

    @graphcommerce/image gains imageUrl(src, { width, quality }), which builds an optimized URL outside of a React tree — for the places that need a bare URL string rather than an <Image>, such as <video poster>, a CSS background-image or an og:image. It routes through the configured loader exactly like <Image> does, so the bytes are served and cached by your own deployment rather than fetched from the origin host by every visitor, which matters when the origin meters bandwidth. width is snapped up to the nearest configured size, since the optimizer rejects any width outside imageSizes/deviceSizes. (@bramvanderholst)

Release 10.1.0-canary.38

Pre-release

Choose a tag to compare

@github-actions github-actions released this 17 Jul 14:15

@graphcommerce/[email protected]

Minor Changes

  • #2650 ccd01e9 - Route server-side Magento traffic over an internal network with the new runtime-only GC_MAGENTO_ENDPOINT_SERVER environment variable (e.g. http://varnish.magento-namespace.svc.cluster.local). When set, every mesh request whose URL starts with the origin of GC_MAGENTO_ENDPOINT — GraphQL and REST — is rewritten to the internal origin and gains an X-Forwarded-Proto: https header, so frontend↔Magento traffic inside a Kubernetes cluster no longer hairpins over the public load balancer. Unset, behavior is unchanged. See the new "Routing Magento traffic over an internal network" section in the mesh docs. (@paales)