Vendure v3.8.2 is a patch release containing a batch of core, dashboard, CLI, @vendure/create and plugin fixes, including a fix for Dashboard extension pages which broke after a redeploy.
If you use RedisCachePlugin, cancel Orders from the ArrangingAdditionalPayment state, serve resized SVG assets, or script vendure auth status or @vendure/create, read Behaviour Changes.
Core
- Recalculating an unchanged Order gives the same totals every time when an item-level Promotion depends on an order total condition such as
minimum_order_amount(#5663, closes #5605). Before, the result could alternate between two values. updatePromotionchecks that each action has its required condition, ascreatePromotiondoes (#5676, closes #5675). Before, an update could leavebuy_x_get_y_freewithout its condition, and order price calculation then threwCannot read properties of undefined (reading 'freeItemsPerLine').- An Order in
ArrangingAdditionalPaymentcan only move toCancelledonce all of its lines are cancelled, as from every other state (#5672, closes #5671). Before, the check was skipped and allocated stock was not released. - The default search index includes every variant passed to
updateVariantsByIdandupdateVariants, not only the first 50 (#5674, closes #5673). This showed when a Collection gained more than 50 variants at once:search({ collectionId })returned 50 of them. - A Customer cannot change their email address to one which belongs to another Customer, including a guest Customer with no User (#5680, relates to #5640). The request returns
EmailAddressConflictError. Before, two Customers could end up with the same address. authenticatereturnsInvalidCredentialsErrorfor an empty input or a strategy withnulldata, instead of an internal error with no error code (#5622, closes #5621).createAssetswithtranslationsand no custom fields returns the translated Asset (#5713). Before, selectingnamefailed with "Cannot return null for non-nullable field Asset.name".- Blocking event handlers:
- A circular dependency between three or more handlers is reported with a
Circular dependency detectederror which names the handlers, instead ofRangeError: Maximum call stack size exceeded(#5632, closes #5630). A handler whose registration was rejected no longer stays registered. - Two handlers which declare the same constraint from both sides (A is
afterB, and B isbeforeA) are ordered instead of throwing aRangeError(#5665, closes #5664).
- A circular dependency between three or more handlers is reported with a
RedisCacheStrategyremoves expired entries from its tag sets and gives each tag set an expiry, so tag sets no longer grow without limit (#5683, closes #5682). The session cache tag gained one member per session.invalidateTagsnow works in batches of 1000 and throws on a command error instead of ignoring it.- The polling job queue waits for a job which is being claimed before it stops (#5565, relates to #4949). Before, the application could close while the job was already marked
RUNNING, and nothing settled it afterwards. - A migration no longer de-duplicates translations in a table which the same migration creates, and logs de-duplication only when it removes rows (#5702). New projects no longer log "The … table does not exist. Skipping de-duplication." for every translation table.
Dashboard
- Every build gives its JavaScript chunks new file names (#5772, closes #5564). Before, an extension chunk could keep its file name while the entry chunk it imports got a new one. Because
/assetsis cached as immutable, the browser then ran the old chunk, and extension pages showed "Not Found" or failed withCannot read properties of null (reading 'useContext')after a redeploy. - After logging out and signing in as another user, the Dashboard loads that user's settings (#5669). Before, the new user saw the previous user's settings, and their active Channel was saved into the new user's settings.
- Dashboard widgets keep the position given in
defaultSize.xanddefaultSize.ywhen the user has no saved layout and the position is free (#5658, closes #5616). The built-in widgets have new default positions. - Expanded child Collections reload in the new language when you switch the content language (#5706, closes #5704).
- Changing a line's quantity in Modify order and on the draft order page works when the OrderLine has a relation custom field (#5697, closes #5696). The field was sent by its output name, which the API rejects.
- The asset picker's select button, the upload toasts and the move Collections dialog use proper plurals in every locale (#5590, relates to #5482).
- In
useExperimentalBundlemode, styles match source mode (#5709). Before, selects could render too narrow and some@vendure-io/uiclasses, such as the breadcrumb separator, were missing.
CLI and @vendure/create
vendure console linkhas a new--console-project <id|slug>option which selects the Console Project without the picker (#5685). With--non-interactiveit creates the Project Link from the login stored byvendure auth login.vendure auth loginandvendure auth statuscheck that the stored login can still be renewed (#5689). Before, a login which could no longer be renewed was reported as valid.vendure console linksays when the stored login has expired or was revoked, instead of "No CLI login … is stored on this machine" (#5710).- The unknown-command hint names an inactive CLI plugin only when that plugin declares the command, and treats a plugin enabled for the project as active (#5692).
@vendure/createno longer hangs when the Docker daemon does not answer (#5693). The daemon check gives up after 10 seconds anddocker compose upafter 5 minutes.@vendure/createexits with code 1 when it cannot copy the email templates (#5694). Before, it stopped with exit code 0.
Plugins
- Asset Server Plugin: a resized
.svgasset with noformatparameter is served asimage/png, which is what the response body contains (#5255). Before, it was labelledimage/svg+xml, and since 3.7.3 the browser downloaded it instead of displaying it. - GraphiQL Plugin: the GraphiQL page sends requests to the server which served it (#5686). Before, a server with
apiOptions.hostnameset, for example0.0.0.0in Docker, sent the page an address the browser could not reach. A customroutenow works too. - Job Queue Plugin:
BullMQJobQueueStrategywaits for its Redis connection to be ready before it subscribes to job cancellations (#5631, closes #5627). Before, the subscription could be lost during startup, and cancelling a running job did nothing. - MCP Plugin:
adjust_stocklocks the stock levels it reads, so concurrent calls no longer overwrite each other (#5653, closes #5652).upload_assetpublishes anAssetEventof typecreated, as thecreateAssetsmutation does (#5662, closes #5661).- An OAuth bearer token resolves on PostgreSQL with
UuidIdStrategy(#5705, closes #5701). Before, it failed withoperator does not exist: uuid = character varying. Sessions created for MCP clients also record the caller's API type. - The Dashboard navigation titles of the plugin are translated, and no longer log "Uncompiled message detected!" (#5660, closes #5641).
- MCP SDK:
idSchemaconverts an id sent as a string of digits, such as"9", to a number when the id strategy is auto-increment (#5647, closes #5626). Before,adjust_stock,update_variant,update_productandupdate_customerfailed with a database error.
Behaviour Changes
No APIs are removed or renamed.
Redis cache tags. RedisCacheStrategy stores tags in sorted sets under a new key name, <namespace>:ztag:<tag>. The old <namespace>:tag:<tag> sets get no new members, and invalidateTags still empties them. During a rolling deploy, an invalidation on an instance still on the old version does not clear entries tagged by upgraded instances. Those entries expire with their TTL. Tagged writes use EVAL, so a Redis ACL user needs the @scripting permission. The documented minimum is Redis 3.2, or a compatible server such as Valkey.
Cancelling from ArrangingAdditionalPayment. The transition to Cancelled now fails unless every line of the Order is cancelled.
Updating a Promotion. updatePromotion returns the same error as createPromotion when an action's required condition is missing. The check runs only when the update sends conditions or actions, so a Promotion already saved without its condition can still be disabled or renamed.
Resized SVG assets. These are cached under a .png file name and served as image/png.
GraphiQL Plugin. The page no longer uses apiOptions.hostname to build its request URLs.
Dashboard build output. JavaScript file names in the build output change with every build, even when the source is unchanged. Set disablePlugins.chunkHash in the Vite plugin options to turn this off.
CLI exit codes and output. vendure auth status exits with 1 when the stored login cannot be renewed, and its --json output has two new fields, renewable and renewalError. @vendure/create exits with 1 when the email templates cannot be copied or the Docker daemon does not answer.
Migration
No database migrations are required. Upgrade all @vendure/* packages together to 3.8.2.
What's Changed
- fix(mcp-sdk): convert numeric id strings of tool inputs to numbers by @Ryrahul in #5647
- fix(core): clear item promotion adjustments before testing conditions by @AyeshaWaseemAhmad in #5663
- fix(job-queue-plugin): subscribe to job cancellations once the connection is ready by @AyeshaWaseemAhmad in #5631
- fix(dashboard): initialize user settings from the next session after logout by @AyeshaWaseemAhmad in #5669
- fix(mcp-plugin): lock the stock levels which adjust_stock reads by @Ryrahul in #5653
- ci: use Bun 1.4.4 in all workflows by @dlhck in #5681
- chore: add build and threat model for Anthropic OSS Scanner by @michaelbromley in #5677
- fix(core): detect circular dependencies between blocking event handlers of any length by @AyeshaWaseemAhmad in #5632
- fix(mcp-plugin): translate dashboard nav titles by @brbousnguar in #5660
- fix(mcp-plugin): publish an AssetEvent for the asset upload_asset cre… by @Ryrahul in #5662
- fix(dashboard): preserve defaultSize.x/y for unobstructed widgets by @JillK83 in #5658
- fix(core): index every variant passed to updateVariantsById and updateVariants by @mwafakturk in #5674
- fix(core): validate required conditions when updating a promotion by @mwafakturk in #5676
- fix(core): refuse an email update to an address owned by a guest Customer by @Tyagiquamar in #5680
- fix(core): bound RedisCacheStrategy tag sets and batch tag invalidation by @oliverstreissi in #5683
- fix(graphiql-plugin): send requests to the server that served the page by @HouseinIsProgramming in #5686
- feat(cli): choose the Console Project in vendure console link by @michaelbromley in #5685
- fix(core): remove translation de-duplication noise from migrations by @michaelbromley in #5702
- fix(dashboard): fix broken styles in experimental bundle mode by @LucidityDesign in #5709
- ci: replace CLA Assistant with our own CLA check by @michaelbromley in #5730
- fix(core): order blocking event handlers that declare a constraint on both sides by @AyeshaWaseemAhmad in #5665
- fix(cli): name a plugin in the unknown-command hint only when it declares the command by @HouseinIsProgramming in #5692
- fix(create): time out Docker calls when the daemon does not respond by @HouseinIsProgramming in #5693
- fix(core): translate assets created with translations by @HouseinIsProgramming in #5713
- fix(dashboard): reload expanded child collections when the content language changes by @AyeshaWaseemAhmad in #5706
- fix(dashboard): send relation custom fields by input name on line adjust by @isxwor in #5697
- ci: retry the back-merge push when minor has moved by @michaelbromley in #5650
- fix(cli): check that a stored login can still be renewed in auth login and auth status by @biggamesmallworld in #5689
- fix(create): exit with code 1 when email templates fail to copy by @HouseinIsProgramming in #5694
- ci: replace CLA Assistant with our own CLA check by @michaelbromley in #5728
- fix(ci): read the changed paths from a here-string in detect-changes by @biggamesmallworld in #5600
- test(core): use literal expectations in the set session token spec by @biggamesmallworld in #5588
- test(core): add e2e coverage for the in-memory job queue strategy and fix a timing-dependent spec by @biggamesmallworld in #5438
- fix(dashboard): use plural messages in the asset picker, upload toasts and move dialog by @biggamesmallworld in #5590
- fix(asset-server-plugin): name cached svg transforms with the png extension by @biggamesmallworld in #5255
- fix(cli): say when the stored login expired in vendure console link by @HouseinIsProgramming in #5710
- fix(core): wait for an in-flight job claim before the polling queue stops by @biggamesmallworld in #5565
- fix(core): return InvalidCredentialsError when authenticate names no strategy by @oschweitzer in #5622
- fix(mcp-plugin): resolve grant session token without cross-type join on Postgres (#5701) by @Tyagiquamar in #5705
- fix(core): run checkAllItemsBeforeCancel when cancelling from ArrangingAdditionalPayment by @Tyagiquamar in #5672
- ci: run dashboard e2e on ubuntu-24.04 by @HouseinIsProgramming in #5774
- fix(dashboard): make chunk file names unique to each build by @HouseinIsProgramming in #5772
New Contributors
- @JillK83 made their first contribution in #5658
- @mwafakturk made their first contribution in #5674
- @Tyagiquamar made their first contribution in #5680
- @oschweitzer made their first contribution in #5622
Full Changelog: v3.8.1...v3.8.2