@mercurjs/dashboard-sdk 2.3.6-canary.0 → 2.3.6-canary.10

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/dist/index.cjs CHANGED
@@ -20,12 +20,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- OPERATION_IMPLICATIONS: () => OPERATION_IMPLICATIONS,
23
+ PERMISSION_RIGHT_RANK: () => PERMISSION_RIGHT_RANK,
24
24
  buildPermission: () => buildPermission,
25
25
  defineCustomFieldsConfig: () => defineCustomFieldsConfig,
26
26
  defineNavigationConfig: () => defineNavigationConfig,
27
27
  defineWidgetConfig: () => defineWidgetConfig,
28
- parsePermission: () => parsePermission
28
+ parsePermission: () => parsePermission,
29
+ satisfiesPermission: () => satisfiesPermission
29
30
  });
30
31
  module.exports = __toCommonJS(index_exports);
31
32
 
@@ -47,33 +48,39 @@ function defineCustomFieldsConfig(config) {
47
48
  }
48
49
 
49
50
  // src/permissions.ts
50
- var OPERATION_IMPLICATIONS = {
51
- read: ["read"],
52
- create: ["create"],
53
- update: ["update"],
54
- delete: ["delete"],
55
- "*": ["read", "create", "update", "delete", "*"]
51
+ var PERMISSION_RIGHT_RANK = {
52
+ view: 1,
53
+ edit: 2,
54
+ manage: 3
56
55
  };
57
56
  function parsePermission(permission) {
58
- const parts = permission.split(":");
59
- if (parts.length !== 2) {
57
+ const index = permission.lastIndexOf(":");
58
+ if (index <= 0) {
59
+ return null;
60
+ }
61
+ const right = permission.slice(index + 1);
62
+ if (!(right in PERMISSION_RIGHT_RANK)) {
60
63
  return null;
61
64
  }
62
- const [resource, operation] = parts;
63
65
  return {
64
- resource,
65
- operation
66
+ key: permission.slice(0, index),
67
+ right
66
68
  };
67
69
  }
68
- function buildPermission(resource, operation) {
69
- return `${resource}:${operation}`;
70
+ function buildPermission(key, right) {
71
+ return `${key}:${right}`;
72
+ }
73
+ function satisfiesPermission(permissions, key, right) {
74
+ const granted = permissions[key];
75
+ return !!granted && PERMISSION_RIGHT_RANK[granted] >= PERMISSION_RIGHT_RANK[right];
70
76
  }
71
77
  // Annotate the CommonJS export names for ESM import in node:
72
78
  0 && (module.exports = {
73
- OPERATION_IMPLICATIONS,
79
+ PERMISSION_RIGHT_RANK,
74
80
  buildPermission,
75
81
  defineCustomFieldsConfig,
76
82
  defineNavigationConfig,
77
83
  defineWidgetConfig,
78
- parsePermission
84
+ parsePermission,
85
+ satisfiesPermission
79
86
  });
package/dist/index.d.cts CHANGED
@@ -1,59 +1,6 @@
1
- export { B as BuiltMercurConfig, M as MercurConfig, O as OPERATION_IMPLICATIONS, P as Permission, a as PermissionOperation, b as PermissionRequirement, c as PermissionResource, d as PermissionsContextValue, e as PermissionsRequirementsContextValue, R as RouteConfig, f as RouteHandle, U as UserPolicy, g as buildPermission, p as parsePermission } from './types-CTuDKgPc.cjs';
2
- import { ComponentType, ReactNode } from 'react';
3
-
4
- /**
5
- * Open registry interfaces. Each panel package ships a generated
6
- * `extension-targets.d.ts` that seeds the built-in ids into these interfaces
7
- * (declaration merging), and a developer augments them by hand to register a
8
- * zone their own page renders. The helper unions read off the interface keys.
9
- *
10
- * @example
11
- * declare module "@mercurjs/dashboard-sdk" {
12
- * interface WidgetZoneRegistry {
13
- * "erp.dashboard.before": true
14
- * }
15
- * }
16
- */
17
- interface WidgetZoneRegistry {
18
- }
19
- interface NavItemRegistry {
20
- }
21
- interface NavParentRegistry {
22
- }
23
- type WidgetZoneId = keyof WidgetZoneRegistry extends never ? string : keyof WidgetZoneRegistry;
24
- type NavItemId = keyof NavItemRegistry extends never ? string : keyof NavItemRegistry;
25
- type NavParentId = keyof NavParentRegistry extends never ? string : keyof NavParentRegistry;
26
- /**
27
- * A widget is a React component attached to a named zone on a page. The
28
- * placement (`before | after`) is encoded as the zone-id suffix, so there is no
29
- * separate rank field — multiple `before`/`after` widgets stack in registration
30
- * order.
31
- */
32
- interface WidgetConfig {
33
- zone: WidgetZoneId | WidgetZoneId[];
34
- /** Stable id; derived from the file path at build time when omitted. */
35
- id?: string;
36
- }
37
- /** Override for a single built-in navigation item. */
38
- interface NavItemOverride {
39
- id: NavItemId;
40
- /** Order within the item's parent (or among top-level items). */
41
- rank?: number;
42
- /** Remove from the sidebar (route may still be reachable directly). */
43
- hidden?: boolean;
44
- /** i18n key or literal replacing the item's label. */
45
- label?: string;
46
- /** Icon component replacing the item's icon. */
47
- icon?: ComponentType;
48
- /**
49
- * Re-parent the item: a built-in parent id moves it under that parent's
50
- * children; `null` promotes a nested item to the top level.
51
- */
52
- nested?: NavParentId | null;
53
- }
54
- interface NavigationConfig {
55
- items: NavItemOverride[];
56
- }
1
+ import { h as NavigationConfig, W as WidgetConfig } from './types-BTgTMD_t.cjs';
2
+ export { B as BuiltMercurConfig, M as MercurConfig, N as NavGroupConfig, a as NavGroupId, b as NavGroupRegistry, c as NavItemId, d as NavItemOverride, e as NavItemRegistry, f as NavParentId, g as NavParentRegistry, P as PERMISSION_RIGHT_RANK, i as Permission, j as PermissionKey, k as PermissionKeys, l as PermissionMap, m as PermissionRight, n as PermissionsContextValue, R as RouteConfig, o as RouteHandle, p as WidgetZoneId, q as WidgetZoneRegistry, r as buildPermission, s as parsePermission, t as satisfiesPermission } from './types-BTgTMD_t.cjs';
3
+ import { ReactNode, ComponentType } from 'react';
57
4
 
58
5
  /**
59
6
  * Minimal structural stand-in for a Zod schema, so the SDK stays free of a zod
@@ -189,7 +136,7 @@ interface CustomFieldsConfig<TModel extends CustomFieldModel = CustomFieldModel>
189
136
  }
190
137
 
191
138
  declare function defineWidgetConfig(config: WidgetConfig): WidgetConfig;
192
- declare function defineNavigationConfig(config: NavigationConfig): NavigationConfig;
139
+ declare function defineNavigationConfig<TGroup extends string = never>(config: NavigationConfig<TGroup>): NavigationConfig<TGroup>;
193
140
  declare function defineCustomFieldsConfig<TModel extends CustomFieldModel>(config: CustomFieldsConfig<TModel>): CustomFieldsConfig<TModel>;
194
141
 
195
- export { type ActivityEntry, type CustomColumn, type CustomDisplayEntry, type CustomDisplayField, type CustomFieldAction, type CustomFieldActionRegistry, type CustomFieldModel, type CustomFieldsConfig, type CustomFieldsRegistry, type CustomFormEntry, type CustomFormField, type CustomFormTab, type CustomFormZone, type CustomListExtension, type FieldValidation, type NavItemId, type NavItemOverride, type NavItemRegistry, type NavParentId, type NavParentRegistry, type NavigationConfig, type SectionAction, type WidgetConfig, type WidgetZoneId, type WidgetZoneRegistry, defineCustomFieldsConfig, defineNavigationConfig, defineWidgetConfig };
142
+ export { type ActivityEntry, type CustomColumn, type CustomDisplayEntry, type CustomDisplayField, type CustomFieldAction, type CustomFieldActionRegistry, type CustomFieldModel, type CustomFieldsConfig, type CustomFieldsRegistry, type CustomFormEntry, type CustomFormField, type CustomFormTab, type CustomFormZone, type CustomListExtension, type FieldValidation, NavigationConfig, type SectionAction, WidgetConfig, defineCustomFieldsConfig, defineNavigationConfig, defineWidgetConfig };
package/dist/index.d.ts CHANGED
@@ -1,59 +1,6 @@
1
- export { B as BuiltMercurConfig, M as MercurConfig, O as OPERATION_IMPLICATIONS, P as Permission, a as PermissionOperation, b as PermissionRequirement, c as PermissionResource, d as PermissionsContextValue, e as PermissionsRequirementsContextValue, R as RouteConfig, f as RouteHandle, U as UserPolicy, g as buildPermission, p as parsePermission } from './types-CTuDKgPc.js';
2
- import { ComponentType, ReactNode } from 'react';
3
-
4
- /**
5
- * Open registry interfaces. Each panel package ships a generated
6
- * `extension-targets.d.ts` that seeds the built-in ids into these interfaces
7
- * (declaration merging), and a developer augments them by hand to register a
8
- * zone their own page renders. The helper unions read off the interface keys.
9
- *
10
- * @example
11
- * declare module "@mercurjs/dashboard-sdk" {
12
- * interface WidgetZoneRegistry {
13
- * "erp.dashboard.before": true
14
- * }
15
- * }
16
- */
17
- interface WidgetZoneRegistry {
18
- }
19
- interface NavItemRegistry {
20
- }
21
- interface NavParentRegistry {
22
- }
23
- type WidgetZoneId = keyof WidgetZoneRegistry extends never ? string : keyof WidgetZoneRegistry;
24
- type NavItemId = keyof NavItemRegistry extends never ? string : keyof NavItemRegistry;
25
- type NavParentId = keyof NavParentRegistry extends never ? string : keyof NavParentRegistry;
26
- /**
27
- * A widget is a React component attached to a named zone on a page. The
28
- * placement (`before | after`) is encoded as the zone-id suffix, so there is no
29
- * separate rank field — multiple `before`/`after` widgets stack in registration
30
- * order.
31
- */
32
- interface WidgetConfig {
33
- zone: WidgetZoneId | WidgetZoneId[];
34
- /** Stable id; derived from the file path at build time when omitted. */
35
- id?: string;
36
- }
37
- /** Override for a single built-in navigation item. */
38
- interface NavItemOverride {
39
- id: NavItemId;
40
- /** Order within the item's parent (or among top-level items). */
41
- rank?: number;
42
- /** Remove from the sidebar (route may still be reachable directly). */
43
- hidden?: boolean;
44
- /** i18n key or literal replacing the item's label. */
45
- label?: string;
46
- /** Icon component replacing the item's icon. */
47
- icon?: ComponentType;
48
- /**
49
- * Re-parent the item: a built-in parent id moves it under that parent's
50
- * children; `null` promotes a nested item to the top level.
51
- */
52
- nested?: NavParentId | null;
53
- }
54
- interface NavigationConfig {
55
- items: NavItemOverride[];
56
- }
1
+ import { h as NavigationConfig, W as WidgetConfig } from './types-BTgTMD_t.js';
2
+ export { B as BuiltMercurConfig, M as MercurConfig, N as NavGroupConfig, a as NavGroupId, b as NavGroupRegistry, c as NavItemId, d as NavItemOverride, e as NavItemRegistry, f as NavParentId, g as NavParentRegistry, P as PERMISSION_RIGHT_RANK, i as Permission, j as PermissionKey, k as PermissionKeys, l as PermissionMap, m as PermissionRight, n as PermissionsContextValue, R as RouteConfig, o as RouteHandle, p as WidgetZoneId, q as WidgetZoneRegistry, r as buildPermission, s as parsePermission, t as satisfiesPermission } from './types-BTgTMD_t.js';
3
+ import { ReactNode, ComponentType } from 'react';
57
4
 
58
5
  /**
59
6
  * Minimal structural stand-in for a Zod schema, so the SDK stays free of a zod
@@ -189,7 +136,7 @@ interface CustomFieldsConfig<TModel extends CustomFieldModel = CustomFieldModel>
189
136
  }
190
137
 
191
138
  declare function defineWidgetConfig(config: WidgetConfig): WidgetConfig;
192
- declare function defineNavigationConfig(config: NavigationConfig): NavigationConfig;
139
+ declare function defineNavigationConfig<TGroup extends string = never>(config: NavigationConfig<TGroup>): NavigationConfig<TGroup>;
193
140
  declare function defineCustomFieldsConfig<TModel extends CustomFieldModel>(config: CustomFieldsConfig<TModel>): CustomFieldsConfig<TModel>;
194
141
 
195
- export { type ActivityEntry, type CustomColumn, type CustomDisplayEntry, type CustomDisplayField, type CustomFieldAction, type CustomFieldActionRegistry, type CustomFieldModel, type CustomFieldsConfig, type CustomFieldsRegistry, type CustomFormEntry, type CustomFormField, type CustomFormTab, type CustomFormZone, type CustomListExtension, type FieldValidation, type NavItemId, type NavItemOverride, type NavItemRegistry, type NavParentId, type NavParentRegistry, type NavigationConfig, type SectionAction, type WidgetConfig, type WidgetZoneId, type WidgetZoneRegistry, defineCustomFieldsConfig, defineNavigationConfig, defineWidgetConfig };
142
+ export { type ActivityEntry, type CustomColumn, type CustomDisplayEntry, type CustomDisplayField, type CustomFieldAction, type CustomFieldActionRegistry, type CustomFieldModel, type CustomFieldsConfig, type CustomFieldsRegistry, type CustomFormEntry, type CustomFormField, type CustomFormTab, type CustomFormZone, type CustomListExtension, type FieldValidation, NavigationConfig, type SectionAction, WidgetConfig, defineCustomFieldsConfig, defineNavigationConfig, defineWidgetConfig };
package/dist/index.js CHANGED
@@ -18,32 +18,38 @@ function defineCustomFieldsConfig(config) {
18
18
  }
19
19
 
20
20
  // src/permissions.ts
21
- var OPERATION_IMPLICATIONS = {
22
- read: ["read"],
23
- create: ["create"],
24
- update: ["update"],
25
- delete: ["delete"],
26
- "*": ["read", "create", "update", "delete", "*"]
21
+ var PERMISSION_RIGHT_RANK = {
22
+ view: 1,
23
+ edit: 2,
24
+ manage: 3
27
25
  };
28
26
  function parsePermission(permission) {
29
- const parts = permission.split(":");
30
- if (parts.length !== 2) {
27
+ const index = permission.lastIndexOf(":");
28
+ if (index <= 0) {
29
+ return null;
30
+ }
31
+ const right = permission.slice(index + 1);
32
+ if (!(right in PERMISSION_RIGHT_RANK)) {
31
33
  return null;
32
34
  }
33
- const [resource, operation] = parts;
34
35
  return {
35
- resource,
36
- operation
36
+ key: permission.slice(0, index),
37
+ right
37
38
  };
38
39
  }
39
- function buildPermission(resource, operation) {
40
- return `${resource}:${operation}`;
40
+ function buildPermission(key, right) {
41
+ return `${key}:${right}`;
42
+ }
43
+ function satisfiesPermission(permissions, key, right) {
44
+ const granted = permissions[key];
45
+ return !!granted && PERMISSION_RIGHT_RANK[granted] >= PERMISSION_RIGHT_RANK[right];
41
46
  }
42
47
  export {
43
- OPERATION_IMPLICATIONS,
48
+ PERMISSION_RIGHT_RANK,
44
49
  buildPermission,
45
50
  defineCustomFieldsConfig,
46
51
  defineNavigationConfig,
47
52
  defineWidgetConfig,
48
- parsePermission
53
+ parsePermission,
54
+ satisfiesPermission
49
55
  };
@@ -0,0 +1,282 @@
1
+ import { ComponentType, ReactNode } from 'react';
2
+
3
+ /**
4
+ * Permission strings follow `{key}:{right}`, e.g. `orders:view`,
5
+ * `products:edit`, `sellers.approval:edit`.
6
+ *
7
+ * Keys mirror the catalog declared server-side with `defineMercurPermissions`
8
+ * (`packages/core/src/permissions/definitions`). Rights nest:
9
+ * `manage` implies `edit`, `edit` implies `view`.
10
+ */
11
+ /**
12
+ * Registry of permission keys. Plugins add their own keys through declaration
13
+ * merging:
14
+ *
15
+ * @example
16
+ * declare module "@mercurjs/dashboard-sdk" {
17
+ * interface PermissionKeys {
18
+ * messaging: true
19
+ * }
20
+ * }
21
+ */
22
+ interface PermissionKeys {
23
+ "sellers": true;
24
+ "sellers.approval": true;
25
+ "sellers.premium": true;
26
+ "members": true;
27
+ "members.invites": true;
28
+ "roles": true;
29
+ "roles.owner": true;
30
+ "orders": true;
31
+ "orders.refunds": true;
32
+ "orders.returns": true;
33
+ "orders.edits": true;
34
+ "order_groups": true;
35
+ "payments": true;
36
+ "customers": true;
37
+ "customer_groups": true;
38
+ "products": true;
39
+ "products.review": true;
40
+ "product_changes": true;
41
+ "product_categories": true;
42
+ "product_collections": true;
43
+ "product_types": true;
44
+ "product_tags": true;
45
+ "product_attributes": true;
46
+ "offers": true;
47
+ "inventory_items": true;
48
+ "reservations": true;
49
+ "stock_locations": true;
50
+ "price_lists": true;
51
+ "price_preferences": true;
52
+ "promotions": true;
53
+ "campaigns": true;
54
+ "commission_rates": true;
55
+ "commission_lines": true;
56
+ "payouts": true;
57
+ "payout_accounts": true;
58
+ "shipping_profiles": true;
59
+ "shipping_options": true;
60
+ "fulfillment_sets": true;
61
+ "regions": true;
62
+ "tax_regions": true;
63
+ "store": true;
64
+ "sales_channels": true;
65
+ "return_reasons": true;
66
+ "refund_reasons": true;
67
+ "users": true;
68
+ "api_keys": true;
69
+ "translations": true;
70
+ "notifications": true;
71
+ "workflow_executions": true;
72
+ "reviews": true;
73
+ }
74
+ type PermissionKey = Extract<keyof PermissionKeys, string>;
75
+ type PermissionRight = "view" | "edit" | "manage";
76
+ type Permission = `${PermissionKey}:${PermissionRight}`;
77
+ /** Effective rights of the acting user, as returned by `fields=+permissions`. */
78
+ type PermissionMap = Partial<Record<string, PermissionRight>>;
79
+ declare const PERMISSION_RIGHT_RANK: Record<PermissionRight, number>;
80
+ interface PermissionsContextValue {
81
+ /** `null` while no access-control module is enforcing permissions. */
82
+ permissions: PermissionMap | null;
83
+ isLoading: boolean;
84
+ /** When `false`, every check resolves to `true`. */
85
+ isEnforced: boolean;
86
+ hasPermission: (permission: Permission) => boolean;
87
+ hasAnyPermission: (permissions: Permission[]) => boolean;
88
+ hasAllPermissions: (permissions: Permission[]) => boolean;
89
+ can: (key: PermissionKey, right?: PermissionRight) => boolean;
90
+ }
91
+ declare function parsePermission(permission: string): {
92
+ key: PermissionKey;
93
+ right: PermissionRight;
94
+ } | null;
95
+ declare function buildPermission(key: PermissionKey, right: PermissionRight): Permission;
96
+ declare function satisfiesPermission(permissions: PermissionMap, key: string, right: PermissionRight): boolean;
97
+
98
+ /**
99
+ * Open registry interfaces. Each panel package ships a generated
100
+ * `extension-targets.d.ts` that seeds the built-in ids into these interfaces
101
+ * (declaration merging), and a developer augments them by hand to register a
102
+ * zone their own page renders. The helper unions read off the interface keys.
103
+ *
104
+ * @example
105
+ * declare module "@mercurjs/dashboard-sdk" {
106
+ * interface WidgetZoneRegistry {
107
+ * "erp.dashboard.before": true
108
+ * }
109
+ * }
110
+ */
111
+ interface WidgetZoneRegistry {
112
+ }
113
+ interface NavItemRegistry {
114
+ }
115
+ interface NavParentRegistry {
116
+ }
117
+ interface NavGroupRegistry {
118
+ }
119
+ type WidgetZoneId = keyof WidgetZoneRegistry extends never ? string : keyof WidgetZoneRegistry;
120
+ type NavItemId = keyof NavItemRegistry extends never ? string : keyof NavItemRegistry;
121
+ type NavParentId = keyof NavParentRegistry extends never ? string : keyof NavParentRegistry;
122
+ type NavGroupId = keyof NavGroupRegistry extends never ? string : keyof NavGroupRegistry;
123
+ /**
124
+ * A widget is a React component attached to a named zone on a page. The
125
+ * placement (`before | after`) is encoded as the zone-id suffix, so there is no
126
+ * separate rank field — multiple `before`/`after` widgets stack in registration
127
+ * order.
128
+ */
129
+ interface WidgetConfig {
130
+ zone: WidgetZoneId | WidgetZoneId[];
131
+ /** Stable id; derived from the file path at build time when omitted. */
132
+ id?: string;
133
+ /**
134
+ * Permission(s) the actor needs for the widget to render. With several, any
135
+ * one is enough.
136
+ */
137
+ permission?: Permission | Permission[];
138
+ /**
139
+ * i18n key or literal naming the tab. Required for a `*.tabs.*` zone, where
140
+ * the widget renders as a tab's panel; ignored elsewhere.
141
+ */
142
+ label?: string;
143
+ }
144
+ /** Override for a single built-in navigation item. */
145
+ interface NavItemOverride<TGroup extends string = never> {
146
+ id: NavItemId;
147
+ /** Order within the item's parent (or among top-level items). */
148
+ rank?: number;
149
+ /** Remove from the sidebar (route may still be reachable directly). */
150
+ hidden?: boolean;
151
+ /** i18n key or literal replacing the item's label. */
152
+ label?: string;
153
+ /** Icon component replacing the item's icon. */
154
+ icon?: ComponentType;
155
+ /**
156
+ * Re-parent the item: a built-in parent id moves it under that parent's
157
+ * children; `null` promotes a nested item to the top level.
158
+ */
159
+ nested?: NavParentId | null;
160
+ /**
161
+ * List the item under a group heading: a built-in settings group id or one
162
+ * declared in `groups`. Main sidebar items only join declared groups.
163
+ */
164
+ group?: NavGroupId | TGroup;
165
+ }
166
+ /**
167
+ * Declares a sidebar group, or overrides a built-in settings group by id. A
168
+ * group renders in whichever sidebar its items live in.
169
+ */
170
+ interface NavGroupConfig<TId extends string = string> {
171
+ id: NavGroupId | TId;
172
+ /** i18n key (with `translationNs`) or literal. Falls back to the id. */
173
+ label?: string;
174
+ translationNs?: string;
175
+ /** Order among groups, lower first. Built-in settings groups rank 0, 1, 2… */
176
+ rank?: number;
177
+ /** Remove the group and its items from the sidebar. */
178
+ hidden?: boolean;
179
+ }
180
+ interface NavigationConfig<TGroup extends string = never> {
181
+ groups?: NavGroupConfig<TGroup>[];
182
+ items?: NavItemOverride<NoInfer<TGroup>>[];
183
+ }
184
+
185
+ /**
186
+ * Configuration for the Mercur dashboard plugin.
187
+ *
188
+ * Passed to `mercurDashboardPlugin()` in `vite.config.ts` to configure
189
+ * the vendor or admin dashboard at build time.
190
+ *
191
+ * @example
192
+ * ```ts
193
+ * mercurDashboardPlugin({
194
+ * medusaConfigPath: '../../packages/api/medusa-config.ts',
195
+ * enableSellerRegistration: true,
196
+ * })
197
+ * ```
198
+ */
199
+ interface MercurConfig {
200
+ /** Path to the MedusaJS config file, resolved relative to the project root. */
201
+ medusaConfigPath: string;
202
+ /**
203
+ * Backend URL for API requests.
204
+ * @default "http://localhost:9000"
205
+ */
206
+ backendUrl?: string;
207
+ /**
208
+ * Absolute base URL for the vendor dashboard, including any path prefix
209
+ * (for example `https://vendors.example.com/seller`).
210
+ *
211
+ * The plugin does not read `.env` files on its own. Load env in your
212
+ * app's `vite.config.ts` and pass the value here when you need
213
+ * cross-origin links such as seller invite URLs.
214
+ *
215
+ * If omitted, the plugin falls back to vendor module dev server
216
+ * detection in development and to the configured vendor path in
217
+ * production.
218
+ */
219
+ vendorUrl?: string;
220
+ /** Dashboard display name. */
221
+ name?: string;
222
+ /** Path to a logo asset for the dashboard. */
223
+ logo?: string;
224
+ /** Internationalization settings. */
225
+ i18n?: {
226
+ /** The default language code (e.g. `"en"`). */
227
+ defaultLanguage: string;
228
+ };
229
+ /** Whether to allow new sellers to register from the login screen. */
230
+ enableSellerRegistration?: boolean;
231
+ /**
232
+ * Maximum allowed size for image uploads, in bytes.
233
+ * Used by dashboard upload components to reject oversized files
234
+ * before they are sent to the backend.
235
+ * @default 2_097_152 // 2 MB
236
+ */
237
+ imageLimit?: number;
238
+ }
239
+ interface BuiltMercurConfig extends MercurConfig {
240
+ backendUrl: string;
241
+ base?: string;
242
+ root: string;
243
+ srcDir: string;
244
+ pluginExtensions: string[];
245
+ imageLimit: number;
246
+ }
247
+ type RouteConfig = {
248
+ label: string;
249
+ icon?: ComponentType;
250
+ rank?: number;
251
+ nested?: string;
252
+ /**
253
+ * Sidebar group to list the route under: one declared in `_navigation.ts`
254
+ * or, for a `/settings/*` route, a built-in settings group. Must be a
255
+ * string literal.
256
+ */
257
+ group?: NavGroupId | (string & {});
258
+ translationNs?: string;
259
+ public?: boolean;
260
+ /**
261
+ * Permissions the actor must hold for this route's sidebar entry to be
262
+ * shown. Enforcement of the route itself comes from `handle.permissions`
263
+ * — this only controls navigation visibility.
264
+ */
265
+ permissions?: Permission[];
266
+ /** If true, ALL `permissions` are required. Defaults to ANY. */
267
+ requireAll?: boolean;
268
+ };
269
+ /**
270
+ * The value of a route module's `export const handle`. Read by react-router's
271
+ * `useMatches()`; `permissions` is enforced by `RoutePermissionGuard`.
272
+ */
273
+ type RouteHandle = {
274
+ permissions?: Permission | Permission[];
275
+ /** If true (default at the route level), ALL permissions are required. */
276
+ requireAll?: boolean;
277
+ /** Where to send the actor on denial. Renders Access Denied when unset. */
278
+ redirectTo?: string;
279
+ breadcrumb?: (match: never) => ReactNode;
280
+ };
281
+
282
+ export { type BuiltMercurConfig as B, type MercurConfig as M, type NavGroupConfig as N, PERMISSION_RIGHT_RANK as P, type RouteConfig as R, type WidgetConfig as W, type NavGroupId as a, type NavGroupRegistry as b, type NavItemId as c, type NavItemOverride as d, type NavItemRegistry as e, type NavParentId as f, type NavParentRegistry as g, type NavigationConfig as h, type Permission as i, type PermissionKey as j, type PermissionKeys as k, type PermissionMap as l, type PermissionRight as m, type PermissionsContextValue as n, type RouteHandle as o, type WidgetZoneId as p, type WidgetZoneRegistry as q, buildPermission as r, parsePermission as s, satisfiesPermission as t };
@@ -0,0 +1,282 @@
1
+ import { ComponentType, ReactNode } from 'react';
2
+
3
+ /**
4
+ * Permission strings follow `{key}:{right}`, e.g. `orders:view`,
5
+ * `products:edit`, `sellers.approval:edit`.
6
+ *
7
+ * Keys mirror the catalog declared server-side with `defineMercurPermissions`
8
+ * (`packages/core/src/permissions/definitions`). Rights nest:
9
+ * `manage` implies `edit`, `edit` implies `view`.
10
+ */
11
+ /**
12
+ * Registry of permission keys. Plugins add their own keys through declaration
13
+ * merging:
14
+ *
15
+ * @example
16
+ * declare module "@mercurjs/dashboard-sdk" {
17
+ * interface PermissionKeys {
18
+ * messaging: true
19
+ * }
20
+ * }
21
+ */
22
+ interface PermissionKeys {
23
+ "sellers": true;
24
+ "sellers.approval": true;
25
+ "sellers.premium": true;
26
+ "members": true;
27
+ "members.invites": true;
28
+ "roles": true;
29
+ "roles.owner": true;
30
+ "orders": true;
31
+ "orders.refunds": true;
32
+ "orders.returns": true;
33
+ "orders.edits": true;
34
+ "order_groups": true;
35
+ "payments": true;
36
+ "customers": true;
37
+ "customer_groups": true;
38
+ "products": true;
39
+ "products.review": true;
40
+ "product_changes": true;
41
+ "product_categories": true;
42
+ "product_collections": true;
43
+ "product_types": true;
44
+ "product_tags": true;
45
+ "product_attributes": true;
46
+ "offers": true;
47
+ "inventory_items": true;
48
+ "reservations": true;
49
+ "stock_locations": true;
50
+ "price_lists": true;
51
+ "price_preferences": true;
52
+ "promotions": true;
53
+ "campaigns": true;
54
+ "commission_rates": true;
55
+ "commission_lines": true;
56
+ "payouts": true;
57
+ "payout_accounts": true;
58
+ "shipping_profiles": true;
59
+ "shipping_options": true;
60
+ "fulfillment_sets": true;
61
+ "regions": true;
62
+ "tax_regions": true;
63
+ "store": true;
64
+ "sales_channels": true;
65
+ "return_reasons": true;
66
+ "refund_reasons": true;
67
+ "users": true;
68
+ "api_keys": true;
69
+ "translations": true;
70
+ "notifications": true;
71
+ "workflow_executions": true;
72
+ "reviews": true;
73
+ }
74
+ type PermissionKey = Extract<keyof PermissionKeys, string>;
75
+ type PermissionRight = "view" | "edit" | "manage";
76
+ type Permission = `${PermissionKey}:${PermissionRight}`;
77
+ /** Effective rights of the acting user, as returned by `fields=+permissions`. */
78
+ type PermissionMap = Partial<Record<string, PermissionRight>>;
79
+ declare const PERMISSION_RIGHT_RANK: Record<PermissionRight, number>;
80
+ interface PermissionsContextValue {
81
+ /** `null` while no access-control module is enforcing permissions. */
82
+ permissions: PermissionMap | null;
83
+ isLoading: boolean;
84
+ /** When `false`, every check resolves to `true`. */
85
+ isEnforced: boolean;
86
+ hasPermission: (permission: Permission) => boolean;
87
+ hasAnyPermission: (permissions: Permission[]) => boolean;
88
+ hasAllPermissions: (permissions: Permission[]) => boolean;
89
+ can: (key: PermissionKey, right?: PermissionRight) => boolean;
90
+ }
91
+ declare function parsePermission(permission: string): {
92
+ key: PermissionKey;
93
+ right: PermissionRight;
94
+ } | null;
95
+ declare function buildPermission(key: PermissionKey, right: PermissionRight): Permission;
96
+ declare function satisfiesPermission(permissions: PermissionMap, key: string, right: PermissionRight): boolean;
97
+
98
+ /**
99
+ * Open registry interfaces. Each panel package ships a generated
100
+ * `extension-targets.d.ts` that seeds the built-in ids into these interfaces
101
+ * (declaration merging), and a developer augments them by hand to register a
102
+ * zone their own page renders. The helper unions read off the interface keys.
103
+ *
104
+ * @example
105
+ * declare module "@mercurjs/dashboard-sdk" {
106
+ * interface WidgetZoneRegistry {
107
+ * "erp.dashboard.before": true
108
+ * }
109
+ * }
110
+ */
111
+ interface WidgetZoneRegistry {
112
+ }
113
+ interface NavItemRegistry {
114
+ }
115
+ interface NavParentRegistry {
116
+ }
117
+ interface NavGroupRegistry {
118
+ }
119
+ type WidgetZoneId = keyof WidgetZoneRegistry extends never ? string : keyof WidgetZoneRegistry;
120
+ type NavItemId = keyof NavItemRegistry extends never ? string : keyof NavItemRegistry;
121
+ type NavParentId = keyof NavParentRegistry extends never ? string : keyof NavParentRegistry;
122
+ type NavGroupId = keyof NavGroupRegistry extends never ? string : keyof NavGroupRegistry;
123
+ /**
124
+ * A widget is a React component attached to a named zone on a page. The
125
+ * placement (`before | after`) is encoded as the zone-id suffix, so there is no
126
+ * separate rank field — multiple `before`/`after` widgets stack in registration
127
+ * order.
128
+ */
129
+ interface WidgetConfig {
130
+ zone: WidgetZoneId | WidgetZoneId[];
131
+ /** Stable id; derived from the file path at build time when omitted. */
132
+ id?: string;
133
+ /**
134
+ * Permission(s) the actor needs for the widget to render. With several, any
135
+ * one is enough.
136
+ */
137
+ permission?: Permission | Permission[];
138
+ /**
139
+ * i18n key or literal naming the tab. Required for a `*.tabs.*` zone, where
140
+ * the widget renders as a tab's panel; ignored elsewhere.
141
+ */
142
+ label?: string;
143
+ }
144
+ /** Override for a single built-in navigation item. */
145
+ interface NavItemOverride<TGroup extends string = never> {
146
+ id: NavItemId;
147
+ /** Order within the item's parent (or among top-level items). */
148
+ rank?: number;
149
+ /** Remove from the sidebar (route may still be reachable directly). */
150
+ hidden?: boolean;
151
+ /** i18n key or literal replacing the item's label. */
152
+ label?: string;
153
+ /** Icon component replacing the item's icon. */
154
+ icon?: ComponentType;
155
+ /**
156
+ * Re-parent the item: a built-in parent id moves it under that parent's
157
+ * children; `null` promotes a nested item to the top level.
158
+ */
159
+ nested?: NavParentId | null;
160
+ /**
161
+ * List the item under a group heading: a built-in settings group id or one
162
+ * declared in `groups`. Main sidebar items only join declared groups.
163
+ */
164
+ group?: NavGroupId | TGroup;
165
+ }
166
+ /**
167
+ * Declares a sidebar group, or overrides a built-in settings group by id. A
168
+ * group renders in whichever sidebar its items live in.
169
+ */
170
+ interface NavGroupConfig<TId extends string = string> {
171
+ id: NavGroupId | TId;
172
+ /** i18n key (with `translationNs`) or literal. Falls back to the id. */
173
+ label?: string;
174
+ translationNs?: string;
175
+ /** Order among groups, lower first. Built-in settings groups rank 0, 1, 2… */
176
+ rank?: number;
177
+ /** Remove the group and its items from the sidebar. */
178
+ hidden?: boolean;
179
+ }
180
+ interface NavigationConfig<TGroup extends string = never> {
181
+ groups?: NavGroupConfig<TGroup>[];
182
+ items?: NavItemOverride<NoInfer<TGroup>>[];
183
+ }
184
+
185
+ /**
186
+ * Configuration for the Mercur dashboard plugin.
187
+ *
188
+ * Passed to `mercurDashboardPlugin()` in `vite.config.ts` to configure
189
+ * the vendor or admin dashboard at build time.
190
+ *
191
+ * @example
192
+ * ```ts
193
+ * mercurDashboardPlugin({
194
+ * medusaConfigPath: '../../packages/api/medusa-config.ts',
195
+ * enableSellerRegistration: true,
196
+ * })
197
+ * ```
198
+ */
199
+ interface MercurConfig {
200
+ /** Path to the MedusaJS config file, resolved relative to the project root. */
201
+ medusaConfigPath: string;
202
+ /**
203
+ * Backend URL for API requests.
204
+ * @default "http://localhost:9000"
205
+ */
206
+ backendUrl?: string;
207
+ /**
208
+ * Absolute base URL for the vendor dashboard, including any path prefix
209
+ * (for example `https://vendors.example.com/seller`).
210
+ *
211
+ * The plugin does not read `.env` files on its own. Load env in your
212
+ * app's `vite.config.ts` and pass the value here when you need
213
+ * cross-origin links such as seller invite URLs.
214
+ *
215
+ * If omitted, the plugin falls back to vendor module dev server
216
+ * detection in development and to the configured vendor path in
217
+ * production.
218
+ */
219
+ vendorUrl?: string;
220
+ /** Dashboard display name. */
221
+ name?: string;
222
+ /** Path to a logo asset for the dashboard. */
223
+ logo?: string;
224
+ /** Internationalization settings. */
225
+ i18n?: {
226
+ /** The default language code (e.g. `"en"`). */
227
+ defaultLanguage: string;
228
+ };
229
+ /** Whether to allow new sellers to register from the login screen. */
230
+ enableSellerRegistration?: boolean;
231
+ /**
232
+ * Maximum allowed size for image uploads, in bytes.
233
+ * Used by dashboard upload components to reject oversized files
234
+ * before they are sent to the backend.
235
+ * @default 2_097_152 // 2 MB
236
+ */
237
+ imageLimit?: number;
238
+ }
239
+ interface BuiltMercurConfig extends MercurConfig {
240
+ backendUrl: string;
241
+ base?: string;
242
+ root: string;
243
+ srcDir: string;
244
+ pluginExtensions: string[];
245
+ imageLimit: number;
246
+ }
247
+ type RouteConfig = {
248
+ label: string;
249
+ icon?: ComponentType;
250
+ rank?: number;
251
+ nested?: string;
252
+ /**
253
+ * Sidebar group to list the route under: one declared in `_navigation.ts`
254
+ * or, for a `/settings/*` route, a built-in settings group. Must be a
255
+ * string literal.
256
+ */
257
+ group?: NavGroupId | (string & {});
258
+ translationNs?: string;
259
+ public?: boolean;
260
+ /**
261
+ * Permissions the actor must hold for this route's sidebar entry to be
262
+ * shown. Enforcement of the route itself comes from `handle.permissions`
263
+ * — this only controls navigation visibility.
264
+ */
265
+ permissions?: Permission[];
266
+ /** If true, ALL `permissions` are required. Defaults to ANY. */
267
+ requireAll?: boolean;
268
+ };
269
+ /**
270
+ * The value of a route module's `export const handle`. Read by react-router's
271
+ * `useMatches()`; `permissions` is enforced by `RoutePermissionGuard`.
272
+ */
273
+ type RouteHandle = {
274
+ permissions?: Permission | Permission[];
275
+ /** If true (default at the route level), ALL permissions are required. */
276
+ requireAll?: boolean;
277
+ /** Where to send the actor on denial. Renders Access Denied when unset. */
278
+ redirectTo?: string;
279
+ breadcrumb?: (match: never) => ReactNode;
280
+ };
281
+
282
+ export { type BuiltMercurConfig as B, type MercurConfig as M, type NavGroupConfig as N, PERMISSION_RIGHT_RANK as P, type RouteConfig as R, type WidgetConfig as W, type NavGroupId as a, type NavGroupRegistry as b, type NavItemId as c, type NavItemOverride as d, type NavItemRegistry as e, type NavParentId as f, type NavParentRegistry as g, type NavigationConfig as h, type Permission as i, type PermissionKey as j, type PermissionKeys as k, type PermissionMap as l, type PermissionRight as m, type PermissionsContextValue as n, type RouteHandle as o, type WidgetZoneId as p, type WidgetZoneRegistry as q, buildPermission as r, parsePermission as s, satisfiesPermission as t };
package/dist/vite.cjs CHANGED
@@ -464,6 +464,13 @@ function processConfigProperties(properties) {
464
464
  if (nested && (0, import_types.isObjectProperty)(nested) && (0, import_types.isStringLiteral)(nested.value)) {
465
465
  nestedValue = nested.value.value;
466
466
  }
467
+ const group = properties.find(
468
+ (prop) => (0, import_types.isObjectProperty)(prop) && (0, import_types.isIdentifier)(prop.key, { name: "group" })
469
+ );
470
+ let groupValue;
471
+ if (group && (0, import_types.isObjectProperty)(group) && (0, import_types.isStringLiteral)(group.value)) {
472
+ groupValue = group.value.value;
473
+ }
467
474
  const translationNs = properties.find(
468
475
  (prop) => (0, import_types.isObjectProperty)(prop) && (0, import_types.isIdentifier)(prop.key, { name: "translationNs" })
469
476
  );
@@ -490,6 +497,7 @@ function processConfigProperties(properties) {
490
497
  icon: hasIcon,
491
498
  rank: rankValue,
492
499
  nested: nestedValue,
500
+ group: groupValue,
493
501
  translationNs: translationNsValue,
494
502
  // Referenced by identifier at runtime rather than extracted here, so
495
503
  // the array may be computed instead of a string-literal list.
@@ -539,6 +547,7 @@ function generateMenuItem(config, file, routesDir, index) {
539
547
  path: getRoute2(file, routesDir),
540
548
  rank: config.rank,
541
549
  nested: config.nested,
550
+ group: config.group,
542
551
  translationNs: config.translationNs ? `${configName}.translationNs` : void 0,
543
552
  permissions: config.permissions ? `${configName}.permissions` : void 0,
544
553
  requireAll: config.requireAll
@@ -551,6 +560,7 @@ function formatMenuItem(menuItem) {
551
560
  ` path: "${menuItem.path}"`,
552
561
  ` rank: ${menuItem.rank !== void 0 ? menuItem.rank : "undefined"}`,
553
562
  ` nested: ${menuItem.nested ? `"${menuItem.nested}"` : "undefined"}`,
563
+ ` group: ${menuItem.group ? JSON.stringify(menuItem.group) : "undefined"}`,
554
564
  ` translationNs: ${menuItem.translationNs || "undefined"}`,
555
565
  ` permissions: ${menuItem.permissions || "undefined"}`,
556
566
  ` requireAll: ${menuItem.requireAll !== void 0 ? menuItem.requireAll : "undefined"}`
@@ -665,7 +675,7 @@ export default [${sources.join(", ")}].reduce(mergeResources, {})
665
675
  // src/widgets.ts
666
676
  var import_fs5 = __toESM(require("fs"), 1);
667
677
  var import_path5 = __toESM(require("path"), 1);
668
- function extractZones(value, zones) {
678
+ function extractStrings(value, zones) {
669
679
  if ((0, import_types.isStringLiteral)(value)) {
670
680
  zones.push(value.value);
671
681
  } else if ((0, import_types.isArrayExpression)(value)) {
@@ -676,18 +686,26 @@ function extractZones(value, zones) {
676
686
  }
677
687
  function readWidgetConfig(properties) {
678
688
  const zones = [];
689
+ const permissions = [];
679
690
  let id;
691
+ let label;
680
692
  for (const prop of properties) {
681
693
  if (!(0, import_types.isObjectProperty)(prop)) continue;
682
694
  if ((0, import_types.isIdentifier)(prop.key, { name: "zone" })) {
683
- extractZones(prop.value, zones);
695
+ extractStrings(prop.value, zones);
696
+ }
697
+ if ((0, import_types.isIdentifier)(prop.key, { name: "permission" })) {
698
+ extractStrings(prop.value, permissions);
684
699
  }
685
700
  if ((0, import_types.isIdentifier)(prop.key, { name: "id" }) && (0, import_types.isStringLiteral)(prop.value)) {
686
701
  id = prop.value.value;
687
702
  }
703
+ if ((0, import_types.isIdentifier)(prop.key, { name: "label" }) && (0, import_types.isStringLiteral)(prop.value)) {
704
+ label = prop.value.value;
705
+ }
688
706
  }
689
707
  if (zones.length === 0) return null;
690
- return { zones, id };
708
+ return { zones, id, label, permissions };
691
709
  }
692
710
  function getConfigProperties(node) {
693
711
  if ((0, import_types.isCallExpression)(node) && node.arguments.length > 0 && (0, import_types.isObjectExpression)(node.arguments[0])) {
@@ -751,7 +769,7 @@ function parseWidgetFile(file, widgetsDir, index) {
751
769
  import: `import ${name} from "${importPath}"`,
752
770
  entry: ` { Component: ${name}, zone: ${zonesLiteral}, widgetId: ${JSON.stringify(
753
771
  id
754
- )} }`
772
+ )}${info.label ? `, label: ${JSON.stringify(info.label)}` : ""}${info.permissions.length ? `, permission: ${JSON.stringify(info.permissions)}` : ""} }`
755
773
  };
756
774
  }
757
775
  function collectWidgets(srcDir, startIndex = 0) {
package/dist/vite.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as Vite from 'vite';
2
- import { M as MercurConfig } from './types-CTuDKgPc.cjs';
2
+ import { M as MercurConfig } from './types-BTgTMD_t.cjs';
3
3
  import 'react';
4
4
 
5
5
  declare function mercurDashboardPlugin(pluginConfig: MercurConfig): Vite.Plugin;
package/dist/vite.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as Vite from 'vite';
2
- import { M as MercurConfig } from './types-CTuDKgPc.js';
2
+ import { M as MercurConfig } from './types-BTgTMD_t.js';
3
3
  import 'react';
4
4
 
5
5
  declare function mercurDashboardPlugin(pluginConfig: MercurConfig): Vite.Plugin;
package/dist/vite.js CHANGED
@@ -444,6 +444,13 @@ function processConfigProperties(properties) {
444
444
  if (nested && isObjectProperty(nested) && isStringLiteral(nested.value)) {
445
445
  nestedValue = nested.value.value;
446
446
  }
447
+ const group = properties.find(
448
+ (prop) => isObjectProperty(prop) && isIdentifier(prop.key, { name: "group" })
449
+ );
450
+ let groupValue;
451
+ if (group && isObjectProperty(group) && isStringLiteral(group.value)) {
452
+ groupValue = group.value.value;
453
+ }
447
454
  const translationNs = properties.find(
448
455
  (prop) => isObjectProperty(prop) && isIdentifier(prop.key, { name: "translationNs" })
449
456
  );
@@ -470,6 +477,7 @@ function processConfigProperties(properties) {
470
477
  icon: hasIcon,
471
478
  rank: rankValue,
472
479
  nested: nestedValue,
480
+ group: groupValue,
473
481
  translationNs: translationNsValue,
474
482
  // Referenced by identifier at runtime rather than extracted here, so
475
483
  // the array may be computed instead of a string-literal list.
@@ -519,6 +527,7 @@ function generateMenuItem(config, file, routesDir, index) {
519
527
  path: getRoute2(file, routesDir),
520
528
  rank: config.rank,
521
529
  nested: config.nested,
530
+ group: config.group,
522
531
  translationNs: config.translationNs ? `${configName}.translationNs` : void 0,
523
532
  permissions: config.permissions ? `${configName}.permissions` : void 0,
524
533
  requireAll: config.requireAll
@@ -531,6 +540,7 @@ function formatMenuItem(menuItem) {
531
540
  ` path: "${menuItem.path}"`,
532
541
  ` rank: ${menuItem.rank !== void 0 ? menuItem.rank : "undefined"}`,
533
542
  ` nested: ${menuItem.nested ? `"${menuItem.nested}"` : "undefined"}`,
543
+ ` group: ${menuItem.group ? JSON.stringify(menuItem.group) : "undefined"}`,
534
544
  ` translationNs: ${menuItem.translationNs || "undefined"}`,
535
545
  ` permissions: ${menuItem.permissions || "undefined"}`,
536
546
  ` requireAll: ${menuItem.requireAll !== void 0 ? menuItem.requireAll : "undefined"}`
@@ -645,7 +655,7 @@ export default [${sources.join(", ")}].reduce(mergeResources, {})
645
655
  // src/widgets.ts
646
656
  import fs5 from "fs";
647
657
  import path5 from "path";
648
- function extractZones(value, zones) {
658
+ function extractStrings(value, zones) {
649
659
  if (isStringLiteral(value)) {
650
660
  zones.push(value.value);
651
661
  } else if (isArrayExpression(value)) {
@@ -656,18 +666,26 @@ function extractZones(value, zones) {
656
666
  }
657
667
  function readWidgetConfig(properties) {
658
668
  const zones = [];
669
+ const permissions = [];
659
670
  let id;
671
+ let label;
660
672
  for (const prop of properties) {
661
673
  if (!isObjectProperty(prop)) continue;
662
674
  if (isIdentifier(prop.key, { name: "zone" })) {
663
- extractZones(prop.value, zones);
675
+ extractStrings(prop.value, zones);
676
+ }
677
+ if (isIdentifier(prop.key, { name: "permission" })) {
678
+ extractStrings(prop.value, permissions);
664
679
  }
665
680
  if (isIdentifier(prop.key, { name: "id" }) && isStringLiteral(prop.value)) {
666
681
  id = prop.value.value;
667
682
  }
683
+ if (isIdentifier(prop.key, { name: "label" }) && isStringLiteral(prop.value)) {
684
+ label = prop.value.value;
685
+ }
668
686
  }
669
687
  if (zones.length === 0) return null;
670
- return { zones, id };
688
+ return { zones, id, label, permissions };
671
689
  }
672
690
  function getConfigProperties(node) {
673
691
  if (isCallExpression(node) && node.arguments.length > 0 && isObjectExpression(node.arguments[0])) {
@@ -731,7 +749,7 @@ function parseWidgetFile(file, widgetsDir, index) {
731
749
  import: `import ${name} from "${importPath}"`,
732
750
  entry: ` { Component: ${name}, zone: ${zonesLiteral}, widgetId: ${JSON.stringify(
733
751
  id
734
- )} }`
752
+ )}${info.label ? `, label: ${JSON.stringify(info.label)}` : ""}${info.permissions.length ? `, permission: ${JSON.stringify(info.permissions)}` : ""} }`
735
753
  };
736
754
  }
737
755
  function collectWidgets(srcDir, startIndex = 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mercurjs/dashboard-sdk",
3
- "version": "2.3.6-canary.0",
3
+ "version": "2.3.6-canary.10",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/mercurjs/mercur",
@@ -31,12 +31,13 @@
31
31
  "scripts": {
32
32
  "dev": "tsup --watch",
33
33
  "build": "tsup",
34
+ "test:types": "tsc -p src/__tests__/types",
34
35
  "lint": "oxlint --max-warnings 0"
35
36
  },
36
37
  "dependencies": {
37
38
  "@babel/parser": "8.0.5",
38
- "@babel/traverse": "7.25.6",
39
- "@babel/types": "8.0.4",
39
+ "@babel/traverse": "8.0.6",
40
+ "@babel/types": "8.0.6",
40
41
  "esbuild-register": "^3.6.0"
41
42
  },
42
43
  "devDependencies": {
@@ -1,142 +0,0 @@
1
- import { ComponentType, ReactNode } from 'react';
2
-
3
- /**
4
- * Permission strings follow the pattern `{resource}:{operation}`, e.g.
5
- * `customer:read`, `product:create`, `order:*`.
6
- *
7
- * The resource list mirrors the policies registered server-side — Medusa's own
8
- * catalog plus Mercur's (`packages/core/src/policies`). A name that is not
9
- * registered there is not a type error, it simply never matches a granted
10
- * policy, so the two are kept in sync by a test in `packages/admin`.
11
- */
12
- type PermissionResource = "api_key" | "campaign" | "commission_line" | "commission_rate" | "commission_rule" | "currency" | "customer" | "customer_address" | "customer_group" | "file" | "fulfillment" | "fulfillment_provider" | "fulfillment_set" | "inventory_item" | "inventory_level" | "invite" | "member_invite" | "notification" | "offer" | "order" | "order_change" | "order_claim" | "order_claim_item" | "order_exchange" | "order_group" | "order_item" | "payment" | "payment_collection" | "payment_method" | "payment_session" | "payout" | "payout_account" | "price" | "price_list" | "price_preference" | "product" | "product_attribute" | "product_attribute_value" | "product_category" | "product_change" | "product_collection" | "product_option" | "product_option_value" | "product_tag" | "product_type" | "product_variant" | "promotion" | "rbac_policy" | "rbac_role" | "refund_reason" | "region" | "reservation_item" | "return" | "return_reason" | "review" | "sales_channel" | "seller" | "seller_member" | "service_zone" | "shipping_option" | "shipping_option_type" | "shipping_profile" | "stock_location" | "store" | "store_locale" | "tax_provider" | "tax_rate" | "tax_region" | "translation" | "translation_setting" | "user" | "workflow_execution";
13
- type PermissionOperation = "read" | "create" | "update" | "delete" | "*";
14
- type Permission = `${PermissionResource}:${PermissionOperation}`;
15
- interface UserPolicy {
16
- permissions: Permission[];
17
- }
18
- interface PermissionRequirement {
19
- permissions: Permission[];
20
- /** If true, ALL permissions are required. Defaults to ANY. */
21
- requireAll?: boolean;
22
- /** Optional label describing where the requirement came from. */
23
- source?: string;
24
- }
25
- interface PermissionsContextValue {
26
- policy: UserPolicy | null;
27
- isLoading: boolean;
28
- /** Whether RBAC is active. When `false`, every check resolves to `true`. */
29
- isRbacEnabled: boolean;
30
- hasPermission: (permission: Permission) => boolean;
31
- hasAnyPermission: (permissions: Permission[]) => boolean;
32
- hasAllPermissions: (permissions: Permission[]) => boolean;
33
- can: (resource: PermissionResource, operation: PermissionOperation) => boolean;
34
- }
35
- interface PermissionsRequirementsContextValue {
36
- requiredPermissions: PermissionRequirement[];
37
- registerRequiredPermissions: (id: string, requirement: PermissionRequirement) => void;
38
- unregisterRequiredPermissions: (id: string) => void;
39
- }
40
- /**
41
- * Operations implied by a granted operation. Only the wildcard fans out.
42
- * Note that `update` does not imply `read` — grants must be explicit.
43
- */
44
- declare const OPERATION_IMPLICATIONS: Record<PermissionOperation, PermissionOperation[]>;
45
- declare function parsePermission(permission: string): {
46
- resource: PermissionResource;
47
- operation: PermissionOperation;
48
- } | null;
49
- declare function buildPermission(resource: PermissionResource, operation: PermissionOperation): Permission;
50
-
51
- /**
52
- * Configuration for the Mercur dashboard plugin.
53
- *
54
- * Passed to `mercurDashboardPlugin()` in `vite.config.ts` to configure
55
- * the vendor or admin dashboard at build time.
56
- *
57
- * @example
58
- * ```ts
59
- * mercurDashboardPlugin({
60
- * medusaConfigPath: '../../packages/api/medusa-config.ts',
61
- * enableSellerRegistration: true,
62
- * })
63
- * ```
64
- */
65
- interface MercurConfig {
66
- /** Path to the MedusaJS config file, resolved relative to the project root. */
67
- medusaConfigPath: string;
68
- /**
69
- * Backend URL for API requests.
70
- * @default "http://localhost:9000"
71
- */
72
- backendUrl?: string;
73
- /**
74
- * Absolute base URL for the vendor dashboard, including any path prefix
75
- * (for example `https://vendors.example.com/seller`).
76
- *
77
- * The plugin does not read `.env` files on its own. Load env in your
78
- * app's `vite.config.ts` and pass the value here when you need
79
- * cross-origin links such as seller invite URLs.
80
- *
81
- * If omitted, the plugin falls back to vendor module dev server
82
- * detection in development and to the configured vendor path in
83
- * production.
84
- */
85
- vendorUrl?: string;
86
- /** Dashboard display name. */
87
- name?: string;
88
- /** Path to a logo asset for the dashboard. */
89
- logo?: string;
90
- /** Internationalization settings. */
91
- i18n?: {
92
- /** The default language code (e.g. `"en"`). */
93
- defaultLanguage: string;
94
- };
95
- /** Whether to allow new sellers to register from the login screen. */
96
- enableSellerRegistration?: boolean;
97
- /**
98
- * Maximum allowed size for image uploads, in bytes.
99
- * Used by dashboard upload components to reject oversized files
100
- * before they are sent to the backend.
101
- * @default 2_097_152 // 2 MB
102
- */
103
- imageLimit?: number;
104
- }
105
- interface BuiltMercurConfig extends MercurConfig {
106
- backendUrl: string;
107
- base?: string;
108
- root: string;
109
- srcDir: string;
110
- pluginExtensions: string[];
111
- imageLimit: number;
112
- }
113
- type RouteConfig = {
114
- label: string;
115
- icon?: ComponentType;
116
- rank?: number;
117
- nested?: string;
118
- translationNs?: string;
119
- public?: boolean;
120
- /**
121
- * Permissions the actor must hold for this route's sidebar entry to be
122
- * shown. Enforcement of the route itself comes from `handle.permissions`
123
- * — this only controls navigation visibility.
124
- */
125
- permissions?: Permission[];
126
- /** If true, ALL `permissions` are required. Defaults to ANY. */
127
- requireAll?: boolean;
128
- };
129
- /**
130
- * The value of a route module's `export const handle`. Read by react-router's
131
- * `useMatches()`; `permissions` is enforced by `RoutePermissionGuard`.
132
- */
133
- type RouteHandle = {
134
- permissions?: Permission | Permission[];
135
- /** If true (default at the route level), ALL permissions are required. */
136
- requireAll?: boolean;
137
- /** Where to send the actor on denial. Renders Access Denied when unset. */
138
- redirectTo?: string;
139
- breadcrumb?: (match: never) => ReactNode;
140
- };
141
-
142
- export { type BuiltMercurConfig as B, type MercurConfig as M, OPERATION_IMPLICATIONS as O, type Permission as P, type RouteConfig as R, type UserPolicy as U, type PermissionOperation as a, type PermissionRequirement as b, type PermissionResource as c, type PermissionsContextValue as d, type PermissionsRequirementsContextValue as e, type RouteHandle as f, buildPermission as g, parsePermission as p };
@@ -1,142 +0,0 @@
1
- import { ComponentType, ReactNode } from 'react';
2
-
3
- /**
4
- * Permission strings follow the pattern `{resource}:{operation}`, e.g.
5
- * `customer:read`, `product:create`, `order:*`.
6
- *
7
- * The resource list mirrors the policies registered server-side — Medusa's own
8
- * catalog plus Mercur's (`packages/core/src/policies`). A name that is not
9
- * registered there is not a type error, it simply never matches a granted
10
- * policy, so the two are kept in sync by a test in `packages/admin`.
11
- */
12
- type PermissionResource = "api_key" | "campaign" | "commission_line" | "commission_rate" | "commission_rule" | "currency" | "customer" | "customer_address" | "customer_group" | "file" | "fulfillment" | "fulfillment_provider" | "fulfillment_set" | "inventory_item" | "inventory_level" | "invite" | "member_invite" | "notification" | "offer" | "order" | "order_change" | "order_claim" | "order_claim_item" | "order_exchange" | "order_group" | "order_item" | "payment" | "payment_collection" | "payment_method" | "payment_session" | "payout" | "payout_account" | "price" | "price_list" | "price_preference" | "product" | "product_attribute" | "product_attribute_value" | "product_category" | "product_change" | "product_collection" | "product_option" | "product_option_value" | "product_tag" | "product_type" | "product_variant" | "promotion" | "rbac_policy" | "rbac_role" | "refund_reason" | "region" | "reservation_item" | "return" | "return_reason" | "review" | "sales_channel" | "seller" | "seller_member" | "service_zone" | "shipping_option" | "shipping_option_type" | "shipping_profile" | "stock_location" | "store" | "store_locale" | "tax_provider" | "tax_rate" | "tax_region" | "translation" | "translation_setting" | "user" | "workflow_execution";
13
- type PermissionOperation = "read" | "create" | "update" | "delete" | "*";
14
- type Permission = `${PermissionResource}:${PermissionOperation}`;
15
- interface UserPolicy {
16
- permissions: Permission[];
17
- }
18
- interface PermissionRequirement {
19
- permissions: Permission[];
20
- /** If true, ALL permissions are required. Defaults to ANY. */
21
- requireAll?: boolean;
22
- /** Optional label describing where the requirement came from. */
23
- source?: string;
24
- }
25
- interface PermissionsContextValue {
26
- policy: UserPolicy | null;
27
- isLoading: boolean;
28
- /** Whether RBAC is active. When `false`, every check resolves to `true`. */
29
- isRbacEnabled: boolean;
30
- hasPermission: (permission: Permission) => boolean;
31
- hasAnyPermission: (permissions: Permission[]) => boolean;
32
- hasAllPermissions: (permissions: Permission[]) => boolean;
33
- can: (resource: PermissionResource, operation: PermissionOperation) => boolean;
34
- }
35
- interface PermissionsRequirementsContextValue {
36
- requiredPermissions: PermissionRequirement[];
37
- registerRequiredPermissions: (id: string, requirement: PermissionRequirement) => void;
38
- unregisterRequiredPermissions: (id: string) => void;
39
- }
40
- /**
41
- * Operations implied by a granted operation. Only the wildcard fans out.
42
- * Note that `update` does not imply `read` — grants must be explicit.
43
- */
44
- declare const OPERATION_IMPLICATIONS: Record<PermissionOperation, PermissionOperation[]>;
45
- declare function parsePermission(permission: string): {
46
- resource: PermissionResource;
47
- operation: PermissionOperation;
48
- } | null;
49
- declare function buildPermission(resource: PermissionResource, operation: PermissionOperation): Permission;
50
-
51
- /**
52
- * Configuration for the Mercur dashboard plugin.
53
- *
54
- * Passed to `mercurDashboardPlugin()` in `vite.config.ts` to configure
55
- * the vendor or admin dashboard at build time.
56
- *
57
- * @example
58
- * ```ts
59
- * mercurDashboardPlugin({
60
- * medusaConfigPath: '../../packages/api/medusa-config.ts',
61
- * enableSellerRegistration: true,
62
- * })
63
- * ```
64
- */
65
- interface MercurConfig {
66
- /** Path to the MedusaJS config file, resolved relative to the project root. */
67
- medusaConfigPath: string;
68
- /**
69
- * Backend URL for API requests.
70
- * @default "http://localhost:9000"
71
- */
72
- backendUrl?: string;
73
- /**
74
- * Absolute base URL for the vendor dashboard, including any path prefix
75
- * (for example `https://vendors.example.com/seller`).
76
- *
77
- * The plugin does not read `.env` files on its own. Load env in your
78
- * app's `vite.config.ts` and pass the value here when you need
79
- * cross-origin links such as seller invite URLs.
80
- *
81
- * If omitted, the plugin falls back to vendor module dev server
82
- * detection in development and to the configured vendor path in
83
- * production.
84
- */
85
- vendorUrl?: string;
86
- /** Dashboard display name. */
87
- name?: string;
88
- /** Path to a logo asset for the dashboard. */
89
- logo?: string;
90
- /** Internationalization settings. */
91
- i18n?: {
92
- /** The default language code (e.g. `"en"`). */
93
- defaultLanguage: string;
94
- };
95
- /** Whether to allow new sellers to register from the login screen. */
96
- enableSellerRegistration?: boolean;
97
- /**
98
- * Maximum allowed size for image uploads, in bytes.
99
- * Used by dashboard upload components to reject oversized files
100
- * before they are sent to the backend.
101
- * @default 2_097_152 // 2 MB
102
- */
103
- imageLimit?: number;
104
- }
105
- interface BuiltMercurConfig extends MercurConfig {
106
- backendUrl: string;
107
- base?: string;
108
- root: string;
109
- srcDir: string;
110
- pluginExtensions: string[];
111
- imageLimit: number;
112
- }
113
- type RouteConfig = {
114
- label: string;
115
- icon?: ComponentType;
116
- rank?: number;
117
- nested?: string;
118
- translationNs?: string;
119
- public?: boolean;
120
- /**
121
- * Permissions the actor must hold for this route's sidebar entry to be
122
- * shown. Enforcement of the route itself comes from `handle.permissions`
123
- * — this only controls navigation visibility.
124
- */
125
- permissions?: Permission[];
126
- /** If true, ALL `permissions` are required. Defaults to ANY. */
127
- requireAll?: boolean;
128
- };
129
- /**
130
- * The value of a route module's `export const handle`. Read by react-router's
131
- * `useMatches()`; `permissions` is enforced by `RoutePermissionGuard`.
132
- */
133
- type RouteHandle = {
134
- permissions?: Permission | Permission[];
135
- /** If true (default at the route level), ALL permissions are required. */
136
- requireAll?: boolean;
137
- /** Where to send the actor on denial. Renders Access Denied when unset. */
138
- redirectTo?: string;
139
- breadcrumb?: (match: never) => ReactNode;
140
- };
141
-
142
- export { type BuiltMercurConfig as B, type MercurConfig as M, OPERATION_IMPLICATIONS as O, type Permission as P, type RouteConfig as R, type UserPolicy as U, type PermissionOperation as a, type PermissionRequirement as b, type PermissionResource as c, type PermissionsContextValue as d, type PermissionsRequirementsContextValue as e, type RouteHandle as f, buildPermission as g, parsePermission as p };