Release

2026-10-27

Skelet 0.2.3

Patch release, released 2026-10-27 - 50 entries. Every release

  1. v0.3.1Patch release
    • Added2
    • Fixed7
    • Security4
  2. v0.3.0Minor release - may break, under 0.x
    • Added6
    • Changed18
    • Removed8
    • Fixed47
  3. v0.2.3Patch release
    • Added8
    • Changed11
    • Fixed25
    • Security6
  4. v0.2.2Patch release
    • Security3
  5. v0.2.1Patch release
    • Added1
    • Changed1
    • Fixed2
  6. v0.2.0First in this changelog
    • Added9

Notes

v0.2.3

v0.2.3

Patch release

Released
Entries
50
  • Added8
  • Changed11
  • Fixed25
  • Security6

The Configurator lands - options with rules, templates, pictures and a step-by-step picker on the product page - along with a full media picker and a faster dashboard. One thing to do when you upgrade: migrations now run as the backend starts, so the migrate, signals-migrate and clickhouse-migrate services are gone from the compose files. Drop any docker compose run --rm migrate from your deploy scripts and any override that names those services; the server brings the schema up to date on its own before it serves.

Added8

  • Configurator templates: a set of options and rules kept once and used on many products. Templates live in a product’s Configurator section - “Use a template” adds one, “Manage templates” lists them all to create, use or open one. A template gets its options, rules and map in the same editor a product has, a currency its flat charges are in, and a “Used by” list to add products to. A product can use several, in an order you pick; their options show first on the product page and in the cart, before the product’s own, and an edit to a template reaches every product using it while orders keep what was picked. On the product, template options show read-only under the template’s name, the product’s own options and rules can read their answers, and “Detach” copies a template onto the product as its own when one product needs it changed. A template still in use can’t be deleted, and one the product’s own options still read can’t be taken off.

  • Product types can hand templates to all of their products. A product type’s new Configurator templates section puts its templates on every simple and configurable product of the type, ahead of the ones a product picks itself, and a product shows them read-only with the way to the type. A template on both the type and the product shows once, and a configurable and its variants are asked and charged for it once. The type’s list can’t drop a template one of its products still reads unless that product picked it itself too.

  • Paged gift card and subscription queries. The admin API gains giftCardsPaged(search) and subscriptionsPaged(status), each returning one page with its totalCount, and PromotionRule.codes takes search and isActive.

  • ⌘S / Ctrl+S saves a dashboard page. It opens the same review dialog as the Save button, and does nothing while there’s nothing to save.

  • Options and their choices can have pictures. In the option editor, each choice gets a small picture slot and the option one of its own - pick from the media library or upload. On the product page, an option whose choices have pictures shows them as tiles to pick from, and an option’s own picture sits under its name, like where an engraving goes. Pictures load with the option rules in a query of their own, so the main product query stays under its complexity cap; deleting the picture from the media library just removes it from the option.

  • Products get a Configurator: options plus rules, “when these answers, then these effects”. The product options section is now the Configurator, and its options can carry rules that react to what the customer picked - make another option required or optional, fix and lock the cart quantity, or move the price by an amount or a percentage (per store, like option charges). The cart applies them on add, a price change lands on the line as its own row named by the rule (“Gift set discount”), the storefront previews required options, quantity and price live, and the cart console follows them too. Rules are edited in a dialog next to the options, and a new map view lays the options out top to bottom the way customers meet them, with conditional options branching off the answers that show them and rules pointing at what they change.

  • Step-by-step configurator on the product page. A product’s Configurator has an “On the product page” setting: all options at once, as before, or step by step. Step by step walks customers through the visible options one at a time with a progress bar, keeps their earlier answers above the current step with a link to change each, holds them on a required option until it’s answered, lets them skip an optional one, and ends on a review before adding to the cart. Steps follow the conditions, so an answer that shows or hides a later option changes the count as they go, and the add button’s “missing option” jump brings that step up.

  • PaymentMethodOption.isOffline on the storefront API says whether a payment method has no third party behind it (cash on delivery, bank transfer, pickup, purchase order).

