Releases: medusajs/medusa
v2.8.3
Highlights
New Analytics module and Posthog provider
This release introduces a new Analytics module, allowing you to easily set up server-side tracking of events and users.
The module comes with two methods:
/**
* This method tracks an event in the analytics provider.
*
* @param {TrackAnalyticsEventDTO} data - The event's details.
* @returns {Promise<void>} Resolves when the event is tracked successfully.
*
* @example
* await analyticsModuleService.track({
* event: "order_placed",
* properties: {
* order_id: "order_123",
* customer_id: "customer_456",
* total: 100,
* }
* })
*/
track(data: TrackAnalyticsEventDTO): Promise<void>
/**
* This method identifies an actor or group in the analytics provider.
*
* @param {IdentifyAnalyticsEventDTO} data - The details of the actor or group.
* @returns {Promise<void>} Resolves when the actor or group is identified successfully.
*
* @example
* await analyticsModuleService.identify({
* actor_id: "123",
* properties: {
* name: "John Doe"
* }
* })
*/
identify(data: IdentifyAnalyticsEventDTO): Promise<void>
To configure the Analytics module, register it in the modules
of your medusa-config.ts
:
...
modules: [
...,
{
resolve: "@medusajs/medusa/analytics",
options: {
providers: [{ ... }],
},
},
],
...
The Analytics module does not do much as is. You need to configure a provider responsible for handling the tracking. This release comes with two moduNopeles, @medusajs/analytics-local
and @medusajs/analytics-posthog
. The former will log the events and can be used for local development. The latter is an official integration with Posthog.
The provider is installed like so:
...
modules: [
...,
{
resolve: "@medusajs/medusa/analytics",
options: {
providers: [
{
resolve: "@medusajs/analytics-posthog",
options: {
posthogEventsKey: "<your-api-key>",
posthogHost: "<your host>",
}
}
}],
},
],
...
Read more about the new module and providers in our documentation.
Features
- feat(index): Only run the synchronisation in worker/shared mode by @adrien2p in #12530
- chore(product, modules-sdk): Add missing index for select-in strategy + allow to pass top level strategy to force the behaviour by @adrien2p in #12508
- feat: Add an analytics module and local and posthog providers by @sradevski in #12505
- feat: process import from pre-processed chunks by @thetutlage in #12527
- feat: add presignedUrl method to upload sdk by @christiananese in #12569
Bugs
- fix: Skip migration if module is not installed by @sradevski in #12507
- test: remove snapshots usage by @thetutlage in #12516
- fix(utils): Missing zod deps by @adrien2p in #12518
- fix(dashboard): refund payment formatting by @fPolic in #12476
- fix(medusa): add estimate count to admin products by @carlos-r-l-rodrigues in #12467
- fix(index): Apply various fixes to the index engine by @carlos-r-l-rodrigues in #12501
- fix: Correctly import MedusaError in the analytics module by @sradevski in #12542
- fix(product): Deep update data retrieval bottleneck by @adrien2p in #12538
- fix: mark posthog-node as a peer dependency by @thetutlage in #12539
- fix(js-sdk): allow passing custom request body parameters in auth.register method by @shahednasser in #12545
- fix(core-flows): skip location check if inventory is not managed by @fPolic in #12540
- fix(core-flows, types): export steps and types related to credit lines by @shahednasser in #12567
- fix(product, utils): handle metadata key deletion on product update by @fPolic in #12478
Documentation
- docs: update recipe contents by @shahednasser in #12515
- docs: add vale rule for Next.js Starter Storefront by @shahednasser in #12517
- docs: change recipes on homepage by @shahednasser in #12520
- docs: fix timeout in order-related workflows reference by @shahednasser in #12533
- docs: added re-order guide by @shahednasser in #12363
- chore(docs): Update npm command by @kashifaris in #12547
- docs: show hooks in when condition in workflows reference by @shahednasser in #12552
- docs-util: extract feature flags in generated OAS by @shahednasser in #12554
- Fix: Incorrect Example File Name in Subscriber Section by @adityaerlangga in #12560
- docs: improve module isolation chapter by @shahednasser in #12565
- docs: show when condition in workflows reference by @shahednasser in #12566
Chores
- test(workflow-engine-redist): Update tests to ensure behaviour validation properly by @adrien2p in #12514
- chore(test-cli-with-database): Update cli tag from latest to preview by @adrien2p in #12529
- chore(index): return ids only by @carlos-r-l-rodrigues in #12543
- chore(core-flows): add tsdocs for hooks in complete cart by @shahednasser in #12555
- chore(types, utils): update TSDocs related to analytics module by @shahednasser in #12564
- chore(index): Few adjustments by @adrien2p in #12557
Other Changes
- Typo/index logs by @adrien2p in #12528
- fix: Fix garbled characters in zhCN.json translation file by @imhuso in #12537
New Contributors
- @imhuso made their first contribution in #12537
- @kashifaris made their first contribution in #12547
- @adityaerlangga made their first contribution in #12560
Full Changelog: v2.8.2...v2.8.3
v2.8.2
Highlights
Fixes regression related to updating weight, height, length, and width on products
Features
- feat: define validators and use normalize-products step by @thetutlage in #12473
Bugs
- fix(types): change update signature of IAuthProvider by @shahednasser in #12496
- fix: fix weight/length/height/width types in updates by @peterlgh7 in #12500
- fix(core-flows): fulfilment cancelation with shared inventory kit item by @fPolic in #12503
Documentation
- docs: add missing methods in guides and references by @shahednasser in #12495
- docs: improve payment-related guides by @shahednasser in #12502
- docs: update examples in sendgrid guide by @shahednasser in #12504
Chores
- chore(types, utils): TSDoc improvements for providers by @shahednasser in #12498
Full Changelog: v2.8.1...v2.8.2
v2.8.1
Highlights
Fixes a regression introduced in v2.8.0
In v2.8.0, we unintentionally introduced a regression that affected ordering products returned from the product module service. This broke workflows (e.g. updateProductsWorkflow
) that rely on consistent ordering for subsequent operations such as applying price updates. This release brings back the correct ordering behavior and resolves the issue.
We strongly recommend upgrading to v2.8.1 as soon as possible to avoid incorrect data.
Features
- feat: create CSV normalizer to normalize a CSV file by @thetutlage in #12396
Bugs
- fix(workflows-sdk): Miss match context usage within run as step by @adrien2p in #12449
- fix(index): query builder handle json array by @carlos-r-l-rodrigues in #12480
- fix: returned updates order in upsertWithReplace by @peterlgh7 in #12486
Documentation
- docs: tax provider updates for next release by @shahednasser in #12431
- docs: update read-only links by @shahednasser in #12468
- docs: fix client side error in references by @shahednasser in #12475
Chores
- chore(types,utils): add featureFlag and version tags by @shahednasser in #12464
- Chore/test runner improvements by @adrien2p in #12439
Full Changelog: v2.8.0...v2.8.1
v2.8.0: Fix regression with product updates
Highlights
Important: This release introduced a regression with product updates
We unintentionally introduced a regression that affected the ordering of products returned from the product module service. This broke workflows (e.g. updateProductsWorkflow) that rely on consistent ordering for subsequent operations such as applying price updates.
Upgrade to v2.8.1 as soon as possible to avoid any issues related to this change.
Cart-completion robustness
Warning
Breaking changes
This release introduces important changes to the completeCartWorkflow
aimed at improving robustness and making it easier to recover from failures.
Changes to idempotency
Until now, the workflow has had the following configuration:
{
name: completeCartWorkflowId
store: true,
idempotent: true,
retentionTime: THREE_DAYS,
}
Here's a brief description of what these properties do, excluding name
:
store
: Store the workflow execution. If no retention time is specified, the workflow is only stored for the duration of its executionidempotent
: Store the result of the first workflow execution and return it on subsequent runsretentionTime
: Store the result in the configured data store for "x" amount of time–here, three days
In this release, we change the idempotent
flag from true to false. Initially, the idea of having an idempotent cart-completion stemmed from wanting to ensure that multiple requests to complete the same cart would lead to the same outcome. This is, in of itself, still reasonable to ensure there is only ever one attempt to complete the same cart. However, in case of failures, the idempotent mechanism makes it challenging to recover, since a new cart with the same details is required to re-attempt the completion. This is because the result of the failing workflow is stored and returned on all subsequent runs, regardless of whether the cart is updated to satisfy the constraints for completion.
With idempotent
set to false, it will be possible to retry failed executions, making it simpler to bring the cart to a satisfying state and eventually create the order.
It is worth mentioning that concurrency continues to be appropriately managed by passing the cart ID as the transactionId
when running the workflow. The transaction ID ensures that only one workflow execution can run at a time.
Changes to the structure
The workflow has been slightly restructured to minimize the risk of payment sessions getting stuck in a state that is difficult to recover from. More specifically, the payment authorization step has been moved to the very end of the workflow, so that all first-party checks are performed before we authorize the payment.
To understand why this is important, we need to cover the compensateIfNeeded
step of the workflow briefly. This step compensates active payment sessions in the case of failure to avoid orphaned third-party payments and recreates the Medusa payment session so that new payment details can be attached.
For example, suppose the payment authorization is performed off-band (e.g., in a webhook), and the cart-completion workflow fails to execute. In that case, the step will void the third-party payment and bring the cart to a correct state, all while ensuring consistency between the two systems.
Moving the payment authorization step to the end of the workflow ensures that we only create an authorized payment if all other cart constraints are satisfied.
Changes to the webhook handler
The webhook handler ensures consistency between Medusa and the third-party payment provider. More specifically, it will handle the following scenarios:
- Payment authorization: when we receive a payment authorization event, we will attempt to authorize and create a payment in Medusa
- Payment capture: when we receive a payment capture event, we will attempt to capture the associated payment in Medusa
- Cart-completion: when we receive a payment authorization event, we will attempt to complete the cart
- Payment authorization and capture: when we receive a payment capture event and there is no existing payment in Medusa, we will attempt to authorize, create, and capture a payment in Medusa
We have only handled the first three scenarios until now. This release introduces functionality for the fourth, a more unusual scenario involving auto-captures in the third-party payment provider.
Line item generation
Warning
Breaking changes
This release changes the source of line item titles and subtitles. The title has changed from the variant title to the product title, and the subtitle has changed from the product title to the variant title.
Cross-module filters (experimental)
This release introduces experimental support for cross-module filters via a new module, @medusajs/index
.
Please remember that this module is experimental and in development, so please use it cautiously. It is subject to change as we finalize the design to enable cross-module filtering.
We will keep the description brief since the module is still in development. Here's what you need to know:
- Medusa's architecture is divided into a range of modules, each being isolated (at the data model level) from one another. By default, the modules share the same database, but the architecture is built to support external data sources, in case your module needs a different type of database, or you want to use a third-party service in place of Medusa's core module
- Consequently, queries to retrieve data are performed per module, and the data is aggregated in the end to create the result set
- Our new index module allows you to ingest data into a central, relational-like structure
- The structure enables efficient filtering over data coming from different data sources (provided it is ingested)
The index module can be enabled by installing the module:
yarn add @medusajs/index
Adding it to your Medusa config:
{
// ...rest of config
modules: [{ resolve: "@medusajs/index", options: {} }]
}
And finally, adding a feature flag to enable it:
// .env
MEDUSA_FF_INDEX_ENGINE=true
Then, apply the migrations to create the related tables:
npx medusa db:migrate
The next time you start your server, we will ingest data into the index engine.
In this first iteration, we only ingest the following core entities:
- Products
- Product Variants
- Prices
- Sales Channels
The data in the index is queried through a new API with semantics identical to the existing query graph:
const { data: products = [], metadata } = await query.index({
entity: "product",
fields: ["id", "name"],
});
In addition to ingesting a few select resources from the core, we have added support for ingesting data from custom modules through link definitions. For example, say you want to introduce a Brand on Products and filter your Products by it.
Assuming you have added a Brand module, you create a link as follows:
import { defineLink } from "@medusajs/framework/utils";
import ProductModule from "@medusajs/medusa/product";
import BrandModule from "../modules/brand";
export default defineLink(
{
linkable: ProductModule.linkable.product,
isList: true,
},
{
linkable: BrandModule.linkable.brand,
}
);
This link will associate the two data models and allow you to query them from both sides of the link.
Furthermore, to ingest the Brand into the index and make the data filterable, you need to add the new filterable
property to the link:
import { defineLink } from "@medusajs/framework/utils";
import ProductModule from "@medusajs/medusa/product";
import BrandModule from "../modules/brand";
export default defineLink(
{
linkable: ProductModule.linkable.product,
isList: true,
},
{
linkable: BrandModule.linkable.brand,
filterable: ["id", "name"]
}
);
The link above specifies that Brands should be ingested into the index and made filterable via the properties id
and title
.
As a result, you can now filter Products by Brands ID or title like so:
const { data: products = [], metadata } = await query.index({
entity: "product",
fields: ["id", "title", "brand.*"],
filters: {
brand: {
name: "Hermes"
}
},
});
An important trade-off in the index module is the need to avoid the performance cost of COUNT(*)
queries, which can be an incredibly high cost for very large data sets. Instead, we use Postgres’ internal query planner to compute an estimated count of the result set. For very small data sets, this estimate can be very inaccurate, but for larger ones it should provide a reasonable estimate. The trade-off allows us to achieve very fast response times making it ideal for user-facing search and browsing experiences. As noted earlier, the tool is still experimental, and we plan to continue iterating and improving it over time.
Custom Tax Providers
This release introduces full support for custom Tax Providers by making these key changes:
- Load custom Tax Providers in the Tax Module
- Manage (create, update) provider per Tax Region in the admin dashboard
- Migrate all Tax Regions to use a tax provider
The migration script will run the first time you start the server after upgrading. It will assign the default system provider to the Tax Region, if the region did not have a custom provider specified already.
Feat...
v2.7.1: Strengthened workflow engine, expanded data for fulfillments, and more robust event handling
Highlights
Expanded fulfillment data
This release extends fulfillment capabilities by exposing more core order data to fulfillment providers–that is, data passed upon creating fulfillments in Medusa.
The additional data included on orders passed to providers are:
display_id
region
customer_id
customer
sales_channel_id
sales_channel
items.variant.upc
items.variant.sku
items.variant.barcode
items.variant.hs_code
items.variant.origin_country
items.variant.product.origin_country
items.variant.product.hs_code
items.variant.product.mid_code
items.variant.product.material
items.tax_lines.rate
subtotal
discount_total
tax_total
item_total
shipping_total
total
created_at
Strengthened workflow engine
We have improved the core internals of our workflow engine, including fixing an issue with events grouping not working correctly for async workflows.
Read more in the PRs below:
Features
- feat(core-flows, js-sdk, medusa): draft order shipping removal by @fPolic in #12124
- feat: Add support for dynamoDB for storing sessions and some types cleanup by @thetutlage in #12140
- feat(orchestration): skip on permanent failure by @carlos-r-l-rodrigues in #12027
- feat: Add support for uploading a file directly to the file provider from the client by @sradevski in #12224
Bugs
- fix(dashboard): display null sku by @fPolic in #12155
- fix(types): fix type of application_method_type filter by @shahednasser in #12160
- fix(core-flows): draft order reservations by @fPolic in #12115
- fix(index): Default schema typings by @adrien2p in #12183
- fix(): Event group id propagation and event managements by @adrien2p in #12157
- fix(payment): properly delete refund by @fPolic in #12193
- fix: apply additional data validator using a global middleware by @thetutlage in #12194
- fix(dashboard): properly register settings custom routes by @fPolic in #12198
- fix(): Properly handle workflow as step now that events are fixed entirely by @adrien2p in #12196
- fix(core-flows): export registerOrderDeliveryStep by @shahednasser in #12221
- fix: expose beforeRefreshingPaymentCollection hook by @thetutlage in #12232
- fix(medusa): sales_channel_id middleware manipulation leading to lost of the sc by @adrien2p in #12234
- fix: steps to return undefined and still chain the config method by @thetutlage in #12255
- fix(core-flows, link-modules): return fulfillment creation by @fPolic in #12227
- fix: oas CLI to convert Windows paths to unix by @thetutlage in #12254
Documentation
- docs: changes for next release by @shahednasser in #12098
- docs: fix schema of product category by @shahednasser in #12145
- Update page.mdx by @Sai-Santhan-Dodda in #12141
- docs: fixed POST request in code snippet for quotation acceptance by @valerioXplo in #11983
- docs: more updates after 2.7.0 update by @shahednasser in #12156
- docs: generate OAS manually for 2.7.0 by @shahednasser in #12158
- docs: generate JS SDK reference with fixes by @shahednasser in #12161
- docs(resources): fix express checkout page for missing prop in CartContext Provider component by @relaxedrohan in #12188
- docs: changes to plugins page by @shahednasser in #12190
- docs: add loyalty points guide by @shahednasser in #12130
- docs: add a note to loyalty points guide by @shahednasser in #12201
- docs: hide colon when a note's title has a punctuation by @shahednasser in #12205
- docs: add framework page by @shahednasser in #12162
- docs: add framework to navbar by @shahednasser in #12206
- docs: capitalize use of Framework across docs by @shahednasser in #12207
- docs: update introduction and homepage following Framework changes by @shahednasser in #12211
- docs: rename Architectural Modules to Infrastructure Modules by @shahednasser in #12212
- docs: add new troubleshooting guides for Query and middlewares by @shahednasser in #12214
- docs: document conditional shipping option prices + expand on price rules by @shahednasser in #12217
- docs: fix links in submenu navigation by @shahednasser in #12220
- docs: expand on promotion rules by @shahednasser in #12218
- docs-util: fix examples in workflow reference that have multiple code by @shahednasser in #12223
- docs: fix step name in long-running workflow documentation by @Vallabh-Mahanta in #12215
- docs: updates to link to other modules docs by @shahednasser in #12225
- docs: add some redirects from v1 to v2 docs by @shahednasser in #12229
- dos: add docs on overriding allowed fields in routes by @shahednasser in #12233
- docs: add LLM dropdown menu to documentation pages by @shahednasser in #12235
- docs: fix redirect infrastructure modules by @shahednasser in #12238
- docs: improvements to admin, query, and read-only link docs by @shahednasser in #12239
- docs: fix index.html.md causing 404 by @shahednasser in #12241
- docs: fix revalidate cache guide by @shahednasser in #12243
- docs: Fix typo "documenation" in env vars page.mdx by @Ezequiel-Schejtman in #12247
- docs: fix messy layout bug by @shahednasser in #12248
- docs: Replace api_key_id with publishable_key_id by @Wcdaren in #12242
- docs: added a section on loader execution by @shahednasser in #12260
- docs: fix no lines code block example by @shahednasser in #12259
- docs: fix badges and titles not showing in code tabs by @shahednasser in #12261
Chores
- chore(utils): fix TSDoc example for payment provider by @ynzier in #12012
- chore: fixes to JS SDK TSDocs by @shahednasser in #12159
- chore(core-flows,types): fix links in TSDocs pointing to architectural modules by @shahednasser in #12213
- chore: fixes and updates to promotion and shipping option TSDocs by @shahednasser in #12222
- chore: Pass more data to fulfillment provider by @olivermrbl in #12186
- Chore/orchestration storage improvements by @adrien2p in #12178
- chore(types): small tsdoc change to file service by @shahednasser in #12240
Other Changes
- added dutch translation by @harmvdhorst in #12167
- added hungarian admin translation by @GergoVandor in #12176
- fix: Ensure shipment includes tracking numbers from fulfillment by @ionutgaina in #11775
- feat(cart): emit updated event in update line item workflow by @bqst in #12119
New Contributors
- @ynzier made their first contribution in #12012
- @valerioXplo made their first contribution in #11983
- @harmvdhorst made their first contribution in #12167
- @GergoVandor made their first contribution in #12176
- @relaxedrohan made their first contribution in #12188
- @ionutgaina made thei...
v2.7.0: Save payment methods, improved links, and performance improvements
Highlights
Support multiple payment account holders for customers
Warning
Breaking changes
This release adds support for a customer to have multiple account holders, therefore supporting multiple payment providers at the same time.
If you were using customer.account_holder
in your implementation, you must change it to customer.account_holders
and choose the relevant one for the provider_id you want to use.
If you haven't done anything custom with payments and account holders, there are no breaking changes.
Enforce integrity constraints on module links
This release enforces integrity constraints on module links in the application layer.
The constraints are enforced as exemplified below:
One-to-one
export default defineLink(
ProductModule.linkable.product,
CustomModule.linkable.customModel
)
// This succeeds
await link.create({
[Modules.PRODUCT]: {
product_id: "prod_123",
},
"customModule": {
custom_id: "cus_123",
},
})
// This throws
await link.create({
[Modules.PRODUCT]: {
product_id: "prod_123",
},
"customModule": {
custom_id: "cus_321",
},
})
// This throws
await link.create({
[Modules.PRODUCT]: {
product_id: "prod_321",
},
"customModule": {
custom_id: "cus_123",
},
})
One-to-many / Many-to-one
export default defineLink(
ProductModule.linkable.product,
{
linkable: CustomModule.linkable.customModel,
isList: true,
}
)
// export default defineLink(
// {
// linkable: ProductModule.linkable.product,
// isList: true,
// },
// CustomModule.linkable.customModel
// )
// This succeeds
await link.create({
[Modules.PRODUCT]: {
product_id: "prod_123",
},
"customModule": {
custom_id: "cus_123",
},
})
// This succeeds
await link.create({
[Modules.PRODUCT]: {
product_id: "prod_123",
},
"customModule": {
custom_id: "cus_321",
},
})
// This throws
await link.create({
[Modules.PRODUCT]: {
product_id: "prod_456",
},
"customModule": {
custom_id: "cus_123",
},
})
Many-to-many
export default defineLink(
{
linkable: ProductModule.linkable.product,
isList: true
},
{
linkable: CustomModule.linkable.customModel,
isList: true,
}
)
// This succeeds
await link.create({
[Modules.PRODUCT]: {
product_id: "prod_123",
},
"customModule": {
custom_id: "cus_123",
},
})
// This succeeds
await link.create({
[Modules.PRODUCT]: {
product_id: "prod_123",
},
"customModule": {
custom_id: "cus_321",
},
})
// This succeeds
await link.create({
[Modules.PRODUCT]: {
product_id: "prod_456",
},
"customModule": {
custom_id: "cus_123",
},
})
Introduce dynamic pricing workflow hooks
This release introduces workflow hooks to override the default pricing context passed to the pricing module when calculating prices for variants and shipping options.
Prices with custom rules need a different context than what is passed by default. For example, say you have a custom rule that ties prices with a specific location location_id: loc_1234
. To account for this rule in the price calculation logic, the pricing context needs to be populated with a location_id
.
The pricing hooks are used as follows:
// src/workflows/hooks/pricing-hooks.ts
import { addToCartWorkflow } from "@medusajs/medusa/core-flows";
import { StepResponse } from "@medusajs/workflows-sdk";
addToCartWorkflow.hooks.setPricingContext(() => {
return new StepResponse({
location_id: "loca_1234", // Special price for in-store purchases
});
});
Assuming you have a custom pricing rule { attribute: "location_id", operator: "eq", value: "loca_1234" }
, this would be part of the pricing result set when retrieving the associated product variant or adding the same to the cart.
The hooks have been introduced to the following workflows:
addToCartWorkflow
createCartsWorkflow
listShippingOptionsForCartWorkflow
refreshCartItemsWorkflow
updateLineItemInCartWorkflow
addLineItemsToOrderWorkflow
updateClaimShippingMethodWorkflow
createOrderWorkflow
updateExchangeShippingMethodWorkflow
createOrderEditShippingMethodWorkflow
updateOrderEditShippingMethodWorkflow
createCompleteReturnWorkflow
updateReturnShippingMethodWorkflow
Please don't hesitate to submit a Github issue, if you want us to consider adding the hook to more workflows.
Resolve issues with broken plugin dependencies in development server
This release reworks how admin extensions are loaded from plugins and how extensions are managed internally in the dashboard project.
Previously we loaded extensions from plugins the same way we do for extensions found in a user's application. This being scanning the source code for possible extensions in .medusa/server/src/admin
, and including any extensions that were discovered in the final virtual modules.
This was causing issues with how Vite optimizes dependencies and would lead to CJS/ESM issues.
To circumvent the above issue, we have changed the strategy for loading extensions from plugins. We now build plugins slightly different–if a plugin has admin extensions, we build those to .medusa/server/src/admin/index.mjs
and .medusa/server/src/admin/index.js
for a ESM and CJS build.
When determining how to load extensions from a source, we follow these rules:
- If the source has a
medusa-plugin-options.json
or is the root application, we determine that it is a local extension source and load extensions as previously through a virtual module. - If it has neither of the above, but has a
./admin
export in itspackage.json
, then we determine that it is a package extension, and we update the entry point for the dashboard to import the package and pass its extensions along to the dashboard manager.
Changes required by plugin authors
The change has no breaking changes, but requires plugin authors to update the package.json of their plugins to also include a ./admin export. It should look like this:
{
"name": "@medusajs/plugin",
"version": "0.0.1",
"description": "A starter for Medusa plugins.",
"author": "Medusa (https://medusajs.com)",
"license": "MIT",
"files": [
".medusa/server"
],
"exports": {
"./package.json": "./package.json",
"./workflows": "./.medusa/server/src/workflows/index.js",
"./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
"./modules/*": "./.medusa/server/src/modules/*/index.js",
"./providers/*": "./.medusa/server/src/providers/*/index.js",
"./*": "./.medusa/server/src/*.js",
"./admin": {
"import": "./.medusa/server/src/admin/index.mjs",
"require": "./.medusa/server/src/admin/index.js",
"default": "./.medusa/server/src/admin/index.js"
}
},
}
Improve performance of price-selection
This release comes with significant optimizations of the pricing retrieval query to improve performance, especially on large datasets.
We have:
- Removed costly subqueries and excessive joins
- Leveraged targeted subqueries and indexed filters for rule checks
- Optimized queries without rule contexts to bypass unnecessary logic
The improvements have been tested on a dataset with ~1,200,000 prices and ~1,200,000 price rules. The impact of the changes are as follows:
- Before: ~718ms execution time
- After: ~17ms execution time
We are dedicated to continuously improve performance, so you can expect further improvements going forward.
Improve the robustness of payment workflows
This release improves the robustness of payment workflows by squashing a range of minor bugs.
These fixes can be found in the following PRs:
Features
- fix(medusa,utils,test-utils,types,framework,dashboard,admin-vite-plugin,admin-bundler): Fix broken plugin dependencies in development server by @kasperkristensen in #11720
- refactor: use module name as the snapshot name by @thetutlage in #11802
- feat: Change customer to account_holder to be one-to-many by @sradevski in #11803
- feat: add check for uniqueness when creating links with isList=false by @thetutlage in #11767
- fix(types,order,medusa): Create credit lines + hooks by @riqwan in #11569
- feat(dashboard,js-sdk,admin-shared): add customer addresses + layout change by @riqwan in #11871
- feat(core-flows,types,utils): make payment optional when cart balance is 0 by @riqwan in #11872
- feat(medusa,types): add enabled plugins route by @riqwan in #11876
- feat: Add support for generating a production-ready config out of the… by @sradevski in #11850
- feat: add support for accessing step results via context by @thetutlage in https://github.com/medusajs/medusa/p...
v2.6.1: Improved Scheduled Jobs
Highlights
Scheduled Jobs execution
Ensure Scheduled Jobs are only executed by server instances running in worker or shared mode. Our workflow engine powers scheduled jobs, and we create a workflow for every job. Scheduled Jobs (i.e. workflows) are not registered in servers that run in server mode. Consequently, if a server instance tries to pick up a Scheduled Job, it will fail, as it cannot find the underlying workflow. This change ensures that only worker and shared server instances execute Scheduled Jobs.
Features
Bugs
- fix(pricing): events by @carlos-r-l-rodrigues in #11742
- fix(workflow-engine-redis): Split the queues and respect worker mode for job executions by @adrien2p in #11740
- fix(core-flows): conditionally create, update or delete products when input is present by @riqwan in #11758
- Fix(framework): http cors middleware order and options by @adrien2p in #11765
- chore: pin swc to 1.5.7 by @thetutlage in #11759
- fix: Add created at and updated at dates to payment session by @sradevski in #11774
- fix(core-flows): fulfillment reservation check by @fPolic in #11735
- fix(dashboard): allow creating fulfillment for items without shipping profile by @fPolic in #11733
- fix(dashboard): display minute instead of month by @fPolic in #11731
- fix(medusa): Apply middleware to GET customers/:id/addresses/:address_id by @kasperkristensen in #11788
- fix(medusa): Use correct query config by @kasperkristensen in #11789
Documentation
- docs: changes for next release by @shahednasser in #11708
- docs-util: fix freshness check script by @shahednasser in #11737
- docs: fix tag for JS SDK draft order + other fixes by @shahednasser in #11747
- Update page.mdx by @MedusaNick in #11752
- docs: changes to main docs sidebar by @shahednasser in #11750
- docs: add sidebar sorting logic by @shahednasser in #11751
- docs-util: add exceptions for routes with custom auth middleware by @shahednasser in #11755
- docs: change link to cloud pricing by @shahednasser in #11753
- chore: remove ranges on medusa packages by @olivermrbl in #11738
- docs: support multiple sidebars in a project by @shahednasser in #11768
- docs: update UI docs to latest version by @shahednasser in #11770
- docs: fix duplicate footer in user guide by @shahednasser in #11785
Chores
- chore(product): Improve product normalization and fix http router with tracing by @adrien2p in #11724
- chore(js-sdk): fix tsdocs for properties and methods by @shahednasser in #11748
- chore(cart, core-flows): Improve tax lines algo management by @carlos-r-l-rodrigues in #11715
Full Changelog: v2.6.0...v2.6.1
v2.6.0
Highlights
Authentication improvements
This release enhances security around resetting passwords.
Updated token handling in password resets
Warning
Breaking changes: this change is not backward compatible due to security considerations
The token for updating provider identity is now accepted as a Bearer token in the authorization header instead of query parameters.
Before:
POST /auth/user/emailpass/update?token=ey...
After:
POST /auth/user/emailpass/update
// headers
{ authorization: "Bearer ey..." }
This enhances security around token management, for example, by preventing token exposure in logs.
More specifically, this will affect the reset password flow that relies on this endpoint.
Required action
- In your client (e.g. Next.js storefront), move the token from query params to the authorization header
Removed deprecated actorType
from reset password event
We have removed a long deprecated property, actorType
from the reset password event. Instead, use the actor_type
.
API Routes loader refactor
Important
While these changes sound dramatic, if you have used middleware and API Routes as intended, there will be no required actions nor breaking changes.
This release includes a refactor of our API Routes loading mechanism. The previous loader had long-standing issues that required hacky workarounds. To resolve these issues, we’ve rebuilt the loader from scratch to ensure predictable behavior in how routes and middleware are registered within the application.
The root of the issues with the old implementation was mainly related to the following decisions:
-
Plugins, Medusa core, and the application used isolated router instances of Express. This means global middleware and error handlers are isolated, too. If you define an error handler in your app, it won't be able to handle errors from the core routes.
-
Middlewares defined inside the
src/api/middlewares.ts
file were always defined before the filesystem routes, and this led to unexpected behavior in many scenarios. For example, the middleware handler registered for routeadmin/payments/payment-providers
will also run when a request comes in for/admin/payments/:id
, even though there is a static route in place for the/admin/payments/payment-providers
URI. -
There was no clarity on the ordering in which routes and middleware were executed. With the new implementation, this has been fixed (more on ordering is discussed below).
-
Overriding an existing route defines multiple express handlers under the hood. It worked previously, because the route handlers never call
next
, hence the previous implementation was never executed. With the newer implementation, the route overrides from a Medusa application will be registered over the routes from the core or the plugins.
To resolve these issues, we’ve rebuilt the loader from scratch to ensure predictable behavior in how routes and middleware are registered within the application. Additionally, the old API Routes loader unintentionally allowed overriding core API routes and middleware. While this behavior remains for now, it will be removed in a future release. Please prepare your application based on the behavior described in the following sections.
Global middleware
A global middleware is defined using the defineMiddlewares
function without specifying an HTTP method.
defineMiddlewares([
{
matcher: '/admin',
middleware: [
(req, res, next) => console.log("Hi from global middleware")
],
}
])
The order of registering global middleware is as follows:
- Global middleware from the core
- Global middleware from plugins (in the order plugins are registered)
- Global middleware from the app
Route middleware
A route middleware is also defined using the defineMiddlewares
method. However, they must always specify the HTTP method(s) they want to target.
defineMiddlewares([
{
matcher: '/admin/*',
methods: ["GET", "POST"],
middleware: [
(req, res, next) => console.log("Hi from route middleware")
],
}
])
The order of registering route middleware is as follows:
- Route middleware from the core
- Route middleware from plugins (in the order plugins are registered)
- Route middleware from the app
API Routes
An API Route is always attached to a method, decided by the exported const from the route file. API Routes are registered by scanning the filesystem.
Two types of API Routes are supported:
- Static segments:
/admin/products/route.ts
- Dynamic segments:
/admin/products/[id]/route.ts
Sorting routes and middleware
Before registering routes + middleware with Express, they are sorted as described below:
- Global middleware
- Route middleware
- API Routes
On top of this, each segment from the API Route matcher is sorted by its specificity:
- Wildcard segments, e.g.
/admin/products/*
- Regex segments, e.g.
/admin/(product-types|product-collections)
- Static segments, e.g.
/admin/products
- Dynamic segments, e.g.
/admin/products/:id
Important note about middleware
Middleware never overrides any existing middleware; they are added to the stack of existing middleware. For example, if you define a custom validation middleware on an existing route, then both the original and the custom validation middleware will run.
Performance improvements
This release contains general and, in some areas, significant performance improvements. Over the past weeks, we have focused on removing performance bottlenecks in Medusa. While many of the improvements impacted the entire application, the most significant ones are for cart operations.
Here's a summary of the changes and improvements:
Application-level changes
- Eliminated unnecessary module calls in cart workflows to ensure we don't create unused transactions and have redundant database communication
- Minimized redundant data fetching in cart workflows, ensuring that only the data that is needed for the operation is fetched
- Changed workflows to accept prefetched data instead of always fetching the data independently
- Introduced finer control over cart refresh logic to ensure we don't perform unnecessary cart refreshes
- Replaced the Redis
del
operation with the non-blocking deleteunlink
to reduce the load on Redis - Replaced inefficient soft and hard deletes by performing the operation in a single query instead of multiple
- (Dashboard) Minimized redundant data fetching for dashboard pages
Database-level changes
These changes are a result of thorough testing of our own infrastructure. Therefore, they should be deemed a recommendation rather than an absolute configuration. Additionally, it is important to highlight that these tests were run against a Neon Database with a PgBouncer setup. Without PgBouncer and Neon, the configuration would likely look different.
// medusa-config.ts
export default defineConfig({
projectConfig: {
// ...other options
databaseDriverOptions: {
pool: {
max: 8, // Fixed pool size of 8
idleTimeoutMillis: 30000, // Close idle connections after 30 seconds
connectionTimeoutMillis: 10000, // Fail on connection attempt after 10 seconds
createRetryIntervalMillis: 100 // Time between connection retries
},
},
...
},
}
Results
We have run two tests:
Test 1:
- Operation: Add to cart
- Database spec:
- 0.25 CPU
- 1 GB RAM
- Server spec:
- 1 CPU
- 2 GB RAM
- Load: 5 carts, operations for each cart run in parallel; for each cart, we add 100 items sequentially
p95: 1180ms -> 866ms
p50: 833ms -> 593ms
Test 2:
- Operation: Add to cart
- Database spec:
- 0.5 CPU
- 2 GB RAM
- Server spec:
- 1 CPU
- 2 GB RAM
- Load: 5 carts, operations for each cart run in parallel; for each cart, we add 100 items sequentially
p95: 1180ms -> 773ms
p50: 849 -> 537ms
Both tests show an approximate performance improvement of 30-35%.
Features
- feat: add middleware and routes sorter by @thetutlage in #11526
- feat(core-flows,types,cart): add credit lines to cart by @riqwan in #11419
- feat: add routes loader by @thetutlage in #11592
- feat: add middleware-file-loader by @thetutlage in #11638
- feat(utils): add error message when manager is not found in context by @riqwan in #11693
- feat: Replace existing router with the new implementation by @thetutlage in #11646
Bugs
- fix(types, medusa): remove fulfillment and payment status filters from validator + http types by @shahednasser in #11604
- fix(types): Allow providing either vite@5 or vite@6 for the peer dependency by @kasperkristensen in #11581
- fix(promotion): scope uniqueness index to non deleted promotions by @riqwan in #11624
- fix(core-flows): support 0 as a valid unit price for custom line items by @riqwan in #11631
- fix(core-flows): unsafe access to a variant property by @fPolic in #11588
- fix(medusa): deleting location level by @fPolic in #11630
- fix: Exclude the health endpoint from being logged by @sradevski in ht...
v2.5.1
Highlights
Support outlet routes, loader, and handle
This release introduces support for
- exporting a loader from a route file
- exporting a handle from a route file
Here's an example using the loader and handle:
// src/admin/routes/articles/[id]/page.tsx
import { Button, Container, Heading } from "@medusajs/ui";
import { Link, LoaderFunctionArgs, Outlet, UIMatch, useLoaderData } from "react-router-dom";
export async function loader({ params }: LoaderFunctionArgs) {
const { id } = params;
return {
id,
};
}
export const handle = {
breadcrumb: (match: UIMatch<{ id: string }>) => {
const { id } = match.params;
return `#${id}`;
},
};
const ProfilePage = () => {
const { id } = useLoaderData() as Awaited<ReturnType<typeof loader>>;
return (
<div>
<Container className="flex justify-between items-center">
<Heading>Article {id}</Heading>
<Button size="small" variant="secondary" asChild>
<Link to="edit">Edit</Link>
</Button>
</Container>
{/* This will be used for the next example of an Outlet route */}
<Outlet />
</div>
);
};
export default ProfilePage;
In the above example we are passing data to the route from a loader, and defining a breadcrumb using the handle.
See more in #11305.
Expand options for instrumentation via registerOtel
This release expands the available options in our registerOtel
function from our instrumentation tooling. More specifically, the function will allow all options from OpenTelemetry's NodeSDK, which will open up support for a broader range of instrumentation tools, including Sentry.
Here's an example of configuring Sentry:
import Sentry from '@sentry/node'
import otelApi from "@opentelemetry/api";
import { registerOtel } from "@medusajs/medusa"
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc"
import { SentrySpanProcessor, SentryPropagator } from "@sentry/opentelemetry-node"
Sentry.init({
dsn: "<INSERT SENTRY DSN>",
tracesSampleRate: 1.0,
instrumenter: "otel",
});
otelApi.propagation.setGlobalPropagator(new SentryPropagator());
export function register() {
registerOtel({
serviceName: "medusa",
spanProcessor: new SentrySpanProcessor(),
traceExporter: new OTLPTraceExporter(),
instrument: {
http: true,
workflows: true,
query: true
},
})
}
See more in #11460.
Enrich and improve structural logging
This release improves structural logging by expanding the data with additional information, including request ID, request size, response size, request duration, and more.
Here's an example log line:
{"level":"http","client_ip":"10.18.85.250","request_id":"53123ef7-f8e9-4e85-8aea-3fecfc7d9ba0","http_version":"1.1","method":"POST","path":"/admin/products","status":200,"response_size":"10754","request_size":"1550","duration":1079.301,"referrer":"-","user_agent":"node","timestamp":"2025-02-24T08:36:17.748Z"}
See more in #11489.
Allow custom storage in JS-SDK
This release introduces support for a custom storage solution in the JS-SDK, instead of using the built-in memory
, session
, and local
. This allows you to pass a custom implementation of the storage interface to be responsible for token management.
See more in #11467.
What's Changed
Features
- feat: query.index by @carlos-r-l-rodrigues in #11348
- feat(dashboard,admin-vite-plugin): Add support for outlet routes, loader, and handle by @kasperkristensen in #11305
- feat: allow all NodeSDK options via registerOtel by @thetutlage in #11460
- feat(dashboard): Pickup option changes by @fPolic in #11306
- feat(): Backport metadata management by @adrien2p in #11469
- feat(ui,dashboard): Move InlineTip to UI package by @kasperkristensen in #11462
- feat: Add support for idempotency key in payments by @sradevski in #11494
- feat(framework): Improve production structured logging with more valuable information as well by @adrien2p in #11489
- feat(core-flows, dashboard, medusa, types): optional shipping profile by @fPolic in #11434
- feat: Add support to update account holder by @sradevski in #11499
- feat(medusa): Rollout index engine behind feature flag by @adrien2p in #11431
- feat(core-flows, types): add has missing inventory flag when listing shipping options by @fPolic in #11493
Bugs
- fix(dashboard): secret key display by @fPolic in #11365
- fix(utils): custom linkable keys by @carlos-r-l-rodrigues in #11400
- fix(admin-vite-plugin,admin-bundler,ui,icons,dashboard,framework,types): Update Vite dependencies by @kasperkristensen in #11414
- fix: Remove unnecessary swc production dependency by @sradevski in #11416
- fix(core-flows, types): reservation of shared inventory item by @fPolic in #11403
- fix(payment): update payment session data by @fPolic in #11410
- fix(medusa): Fix draft order validator, and endpoint by @kasperkristensen in #11398
- fix(design-system): singleton prompt by @fPolic in #11352
- fix: listVariantsList types by @thetutlage in #11441
- fix: relationships to accept ids by @thetutlage in #11399
- fix(medusa): Re throw error in instrumentation after reporting it by @adrien2p in #11464
- fix(medusa): Allow filtering by handle and title as arrays by @kasperkristensen in #11472
- fix(core-flows): Fix line item ids passed to deleteReservationsByLineItemsStep by @kasperkristensen in #11465
- fix(types): fix shipping profile type optional in create type / method by @shahednasser in #11453
- fix(dashboard): Fix size of buttons and use Link to navigate by @kasperkristensen in #11366
- fix: generate posix paths for migrations by @thetutlage in #11468
- fix(dashboard): Allow using the Enter key in product create Textarea by @kasperkristensen in #11488
- fix(dashboard): Properly delete metadata keys, and fix number parsing by @kasperkristensen in #11466
- fix(framework): add missing storefrontUrl from configuration type by @shahednasser in #11511
- fix(core-flows): add no_notification to fulfillment created event by @riqwan in #11507
- fix: allow setting DB_PORT and DATABASE_URL env variables by @thetutlage in #11519
- fix: typings for list payment method were incorrect by @sradevski in #11523
- fix(dashboard): Prevent overfetching data by @adrien2p in #11532
- fix(utils): Handle 0 correctly in build query by @olivermrbl in #11525
- fix(medusa,js-sdk,types): Add basic draft order operations to js-sdk by @kasperkristensen in #11514
- fix(): handle empty q filters - allow to query deleted records from graph API - staled_at fixes by @adrien2p in #11544
- fix(order): calculate taxes on order edit flows by @carlos-r-l-rodrigues in #11518
- fix(core-flows): Allow adding shipping methods through order edits by @kasperkristensen in #11504
- fix(js-sdk): Export Draft Order methods by @kasperkristensen in #11572
Documentation
- docs: update default for medusa start by @shahednasser in #11391
- docs: document payment changes + account holder by @shahednasser in #11242
- docs: add documentation for product <> shipping profile link by @shahednasser in #11397
- docs: general updates after 2.5.0 update by @shahednasser in #11402
- docs: added express checkout guide by @shahednasser in #10810
- docs: small fixes to workflow constraints chapter by @shahednasser in #11421
- docs: fix managing relationship for hasOne by @shahednasser in #11422
- docs: add section on validating module options by @shahednasser in #11427
- docs-util: fix inline code escape in generated references by @shahednasser i...
v2.5.0
Highlights
Revamped Payment Provider interface
Warning
Breaking changes
The payment provider interface, originally designed for Medusa V1, has now been redesigned for V2–a long overdue update. These changes deliver a more unified and intuitive API.
We apologise in advance for the inconvenience it might cause for your payment provider implementations.
The following are breaking changes:
Payment Provider interface
export interface IPaymentProvider {
...
- initiatePayment(
- data: CreatePaymentProviderSession
- ): Promise<PaymentProviderError | PaymentProviderSessionResponse>
+ initiatePayment(data: InitiatePaymentInput): Promise<InitiatePaymentOutput>
- updatePayment(
- context: UpdatePaymentProviderSession
- ): Promise<PaymentProviderError | PaymentProviderSessionResponse>
+ updatePayment(data: UpdatePaymentInput): Promise<UpdatePaymentOutput>
- deletePayment(
- paymentSessionData: Record<string, unknown>
- ): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
+ deletePayment(data: DeletePaymentInput): Promise<DeletePaymentOutput>
- authorizePayment(
- paymentSessionData: Record<string, unknown>,
- context: Record<string, unknown>
- ): Promise<PaymentProviderError | PaymentProviderAuthorizeResponse>
+ authorizePayment(data: AuthorizePaymentInput): Promise<AuthorizePaymentOutput>
- capturePayment(
- paymentSessionData: Record<string, unknown>
- ): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
+ capturePayment(data: CapturePaymentInput): Promise<CapturePaymentOutput>
- refundPayment(
- paymentSessionData: Record<string, unknown>,
- refundAmount: BigNumberInput
- ): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
+ refundPayment(data: RefundPaymentInput): Promise<RefundPaymentOutput>
- retrievePayment(
- paymentSessionData: Record<string, unknown>
- ): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
+ retrievePayment(data: RetrievePaymentInput): Promise<RetrievePaymentOutput>
- cancelPayment(
- paymentSessionData: Record<string, unknown>
- ): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]>
+ cancelPayment(data: CancelPaymentInput): Promise<CancelPaymentOutput>
+
+ createAccountHolder?(
+ data: CreateAccountHolderInput
+ ): Promise<CreateAccountHolderOutput>
+
+ deleteAccountHolder?(
+ data: DeleteAccountHolderInput
+ ): Promise<DeleteAccountHolderOutput>
- listPaymentMethods?(
- context: PaymentProviderContext
- ): Promise<PaymentMethodResponse[]>
+ listPaymentMethods?(
+ data: ListPaymentMethodsInput
+ ): Promise<ListPaymentMethodsOutput>
- savePaymentMethod?(
- input: SavePaymentMethod
- ): Promise<PaymentProviderError | SavePaymentMethodResponse>
+ savePaymentMethod?(
+ data: SavePaymentMethodInput
+ ): Promise<SavePaymentMethodOutput>
- getPaymentStatus(
- paymentSessionData: Record<string, unknown>
- ): Promise<PaymentSessionStatus>
+ getPaymentStatus(data: GetPaymentStatusInput): Promise<GetPaymentStatusOutput>
}
The input and return types of the updated interface can be found in this file.
Additionally, we've updated the interface to require payment providers to throw errors instead of returning them to the upstream payment module service.
Create Payment Sessions endpoint
In addition to the interface changes, we've also removed the context
field from the endpoint creating payment sessions:
`POST /store/payment-collections/:id/payment-sessions
This change addresses a security risk. The context
accepted by the payment module now only accept known fields, limiting the surface area of what can be passed through it.
Account Holders
Fortunately, the revamp did not only introduce breaking changes. This release also introduces support for Account Holders in payment providers. In Stripe, for example, we will create a customer for every signed up user on your platform so you can track payments more easily within Stripe. These methods are optional for payment providers.
You can find all the changes in this pull request.
Overriding Shipping Option for Fulfillments
Note
Schemas changes
This release improves fulfillment capabilities, allowing merchants to override the shipping option originally selected during order creation. For example, a different shipping option can now be specified directly in the dashboard when creating a fulfillment.
Key changes:
- Product <> Shipping Profile link
A link between Products and Shipping Profiles has been introduced, similar to the relationship in Medusa V1. Products now require a Shipping Profile upon creation. The create and update forms in the dashboard have been updated accordingly.
- Override Shipping Option on Fulfillments
The fulfillment creation endpoint now accept an optional shipping_option_id
. If passed, it will override the shipping option used when placing the order.
POST /admin/orders/:id/fulfillments
{ ..., shipping_option_id: "so_1234"}
This functionality is also available in the dashboard, allowing merchants to specify the override when creating a fulfillment.
- Data migration script
Since products now require a shipping profile, this release includes a data migration script. The script will assign all products to the default shipping profile, if it exists. The default shipping profile is determined by looking for a shipping profile with default
(case insensitive) in its name.
By default, data migration scripts run as part of the medusa db:migrate
command.
Alternatively, you can run the data migration separately from the regular database migrations. To do this, first run the migration command with an argument skipping scripts:
medusa db:migrate --skip-scripts
Then, run the command again without the argument:
medusa db:migrate
Type-hinting for custom module services
This release improves type-hinting for custom module services. The input types of autogenerated create and update methods will provide an approximate type inferred from the related data model. Please note, in this first iteration, all properties will be marked as optional in the type, even though they might be required at the DB-level. We intend to further improve this to bring the type even closer to what the database expects.
Clearing workflow_execution
s exceeding their retention time
Note
Schemas changes
This release introduces a recurring job within the workflow engine to clear stale workflow executions from the database. The job runs every hour and deletes workflow executions that have exceeded their configured retention time.
Retention time is set in the workflow execution options. However, to simplify this operation, we’ve introduced a retention_time
column in the workflow_execution
table.
For existing workflow executions, the retention time will be automatically set if it was previously configured in the workflow execution options. The retention time is expected to be specified in seconds. If it was mistakenly configured in milliseconds, you may encounter a Postgres error when casting the retention time to an integer.
If you run into this issue, you can manually run the migration while specifying the correct retention time for your workflow executions.
Other noteworthy changes
Changes to medusa start
The medusa start
command is intended to be used for production envionments. Therefore, the NODE_ENV
now defaults to production
instead of development
.
New Divider component
The Divider
component has been added to the UI library.
Added Metadata forms
Metadata forms have been added for the Product Collection and Order pages.
Features
- feat(core-flows, dashboard, link-modules,medusa, types, utils): fulfillment shipping changes by @fPolic in #10902
- feat: Add tasks orchestrator by @thetutlage in #11161
- Feat/index sync data by @adrien2p in #11169
- feat: Revamp payment provider interface by @sradevski in #11015
- feat(medusa): add sku to variant query params by @riqwan in #11151
- feat(orchestration): hydrate resultset by @carlos-r-l-rodrigues in #11263
- feat: make AbstractModuleService create method type-safe by @thetutlage in #11216
- feat(core-flows): cart complete shipping validate by @fPolic in #10984
- feat: map container types for core services to interface by @thetutlage in #11295
- feat(index): full sync operations by @adrien2p in #11178
- feat(ui,dashboard): Migrate SC tables to DataTable by @kasperkristensen in #11106
- feat(ui,dashboard): Move Divider component to UI package by @kasperkristensen in #11357
Bugs
- fix(js-sdk,admin-bundler): Fix qs import by @kasperkristensen in #11172
- fix(dashboard): Matching product profile with shipping profile by @olivermrbl i...