github vendurehq/vendure v3.6.3

4 hours ago

Highlights

This patch release lands a substantial round of stability, concurrency and dashboard fixes. The headline items address race conditions in promotion and coupon handling, prevent out-of-memory issues on large channel assignments, and harden the scheduled-task and state-machine subsystems against duplicated or partially-applied work. Thanks as ever to everyone who reported issues and contributed PRs.

Concurrency and integrity fixes

This release closes several long-standing edge cases that could cause data integrity issues under load or concurrent traffic:

  • Coupon usage limits enforced under concurrent checkout (#4660) -- A race condition allowed customers to bypass the per-coupon usage limit when multiple checkouts completed concurrently. The check is now serialised correctly so the configured limit is always respected.
  • Auto-applied promotion usage limits enforced (#4405) -- Promotions that auto-apply (no coupon code required) were not honouring their configured usage limits. They now respect the same limits as code-applied promotions.
  • Atomic state-machine transitions on hook failure (#4689) -- If a state-transition hook threw after the entity state had been written, the entity could be left in an inconsistent state with hooks partially applied. Transitions are now atomic — a failing hook reverts the state change cleanly.
  • No duplicate execution of fast scheduled tasks (#4681) -- Scheduled tasks running on a short interval could be picked up and executed by multiple workers in the same window. The scheduler now correctly deduplicates these executions.

Performance and scalability

  • Avoid OOM on large product-to-channel operations (#4669) -- assignProductsToChannel and removeProductsFromChannel now use a query relation strategy that no longer hydrates the full product graph into memory. This unblocks customers who hit out-of-memory crashes when assigning thousands of products to a channel.
  • Job queues now created in onModuleInit (#4680) -- Moving job-queue creation from onApplicationBootstrap to onModuleInit means queues exist before any other module's bootstrap code runs, eliminating a class of "queue not found" startup races.

Core fixes

  • Asset update with custom field relations (#4696) -- Updating an Asset that had a custom-field relation defined no longer fails. This pairs with the corresponding dashboard fix (#4695).
  • Entity hydrator handles undefined relations (#4672) -- Hydrating an entity where a requested relation was undefined no longer throws.
  • Customer user resolution via relation (#4468) -- The Customer.user resolver now uses the configured relation instead of doing a separate email-based lookup, fixing edge cases where the email had been changed on the user record.
  • Admin UI handles tokenMethod array form (#4663, fixes #4656) -- When tokenMethod is configured as an array (['cookie', 'bearer']), the generated ui-config now serialises it correctly instead of producing an invalid value.

Dashboard fixes and improvements

  • Asset save with custom fields (#4695) -- Saving an Asset with custom fields defined now works correctly from the dashboard.
  • Action bar positioning relative to extensions (#4676) -- Extensions can now position action bar items relative to other extensions rather than only to built-in items, giving extension authors more control over toolbar layout.
  • Graceful fallback on denied replace-extension (#4694) -- When a permission check denies a replace-style extension, the dashboard now falls back to the original block instead of rendering nothing.
  • Custom-page permission checks (#4679) -- Custom pages now respect their declared requiredPermissions and won't render for users who lack them.
  • isFullWidth metadata prop implemented (#4638) -- PageBlocks can now opt into a full-width layout via metadata, useful for components like rich text editors.
  • Direct @base-ui/react imports dropped (#4697) -- Dashboard internals no longer import directly from @base-ui/react, going through the wrapped component layer instead. This keeps the public surface area consistent for extension authors.
  • Order modification preview includes nested fragments (#4640) -- Modification previews now include all required nested fields, so the preview matches what the modification will actually produce.
  • Draft order mutation error messages (#4381) -- updateOrder* mutations on draft orders now surface their error messages to the UI rather than failing silently.
  • Promotions list default sort (#4688) -- The promotions list now opens with a sensible default sort instead of arbitrary order.
  • Chart widget dynamic Y-axis width (#4516) -- The dashboard chart widget now sizes its Y-axis dynamically so long labels are no longer clipped.
  • Empty customFields selection handled (#4652) -- Custom-field components with no selection no longer throw.
  • Fulfillment arg defaults are strings (#4658) -- Fulfillment handler argument default values are now correctly stringified.
  • Tanstack router generator stability (#4666) -- Inlining the route literal sidesteps a tanstack router-generator quirk that could produce broken route trees in certain layouts.

New dashboard features

  • Bulk cancel action with human-readable durations (#4361) -- Order lists now expose a bulk-cancel action, and durations are rendered as human-readable strings (e.g. "3 days ago") rather than raw timestamps.
  • Romanian translations (#4598) -- Romanian (ro) is now supported in the dashboard.

i18n improvements

  • Italian translations updated (#4645) -- A round of missing Italian (it) strings has been added.
  • Swedish corrections (#4684) -- Several mistranslated Swedish (sv.po) strings have been corrected.
  • Wrong-language msgstrs repaired (#4685) -- A bulk fix across hr, nb, tr, it, ja, ko, he and ro removes msgstr entries that had drifted into the wrong language. The i18n:apply script has been hardened to prevent recurrences.

Plugins

  • BullMQJobQueuePlugin filtering (#4523) -- Job filtering in the BullMQ plugin now produces correct results when combining multiple filter fields.

What's Changed

  • fix(ci): Remove [skip ci] from generate_docs workflow by @oliverstreissi in #4646
  • fix(ci): Add gate job to unblock non-package PRs by @michaelbromley in #4649
  • fix(docs): Fix broken links and outdated type names in custom form components docs by @gabriellbui in #4648
  • docs: Fix typo in navigation-menu arrayToTree code snippet by @gabriellbui in #4593
  • fix(core): Enforce usage limits for auto-applied promotions by @HouseinIsProgramming in #4405
  • fix: Add missing include nested fragments in order modification preview by @Ryrahul in #4640
  • fix: Add dynamic y axis width by @Ryrahul in #4516
  • feat(dashboard): Add Romanian translations by @alingabrieldm in #4598
  • fix: Add recursive flatten job filter for bull mq when filter is sent… by @Ryrahul in #4523
  • fix: Make tab view scrollable by @Ryrahul in #4644
  • feat(dashboard): Add bulk cancel action and human-readable duration t… by @Ryrahul in #4361
  • chore: Bump @vendure-io/docs-generator to 0.1.1 by @michaelbromley in #4664
  • fix(admin-ui): Handle tokenMethod array form when generating ui-config (#4656) by @Draykee in #4663
  • fix(dashboard): Inline route literal for tanstack router-generator by @michaelbromley in #4666
  • fix(core): Avoid OOM in product-to-channel assign/remove via query relation strategy by @arthur-nesterenko in #4669
  • chore: Migrate package management from npm to Bun by @michaelbromley in #4675
  • fix(core): Create job queues in onModuleInit instead of onApplicationBootstrap by @michaelbromley in #4680
  • fix(core): Prevent coupon usage limit bypass via concurrent checkout race condition by @grolmus in #4660
  • fix(core): Resolve customer user via relation instead of email lookup by @grolmus in #4468
  • fix(dashboard): Support action bar positioning relative to extensions by @izumi0uu in #4676
  • chore: Add local asset storage strategy and its factory to exports by @DanielBiegler in #4671
  • fix(core): Handle undefined relation in entity hydrator by @izumi0uu in #4672
  • fix(dashboard): Handle empty customFields selection when all fields h… by @Ryrahul in #4652
  • fix(dashboard): Add missing Italian translations by @claudiolor in #4645
  • feat(dashboard): implement isFullWidth metadata prop by @casperiv0 in #4638
  • fix(dashboard): Repair wrong-language msgstrs across hr/nb/tr/it/ja/ko/he/ro and harden i18n:apply by @michaelbromley in #4685
  • fix(dashboard): correct mistranslated Swedish strings in sv.po by @comega-johan in #4684
  • fix(dashboard): Ensure fulfillment arg default value is a string by @kyunal in #4658
  • chore(dashboard): Add error messages to update draft mutations by @LucidityDesign in #4381
  • fix(core): Make state-machine transitions atomic on hook failure by @michaelbromley in #4689
  • fix(dashboard): set default sort on promotions list by @casperiv0 in #4688
  • fix(core): Prevent duplicate execution of fast scheduled tasks by @BibiSebi in #4681
  • fix(dashboard): Check required permissions when rendering custom page… by @LucidityDesign in #4679
  • fix(dashboard): Fall back to original on denied replace extension by @michaelbromley in #4694
  • fix(dashboard): Align asset mutation selection set with other detail pages by @michaelbromley in #4695
  • fix(core): Save Asset before running custom-field relation updates by @michaelbromley in #4696
  • chore(dashboard): Drop direct @base-ui/react imports by @michaelbromley in #4697

New Contributors

Full Changelog: v3.6.2...v3.6.3

Don't miss a new vendure release

NewReleases is sending notifications on new releases.