Changed11

  • The dashboard starts faster and idles quieter. The sign-in page downloads less than half as much (576 KB to 260 KB gzipped), and a signed-in page 381 KB instead of 591 KB. Hidden workspace tabs stop polling, and a mutation refetches only the queries on screen.

  • A refused delete in the dashboard now says why, in its confirmation. Deleting a collection, a collection translation or a CMS page used to close the dialog and drop the server’s reason; it now stays open and shows it. Rotating or deleting a webhook, removing a product download, image or URL redirect, and clearing a segment show their failure in the dialog instead of behind it or in a toast.

  • The dashboard settles into place instead of popping in. A reload paints the shell’s frame in its own colours straight away rather than a white page. The sidebar and the home page hold skeletons until your permissions and saved layout are in, then appear once, so sections no longer reorder under you. Stat tiles keep their place while loading instead of showing 0, and page loaders too brief to read no longer flash.

  • The header is a little shorter, 48px instead of 56.

  • Dashboard confirmations use the dashboard’s own dialog. Deleting a workspace, a media asset or directory, a shipping method, an email or email group, or a promotion code, and changing a promotion condition’s type, no longer pop up the browser’s native confirm box.

  • A full media picker everywhere the dashboard picks from the library. CMS image fields and attributes, promotion badges and the email builder now open one large modal with the library’s folder tree, search across names, titles, alt text and captions, sorting (newest, oldest, name, largest), grid or list view with two tile sizes, and a details panel to fix alt text (or write it with AI), title, caption and the crop focal point without leaving. Files upload by drag and drop, paste or the Upload button into the open folder, several at once with progress, and land already picked. Fields that take several assets pick several at a time, and the arrows and Enter work the grid. It keeps loading as you scroll, however big the library is. Media search takes a sort order to back it, with a stable order so paging never skips or repeats a file.

  • Migrations run as the backend boots, with no migrate containers. The server applies the commerce schema, the plugins’ migrations and the data patches before it serves; the signals and ingest processes do the same for the Signals database and the ClickHouse spine. Processes booting together take turns on a lock, so each migration runs once, and a boot refuses to start if a migration it applied has since been edited. skelet migrate runs the commerce step by hand for a deploy that wants it first, and the prod images no longer ship dbmate.

  • Configurator edits no longer re-index products in search. Changing a product’s options, a template or a product type’s templates used to re-index every product it reached, though search holds none of it - a template on a product type with thousands of products meant thousands of re-indexes. Caches and webhooks still hear about each product, now from batched reads.

  • Product options are edited in one dialog. Each option opens a wide editor with its name, how customers answer (picked from described cards), its choices or charge, and when it shows - instead of separate inline forms for the option and each value. The list reads as a summary: choices with their charges, character and file limits, and conditions in words (“Only when Wrapping is Gift”). Charges are typed in money (“3.50 USD”, per-store amounts in each store’s currency) rather than cents, file limits in MB, options reorder with arrows instead of sort numbers, and a choice can be linked to a product - or several products added as choices at once - through the shared product picker. A staged delete stays visible with an Undo.

  • Product recommendations get the full width. Related, upsells and cross-sells are tabs over one sortable table (image, name, SKU, reorder and remove), each tab shows its count and flags unsaved changes, and products are added through the shared product picker. The inline search box is gone - the picker searches too. A list past ten products scrolls inside its own frame and gets a filter box; reordering pauses while a filter is on.

  • Product and category pages make about 40% fewer database queries. An uncached product page went from 32 queries to 19 and a category page from around 35 to 22. Stock is read once per product, the per-question review breakdown is only built when a query asks for it, and attribute, product type and translation metadata is shared across requests for up to 30 seconds. Edits to translations, product type names and attribute groups can take up to 30 seconds longer to reach the storefront.

