Highlights
Vendure Core v3.8 brings AI agent access to your store via MCP, encrypted secret fields, more capable custom fields, fresher order pricing, a Dashboard refreshed with the latest Vendure design system, an upgrade to NestJS 12 and Apollo Server 5, and full support for TypeORM v1 alongside v0.3. It also ships the fix for a high-severity session advisory.
It's a minor release, but it contains schema changes and a few behaviour changes. Read the Migration Guide below before upgrading: in particular, check whether you need the order line promotion data migration, which must run in the same deployment as the upgrade.
MCP Server
Two new packages expose your Vendure store to AI agents over the Model Context Protocol (#5262):
@vendure/mcp-pluginships 42 built-in tools (18 on the shop toolset, 24 on the admin toolset) and supports two kinds of assistant:- one that runs inside the Vendure server and calls tools directly
- one that runs outside it and uses OAuth with consent, so it acts on behalf of a signed-in customer or administrator
@vendure/mcp-sdklets any plugin add its own tools with the@McpTooldecorator. It also provides helpers for lists, input schemas, money, custom fields and the active order (#5474, #5505).
See the MCP Server Quick Start.
Core
- Secret fields encrypted at rest (#5051): custom fields and configurable-operation arguments can be marked
secret: true. They are then encrypted in the database and redacted in API responses. - Custom fields:
- relation custom fields expose their entity IDs in the GraphQL APIs (#5012, #5013)
- relation custom fields support cascade options (#4448)
- custom fields can be indexed (#5074)
- custom fields are now supported on
Role(#5250), and localized custom fields onSeller(#5198) customFieldsis initialised automatically for entities that support them (#4965)
- Order pricing:
- checkout now recalculates the order and re-checks shipping eligibility when it moves to
ArrangingPayment(see the Migration Guide) - a new
OrderRecalculationStrategy, including a built-in TTL strategy, can keep long-lived carts in sync with price and promotion changes (#4928)
- checkout now recalculates the order and re-checks shipping eligibility when it moves to
- Pluggable refund destinations (#4594), for example refunding to store credit.
- Configurable operation strings are translated from i18n catalogs and resolved from
Accept-Language(#5079, #5091). - Composed plugins (#5162): a plugin can bundle other plugins through its
@VendurePluginmetadata. - Administrator password reset flow, with email support (#4974).
verifyCustomerAccountmutation in the Admin API (#4773).- Smaller additions:
- Translation tables get a unique constraint per language, and generated migrations remove existing duplicates automatically (#5007).
- NestJS 12,
@nestjs/graphql14 and Apollo Server 5 (#5386). - TypeORM v1 support (#5106, #5122). The same
@vendure/corenow runs on TypeORM v1.x as well as v0.3, and CI tests both on every commit. Projects stay on v0.3 by default. See TypeORM v1 below for how to opt in.
Dashboard
- Refreshed with v2 of the Vendure design system (#4957, #5502), including updated shell navigation (#4995).
- Catalog:
- Orders: an improved order detail layout (#4991), and suggested tax descriptions for surcharges (#5082).
- Insights page: widget visibility, multiple instances, configuration, filters and new widgets (#4976).
- Extensibility: user-aware navigation and route visibility (#5156), page block descriptions (#5050), custom fields on the Role page (#5250), and
setValuesForCreateonuseDetailPage/DetailPage, so a create page can start with values taken from the route, such as a parent entity's ID (#5431). - Updates send only changed fields on the Product, ProductVariant and Collection pages (#4916).
CLI
- Plugin API: plugins can register and override commands, with nested command trees, shared options and commands that have both an action and subcommands (#5139, #5263, #5300). Help output is grouped by the package that provides each command (#5358).
migrate: can create the initial migration from an empty database (#4959).- More flexible setups: globally installed CLI and plugin packages (#5400), and workspace project discovery for
vendure dev(#5475).
Performance
- Faster server and worker bootstrap (#5170).
Product.facetValueslookups are batched per request, instead of several queries per product on product lists (#5546).- Missing indexes added on STI discriminator columns and
search_index_item.productId(#5287). - Eager relations are joined under the query relation load strategy (#5193).
- List queries no longer load the
channelsrelation when they don't need it (#5368).
Security
| Severity | Vulnerability | Advisory | Fixed in |
|---|---|---|---|
| High | An authenticated session was not bound to the API it was created for, so a Shop API session of a user who is also an administrator could be replayed against the Admin API | GHSA-5fpx-9m35-w745 | 3.8.0 |
3.8.0 also hardens the earlier fix for GHSA-32jm-mf7r-7qw5, which was fixed in 3.7.3. A serialized RequestContext is now built from an explicit allowlist of fields, so it never persists the session token or request headers into job data. See the breaking change below.
All security fixes released in the 3.7.x patches are included in 3.8.0.
Our thanks to the researchers who reported these responsibly.
TypeORM v1
Vendure 3.8 fully supports TypeORM v1 as well as v0.3, from the same code. Compatibility with both versions is checked by a dedicated CI job on every commit.
Nothing changes unless you opt in. @vendure/core still depends on typeorm ^0.3.21, so existing and new projects stay on v0.3. We didn't widen that range, because package managers install the highest version a range allows: a wider range would have moved everyone onto v1 on their next install or npm update. In the next major, typeorm becomes a peer dependency, and your project chooses the version.
Before you opt in
- Node.js: TypeORM v1 needs
^20.19.0 || ^22.13.0 || >=24.11.0. That's stricter than Vendure's own requirement on Node 22 and 24. - Database driver: TypeORM v1 requires at least
better-sqlite3^12,pg^8.5.1,mysql2^3.15.3orsql.js^1.4.0. The plainsqlite3driver is no longer supported, so a project using it must switch tobetter-sqlite3first. - Plugins: the override below forces every copy of TypeORM in your project onto v1, including the one used by third-party plugins. A plugin that relies on TypeORM v0.3 APIs fails at runtime, not at install, so check your plugins as well as your own code.
- Your own code: TypeORM v1 removes some v0.3 APIs, such as the string-array form of
relationsandselectin find options, andConnection/createConnection. See the TypeORM v1 release notes.
How to opt in
Force TypeORM v1 across the whole dependency tree with your package manager's override field. The field name differs between package managers, and a wrong name is silently ignored.
npm and bun (overrides):
{
"overrides": {
"typeorm": "^1.1.0"
}
}yarn (resolutions):
{
"resolutions": {
"typeorm": "^1.1.0"
}
}pnpm (pnpm.overrides):
{
"pnpm": {
"overrides": {
"typeorm": "^1.1.0"
}
}
}If typeorm is also listed in your own dependencies, set it to the same version string. Projects created by @vendure/create with pnpm have this direct dependency. npm refuses an override that conflicts with a direct dependency.
Then reinstall, and check that exactly one version of TypeORM is installed: npm ls typeorm, yarn why typeorm, pnpm why typeorm or bun pm ls --all | grep typeorm. Two copies of TypeORM cause entity metadata errors that point nowhere near the real cause.
Switching in either direction needs no database migration: TypeORM v1 generates the same schema as v0.3 for Vendure's entities.
To go back to v0.3, remove the override and reinstall.
Breaking Changes
v3.8 is a minor release, but a few changes need your attention. Each is covered in the Migration Guide below.
- Database migration required. The schema changes include a new unique constraint on every translation table.
- Order line promotion data migration. If you have partially cancelled order lines from before 3.8, you must run
rescaleOrderLinePromotionAdjustmentsin the same deployment as the upgrade. - NestJS 12,
@nestjs/graphql14 and Apollo Server 5.- Align any direct dependencies with these versions.
- GraphQL Playground is replaced by GraphiQL.
adminApiDebugandshopApiDebughave no effect.
- Admin API sessions are bound to the Admin API (GHSA-5fpx-9m35-w745). Every administrator has to log in again once after the upgrade.
- Checkout recalculates the order on
ArrangingPaymentand refuses the transition if the selected shipping method is no longer eligible. ctx.session.tokenisstring | undefined, and is alwaysundefinedon job queue workers (GHSA-32jm-mf7r-7qw5 hardening).- The Dashboard sends only changed fields when updating Products, ProductVariants and Collections.
Migration Guide
This guide covers what you need to do when upgrading an existing Vendure project from v3.7 to v3.8. Update all @vendure/* packages to the same version as described in Updating Vendure, then work through the checklist.
Checklist
Every project:
- Back up your database, then generate and run a database migration.
- Before upgrading, check whether you need the order line promotion data migration. If you do, it must run in the same deployment as the upgrade. It cannot be added later.
- Update any of your own dependencies on
@nestjs/*or@apollo/serverto NestJS 12 and Apollo Server 5. - Tell your administrators they will need to log in again once after the upgrade.
Check these if they apply to you:
- Storefronts: handle the new checkout refusal for ineligible shipping methods.
- Plugins that read
ctx.session.tokenin job queue workers: seeRequestContext.session.tokenis optional. - Custom resolvers or event handlers for Product, ProductVariant or Collection updates: see partial updates from the Dashboard.
- Custom authentication strategies and SSO flows: see session API binding.
- Review the other changes at the end.
1. Database migration
Who is affected: every project.
v3.8 contains schema changes. You must generate and run a migration before starting the server against an existing database.
Warning
Make a backup of your database before running the migration. Most of the changes only add tables, columns and indexes. The exception is the new unique constraint on translation tables (see below): if a table contains duplicate translations, the migration deletes all but the most recently updated row for each language before adding the constraint.
The changes are:
- New table:
seller_translation(Sellers now support localized custom fields). - New columns:
order.pricingUpdatedAtrefund.destinationasset.usage(defaults toLIBRARY, so existing assets are unaffected)administrator.avatarIdapi_key.apiTypesession.apiType(used by authenticated sessions)- Columns for any custom fields you define on
Role, which now supports custom fields
- New indexes: on the
discriminatorcolumn ofhistory_entry,regionandstock_movement, thetypecolumn ofsession, andsearch_index_item.productId. - New unique constraints: a
(languageCode, baseId)unique constraint on every translation table, including translation tables defined by your own plugins. - MCP plugin tables: only if you add the new MCP plugin to your config.
Duplicate translations
Earlier versions could, under concurrent updates, store two translations of the same entity in the same language. The new unique constraint cannot be added while such duplicates exist.
When you generate the migration with npx vendure migrate or generateMigration, Vendure detects which translation tables gain the constraint and inserts a deduplicateTranslations call at the top of up(). It keeps the most recently updated row for each (baseId, languageCode) pair and deletes the others. You don't need to do anything extra.
If you write migrations by hand, or move the constraint DDL into a separate migration file, call it yourself before the constraints are added:
// src/migrations/1234567890-v38.ts
import { MigrationInterface, QueryRunner } from 'typeorm';
import { deduplicateTranslations } from '@vendure/core';
export class V381234567890 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await deduplicateTranslations(queryRunner, [
'product_translation',
'product_variant_translation',
// ...every translation table which gains the constraint,
// including those of your own plugins
]);
// ...the ALTER TABLE / CREATE UNIQUE INDEX statements follow
}
public async down(queryRunner: QueryRunner): Promise<any> {
// ...
}
}What to do: back up the database, then follow the Migrations guide to generate, review and run the migration. Test it against a copy of production data first.
2. Order line promotion data migration
Who is affected: projects with orders that have partially cancelled order lines created before v3.8 (for example, cancelling 1 of 3 units of a line with the cancelOrder mutation).
v3.8 fixes how promotion discounts on an OrderLine are prorated when its quantity changes. Before v3.8, a partial cancellation reduced the line's quantity without rescaling its stored PROMOTION adjustments. The corrected calculation reads those stale rows with the wrong basis, which would inflate the discount shown on those lines.
The rescaleOrderLinePromotionAdjustments helper rewrites the affected rows. Unlike deduplicateTranslations, it is not added to generated migrations automatically.
Check whether you need it
Run this query against your database before upgrading. It lists the order lines the helper would consider: partially cancelled lines that store a PROMOTION adjustment.
PostgreSQL and SQLite:
SELECT ol.id, ol."orderId", ol.quantity, ol."orderPlacedQuantity"
FROM order_line ol
WHERE ol.quantity > 0
AND ol.quantity < ol."orderPlacedQuantity"
AND ol.adjustments LIKE '%"type":"PROMOTION"%';MySQL and MariaDB:
SELECT ol.id, ol.orderId, ol.quantity, ol.orderPlacedQuantity
FROM order_line ol
WHERE ol.quantity > 0
AND ol.quantity < ol.orderPlacedQuantity
AND ol.adjustments LIKE '%"type":"PROMOTION"%';- No rows: you don't need the helper.
- One or more rows: add the helper. It works out per line whether the stored amounts are stale, so it may change fewer rows than the query returns.
If you use a custom TypeORM naming strategy, adjust the table and column names to match.
Run it in the upgrade deployment
Warning
The helper must run in the same deployment as the upgrade to v3.8, before the server handles any order cancellation. Never add it in a later migration.
From v3.8, a partial cancellation rescales the line's PROMOTION adjustments itself. The helper cannot tell those lines apart from lines cancelled before v3.8, so it would scale them a second time and shrink their discount.
What to do: add it to the up() method of your v3.8 migration, or to a separate migration that runs in the same deployment. It needs no schema change, so it can run at any point in the migration:
// src/migrations/1234567890-rescale-order-line-promotions.ts
import { MigrationInterface, QueryRunner } from 'typeorm';
import { rescaleOrderLinePromotionAdjustments } from '@vendure/core';
export class RescaleOrderLinePromotionAdjustments1234567890 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await rescaleOrderLinePromotionAdjustments(queryRunner);
}
public async down(queryRunner: QueryRunner): Promise<any> {
// This is a one-way data migration - the pre-migration amounts are not recoverable.
}
}It only touches lines whose quantity was reduced by a cancellation after their adjustments were last calculated, and it logs how many rows it rescaled. If you have no partially cancelled lines, it changes nothing, so including it in the upgrade migration is safe when in doubt.
Note
If an order modification and a cancellation on the same order were stored with the same timestamp, the helper cannot tell which came first. It throws before writing anything and lists the affected OrderLine IDs. Inspect them and pass the correct basis for each via the ambiguousOrderLineQuantityBases option. See the reference docs for details.
3. NestJS 12 and Apollo Server 5
Who is affected: projects or plugins that depend directly on NestJS or Apollo packages, use the GraphQL Playground, or use the Harden plugin.
Vendure v3.8 upgrades to NestJS 12, @nestjs/graphql 14 and Apollo Server 5.
What to do:
-
Update any direct dependencies in your project and plugins to match
@vendure/core:{ "dependencies": { "@nestjs/common": "^12.0.1", "@nestjs/core": "^12.0.1", "@nestjs/graphql": "~14.0.0", "@apollo/server": "^5.5.1" } }Mismatched versions lead to duplicate copies of NestJS in
node_modules, which usually shows up as dependency injection errors at bootstrap. -
If you use
@vendure/harden-plugin, its@apollo/serverpeer dependency is now^5.0.0. -
If you pass custom Apollo Server plugins via
apiOptions.apolloServerPlugins, check them against Apollo's Migrating from Apollo Server 4 guide. -
GraphQL Playground is gone. Apollo Server 5 removed it. Setting
apiOptions.adminApiPlaygroundorapiOptions.shopApiPlaygroundto a truthy value now serves GraphiQL at the API path instead. The object form of these options (Playground settings) is accepted but ignored; only its truthiness counts. -
adminApiDebugandshopApiDebugare deprecated and have no effect. You can remove them from your config.
The supported Node.js versions are unchanged: ^20.19.0 || >=22.12.0. See Installation for the full requirements.
4. Admin API sessions are bound to the Admin API
Who is affected: every project with administrators, and especially projects with custom authentication strategies or code that creates sessions itself.
Since v3.8, every session records the API it was created for (the new apiType column), and the Admin API only accepts sessions created for the Admin API. This closes a hole where a session created on the Shop API for a User who is also an administrator could be used against the Admin API (GHSA-5fpx-9m35-w745).
What to do:
- Expect administrators to be logged out once. Sessions created before the upgrade have no
apiTypeand are refused on the Admin API, so administrators need to log in again. API keys created before the upgrade keep working. - Keep authentication strategies that can resolve administrators on
authOptions.adminAuthenticationStrategyonly. - If you create sessions yourself, for example in a REST controller that completes an SSO callback, pass
'admin'as theapiTypeargument ofAuthService.authenticate(),AuthService.createAuthenticatedSessionForUser()orSessionService.createNewAuthenticatedSession(). A REST route's context has anapiTypeofcustom, and sessions created from contexts with no request (scripts, job queue workers) record the Shop API. - REST routes added by plugins are not checked. If you have one gated by an admin permission, check
ctx.session?.apiType === 'admin'in the handler.
See the Auth guide for the full rules.
5. Checkout recalculates prices and re-checks shipping eligibility
Who is affected: all storefronts, and projects with time-sensitive prices, promotions or shipping eligibility checkers.
The default order process now recalculates the order when it transitions to ArrangingPayment:
- Prices, promotions, taxes and shipping promotions are recalculated, without changing the selected shipping method.
- If the order has a shipping method that is no longer eligible (for example, a
ShippingEligibilityCheckerwith a minimum order value that the recalculated total no longer meets), the transition is refused. The client receives anOrderStateTransitionErrorwhosetransitionErroris the translated message for thecannot-transition-to-payment-with-ineligible-shipping-methodkey. In English it reads:Cannot transition Order to the "ArrangingPayment" state because the selected ShippingMethod is no longer eligible. - If all checks pass, a full recalculation runs, including the shipping rate.
So the totals at checkout may differ from what the customer last saw if prices or promotions changed in the meantime.
What to do:
- In your storefront, handle an
OrderStateTransitionErrorfromtransitionOrderToState(state: "ArrangingPayment")whosetransitionErrorsays the shipping method is no longer eligible. Re-queryeligibleShippingMethods, let the customer pick again, then retry. - Re-read the order totals after the transition rather than relying on values cached from the cart page.
The recalculation itself cannot be turned off. The ineligible-shipping check is controlled by the arrangingPaymentRequiresShipping option of configureDefaultOrderProcess, but setting it to false also removes the check that an order has a shipping method at all, and skips the final recalculation in step 3, so the shipping rate is not refreshed:
// src/vendure-config.ts
import { configureDefaultOrderProcess, VendureConfig } from '@vendure/core';
export const config: VendureConfig = {
orderOptions: {
process: [
configureDefaultOrderProcess({
// Disables the "has a shipping method" and the "shipping method
// is still eligible" checks, and the shipping rate refresh.
arrangingPaymentRequiresShipping: false,
}),
],
},
};See Configuring the default order process.
6. RequestContext.session.token is optional
Who is affected: plugins that read ctx.session.token (or ctx.req) in code that runs on the job queue worker.
As part of hardening the fix for GHSA-32jm-mf7r-7qw5, serialized RequestContext objects no longer contain the session token or the raw request, because serialized contexts are persisted in job data. RequestContext.session is now typed as CachedSession | DeserializedCachedSession, so ctx.session.token is string | undefined, and it is always undefined on a worker.
What to do: fix any TypeScript errors this causes. If a job needs a credential to call another system, pass it in the job payload or, better, use a service-level credential. See Passing the RequestContext in the worker guide.
7. The Dashboard sends only changed fields for some updates
Who is affected: projects with custom resolvers, interceptors or event handlers that assume the updateProduct, updateProductVariant(s) or updateCollection mutations receive the complete input.
The Product, ProductVariant and Collection detail pages in the Dashboard now send only the top-level input fields that were changed, plus id and any non-nullable fields. For example, changing a product's name sends translations, but no longer sends facetValueIds, assetIds or customFields. Other detail pages still send the full input.
What to do: if your code reads fields from the update input, for example via the input of a ProductEvent or CollectionEvent, treat missing fields as "unchanged" rather than "cleared". Load the entity if you need its current state.
8. Other changes
-
New Channels get the default roles.
ChannelService.create()now assigns the new Channel to the SuperAdmin and Customer roles. If your code did this itself after creating a Channel, you can remove it. -
New
orderRecalculationStrategyoption.orderOptions.orderRecalculationStrategycontrols whether an active order is recalculated when it's read. The default,NoOrderRecalculationStrategy, keeps the previous behaviour. UseTtlOrderRecalculationStrategyto keep long-lived carts in sync with price and promotion changes. -
List queries no longer load
channelsimplicitly.ListQueryBuilder.build()with achannelIdused to add thechannelsrelation to the query. It now filters by channel without loading it. If your plugin readsentity.channelsfrom list results, addchannelsto therelationsoption. -
Telemetry plugin.
@vendure/telemetry-pluginnow uses OpenTelemetry0.223.x/2.12.x. If you installed OpenTelemetry exporters or SDK packages yourself, update them to the same versions. These instrumentations emit only the stable semantic conventions, so HTTP and database span attributes are renamed (for example,http.methodanddb.statementno longer exist). Update any dashboards and alerts that query the old names. See Upgrading from earlier versions in the telemetry guide for the full list. -
Dashboard extensions. The Dashboard moved to v2 of the Vendure design system (
@vendure-io/ui). Rebuild your Dashboard extensions and check them visually. -
SlugInputin custom forms.SlugInputnow generates a slug whenever the field's form default value is empty, including when a new language is added to an existing entity. Forms built withuseDetailPage, or that load the entity withuseForm({ values })orreset(), are unaffected. If your extension loads an existing entity into the form withsetValue(), or the form has no default values, an existing slug can now be overwritten when the name changes. SeeSlugInput. -
ProductVariantService.getVariantsByProductId()no longer returns a partialproduct(#5122). It used to join the Product only to scope the query, and that join also filled in a partialvariant.product. Code that readvariant.productwithout asking for theproductrelation now getsundefined, so request the relation explicitly. This also fixes a requestedproductrelation being overwritten by the partial copy. -
Two TypeORM patches are applied at bootstrap (#5122). They affect every query, including query builders in plugins:
OrmUtils.deepValue()returnsundefinedfor a missing intermediate path segment instead of throwing.- An eager relation is no longer loaded a second time when the main query already joins and selects it.
Both work around TypeORM defects (typeorm/typeorm#12774, typeorm/typeorm#12775) and will be removed once a fixed TypeORM is the minimum supported version.
-
Deprecations:
apiOptions.adminApiDebugandapiOptions.shopApiDebug(no effect, see above).- The top-level
widgetsoption ofdefineDashboardExtension(). Useinsights.widgetsinstead; the old option still works for now.
What's Changed
- fix(core): prevent ReDoS in SQLite regex filters using RE2 by @michaelbromley in #4956
- feat(core): bootstrap initial migration from an empty database by @michaelbromley in #4959
- feat(dashboard): Migrate to Vendure Design System v2 by @dlhck in #4957
- feat(cli): Show one-time GitHub star prompt on shutdown by @dlhck in #4973
- feat(core,email-plugin,dashboard): Add administrator password reset flow by @dlhck in #4974
- feat(dashboard): Insights page improvements (widget visibility, multi-instance, config, filters, new widgets) by @dlhck in #4976
- feat(dashboard): Product list, product detail & variants editor improvements by @dlhck in #4983
- fix(core): Assign default roles when creating a Channel by @michaelbromley in #4980
- feat(dashboard): Facets & collections UI/UX improvements by @dlhck in #4984
- feat(dashboard): Product variant & option group UX improvements by @dlhck in #4985
- refactor(dashboard): make data table views opt-in by @dlhck in #4993
- feat(dashboard): improve order detail layout by @dlhck in #4991
- fix(dashboard): aggregate order metrics in database by @dlhck in #4994
- feat(dashboard): improve asset management views by @dlhck in #4992
- feat(core): Recalculate active orders for current pricing, promotions & shipping eligibility by @grolmus in #4928
- feat(core): Add entity id properties for relation custom fields by @michaelbromley in #5012
- feat(core): Expose relation custom field ids in GraphQL APIs by @michaelbromley in #5013
- feat(dashboard): Overhaul shell navigation and identity by @dlhck in #4995
- fix(core): Patch TypeORM relation id alias mismatch breaking relation custom field hydration by @michaelbromley in #5030
- fix(dashboard): Preserve context in operation value previews by @dlhck in #5033
- fix(core): Prevent relation custom fields being cleared by partial updates by @michaelbromley in #5047
- fix(dashboard): Display boolean table values as badges by @dlhck in #5048
- feat(dashboard): Support page block descriptions by @dlhck in #5050
- fix(dashboard): update UI package for collapsed sidebar by @dlhck in #5053
- fix(dashboard): re-export all design system components by @dlhck in #5054
- fix(dashboard): Use standard background for affixed inputs by @dlhck in #5060
- feat(core): Auto-initialise customFields for entities that support them by @grolmus in #4965
- feat(core): Encrypt secret custom fields and config args at rest by @michaelbromley in #5051
- fix(dashboard): Import vendored shadcn CSS from design-tokens by @michaelbromley in #5081
- feat(asset-server-plugin): Add background color query param for transparent images by @michaelbromley in #5089
- feat(core): Resolve configurable operation strings from i18n catalogs by @michaelbromley in #5079
- fix(core): Scope customFields auto-init to this server's entities by @grolmus in #5068
- feat(core): Translate configurable operation strings and resolve them from Accept-Language by @michaelbromley in #5091
- chore(repo): Add TypeORM version test harness by @michaelbromley in #5106
- fix(core): Prevent stock oversell under concurrent checkout by @grolmus in #4930
- refactor(core): Move
@vendure/coreoff the TypeORM APIs removed in v1 by @michaelbromley in #5122 - ci: Scope the TypeORM v1 compatibility runs by @michaelbromley in #5137
- feat(core): Extend custom relational field config by @LucidityDesign in #4448
- feat(cli): Add plugin API to register and override commands by @dlhck in #5139
- feat(core): add composed Vendure plugin support by @dlhck in #5162
- fix(dashboard): discover each decorated plugin independently by @dlhck in #5161
- feat(dashboard): Send only changed fields on detail page updates by @grolmus in #4916
- feat(cli): add Console project linking by @dlhck in #5157
- fix(core): prevent duplicate translations from concurrent saves by @brmk in #5007
- feat(dashboard): add per file upload progress modal for assets by @latifniz in #5092
- feat(core): add index support for custom fields by @Hemang-ai in #5074
- feat(core): add auth options disable last login update property by @biggamesmallworld in #5184
- perf(core): reduce server and worker bootstrap time by @michaelbromley in #5170
- perf(core): join eager relations under the query relation load strategy by @michaelbromley in #5193
- fix(core): allow external auth to set an Administrator avatar by @michaelbromley in #5243
- feat: add custom field support for Role entity by @gabriellbui in #5250
- feat(cli): add nested command trees and shared options to the plugin API by @michaelbromley in #5263
- feat(cli): support commands that have both an action and subcommands by @michaelbromley in #5300
- fix(asset-server-plugin): update sharp to 0.35 to resolve security advisory by @michaelbromley in #5218
- feat(cli): let plugins run after a Console project link by @dlhck in #5314
- feat(cli): settle a Console session with the Project Link approval by @dlhck in #5315
- feat(email-plugin): allow a plain-text body to be set on an email by @PavanendraBaahubali in #3960
- refactor(core): consolidate the translation-entity detection heuristics by @grolmus in #5069
- ci: skip the dashboard deploy on fork pull requests by @biggamesmallworld in #5333
- fix(core): report and tolerate apply-collection-filters failures by @michaelbromley in #5335
- fix(dashboard): keep Create disabled until a generated slug or code arrives by @gabriellbui in #5326
- feat(dashboard): suggest tax descriptions for surcharges by @supermadu7 in #5082
- feat(core,dashboard): add verifyCustomerAccount mutation to the admin API by @latifniz in #4773
- feat(core): add timezone option for scheduled tasks by @comega-johan in #5203
- fix(telemetry-plugin): bump opentelemetry to 0.222.x (GHSA-q7rr-3cgh-j5r3, GHSA-45rx-2jwx-cxfr) by @biggamesmallworld in #5154
- feat(cli): group the CLI help output by the package that provided each command by @michaelbromley in #5358
- feat(dashboard): user-aware navigation and route visibility context by @kevmtt in #5156
- perf(core): avoid loading redundant channels relation in list queries by @michaelbromley in #5368
- feat(core): update to NestJS 12,
@nestjs/graphql14 and Apollo Server 5 by @michaelbromley in #5386 - chore(deps): update graphql to 16.14.2 and refresh the graphql ecosystem by @michaelbromley in #5391
- test(dashboard): dismiss Base UI popups reliably in e2e tests by @michaelbromley in #5398
- feat(cli): support globally installed CLI and plugin packages by @michaelbromley in #5400
- feat(core): support localized custom fields on Seller by @gabriellbui in #5198
- feat(cli): record console origins and remove custom console host options by @dlhck in #5414
- fix(dashboard): include composed plugins when filtering active dashboard extensions by @dlhck in #5430
- feat(core, dashboard): support pluggable refund destinations by @oliverstreissi in #4594
- fix(core,cli): include composed plugins in schema generation outside bootstrap by @dlhck in #5434
- perf(core): add missing indexes on STI discriminators and productId by @brmk in #5287
- feat(mcp-plugin): add MCP server plugin and
@McpToolSDK by @gabriellbui in #5262 - fix(dashboard): surface validation errors when the submit button is disabled by @grolmus in #5425
- fix(core): put the index of a single relation custom field on its id column by @biggamesmallworld in #5457
- fix(core): respect find options when joining eager relations by @dlhck in #5465
- feat(cli): support structured non-interactive console linking by @dlhck in #5466
- feat(cli): export Console browser login and session refresh by @dlhck in #5468
- docs(mcp-plugin): make the MCP guides easier to read by @gabriellbui in #5470
- fix(mcp-plugin): apply entityAccessControlStrategy to MCP tool calls by @gabriellbui in #5472
- feat(mcp-sdk): export list, input-schema, money and custom-field helpers by @gabriellbui in #5474
- fix(mcp-plugin): return the message of every I18nError thrown by a tool by @gabriellbui in #5473
- feat(cli): discover workspace projects for dev by @dlhck in #5475
- fix(core): use the translations relation to pick entities for the translation unique constraint by @biggamesmallworld in #5458
- feat(cli): add vendure auth login with a shared WorkOS session by @michaelbromley in #5489
- fix(mcp-plugin): run cart tools in the cart's currency by @gabriellbui in #5487
- fix(core): restore TypeORM v1 compatibility by @michaelbromley in #5490
- ci: stop the whole vendure dev process tree after the Windows smoke tests by @michaelbromley in #5488
- feat(cli): link projects with the vendure auth login by @dlhck in #5494
- feat(cli): point to the docs MCP after a Console link by @dlhck in #5499
- docs(skills): add Vendure troubleshooting skill by @dlhck in #5501
- feat(dashboard): adopt
@vendure-io/ui2.0.0-rc.1 and scenario illustrations (PDEV-524) by @dlhck in #5502 - feat(cli): suggest the project path when vendure console finds no project by @dlhck in #5503
- fix(cli): scope a first Console login in console link and match the setup guide by @dlhck in #5504
- feat(mcp-sdk): export findOrCreateActiveOrder for plugin cart tools by @dlhck in #5505
- fix(core): preserve secret custom fields in fragment-wrapped and multi-field mutations by @michaelbromley in #5510
- fix(dashboard): preserve refund allocations when destinations finish loading by @michaelbromley in #5509
- fix(core): respect naming strategies when de-duplicating translations by @michaelbromley in #5512
- fix(cli): run vendure console from a workspace root by @dlhck in #5521
- fix(cli): ask which account to link when a scoped login has several by @dlhck in #5523
- fix(core): apply OrderLine custom field defaults and validation to nested inputs by @michaelbromley in #5517
- fix(core): reject secret placeholders on creates through any input type by @michaelbromley in #5516
- fix(core): decode ID arguments in named fragments and same-named sibling arguments by @michaelbromley in #5515
- chore: include MCP packages in release tooling and fix 3.8 notes by @michaelbromley in #5530
- fix(telemetry-plugin): bump OpenTelemetry packages for NestJS 12 support by @michaelbromley in #5532
- fix(mcp-sdk): Require NestJS 12 and add package READMEs by @michaelbromley in #5533
- test(core): identify payments by transactionId in order modification refund spec by @michaelbromley in #5535
- chore: remove CLAUDE.md, keep AGENTS.md only by @dlhck in #5536
- fix(dashboard): generate the slug of a translation added to an existing entity by @brmk in #5477
- docs: update telemetry, auth and migration helper docs for v3.8 by @michaelbromley in #5534
- feat(dashboard): add setValuesForCreate to set starting values on create forms by @gabriellbui in #5431
- fix(core): await scheduled task so preventOverlap protects long runs by @wakqasahmed in #5481
- fix(dashboard): activate UI language as a BCP 47 tag by @michaelbromley in #5544
- chore(dashboard): move to stable Vendure design system releases by @dlhck in #5545
- perf(core): batch Product.facetValues lookups per request by @michaelbromley in #5546
- fix(dashboard): point root types entry at shipped index.ts by @michaelbromley in #5548
New Contributors
- @Hemang-ai made their first contribution in #5074
Full Changelog: v3.7.4...v3.8.0