@mercurjs/docs 2.3.1 → 2.3.2-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/content/learn/architecture.mdx +268 -218
- package/llms.txt +1 -1
- package/package.json +1 -1
|
@@ -1,286 +1,336 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Architecture"
|
|
3
|
-
description: "How the
|
|
3
|
+
description: "How Mercur is put together: the runtime layers, the boundary between the marketplace platform and your own code, and the zones you extend."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Mercur is
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
Mercur is an open-source marketplace platform. It gives you a multi-vendor
|
|
7
|
+
commerce backend, an admin panel, and a vendor portal that you deploy on your own
|
|
8
|
+
infrastructure and change at the source level.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
along with an admin panel and a vendor portal. Operators run the marketplace with
|
|
15
|
-
role-based access control and an auditable change history, on a codebase they own
|
|
16
|
-
outright.
|
|
10
|
+
It is not a hosted service you integrate with, and it is not a starter kit you
|
|
11
|
+
finish yourself. Mercur is a running system on day one, with defined seams for
|
|
12
|
+
the parts every marketplace does differently: onboarding rules, commission logic,
|
|
13
|
+
approval policy, back-office integrations, and the screens your teams work in.
|
|
17
14
|
|
|
18
|
-
This page
|
|
15
|
+
This page describes those layers and those seams. If you are evaluating Mercur,
|
|
16
|
+
the two questions it answers are **what runs where** and **where does my code go**.
|
|
19
17
|
|
|
20
|
-
##
|
|
18
|
+
## Design principles
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
beneath it, so you can reason about, extend, or replace any layer on its own.
|
|
20
|
+
Everything below follows from four decisions.
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
B[Vendor Portal]
|
|
30
|
-
C[Storefront]
|
|
31
|
-
end
|
|
22
|
+
- **You run it.** MIT-licensed, PostgreSQL, Node. No vendor in the request path, no GMV fee, no proprietary runtime. Self-host, or deploy to any cloud that runs a Node process and a database.
|
|
23
|
+
- **Extension over configuration.** Instead of hundreds of settings, the platform exposes typed extension zones: workflow hooks, API routes, modules, custom fields, pages, and widgets. When a zone isn't enough, the source is yours to change.
|
|
24
|
+
- **Marketplace on top of commerce, not instead of it.** Products, carts, orders, payments, and fulfillment are handled by [Medusa](https://medusajs.com), a mature commerce engine. Mercur adds only what multi-vendor requires: sellers, offers, order splitting, commissions, payouts, and governance.
|
|
25
|
+
- **Governance is structural.** Role scoping, the product change pipeline, and exact-precision money arithmetic are properties of the architecture, not features you enable.
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
end
|
|
27
|
+
## The layers
|
|
28
|
+
|
|
29
|
+
Mercur is a single deployable backend with three API surfaces, plus two panel
|
|
30
|
+
applications that are ordinary clients of those APIs.
|
|
38
31
|
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
```mermaid
|
|
33
|
+
graph TD
|
|
34
|
+
subgraph Clients
|
|
35
|
+
A[Admin Panel<br/>React]
|
|
36
|
+
B[Vendor Portal<br/>React]
|
|
37
|
+
C[Storefront<br/>any framework]
|
|
38
|
+
D[Your services<br/>ERP · PIM · agents]
|
|
41
39
|
end
|
|
42
40
|
|
|
43
|
-
subgraph
|
|
44
|
-
|
|
41
|
+
subgraph "Backend — one Node process"
|
|
42
|
+
E["/admin/*"]
|
|
43
|
+
F["/vendor/*"]
|
|
44
|
+
G["/store/*"]
|
|
45
|
+
H[Marketplace domain<br/>sellers · offers · commissions · payouts · governance]
|
|
46
|
+
I[Commerce domain<br/>products · carts · orders · payments · fulfillment]
|
|
45
47
|
end
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
J[(PostgreSQL)]
|
|
50
|
+
K[Redis · event bus, workflow engine, cache]
|
|
51
|
+
L[Providers<br/>payment · payout · fulfillment · notification · search]
|
|
48
52
|
|
|
49
|
-
A -->
|
|
50
|
-
B -->
|
|
51
|
-
C -->
|
|
52
|
-
D -->
|
|
53
|
-
E -->
|
|
54
|
-
F -->
|
|
53
|
+
A --> E
|
|
54
|
+
B --> F
|
|
55
|
+
C --> G
|
|
56
|
+
D --> E
|
|
57
|
+
E --> H
|
|
58
|
+
F --> H
|
|
55
59
|
G --> H
|
|
56
60
|
H --> I
|
|
61
|
+
I --> J
|
|
62
|
+
H --> J
|
|
63
|
+
H --> K
|
|
64
|
+
H --> L
|
|
57
65
|
```
|
|
58
66
|
|
|
59
|
-
###
|
|
67
|
+
### Data
|
|
68
|
+
|
|
69
|
+
One PostgreSQL database. Every domain owns its own tables and never reaches into
|
|
70
|
+
another's; relationships across domains are declared explicitly as links (see
|
|
71
|
+
[Links](#links)). That isolation is what makes a domain replaceable, and what
|
|
72
|
+
keeps a schema change local instead of platform-wide.
|
|
60
73
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
links, workflows, and API routes. This is the one place the word framework
|
|
65
|
-
applies. Medusa is the commerce framework, and Mercur is the platform you run on
|
|
66
|
-
it.
|
|
74
|
+
Redis backs the event bus, the workflow engine, and caching in production. In
|
|
75
|
+
development both fall back to in-memory implementations, so a laptop needs
|
|
76
|
+
nothing but Postgres.
|
|
67
77
|
|
|
68
|
-
###
|
|
78
|
+
### Domain
|
|
69
79
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
Links connect these modules to Medusa's core entities without modifying the
|
|
75
|
-
original models.
|
|
80
|
+
The domain layer is where marketplace behaviour lives: seller accounts and
|
|
81
|
+
members, offers against a shared product catalog, commission rules and their
|
|
82
|
+
resolution, payout accounts and transfers, order groups, the product change
|
|
83
|
+
pipeline. It calls into the commerce domain for anything commerce already does.
|
|
76
84
|
|
|
77
|
-
|
|
85
|
+
Two things about this layer matter to an architect:
|
|
78
86
|
|
|
79
|
-
Mercur
|
|
87
|
+
- **It is not a wrapper.** Mercur does not proxy or re-implement commerce endpoints. Marketplace concepts are first-class records with their own lifecycle, joined to commerce records through links.
|
|
88
|
+
- **Your own domains sit beside it.** A module you write, a module Mercur ships, and a module Medusa ships are the same kind of object, registered the same way, with the same access to the container, the event bus, and the workflow engine. There is no privileged inner ring.
|
|
80
89
|
|
|
81
|
-
|
|
82
|
-
| ---------- | ----------- | -------------------------------------------------------------------------- |
|
|
83
|
-
| **Admin** | `/admin/*` | Platform administration: manage sellers, configure commission rates, view payouts. |
|
|
84
|
-
| **Vendor** | `/vendor/*` | Seller operations: manage products, orders, fulfillment, shipping, inventory, payouts. |
|
|
85
|
-
| **Store** | `/store/*` | Storefront: browse sellers, manage carts, check out with order splitting. |
|
|
90
|
+
### API
|
|
86
91
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
request is scoped to its own seller's data before the handler runs. See the
|
|
90
|
-
[API conventions](/references/api/conventions) for authentication and scoping.
|
|
92
|
+
Three HTTP surfaces, one per audience, separated because their authorization
|
|
93
|
+
models differ, not because their data does.
|
|
91
94
|
|
|
92
|
-
|
|
95
|
+
| Surface | Path | Audience | Scoping |
|
|
96
|
+
| ---------- | ----------- | --------------------- | ------------------------------------------------------------------ |
|
|
97
|
+
| **Admin** | `/admin/*` | Marketplace operators | Platform-wide, gated by role |
|
|
98
|
+
| **Vendor** | `/vendor/*` | Sellers | Every request is narrowed to the caller's seller before the handler |
|
|
99
|
+
| **Store** | `/store/*` | Customers, storefront | Public catalog and the caller's own cart, orders, and account |
|
|
93
100
|
|
|
94
|
-
|
|
101
|
+
A route is a thin adapter: middleware authenticates and scopes, a Zod validator
|
|
102
|
+
checks the payload, and the handler runs a workflow for writes or the query
|
|
103
|
+
engine for reads. Business logic does not live in routes, which is why adding
|
|
104
|
+
one is cheap and overriding one is safe. See the
|
|
105
|
+
[API conventions](/references/api/conventions).
|
|
95
106
|
|
|
96
|
-
|
|
97
|
-
- **Vendor Portal:** a React application for sellers to manage products, orders, fulfillment, and payouts, scoped to their own store.
|
|
98
|
-
- **Storefront:** the customer-facing application. Build it with any frontend that consumes the Store API.
|
|
107
|
+
### Clients
|
|
99
108
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
109
|
+
The admin panel and the vendor portal are separate React applications, not a
|
|
110
|
+
templated back office. They talk to the backend through `@mercurjs/client`, a
|
|
111
|
+
typed fetch wrapper generated from the real route definitions — including routes
|
|
112
|
+
you add — so a backend change that breaks a caller fails at `tsc`, not in
|
|
113
|
+
production.
|
|
103
114
|
|
|
104
|
-
|
|
115
|
+
The storefront is deliberately not shipped as a fixed application. Anything that
|
|
116
|
+
speaks HTTP consumes the Store API; a reference Next.js storefront is available
|
|
117
|
+
to start from.
|
|
105
118
|
|
|
106
|
-
The
|
|
107
|
-
|
|
119
|
+
## The building blocks
|
|
120
|
+
|
|
121
|
+
Four primitives compose the domain layer. You use the same four to extend it.
|
|
108
122
|
|
|
109
123
|
### Modules
|
|
110
124
|
|
|
111
|
-
A module
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
[Learn about modules](/resources/best-practices/modules).
|
|
125
|
+
A module owns one domain: its data models, its service, its migrations. Modules
|
|
126
|
+
never import each other. That constraint is what lets you swap Mercur's
|
|
127
|
+
commission logic for your own, or drop a module you don't use, without a
|
|
128
|
+
refactor rippling outward. [Modules →](/resources/best-practices/modules)
|
|
116
129
|
|
|
117
130
|
### Links
|
|
118
131
|
|
|
119
|
-
A link
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
132
|
+
A link declares a relationship between records in two modules without either
|
|
133
|
+
module knowing about the other. The product–seller link, for example, is what
|
|
134
|
+
allowlists which sellers may sell a given product — expressed as a link rather
|
|
135
|
+
than a foreign key, so neither the product model nor the seller model is
|
|
136
|
+
modified. Your modules link to built-in entities exactly the same way.
|
|
137
|
+
[Links →](/resources/best-practices/module-links)
|
|
124
138
|
|
|
125
139
|
### Workflows
|
|
126
140
|
|
|
127
|
-
A workflow
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
141
|
+
A workflow is a multi-step operation with automatic compensation: if step five
|
|
142
|
+
fails, steps one through four roll back. Anything that crosses domains or must
|
|
143
|
+
not half-happen is a workflow — seller approval, payout transfer, and above all
|
|
144
|
+
cart completion, which validates the cart, splits it by seller, creates an order
|
|
145
|
+
each, allocates payment, and computes commission lines as one atomic unit.
|
|
146
|
+
|
|
147
|
+
Workflows also expose **hooks**, which is the primary backend extension zone.
|
|
148
|
+
[Workflows →](/resources/best-practices/workflows)
|
|
149
|
+
|
|
150
|
+
### Events and subscribers
|
|
133
151
|
|
|
134
|
-
|
|
152
|
+
Workflows emit events; subscribers react asynchronously. Notifications, webhooks
|
|
153
|
+
to your systems, search indexing, and payout side effects all hang off this,
|
|
154
|
+
which keeps the transactional path short and lets you add behaviour without
|
|
155
|
+
touching the workflow that triggered it.
|
|
156
|
+
[Subscribers and jobs →](/resources/best-practices/subscribers-and-jobs)
|
|
135
157
|
|
|
136
|
-
|
|
137
|
-
as sending notifications, calling webhooks, or transferring payouts. This keeps
|
|
138
|
-
the core workflows focused while the platform reacts to change.
|
|
139
|
-
[Learn about subscribers and jobs](/resources/best-practices/subscribers-and-jobs).
|
|
158
|
+
## Extension zones
|
|
140
159
|
|
|
141
|
-
|
|
160
|
+
This is the part that determines what a build actually costs. Mercur exposes six
|
|
161
|
+
zones. Each one is typed, each one is additive, and none of them require forking
|
|
162
|
+
or patching platform code.
|
|
142
163
|
|
|
143
|
-
|
|
144
|
-
|
|
164
|
+
```mermaid
|
|
165
|
+
graph LR
|
|
166
|
+
subgraph Backend
|
|
167
|
+
A[Workflow hooks<br/>inject steps into existing flows]
|
|
168
|
+
B[API routes<br/>add or override endpoints]
|
|
169
|
+
C[Modules and links<br/>add domains]
|
|
170
|
+
D[Custom fields<br/>extra data on built-in entities]
|
|
171
|
+
end
|
|
172
|
+
subgraph Panels
|
|
173
|
+
E[Pages<br/>new routes]
|
|
174
|
+
F[Widgets<br/>components in named zones]
|
|
175
|
+
G[Custom field UI<br/>forms · rows · columns]
|
|
176
|
+
H[Navigation<br/>reorder · relabel · hide]
|
|
177
|
+
end
|
|
178
|
+
```
|
|
145
179
|
|
|
146
|
-
|
|
147
|
-
- **An auditable change pipeline.** Every product edit is captured as an immutable `ProductChange` record: who changed what, and who approved it. Low-risk edits auto-confirm, and the rest wait for operator review.
|
|
148
|
-
- **Financial accuracy.** All commission arithmetic uses BigNumber with arbitrary precision, so split payments and payouts stay exact to the cent.
|
|
149
|
-
- **A governed surface for AI agents.** The typed client, exposed workflows, and `llms.txt` give AI agents structured contracts to build against, inside the same role and review guardrails as human users. Agents extend the platform. They do not bypass its governance.
|
|
150
|
-
- **You own the deployment.** Mercur is MIT-licensed and runs on infrastructure you control. Blocks ship as source code, so you own every line, with no hosted vendor in the request path and no commission on gross merchandise value.
|
|
180
|
+
### 1. Workflow hooks — change what happens
|
|
151
181
|
|
|
182
|
+
The default way to change platform behaviour. A hook is a declared point inside
|
|
183
|
+
an existing workflow where you register your own step. Your step runs inside the
|
|
184
|
+
original transaction and participates in its rollback, so you are not
|
|
185
|
+
reimplementing the flow to add one rule.
|
|
152
186
|
|
|
153
|
-
|
|
187
|
+
```ts src/workflows/hooks/seller-approved.ts
|
|
188
|
+
import { approveSellerWorkflow } from "@mercurjs/core/workflows"
|
|
189
|
+
|
|
190
|
+
approveSellerWorkflow.hooks.sellerApproved(
|
|
191
|
+
async ({ seller_id }, { container }) => {
|
|
192
|
+
await container.resolve("erp").createVendorAccount(seller_id)
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
```
|
|
154
196
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
6. Each seller's order is **credited to its payout account** after commission (Payout module).
|
|
164
|
-
7. **Events are emitted**, triggering notifications, webhook calls, and other side effects (subscribers).
|
|
165
|
-
8. Sellers **manage their orders** through the Vendor Portal (Vendor API).
|
|
166
|
-
9. The operator **monitors everything** through the Admin Panel (Admin API).
|
|
167
|
-
|
|
168
|
-
## Technology stack
|
|
169
|
-
|
|
170
|
-
| Layer | Technology |
|
|
171
|
-
| -------------------- | --------------------------------------------------- |
|
|
172
|
-
| Runtime | Node.js 20+, TypeScript |
|
|
173
|
-
| Commerce framework | Medusa v2 |
|
|
174
|
-
| Database | PostgreSQL |
|
|
175
|
-
| Frontend | React 18, React Router, Vite |
|
|
176
|
-
| Data fetching | TanStack React Query |
|
|
177
|
-
| UI components | Medusa UI, Radix UI |
|
|
178
|
-
| Form handling | React Hook Form, Zod |
|
|
179
|
-
| Tables | TanStack React Table |
|
|
180
|
-
| Build | Turborepo (monorepo), Bun (package manager), tsup |
|
|
181
|
-
| Internationalization | i18next |
|
|
182
|
-
|
|
183
|
-
## Core plugin layout
|
|
184
|
-
|
|
185
|
-
`@mercurjs/core` is the package that holds all marketplace logic. It is structured
|
|
186
|
-
as a standard Medusa plugin.
|
|
197
|
+
Reach for a hook when the answer is "the standard flow, plus something."
|
|
198
|
+
[Extend a workflow →](/resources/customization/extend-a-workflow)
|
|
199
|
+
|
|
200
|
+
### 2. API routes — add endpoints
|
|
201
|
+
|
|
202
|
+
Drop a `route.ts` under `src/api/**` and the endpoint exists. Routes nest under
|
|
203
|
+
the built-in ones, so a new resource can hang off an existing entity rather than
|
|
204
|
+
living off to the side.
|
|
187
205
|
|
|
188
206
|
```
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
│ ├── seller/ # Seller registration, profiles, members, order groups
|
|
192
|
-
│ ├── commission/ # Commission rates, rules, calculation
|
|
193
|
-
│ ├── offer/ # Seller listings against the shared product catalog
|
|
194
|
-
│ ├── payout/ # Payout accounts, onboarding, payouts
|
|
195
|
-
│ ├── product-attribute/ # Typed attribute catalog and values
|
|
196
|
-
│ ├── product-edit/ # Product change requests and audit trail
|
|
197
|
-
│ └── ... # Media, custom fields, and more
|
|
198
|
-
├── links/ # Relationships between modules
|
|
199
|
-
├── workflows/ # Multi-step business processes
|
|
200
|
-
│ ├── seller/ # Seller lifecycle workflows
|
|
201
|
-
│ ├── cart/ # Cart completion with order splitting
|
|
202
|
-
│ ├── commission/ # Commission rate and line management
|
|
203
|
-
│ ├── payout/ # Payout processing and crediting
|
|
204
|
-
│ ├── offer/ # Offer lifecycle
|
|
205
|
-
│ ├── product/ # Product approval and seller linking
|
|
206
|
-
│ ├── product-edit/ # Change-request lifecycle
|
|
207
|
-
│ ├── order-group/ # Order group operations
|
|
208
|
-
│ └── ... # Attributes, shipping, inventory, promotions
|
|
209
|
-
├── api/ # HTTP route handlers
|
|
210
|
-
│ ├── admin/ # Admin API routes
|
|
211
|
-
│ ├── vendor/ # Vendor API routes
|
|
212
|
-
│ ├── store/ # Store API routes
|
|
213
|
-
│ └── hooks/ # Webhook handlers
|
|
214
|
-
├── subscribers/ # Event listeners
|
|
215
|
-
├── providers/ # Third-party provider integrations
|
|
216
|
-
└── jobs/ # Scheduled background tasks
|
|
207
|
+
src/api/vendor/sellers/documents/route.ts → GET/POST /vendor/sellers/documents
|
|
208
|
+
src/api/vendor/sellers/documents/[id]/route.ts → GET/DELETE /vendor/sellers/documents/:id
|
|
217
209
|
```
|
|
218
210
|
|
|
219
|
-
|
|
211
|
+
Your routes get the same treatment as the built-in ones: vendor requests are
|
|
212
|
+
already scoped to the caller's seller by the surrounding middleware, and codegen
|
|
213
|
+
picks the route up so it appears on the typed client with real request and
|
|
214
|
+
response types.
|
|
215
|
+
|
|
216
|
+
[Create an API route →](/resources/best-practices/api-routes)
|
|
220
217
|
|
|
221
|
-
|
|
222
|
-
source code directly into your project, so a block is a self-contained piece of
|
|
223
|
-
functionality: a module, a workflow, an API route, or a UI extension.
|
|
218
|
+
### 3. Modules and links — add domains
|
|
224
219
|
|
|
225
|
-
|
|
220
|
+
When the data has its own lifecycle — subscriptions, RMAs, contracts, quotas —
|
|
221
|
+
it is a module, not a field. You define the models and service, link the module
|
|
222
|
+
to the built-in entities it relates to, and it becomes queryable in the same
|
|
223
|
+
graph as everything else. This is how substantial vertical features get built,
|
|
224
|
+
and it is the same mechanism Mercur's own domains use.
|
|
226
225
|
|
|
227
|
-
|
|
228
|
-
- **You can modify any block** to fit your business requirements.
|
|
229
|
-
- **There are no hidden abstractions** or version conflicts.
|
|
230
|
-
- **Updates are explicit.** You diff against the registry and apply the changes you want.
|
|
226
|
+
### 4. Custom fields — add data to built-in entities
|
|
231
227
|
|
|
232
|
-
|
|
233
|
-
|
|
228
|
+
For one plain property on an existing record — `is_featured` on a product,
|
|
229
|
+
`tier` on a customer — you declare the field in configuration and the storage,
|
|
230
|
+
link, and migration are generated for you.
|
|
234
231
|
|
|
235
|
-
|
|
232
|
+
```ts medusa-config.ts
|
|
233
|
+
{
|
|
234
|
+
resolve: "@mercurjs/core/modules/custom-fields",
|
|
235
|
+
options: {
|
|
236
|
+
customFields: {
|
|
237
|
+
Product: { is_featured: { type: "boolean", nullable: true } },
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
}
|
|
241
|
+
```
|
|
236
242
|
|
|
237
|
-
|
|
238
|
-
|
|
243
|
+
One row per parent, no lifecycle of its own; anything more is a module.
|
|
244
|
+
[Custom fields →](/resources/best-practices/custom-fields)
|
|
239
245
|
|
|
240
|
-
|
|
241
|
-
import {
|
|
242
|
-
createWorkflow,
|
|
243
|
-
createStep,
|
|
244
|
-
StepResponse,
|
|
245
|
-
WorkflowResponse,
|
|
246
|
-
} from "@medusajs/framework/workflows-sdk"
|
|
247
|
-
import { MercurModules } from "@mercurjs/types"
|
|
246
|
+
### 5. Panel pages and widgets — change what teams see
|
|
248
247
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
async ({ seller_id }: { seller_id: string }, { container }) => {
|
|
252
|
-
const sellerService = container.resolve(MercurModules.SELLER)
|
|
253
|
-
const seller = await sellerService.retrieveSeller(seller_id)
|
|
248
|
+
The panels are extended by file convention. A file's folder decides what it does,
|
|
249
|
+
and there is no manifest to maintain.
|
|
254
250
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
251
|
+
| Path | Adds |
|
|
252
|
+
| ------------------------ | ------------------------------------- |
|
|
253
|
+
| `src/routes/**/page.tsx` | A new page and its route |
|
|
254
|
+
| `src/widgets/**` | A component in a named zone on a built-in page |
|
|
255
|
+
| `src/custom-fields/**` | Form fields, detail rows, and list columns on a built-in model |
|
|
256
|
+
| `src/_navigation.ts` | Sidebar order, labels, and visibility |
|
|
258
257
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
)
|
|
258
|
+
A widget targets a slot on an existing page, which is how you put a payout
|
|
259
|
+
summary on the order detail screen without owning that screen:
|
|
262
260
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const seller = validateSellerStep({ seller_id: input.seller_id })
|
|
261
|
+
```tsx apps/vendor/src/widgets/payout-summary.tsx
|
|
262
|
+
export const config = defineWidgetConfig({ zone: "orders.detail.side.after" })
|
|
263
|
+
```
|
|
267
264
|
|
|
268
|
-
|
|
265
|
+
Zone ids, model names, and field ids are generated per panel into
|
|
266
|
+
`extension-targets.d.ts`, so a mistargeted extension fails type-checking instead
|
|
267
|
+
of silently not rendering.
|
|
268
|
+
[Panel extensions →](/references/panel-extensions/overview)
|
|
269
269
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
270
|
+
### 6. Blocks — install features as source
|
|
271
|
+
|
|
272
|
+
Larger features ship as blocks. The CLI copies the source into your project
|
|
273
|
+
rather than adding a dependency, so a block is yours from the moment it lands:
|
|
274
|
+
readable, debuggable, and modifiable without waiting on an upstream release.
|
|
275
|
+
Updates are explicit — `diff` against the registry and take what you want.
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
bunx @mercurjs/cli@latest add reviews
|
|
273
279
|
```
|
|
274
280
|
|
|
275
|
-
|
|
281
|
+
[Blocks →](/learn/blocks)
|
|
282
|
+
|
|
283
|
+
### Choosing a zone
|
|
284
|
+
|
|
285
|
+
| You want to… | Use |
|
|
286
|
+
| --------------------------------------------------- | ----------------------- |
|
|
287
|
+
| Add a rule or side effect to an existing operation | Workflow hook |
|
|
288
|
+
| Expose new data or a new operation over HTTP | API route |
|
|
289
|
+
| Model something with its own lifecycle | Module and link |
|
|
290
|
+
| Store one extra property on a built-in entity | Custom field |
|
|
291
|
+
| Show or capture something in the panels | Page, widget, or custom-field UI |
|
|
292
|
+
| Adopt a whole feature and own it | Block |
|
|
293
|
+
|
|
294
|
+
## Governance
|
|
276
295
|
|
|
277
|
-
|
|
296
|
+
Multi-vendor means untrusted writers, so the guarantees operators need are built
|
|
297
|
+
into the layers rather than layered on.
|
|
278
298
|
|
|
279
|
-
- **
|
|
280
|
-
- **
|
|
281
|
-
- **
|
|
282
|
-
- **
|
|
283
|
-
|
|
299
|
+
- **Scoping before handling.** Vendor requests are narrowed to the caller's seller in middleware, ahead of any handler. A route cannot accidentally leak across sellers, including a route you add.
|
|
300
|
+
- **An immutable change pipeline.** Product edits are recorded as `ProductChange` entries — who changed what, who approved it, when. Low-risk edits auto-confirm; the rest queue for review. The audit trail is the mechanism, not a log written beside it.
|
|
301
|
+
- **Exact money.** Commission and payout arithmetic uses arbitrary-precision numbers throughout, so splitting a payment across sellers stays exact.
|
|
302
|
+
- **Agents inside the same guardrails.** The typed client, exposed workflows, `llms.txt`, and the MCP server give AI agents structured contracts — subject to the same roles and the same review pipeline as human users.
|
|
303
|
+
|
|
304
|
+
## How a multi-vendor order flows
|
|
305
|
+
|
|
306
|
+
One cart, several sellers. This is the path that touches every layer.
|
|
307
|
+
|
|
308
|
+
1. The customer adds items from multiple sellers to a single cart (Store API).
|
|
309
|
+
2. Cart completion starts the split-order workflow.
|
|
310
|
+
3. Items are grouped by seller and an order is created per seller, all under one order group.
|
|
311
|
+
4. Commission lines are computed per order from the matching rules.
|
|
312
|
+
5. Payment is allocated proportionally across the seller orders.
|
|
313
|
+
6. Each seller's share is credited to its payout account, net of commission.
|
|
314
|
+
7. Events fire: notifications, webhooks, indexing, and any hook you registered.
|
|
315
|
+
8. Sellers fulfill their own orders in the vendor portal; the operator sees all of it in the admin panel.
|
|
316
|
+
|
|
317
|
+
Steps 2 through 6 are a single workflow. If any of it fails, none of it happened.
|
|
318
|
+
|
|
319
|
+
## Stack
|
|
320
|
+
|
|
321
|
+
| Concern | Technology |
|
|
322
|
+
| ------------------ | -------------------------------------------- |
|
|
323
|
+
| Runtime | Node.js 20+, TypeScript |
|
|
324
|
+
| Commerce engine | Medusa v2 |
|
|
325
|
+
| Database | PostgreSQL |
|
|
326
|
+
| Events, cache | Redis (in-memory fallback in development) |
|
|
327
|
+
| Panels | React 18, Vite, Medusa UI, TanStack Query |
|
|
328
|
+
| Validation, forms | Zod, React Hook Form |
|
|
329
|
+
| Build | Turborepo, Bun, tsup |
|
|
330
|
+
|
|
331
|
+
Mercur's own code is distributed as `@mercurjs/core`, a Medusa plugin registered
|
|
332
|
+
by `withMercur()` in your `medusa-config.ts`. That packaging matters when you
|
|
333
|
+
upgrade; it does not change any of the above.
|
|
284
334
|
|
|
285
335
|
## Next steps
|
|
286
336
|
|
|
@@ -288,13 +338,13 @@ These principles explain why the architecture looks the way it does.
|
|
|
288
338
|
<Card title="Platform modules" href="/platform/store/overview">
|
|
289
339
|
Data models, workflows, and events for each marketplace domain.
|
|
290
340
|
</Card>
|
|
291
|
-
<Card title="
|
|
292
|
-
|
|
341
|
+
<Card title="Panel extensions" href="/references/panel-extensions/overview">
|
|
342
|
+
Pages, widgets, custom fields, and navigation.
|
|
343
|
+
</Card>
|
|
344
|
+
<Card title="Extend a workflow" href="/resources/customization/extend-a-workflow">
|
|
345
|
+
Inject a step into an existing flow through a hook.
|
|
293
346
|
</Card>
|
|
294
347
|
<Card title="API reference" href="/references/api/conventions">
|
|
295
|
-
Authentication, seller scoping, and the
|
|
296
|
-
</Card>
|
|
297
|
-
<Card title="Panel extensions" href="/references/panel-extensions/overview">
|
|
298
|
-
Extend the admin and vendor panels without forking them.
|
|
348
|
+
Authentication, seller scoping, and the three API surfaces.
|
|
299
349
|
</Card>
|
|
300
350
|
</CardGroup>
|
package/llms.txt
CHANGED
|
@@ -7,7 +7,7 @@ package (`node_modules/@mercurjs/docs/`).
|
|
|
7
7
|
|
|
8
8
|
## Learn — concepts and getting started
|
|
9
9
|
|
|
10
|
-
- [Architecture](content/learn/architecture.mdx) — How the
|
|
10
|
+
- [Architecture](content/learn/architecture.mdx) — How Mercur is put together: the runtime layers, the boundary between the marketplace platform and your own code, and the zones you extend.
|
|
11
11
|
- [Product Attributes](content/learn/attributes.mdx) — Typed, filterable product attributes on top of Medusa's native product options — including variant axes shared across the catalog.
|
|
12
12
|
- [Blocks](content/learn/blocks.mdx) — Self-contained pieces of marketplace functionality that get copied into your project as source code.
|
|
13
13
|
- [Commission](content/learn/commissions.mdx) — How marketplace commissions work in Mercur — rates, rules, the matching algorithm, and commission lines.
|