Fixed25

  • Every shopper cart, gift card, subscription and promotion code can be reached in the dashboard. These lists used to load the first 100 (1000 for codes) and page or search only those; they now page and search on the server.

  • The sidebar’s resize line sits on the content’s edge and stays on top all the way down, instead of running 8px into the page and disappearing under the blurred page title.

  • ⌘K finds every dashboard page, not only the ones in the sidebar, still limited to what the admin may open. Signup forms, web forms and the two Segments pages no longer share titles.

  • The category and media folder trees work from the keyboard, icon-only buttons have names, and form labels point at their inputs, so screen readers announce what each control is.

  • The nightly sweep of abandoned customer option uploads no longer removes an asset that a product option or choice uses as its picture, and option pictures upload to their own option-pictures library folder.

  • Saving product options writes in an order the backend accepts: a reorder together with a new condition, or removing a condition and the choice it named, no longer fails partway through. Moves that would put a condition above the option it reads, and deletes of an option or choice a condition still uses, are refused up front instead of at save.

  • productsByIds returned every product without a name, and the admin cache spread that blank over names other queries had loaded - recommendations showed SKUs where names belonged.

  • Opening a product picker on a product page no longer reloads the page and closes the picker. The picker’s type filter replaced the page’s cached type attributes with a narrower copy.

  • Recommendations past the first 100 linked products (across all three lists) showed as “Unavailable product” - productsByIds answers at most 100 ids, and the editor asked for all of them at once. It now asks in batches.

  • A struggling OpenSearch no longer takes checkout down with it. When OpenSearch keeps failing, the storefront stops asking it for a few seconds and lets a single request check whether it’s back. Postgres still answers in its place, but for at most a quarter of DB_MAX_CONNS at a time, so carts and checkout keep their connections. Past that, a search comes back empty instead of queueing.

  • A browse that fails on every search tier is no longer cached as an empty catalogue for a minute.

  • The section index on long detail pages no longer covers buttons. While you scroll it shows only its ticks, which fit in the page’s right margin, and its labels open over the page only when you point at it or tab to it - before, they spread over buttons at the right edge, like a section’s “Use a template”, and could catch the click meant for them.

  • The backend’s response cache now actually serves the storefront. The storefront never sent an operation name and the cache only recognises queries by name, so no storefront read was ever cached there. It sends one now, and a few more public queries joined the cacheable list: load-more on listings, product compare, blog authors and taxonomy, the store product lookup, the free-shipping threshold and channel locales.

  • A storefront purge can’t be refilled with the old answer any more. Each copy the storefront caches names its purge surface, and a purge gives that surface a fresh epoch in the backend’s response cache before the storefront hears about it, so the refetch always reaches current data.

  • Settings → Cache now controls the storefront. cache/<surface>/enabled and ttl_seconds showed in the admin but were never read. A disabled surface now refreshes every second while staying purgeable, and an enabled one keeps its data for the TTL you set.

  • Cached responses are stored as they were sent rather than base64 inside JSON, so the response cache needs 15-30% less memory per entry.

  • Vetrina edits the shopper’s own cart, and no other. Signed-in shoppers can change, remove, save and estimate cart lines without a cart cookie, because every cart action now resolves through the customer session or the guest cookie. Only adding to the cart creates a guest cart, so a coupon link without a cart waits for the first addition. A backend failure fails the action instead of switching to another cart. The cart in the browser also reloads after sign-in merges the guest cart, and after an edit fails.

  • Vetrina shows and reports money at each currency’s own precision. Prices for a currency with zero or three decimals (JPY, BHD) were divided by a hundred everywhere. Product structured data, view_item, begin_checkout and purchase events and code-only price formatting now use the store’s configured decimals, or ISO 4217’s when only a currency code is known. Order pages convert with the order’s own currency.

  • Vetrina account actions keep the shopper’s language. Signing in or registering, password resets, verification emails, addresses, subscriptions, saved cards, loyalty redemptions, reviews and logout redirected to unprefixed paths, which sent a German shopper back to English. They now go to the localized path. Return destinations keep their query, and a path that already carries the locale, such as /de?welcome=1, no longer gets a second prefix.

  • Vetrina only takes orders it can actually get paid for. The demo storefront ships no payment gateway SDK, so checkout now offers only offline methods, and it re-checks before placing the order that the chosen method is still eligible. If a gateway still asks for a payment action, the shopper lands on the order marked “created, payment not complete”. Before, the storefront tried to finalize the payment itself and redirected as if it had succeeded. Orders fully covered by gift cards or store credit still need no method.

  • A Vetrina coupon link applies whichever way the shopper adds to the cart. A code held from a promotion link was applied only after simple and configurable-product additions. Bundles, grouped products and quick order now apply it too. It applies exactly once, and a grouped or quick-order batch where only some items were added still gets it.

  • Vetrina’s search suggestions always match what was typed. A slow response for an earlier query could replace the results for the current one, and a response that arrived after the search dialog closed showed up when it reopened. Only the response for the current query in the open dialog is shown now.

  • Sorting Vetrina search results keeps the search. Choosing a sort order on /search?q=shirt dropped the search term and any query-string filters. Sort links now keep them and go back to the first page.

  • Videos play in Vetrina CMS pages and product content. The storefront’s own CSP blocked YouTube and Vimeo players (there was no frame-src) and video files (there was no media-src). Embeds now always use YouTube’s privacy-enhanced youtube-nocookie.com host or Vimeo’s player, and the CSP allows exactly those two players plus same-origin, upload-preview and HTTPS video (HTTP too in development). Embed links for any other host are no longer rendered. Who may frame the storefront is unchanged.

  • An item moved from the Vetrina wishlist to the cart stays out of the wishlist. The browser’s own copy of the wishlist kept the moved item, and the next guest-wishlist import added it back. The local entry is now removed before the page refreshes.

Security6

  • golang.org/x/net v0.62.0. Fixes quadratic-time text coalescing in its HTML parser (GO-2026-6860), which the SEO link crawler runs on every page it fetches, so a crafted page could stall a crawl. The other golang.org/x modules move with it.

  • No pnpm audit findings in either app. body-parser 2.4.0 (a denial of service on compressed request bodies) and fast-uri 3.1.9 (four URI parsing advisories), both already inside their parents’ ranges. Only dev tooling reaches them - the shadcn CLI and Sentry’s webpack plugin - so nothing that ships was exposed.

  • Frontend lockfiles refresh weekly. Renovate’s lockfile maintenance re-resolves the dashboard’s and each storefront’s transitive dependencies within their ranges, so a patched release reaches them without an override. Both lockfiles are refreshed now, which clears the markdown-it advisory and drops every audit override but @graphql-tools/utils, whose parent still excludes the fix.

  • Go 1.27.3. Distrusts the compromised intermediate certificates in crypto/x509 (GO-2026-6809).

  • Next.js 16.4.2 in Vetrina. Clears the high-severity advisory against Next.js 16.0.0 through 16.4.0 that pnpm audit reports.

  • The demo storefront no longer records shoppers’ private pages. Session replay and heatmap snapshots skip account, cart, checkout, order, order-tracking, gift-card, email-verification, shared-wishlist and preview pages, with or without a locale prefix, and the signed-in name in the header, the masquerade banner and the sign-in drawer are blanked everywhere. Page views and click heat still count those pages. Replay pauses on a private page and resumes with a fresh snapshot on the next public one. A storefront test fails when a component renders a customer’s name, email or phone outside those pages without blanking it.

Around it

Newer and older