diff --git a/ui-plugins/muse-boot-default/MUSE_README.md b/ui-plugins/muse-boot-default/MUSE_README.md new file mode 100644 index 00000000..c7c5afe1 --- /dev/null +++ b/ui-plugins/muse-boot-default/MUSE_README.md @@ -0,0 +1,400 @@ +# Plugin Integration Guide: @ebay/muse-boot-default + +**Generated**: 2026-03-28 +**Plugin Type**: boot + +--- + +## 1. Plugin Purpose & Overview + +### Purpose + +`@ebay/muse-boot-default` is the default bootstrap plugin for MUSE applications. It orchestrates the entire application startup sequence, loading plugins in the correct order and initializing the MUSE runtime environment. + +### Key Capabilities + +**Bootstrap Orchestration** +- Loads and executes plugins in the correct order: boot → init → lib → normal +- Manages the plugin loading lifecycle with parallel loading for optimal performance +- Provides loading progress feedback to users +- Handles error states and recovery during bootstrap + +**MUSE Global Environment Setup** +- Initializes `window.MUSE_GLOBAL` with core utilities and configuration +- Sets up the shared modules system (`__shared__`) +- Configures app variables, plugin variables, and configuration merging +- Provides message engine for parent-child communication (sub-app scenarios) + +**Plugin Loading System** +- Supports multiple plugin sources: CDN, local development, linked plugins +- Implements parallel loading for lib and normal plugins +- Handles ES module and UMD plugin formats +- Supports `forcePlugins` query parameter for debugging/testing + +**Service Worker Registration** +- Registers service workers for offline support +- Manages service worker lifecycle + +**Error Handling & Loading UI** +- Displays loading progress during bootstrap +- Shows error messages when bootstrap fails +- Provides fallback UI for failed states + +### Integration Role + +As a **boot plugin**, this plugin: +- **MUST be the first plugin loaded** in any MUSE application +- Does not use the js-plugin extension point system (it runs before plugins are loaded) +- Provides the foundational environment that allows other plugins to function +- Is typically referenced in `index.html` as the entry script + +--- + +## 2. Extension Points Exposed + +This plugin does not expose extension points. As a boot plugin, it runs before the js-plugin system is initialized and focuses solely on loading other plugins and setting up the runtime environment. + +--- + +## 3. Extension Points Contributed + +This plugin does not contribute to extension points from other plugins. As the first plugin to load, there are no other plugins available to extend when this plugin runs. + +### Bootstrap Integration Points + +While this plugin doesn't use extension points, it provides several integration mechanisms through `window.MUSE_GLOBAL`: + +#### `waitForLoaders` +**Type**: `Array` +**File Reference**: `src/boot.js:23, 273-286` + +Plugins can register async loaders that must complete before the app starts: + +```javascript +// In an init plugin +window.MUSE_GLOBAL.waitFor(async () => { + await performAuth(); + return true; // Return false to prevent app from starting +}); +``` + +--- + +#### `initEntries` +**Type**: `Array<{ func: Function, order?: number }>` +**File Reference**: `src/boot.js:44, 201-208` + +Init plugins can register initialization functions executed after init plugins load: + +```javascript +// In an init plugin +window.MUSE_GLOBAL.initEntries.push({ + order: 10, + func: async () => { + await initialize(); + // Return false to prevent app from starting + } +}); +``` + +--- + +#### `appEntries` +**Type**: `Array<{ name: string, func: Function }>` +**File Reference**: `src/boot.js:43, 289-314` + +Lib plugins can register app entry points (typically from `@ebay/muse-lib-react`): + +```javascript +// In a lib plugin with isAppEntry: true +window.MUSE_GLOBAL.appEntries.push({ + name: '@ebay/muse-lib-react', + func: renderApp +}); +``` + +--- + +#### `pluginEntries` +**Type**: `Array<{ func: Function }>` +**File Reference**: `src/boot.js:45, 267-270` + +Build system can register plugin initialization code: + +```javascript +window.MUSE_GLOBAL.pluginEntries.push({ + func: () => { + // Plugin initialization + } +}); +``` + +--- + +## 4. Exported Functionality + +This plugin sets up `window.MUSE_GLOBAL` with the following utilities and systems: + +### Global Utilities + +#### `msgEngine` +**File Reference**: `src/boot.js:38`, `src/msgEngine.js` +**Purpose**: Message passing system for parent-child window communication + +**Why it exists**: Enables MUSE apps embedded in iframes to communicate with their parent windows. + +**Methods**: +- `sendToParent(message)` - Send message to parent window +- `addListener(key, handler)` - Listen for messages +- `removeListener(key)` - Remove message listener + +--- + +#### `loading` +**File Reference**: `src/boot.js:39`, `src/loading.js` +**Purpose**: Loading UI management + +**Why it exists**: Provides user feedback during the bootstrap process. + +**Methods**: +- `init()` - Initialize loading UI +- `showMessage(message)` - Display loading message +- `hide()` - Hide loading UI + +--- + +#### `error` +**File Reference**: `src/boot.js:40`, `src/error.js` +**Purpose**: Error UI management + +**Why it exists**: Displays error messages when bootstrap fails. + +**Methods**: +- `showMessage(message)` - Display error message + +--- + +#### `getPublicPath(pluginName, assetPath)` +**File Reference**: `src/boot.js:51-76` +**Purpose**: Resolve public asset paths for plugins + +**Why it exists**: Allows plugins to reference their public assets (images, fonts, etc.) correctly regardless of deployment environment. + +**Usage**: +```javascript +const logoUrl = window.MUSE_GLOBAL.getPublicPath('@ebay/my-plugin', 'logo.png'); +// Returns: /muse-assets/cdn/p/my-plugin/v1.0.0/dist/logo.png +``` + +--- + +#### `waitFor(asyncFuncOrPromise)` +**File Reference**: `src/boot.js:47-49` +**Purpose**: Register async operations that must complete before app starts + +**Why it exists**: Allows init plugins to perform async initialization (auth, config loading) before the main app renders. + +**Usage**: +```javascript +// In init plugin +window.MUSE_GLOBAL.waitFor(async () => { + const user = await fetchUser(); + window.MUSE_GLOBAL.currentUser = user; +}); +``` + +--- + +#### `getUser()` +**File Reference**: `src/boot.js:42` +**Purpose**: Get current user (default returns null, overridden by init plugins) + +**Why it exists**: Provides a standard way to access user information across plugins. + +--- + +### Shared Modules System + +#### `__shared__` +**File Reference**: `src/boot.js:78-83` +**Purpose**: Runtime module sharing container + +**Why it exists**: Enables lib plugins to provide shared modules (React, Redux, etc.) that other plugins consume without bundling. + +**Properties**: +- `modules` - Container for shared modules +- `register(id, module)` - Register a shared module +- `require(id)` - Require a shared module +- `parseMuseId(id)` - Parse MUSE module identifier + +**Note**: This is managed by `@ebay/muse-modules` package. + +--- + +### Configuration System + +#### `appConfig` +**File Reference**: `src/boot.js:27-37` +**Purpose**: Merged app and environment configuration + +**Why it exists**: Provides a single source of truth for app configuration, with env config overriding app config. + +**Usage**: +```javascript +const routerType = window.MUSE_GLOBAL.appConfig.routerType; +``` + +--- + +#### `appVariables` +**File Reference**: `src/boot.js:35` +**Purpose**: App-level runtime variables + +--- + +#### `pluginVariables` +**File Reference**: `src/boot.js:36` +**Purpose**: Plugin-level runtime variables + +--- + +### Environment Flags + +#### `isSubApp` +**File Reference**: `src/boot.js:41` +**Purpose**: Boolean indicating if app is running in an iframe + +**Why it exists**: Allows plugins to adjust behavior when embedded as a sub-app. + +--- + +#### `isDev` +**File Reference**: `src/boot.js:86` +**Purpose**: Boolean indicating development mode + +--- + +#### `isLocal` +**File Reference**: From `MUSE_GLOBAL` +**Purpose**: Boolean indicating local development mode + +--- + +#### `isE2eTest` +**File Reference**: `src/boot.js:86` +**Purpose**: Boolean indicating E2E test mode + +--- + +## 5. Integration Examples + +**Note**: This boot plugin does not use the js-plugin extension point system. The examples below show how to integrate with the bootstrap environment it provides. + +### Example 1: Creating an Init Plugin + +Init plugins run after boot and before lib/normal plugins. They're perfect for authentication, configuration loading, or permission checks. + +```javascript +// src/index.js in an init plugin +const initFunc = async () => { + // Perform authentication + const user = await fetch('/api/user').then(r => r.json()); + + // Make user available globally + window.MUSE_GLOBAL.getUser = () => user; + + // If auth fails, return false to prevent app from starting + if (!user) { + window.MUSE_GLOBAL.error.showMessage('Authentication failed'); + return false; + } + + return true; // Continue startup +}; + +// Register init entry +window.MUSE_GLOBAL.initEntries.push({ + order: 10, // Lower numbers run first + func: initFunc +}); +``` + +--- + +### Example 2: Using waitFor for Async Initialization + +```javascript +// In an init plugin +window.MUSE_GLOBAL.waitFor(async () => { + // Load configuration from API + const config = await fetch('/api/config').then(r => r.json()); + + // Store in MUSE_GLOBAL + window.MUSE_GLOBAL.appVariables.apiEndpoint = config.apiEndpoint; + + // Return false to prevent app start if config fails + if (!config) return false; +}); +``` + +--- + +### Example 3: Registering an App Entry (Lib Plugin) + +```javascript +// In a lib plugin like @ebay/muse-lib-react +const renderApp = () => { + const root = createRoot(document.getElementById('root')); + root.render(); +}; + +window.MUSE_GLOBAL.appEntries.push({ + name: '@ebay/muse-lib-react', + func: renderApp +}); +``` + +--- + +### Example 4: Using getPublicPath for Assets + +```javascript +// In any plugin after bootstrap +import React from 'react'; + +const MyComponent = () => { + const logoPath = window.MUSE_GLOBAL.getPublicPath( + '@ebay/my-plugin', + 'images/logo.png' + ); + + return Logo; +}; +``` + +--- + +### Example 5: Force Loading Specific Plugin Versions + +For debugging or testing, use the `forcePlugins` query parameter: + +``` +https://myapp.com?forcePlugins=@ebay/my-plugin@1.2.3;other-plugin@2.0.0 +``` + +This overrides the deployed plugin versions with specific versions. + +--- + +## Notes + +- **This is a boot plugin** (`muse.type: "boot"`) - it MUST be loaded first via `index.html` +- Does not use js-plugin extension points (runs before plugin system is initialized) +- Provides the foundational environment for all other plugins +- Handles plugin loading order: boot → init → lib → normal +- Init plugins can use `initEntries` or `waitFor` to perform async initialization +- Lib plugins with `isAppEntry: true` register app entry functions +- The `forcePlugins` query parameter is useful for debugging specific plugin versions +- Service worker registration is automatic but can be customized +- All plugin loading happens in parallel for performance (within each type group) +- The loading UI provides user feedback during the bootstrap process diff --git a/ui-plugins/muse-boot-default/package.json b/ui-plugins/muse-boot-default/package.json index 9e965c8e..a76a1459 100644 --- a/ui-plugins/muse-boot-default/package.json +++ b/ui-plugins/muse-boot-default/package.json @@ -1,6 +1,6 @@ { "name": "@ebay/muse-boot-default", - "version": "1.3.1", + "version": "1.3.3", "main": "index.js", "license": "MIT", "muse": { diff --git a/ui-plugins/muse-layout-antd/MUSE_README.md b/ui-plugins/muse-layout-antd/MUSE_README.md new file mode 100644 index 00000000..d0559871 --- /dev/null +++ b/ui-plugins/muse-layout-antd/MUSE_README.md @@ -0,0 +1,637 @@ +# Plugin Integration Guide: @ebay/muse-layout-antd + +**Generated**: 2026-03-28 +**Plugin Type**: normal + +--- + +## 1. Plugin Purpose & Overview + +### Purpose +Provides a complete application layout system for MUSE apps with customizable header, sidebar navigation, and main content areas using Ant Design components. + +### Key Features +- **Responsive Layout Management**: Provides a main layout component (`MainLayout`) with configurable header and sidebar +- **Extensible Header**: Supports customizable header with logo, title, navigation items, and user menu +- **Flexible Sidebar**: Offers multiple sidebar modes (drawer, collapsable, collapsed, fixed, none) with menu navigation +- **Layout Providers**: Supports wrapping the entire layout with custom React context providers +- **Theme Support**: Integrates with dark/light theme switching via `muse-lib-antd` +- **Sub-App Support**: Handles visibility of header/sidebar when running as a sub-application +- **Redux Integration**: Manages sidebar collapse state and layout updates through Redux + +### Use Cases +- Building consistent application layouts across MUSE apps +- Providing navigation structure with header and sidebar menus +- Supporting both standalone apps and micro-frontend sub-apps +- Enabling plugins to contribute navigation items and layout providers + +--- + +## 2. Extension Points Exposed + +This plugin exposes extension points that allow other plugins to customize and extend the application layout. + +### 2.1 `museLayout.header.getConfig` + +**Type**: Direct (returns configuration object) + +**Purpose**: Allows plugins to configure the header appearance and behavior. + +**Signature**: +```typescript +() => HeaderConfig + +interface HeaderConfig { + backgroundColor?: string; + icon?: ReactNode; + title?: string; + noUserMenu?: boolean; + themeSwitcher?: boolean; + subTitle?: string; + mode?: 'none' | 'show-in-sub-app' | undefined; + [key: string]: any; +} +``` + +**Usage**: +```javascript +plugin.register({ + name: 'my-plugin', + museLayout: { + header: { + getConfig: () => ({ + backgroundColor: '#1890ff', + icon: '/path/to/icon.png', + title: 'My App', + subTitle: 'Application subtitle', + themeSwitcher: true, + noUserMenu: false + }) + } + } +}); +``` + +**File Reference**: `src/ext-points.d.ts:3-11`, `src/features/home/Header.jsx:210-216` + +--- + +### 2.2 `museLayout.header.getItems` + +**Type**: Direct (returns header item(s)) + +**Purpose**: Allows plugins to add navigation items, buttons, or menus to the application header. + +**Signature**: +```typescript +() => HeaderItem | HeaderItem[] + +interface HeaderItem { + position?: 'left' | 'center' | 'right'; + order?: number; + key: string; + label?: string; + link?: string; + linkTarget?: string; + onClick?: Function; + icon?: ReactNode; + type?: 'menu' | string; + className?: string; + menuMeta?: Record; + render?: () => ReactNode; +} +``` + +**Usage**: +```javascript +plugin.register({ + name: 'my-plugin', + museLayout: { + header: { + getItems: () => [ + { + key: 'settings', + icon: 'SettingOutlined', + position: 'right', + order: 50, + link: '/settings' + }, + { + key: 'help-menu', + type: 'menu', + position: 'right', + order: 100, + menuMeta: { + trigger: { label: 'Help' }, + items: [ + { key: 'docs', label: 'Documentation' }, + { key: 'support', label: 'Support' } + ] + } + } + ] + } + } +}); +``` + +**File Reference**: `src/ext-points.d.ts:13-31`, `src/features/home/Header.jsx:235` + +--- + +### 2.3 `museLayout.header.processItems` + +**Type**: Lifecycle (processes header items before rendering) + +**Purpose**: Allows plugins to modify, filter, or transform the complete list of header items after they have been collected from all plugins. + +**Signature**: +```typescript +(items: HeaderItem[]) => void +``` + +**Usage**: +```javascript +plugin.register({ + name: 'my-plugin', + museLayout: { + header: { + processItems: (items) => { + // Example: Remove specific items + const index = items.findIndex(item => item.key === 'unwanted-item'); + if (index !== -1) items.splice(index, 1); + + // Example: Modify items + items.forEach(item => { + if (item.key === 'special') { + item.className = 'highlighted'; + } + }); + } + } + } +}); +``` + +**File Reference**: `src/ext-points.d.ts:27-30`, `src/features/home/Header.jsx:267` + +--- + +### 2.4 `museLayout.sider.getConfig` + +**Type**: Direct (returns configuration object) + +**Purpose**: Allows plugins to configure the sidebar appearance and behavior. + +**Signature**: +```typescript +() => SiderConfig + +interface SiderConfig { + mode?: 'fixed' | 'drawer' | 'collapsable' | 'collapsed' | 'none'; + siderDefaultCollapsed?: boolean; + homeMenu?: boolean; + theme?: 'dark' | 'light' | 'custom'; + width?: number; + menuProps?: Record; + [key: string]: any; +} +``` + +**Usage**: +```javascript +plugin.register({ + name: 'my-plugin', + museLayout: { + sider: { + getConfig: () => ({ + mode: 'collapsable', + siderDefaultCollapsed: false, + homeMenu: true, + theme: 'dark', + width: 280 + }) + } + } +}); +``` + +**File Reference**: `src/ext-points.d.ts:33-41`, `src/features/home/Sider.jsx:10-14` + +--- + +### 2.5 `museLayout.sider.getItems` + +**Type**: Direct (returns sidebar menu item(s)) + +**Purpose**: Allows plugins to add navigation menu items to the application sidebar. + +**Signature**: +```typescript +() => SiderItem | SiderItem[] + +interface SiderItem { + key: string; + icon?: ReactNode; + link?: string; + label: ReactNode; + order?: number; + children?: SiderItem[]; + parent?: string; + type?: 'group' | string; +} +``` + +**Usage**: +```javascript +plugin.register({ + name: 'my-plugin', + museLayout: { + sider: { + getItems: () => [ + { + key: 'dashboard', + icon: 'DashboardOutlined', + label: 'Dashboard', + order: 10, + children: [ + { key: 'overview', label: 'Overview', link: '/dashboard' }, + { key: 'analytics', label: 'Analytics', link: '/dashboard/analytics' } + ] + }, + { + key: 'settings', + icon: 'SettingOutlined', + label: 'Settings', + link: '/settings', + order: 100 + } + ] + } + } +}); +``` + +**File Reference**: `src/ext-points.d.ts:43-54`, `src/features/home/Sider.jsx:87` + +--- + +### 2.6 `museLayout.sider` (nested menu items via MetaMenu) + +**Type**: Nested extension point pattern (via `baseExtPoint="museLayout.sider"`) + +**Purpose**: The sidebar uses `MetaMenu` component which automatically creates nested extension points for each menu item, allowing plugins to add sub-items to existing menus. + +**Pattern**: For a menu item with `key: 'myMenu'`, other plugins can contribute to `museLayout.sider.myMenu.getItems` + +**File Reference**: `src/features/home/Sider.jsx:87` + +--- + +### 2.7 `museLayout.header.{itemKey}` (nested menu items via MetaMenu) + +**Type**: Nested extension point pattern (via `baseExtPoint`) + +**Purpose**: Header items of type 'menu' automatically create nested extension points allowing plugins to add items to those menus. + +**Pattern**: For a header menu item with `key: 'helpMenu'`, other plugins can contribute to `museLayout.header.helpMenu.getItems` + +**File Reference**: `src/features/home/HeaderItem.jsx:47` + +--- + +### 2.8 `museLayout.providers` + +**Type**: Array extension (via `extendArray`) + +**Purpose**: Allows plugins to wrap the entire layout with React context providers (e.g., for modals, notifications, or global state). + +**Signature**: +```typescript +{ + order?: number; + key: string; + provider?: React.ComponentType<{children: React.ReactNode}>; + props?: Record; + renderProvider?: (children: React.ReactNode) => React.ReactNode; +} +``` + +**Usage**: +```javascript +import { NiceModal } from '@ebay/nice-modal-react'; + +plugin.register({ + name: 'my-plugin', + museLayout: { + providers: { + getItems: () => ({ + order: 40, + key: 'nice-modal', + provider: NiceModal.Provider, + props: { /* provider props */ } + }) + } + } +}); + +// Alternative using renderProvider +plugin.register({ + name: 'my-plugin', + museLayout: { + providers: { + getItems: () => ({ + order: 50, + key: 'custom-provider', + renderProvider: (children) => ( + + {children} + + ) + }) + } + } +}); +``` + +**File Reference**: `src/features/home/MainLayout.jsx:178`, `src/features/home/MainLayout.jsx:167-201` + +--- + +## 3. Extension Points Contributed + +This plugin implements extension points from other MUSE plugins to integrate with the ecosystem. + +### 3.1 Contributions to `home` extension point + +#### 3.1.1 `home.mainLayout` + +**Source Plugin**: Core routing system (expected by MUSE framework) + +**Purpose**: Provides the main layout component that wraps all application routes. + +**Implementation**: Exports the `MainLayout` React component that renders header, sidebar, and page content. + +**File Reference**: `src/ext/home.js:4` + +**Why**: This allows the MUSE framework to use this plugin's layout as the application shell that wraps all page content. + +--- + +### 3.2 Contributions via `route` property + +#### 3.2.1 `route` + +**Source Plugin**: MUSE routing system (standard plugin property) + +**Purpose**: Registers route configuration for the plugin's own pages. + +**Implementation**: Provides a route configuration object for the `/plugin-muse-layout/home` path. + +**File Reference**: `src/index.js:11`, `src/common/routeConfig.js` + +**Why**: Standard MUSE plugin pattern to register routes, though this plugin's routes are primarily for internal structure rather than user-facing pages. + +--- + +### 3.3 Contributions via `reducer` property + +#### 3.3.1 `reducer` + +**Source Plugin**: MUSE Redux integration (standard plugin property) + +**Purpose**: Registers Redux reducer to manage layout state (sidebar collapse, layout updates). + +**Implementation**: Provides a Redux reducer that handles: +- `MUSE_LAYOUT$HOME_SET_SIDER_COLLAPSED` - Toggle sidebar collapse state +- `MUSE_LAYOUT$HOME_SET_LAYOUT_CONFIG` - Update layout configuration +- `MUSE_LAYOUT$HOME_UPDATE_MUSE_LAYOUT` - Force layout re-render + +**File Reference**: `src/index.js:12`, `src/common/rootReducer.js` + +**Why**: Manages global layout state that needs to be accessible across the application and persist during navigation. + +--- + +### 3.4 Demo Extension Points (from ext/museLayout.js) + +**Note**: The file `src/ext/museLayout.js` contains example/demo implementations of this plugin's own extension points. These are for demonstration purposes in the MUSE demo app. + +#### 3.4.1 `museLayout.header.getConfig` (demo) +**Purpose**: Demo configuration showing header with Muse branding +**File Reference**: `src/ext/museLayout.js:4-12` + +#### 3.4.2 `museLayout.header.userAvatar.getItems` (demo) +**Purpose**: Demo showing how to add items to user avatar menu +**File Reference**: `src/ext/museLayout.js:13-21` + +#### 3.4.3 `museLayout.header.getItems` (demo) +**Purpose**: Demo showing various header items (menus, icons, center items) +**File Reference**: `src/ext/museLayout.js:22-73` + +#### 3.4.4 `museLayout.sider.getConfig` (demo) +**Purpose**: Demo configuration showing drawer mode sidebar +**File Reference**: `src/ext/museLayout.js:75-81` + +#### 3.4.5 `museLayout.sider.getItems` (demo) +**Purpose**: Demo showing comprehensive sidebar menu structure with nested items, groups, and various component examples +**File Reference**: `src/ext/museLayout.js:82-349` + +**Why**: These demo implementations serve as living examples for developers learning how to extend the layout plugin. They are currently commented out in `src/ext/index.js:1` (not active by default). + +--- + +## 4. Exported Functionality + +This plugin exports functionality for other plugins to use programmatically. + +### 4.1 `updateMuseLayout` + +**Type**: Function export + +**Purpose**: Forces the layout to re-render by dispatching a Redux action. Useful when plugins dynamically change layout configuration and need to trigger an immediate update. + +**Signature**: +```typescript +() => void +``` + +**Usage**: +```javascript +const layoutPlugin = plugin.getPlugin('@ebay/muse-layout-antd'); +layoutPlugin.exports.updateMuseLayout(); +``` + +**File Reference**: `src/index.js:13`, `src/features/home/updateMuseLayout.js` + +**Why**: When plugins dynamically modify layout configuration (e.g., changing header items based on runtime conditions), they need a way to notify the layout to re-collect contributions and re-render. This function triggers that update. + +**Common Use Cases**: +- After dynamically adding/removing navigation items +- After changing user permissions that affect visible menus +- After configuration changes that affect layout appearance + +--- + +## 5. Integration Examples + +**CRITICAL**: Extension points are **nested object properties**, NOT string paths! + +### Example 1: Adding Navigation to Header and Sidebar (CORRECT Syntax) + +```javascript +// ✅ CORRECT - nested object properties +plugin.register({ + name: '@my/plugin', + museLayout: { + header: { + getItems: () => ({ + key: 'my-tool', + icon: 'ToolOutlined', + label: 'My Tool', + position: 'right', + link: '/my-tool' + }) + }, + sider: { + getItems: () => ({ + key: 'my-section', + icon: 'AppstoreOutlined', + label: 'My Section', + order: 50, + children: [ + { key: 'page1', label: 'Page 1', link: '/my/page1' }, + { key: 'page2', label: 'Page 2', link: '/my/page2' } + ] + }) + } + } +}); + +// ❌ INCORRECT - DO NOT use string paths +plugin.register({ + name: '@my/plugin', + 'museLayout.header.getItems': () => ({ /* ... */ }), // WRONG! + 'museLayout.sider.getItems': () => ({ /* ... */ }) // WRONG! +}); +``` + +### Example 2: Customizing Layout Configuration + +```javascript +plugin.register({ + name: '@my/plugin', + museLayout: { + header: { + getConfig: () => ({ + backgroundColor: '#722ed1', + title: 'My Custom App', + subTitle: 'Powered by MUSE', + icon: '/assets/logo.png', + noUserMenu: false, + themeSwitcher: true + }) + }, + sider: { + getConfig: () => ({ + mode: 'collapsable', + siderDefaultCollapsed: true, + theme: 'dark', + width: 280, + homeMenu: true + }) + } + } +}); +``` + +### Example 3: Adding Layout Providers + +```javascript +import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; + +const queryClient = new QueryClient(); + +plugin.register({ + name: '@my/plugin', + museLayout: { + providers: { + getItems: () => ({ + order: 30, + key: 'react-query', + provider: QueryClientProvider, + props: { client: queryClient } + }) + } + } +}); +``` + +### Example 4: Processing Header Items + +```javascript +plugin.register({ + name: '@my/plugin', + museLayout: { + header: { + processItems: (items) => { + // Add a badge to specific items + items.forEach(item => { + if (item.key === 'notifications') { + item.badge = { count: 5 }; + } + }); + + // Remove items based on permissions + const userPermissions = window.MUSE_GLOBAL.getUser().permissions; + if (!userPermissions.includes('admin')) { + const adminIndex = items.findIndex(item => item.key === 'admin-panel'); + if (adminIndex !== -1) items.splice(adminIndex, 1); + } + } + } + } +}); +``` + +### Example 5: Using Exported Functions + +```javascript +// Get the layout plugin +const layoutPlugin = plugin.getPlugin('@ebay/muse-layout-antd'); + +// Trigger layout update after dynamic changes +function updateMyMenuItems() { + // Your logic to modify menu items... + + // Force layout to re-render with new items + layoutPlugin.exports.updateMuseLayout(); +} +``` + +### Example 6: Adding Items to Existing Header Menus + +```javascript +// Assuming another plugin created a 'help' menu in the header +plugin.register({ + name: '@my/plugin', + museLayout: { + header: { + help: { // This matches the 'key' of the parent menu item + getItems: () => [ + { + key: 'my-docs', + label: 'My Plugin Docs', + link: '/docs/my-plugin' + }, + { + key: 'my-tutorial', + label: 'Tutorial', + onClick: () => window.open('/tutorial') + } + ] + } + } + } +}); +``` diff --git a/ui-plugins/muse-layout-antd/package.json b/ui-plugins/muse-layout-antd/package.json index f18ff0fa..68bad566 100644 --- a/ui-plugins/muse-layout-antd/package.json +++ b/ui-plugins/muse-layout-antd/package.json @@ -1,6 +1,6 @@ { "name": "@ebay/muse-layout-antd", - "version": "1.3.2", + "version": "1.3.3", "repository": "", "type": "module", "muse": { diff --git a/ui-plugins/muse-lib-antd/MUSE_README.md b/ui-plugins/muse-lib-antd/MUSE_README.md new file mode 100644 index 00000000..278586d0 --- /dev/null +++ b/ui-plugins/muse-lib-antd/MUSE_README.md @@ -0,0 +1,971 @@ +# Plugin Integration Guide: @ebay/muse-lib-antd + +**Generated**: 2026-03-28 +**Plugin Type**: lib + +--- + +## Table of Contents + +1. [Plugin Purpose & Overview](#1-plugin-purpose--overview) +2. [Extension Points Exposed](#2-extension-points-exposed) +3. [Extension Points Contributed](#3-extension-points-contributed) +4. [Exported Functionality](#4-exported-functionality) +5. [Integration Examples](#5-integration-examples) + +--- + +## 1. Plugin Purpose & Overview + +### What This Plugin Does + +`@ebay/muse-lib-antd` is a library plugin that provides Ant Design (antd) UI components and utilities to MUSE applications. It serves as a shared UI framework layer, preventing code duplication by providing a single instance of Ant Design that all consuming plugins can use. + +### Key Features + +- **Ant Design Components**: Provides the complete Ant Design 5.x component library as shared modules +- **Reusable UI Components**: Offers custom-built components like MetaMenu, DropdownMenu, ErrorBox, CodeViewer, and more +- **Form Utilities**: Provides extensible form and array utilities via `extendFormMeta()` and `extendArray()` +- **Nice Form Integration**: Pre-configured Nice Form React with Ant Design adapter and custom widgets +- **Dark Mode Support**: Built-in dark mode theme switching with Redux state management +- **React Router Integration**: Provides route configuration and React Router setup +- **Redux State Management**: Includes Redux reducer for common state (dark mode, etc.) +- **Config Provider Wrapper**: Extensible Ant Design ConfigProvider with theme management + +### Plugin Type: lib + +As a **lib plugin**, this plugin loads before normal plugins and provides shared modules to avoid duplicating dependencies. Other plugins that depend on Ant Design or React will consume these modules from this plugin rather than bundling their own copies, reducing bundle size and ensuring version consistency. + +--- + +## 2. Extension Points Exposed + +This plugin exposes the following extension points that OTHER plugins can implement to extend its functionality. + +### Summary + +- **Total Extension Points**: 7 base patterns (expandable to unlimited via parameter-based extension points) +- **Categories**: Form Extensions, Array Extensions, Menu Extensions, ConfigProvider Extensions + +### Extension Point Patterns + +This plugin uses **parameter-based extension points**, where the extension point name is passed as a parameter to utility functions. This allows unlimited extensibility without hardcoding specific extension point names. + +#### Form Extension Pattern (via `extendFormMeta`) + +**Base Extension Point**: `${extBase}.*` (where `extBase` is provided by caller) + +When a plugin calls `extendFormMeta(meta, extBase, ...args)`, other plugins can implement these extension points: + +##### `${extBase}.preProcessMeta` + +- **Purpose**: Hook to modify form metadata before fields are added +- **When Invoked**: Called at the start of `extendFormMeta()`, before any field processing +- **Context Parameters**: + - `...args`: Variable arguments passed through from the caller +- **Expected Return**: `void` (modifies arguments in place) +- **Use Case Example**: Initialize form state, add validators, or set up form-level configuration +- **File Reference**: src/utils.js:12 + +##### `${extBase}.getFields` + +- **Purpose**: Allows plugins to contribute additional fields to the form +- **When Invoked**: Called during form metadata construction to gather field definitions +- **Context Parameters**: + - `...args`: Variable arguments passed through from the caller (typically includes form context, record data, etc.) +- **Expected Return**: `Array` - Array of Nice Form field definitions +- **Use Case Example**: Add custom fields to app creation form, plugin info form, or any extensible form +- **File Reference**: src/utils.js:13 + +##### `${extBase}.processMeta` + +- **Purpose**: Hook to modify the complete form metadata after fields are added +- **When Invoked**: Called after all fields are collected but before post-processing +- **Context Parameters**: + - `...args`: Variable arguments passed through from the caller +- **Expected Return**: `void` (modifies metadata in place) +- **Use Case Example**: Reorder fields, add field dependencies, modify field properties based on other fields +- **File Reference**: src/utils.js:15 + +##### `${extBase}.postProcessMeta` + +- **Purpose**: Final hook to modify form metadata after all processing +- **When Invoked**: Called after all other form processing is complete +- **Context Parameters**: + - `...args`: Variable arguments passed through from the caller +- **Expected Return**: `void` (modifies metadata in place) +- **Use Case Example**: Apply final validation rules, add computed fields, or finalize form layout +- **File Reference**: src/utils.js:16 + +##### `${extBase}.getWatchingFields` + +- **Purpose**: Allows plugins to define fields that should be watched for changes +- **When Invoked**: Called during form metadata construction to gather watching field configurations +- **Context Parameters**: + - `...args`: Variable arguments passed through from the caller +- **Expected Return**: `Array` - Array of watching field configurations for Nice Form +- **Use Case Example**: Define field dependencies where one field's value affects another's visibility or validation +- **File Reference**: src/utils.js:20 + +#### Array Extension Pattern (via `extendArray`) + +**Base Extension Point**: `${extBase}.*` (where `extBase` and `extName` are provided by caller) + +When a plugin calls `extendArray(arr, extName, extBase, ...args)`, other plugins can implement these extension points: + +##### `${extBase}.preProcess${CapitalizedExtName}` + +- **Purpose**: Hook to process array before items are added +- **When Invoked**: Called at the start of `extendArray()`, before collecting items +- **Context Parameters**: + - `...args`: Variable arguments passed through from the caller +- **Expected Return**: `void` (modifies arguments in place) +- **Use Case Example**: Initialize array context, set up filters +- **File Reference**: src/utils.js:38 + +##### `${extBase}.get${CapitalizedExtName}` + +- **Purpose**: Allows plugins to contribute items to the array +- **When Invoked**: Called during array construction to gather items +- **Context Parameters**: + - `...args`: Variable arguments passed through from the caller +- **Expected Return**: `Array` - Array of items to add +- **Use Case Example**: Add table columns, menu items, action buttons, tabs, etc. +- **File Reference**: src/utils.js:39 + +##### `${extBase}.process${CapitalizedExtName}` + +- **Purpose**: Hook to modify array after items are collected +- **When Invoked**: Called after all items are gathered but before post-processing +- **Context Parameters**: + - `...args`: Variable arguments passed through from the caller +- **Expected Return**: `void` (modifies array in place) +- **Use Case Example**: Filter, reorder, or transform items +- **File Reference**: src/utils.js:41 + +##### `${extBase}.postProcess${CapitalizedExtName}` + +- **Purpose**: Final hook to modify array after all processing +- **When Invoked**: Called after all other processing is complete +- **Context Parameters**: + - `...args`: Variable arguments passed through from the caller +- **Expected Return**: `void` (modifies array in place) +- **Use Case Example**: Apply final transformations, add computed items +- **File Reference**: src/utils.js:42 + +#### MetaMenu Extension Pattern + +**Base Extension Point**: `${baseExtPoint}.*` (where `baseExtPoint` is provided via props) + +##### `${baseExtPoint}.getItems` + +- **Purpose**: Allows plugins to contribute menu items to a MetaMenu component +- **When Invoked**: Called when MetaMenu renders, if `baseExtPoint` prop is provided +- **Context Parameters**: + - `meta`: Object - The menu metadata object containing configuration +- **Expected Return**: `Array` - Array of menu item definitions +- **Use Case Example**: Add custom menu items to application sidebar, header menu, or dropdown menus +- **File Reference**: src/features/common/MetaMenu.js:23 + +##### `${baseExtPoint}.processItems` + +- **Purpose**: Hook to modify all menu items after they're collected and normalized +- **When Invoked**: Called after all items (from meta and extensions) are collected, normalized, and have parent-child relationships resolved +- **Context Parameters**: + - `meta`: Object - The menu metadata object + - `newItems`: Array - The normalized array of menu items + - `itemByKey`: Object - Map of menu items keyed by their key property +- **Expected Return**: `void` (modifies items in place) +- **Use Case Example**: Reorder items, hide items based on permissions, add badges or notifications +- **File Reference**: src/features/common/MetaMenu.js:57 + +#### DropdownMenu Extension Pattern + +**Extension Point**: Custom (passed via `extPoint` prop) + +When a DropdownMenu component is rendered with an `extPoint` prop, it invokes that extension point: + +##### Custom Extension Point (via `extPoint` prop) + +- **Purpose**: Allows plugins to modify dropdown menu items dynamically +- **When Invoked**: Called during DropdownMenu rendering, before items are processed +- **Context Parameters**: + - `items`: Array - The menu items array to modify + - `...extPointParams`: Variable arguments passed via the `extPointParams` prop +- **Expected Return**: `void` (modifies items array in place) +- **Use Case Example**: Add context-specific actions to record dropdown menus, filter items based on permissions +- **File Reference**: src/features/common/DropdownMenu.js:38 + +#### ConfigProvider Extension + +##### `museLibAntd.configProvider.processProps` + +- **Purpose**: Allows plugins to modify Ant Design ConfigProvider props (e.g., theme tokens, component defaults) +- **When Invoked**: Called when ConfigProviderWrapper renders, before passing props to Ant Design's ConfigProvider +- **Context Parameters**: + - `configProps`: Object - The ConfigProvider props object with structure `{ theme: { algorithm: ... } }` +- **Expected Return**: `void` (modifies configProps in place) +- **Use Case Example**: Customize theme tokens (borderRadius, colors), add locale configuration, set component-level defaults +- **File Reference**: src/features/common/ConfigProviderWrapper.js:16 + +### Usage Example + +**CRITICAL**: Extension points are **nested object properties**, NOT string paths! + +```javascript +// Example: Extending a form via extendFormMeta +// ✅ CORRECT - nested object properties +plugin.register({ + name: 'my-custom-plugin', + am: { + createAppForm: { + getFields: (context) => { + return [ + { + key: 'customField', + label: 'Custom Field', + widget: 'input', + order: 100, + } + ]; + }, + processMeta: (context) => { + // Modify form metadata after fields are added + context.meta.fields.forEach(field => { + if (field.key === 'appName') { + field.required = true; + } + }); + } + } + } +}); + +// Example: Customizing Ant Design theme +// ✅ CORRECT - nested object properties +plugin.register({ + name: 'my-theme-plugin', + museLibAntd: { + configProvider: { + processProps: (configProps) => { + configProps.theme.token = { + colorPrimary: '#00b96b', + borderRadius: 2, + }; + } + } + } +}); + +// Example: Adding items to a MetaMenu +// ✅ CORRECT - nested object properties +plugin.register({ + name: 'my-menu-plugin', + museLayout: { + siderMenu: { + getItems: (meta) => { + return [ + { + key: 'custom-item', + label: 'Custom Menu Item', + icon: 'star', + link: '/custom-page', + order: 50, + } + ]; + } + } + } +}); +``` + +--- + +## 3. Extension Points Contributed + +This plugin implements the following extension points from OTHER plugins to integrate with the MUSE ecosystem. + +### Summary + +- **Total Contributions**: 3 +- **Host Plugins**: @ebay/muse-lib-react + +### By Host Plugin + +#### Contributes to: @ebay/muse-lib-react + +##### `route` + +- **Invoked By**: @ebay/muse-lib-react routing system +- **What This Plugin Provides**: Route configuration for `/plugin-muse-antd` path with common feature routes +- **Why Needed**: Registers the plugin's route structure so it can be accessed via React Router +- **File Reference**: src/index.js:17, src/common/routeConfig.js:1-16 + +##### `root.getProviders` + +- **Invoked By**: @ebay/muse-lib-react root component provider chain +- **What This Plugin Provides**: Ant Design ConfigProviderWrapper with dark mode support +- **Why Needed**: Wraps the app with Ant Design's ConfigProvider to enable theming, localization, and component configuration. Sets order to 35 to ensure it wraps most other providers. +- **File Reference**: src/index.js:18-26, src/features/common/ConfigProviderWrapper.js:1-18 + +##### `reducer` + +- **Invoked By**: @ebay/muse-lib-react Redux store configuration +- **What This Plugin Provides**: Redux reducer managing common state (currently dark mode preference) +- **Why Needed**: Manages shared state for dark mode theme switching and persists preference to localStorage +- **File Reference**: src/index.js:27, src/common/rootReducer.js:1-14 + +--- + +## 4. Exported Functionality + +This plugin exports the following functionality for use by other plugins. + +**Access via**: `plugin.getPlugin('@ebay/muse-lib-antd').exports` + +### Library Modules (Shared Dependencies) + +As a **lib plugin**, this plugin's primary export is the complete set of Ant Design and icon modules, which are automatically shared with consuming plugins via MUSE's module federation system: + +#### Default Export Object + +```javascript +{ + antd, // Complete Ant Design 5.x library (all components) + icons, // @ant-design/icons (all Ant Design icons) + utils, // This plugin's utility functions +} +``` + +- **`antd`**: Complete Ant Design 5.19.0 component library + - **Access**: Other plugins import from 'antd' and it resolves to this shared instance + - **Why Exported**: Prevents duplicate Ant Design bundles across plugins, ensures version consistency + - **Components Include**: Button, Table, Form, Modal, Drawer, Menu, Layout, Input, Select, DatePicker, etc. (100+ components) + +- **`icons`**: Complete @ant-design/icons 5.3.7 icon library + - **Access**: Other plugins import from '@ant-design/icons' and it resolves to this shared instance + - **Why Exported**: Prevents duplicate icon bundles, ensures consistent icon versions + - **Icons Include**: All Ant Design icons (outlined, filled, two-tone) + +- **`utils`**: Utility functions for extensibility + - **Access**: Import from '@ebay/muse-lib-antd/src/utils' + - **Why Exported**: Provides helpers for making forms and arrays extensible via extension points + +### Components (from src/features/common) + +The following custom components are exported via standard ES6 exports (not via plugin.exports): + +#### UI Components + +- **`MetaMenu`**: Metadata-driven menu component with extension point support + - **Purpose**: Render Ant Design menus from declarative configuration with plugin extensibility + - **Props**: `meta` (menu config), `baseExtPoint` (extension point base), `autoSort` (auto-sort items) + - **Use Cases**: Sidebar menus, header menus, dropdown menus with plugin-contributed items + - **File Reference**: src/features/common/MetaMenu.js:1-165 + +- **`DropdownMenu`**: Dropdown menu with actions and extension point support + - **Purpose**: Render dropdown menus with highlighted actions and extension points + - **Props**: `items`, `triggerNode`, `extPoint`, `extPointParams`, `size` + - **Use Cases**: Record action menus, context menus + - **File Reference**: src/features/common/DropdownMenu.js:1-93 + +- **`ErrorBox`**: Error display component with retry functionality + - **Purpose**: Display errors with stack traces, retry buttons, and customizable messaging + - **Props**: `title`, `content`, `error`, `onRetry`, `showStack`, `btnSize` + - **Use Cases**: Error boundaries, API error display, validation errors + - **File Reference**: src/features/common/ErrorBox.js + +- **`ErrorBoundary`**: React error boundary component + - **Purpose**: Catch React rendering errors and display fallback UI + - **Props**: `message`, `children` + - **Use Cases**: Wrap components to prevent entire app crashes + - **File Reference**: src/features/common/ErrorBoundary.js + +- **`GlobalLoading`**: Full-page or container loading indicator + - **Purpose**: Display loading spinner overlays + - **Props**: `full` (boolean for full-page mode) + - **Use Cases**: Page transitions, async data loading + - **File Reference**: src/features/common/GlobalLoading.js + +- **`GlobalErrorBox`**: Modal error display + - **Purpose**: Display errors in modal dialogs + - **Props**: `title`, `error`, `onOk`, `okText`, `onClose` + - **Use Cases**: Critical errors, API failures that need user acknowledgment + - **File Reference**: src/features/common/GlobalErrorBox.js + +- **`CodeViewer`**: Syntax-highlighted code display + - **Purpose**: Display formatted code with syntax highlighting and copy functionality + - **Props**: `code`, `language`, `theme`, `allowCopy`, `title` + - **Use Cases**: Display JSON, YAML, JavaScript, or other code snippets + - **File Reference**: src/features/common/CodeViewer.js + +- **`BlockView`**: Formatted value display component + - **Purpose**: Display values in a consistent block format, with email link support + - **Props**: `value`, `openEmail` + - **Use Cases**: Display field values in view mode, form field previews + - **File Reference**: src/features/common/BlockView.js + +- **`DateView`**: Date/time display component + - **Purpose**: Format and display dates/times with various format options + - **Props**: `value`, `dateOnly`, `timeOnly`, `dateFormat`, `timeFormat` + - **Use Cases**: Display timestamps, dates, or times in consistent format + - **File Reference**: src/features/common/DateView.js + +- **`Highlighter`**: Text search highlighting component + - **Purpose**: Highlight search terms within text + - **Props**: `search`, `text` + - **Use Cases**: Search results, filtered lists + - **File Reference**: src/features/common/Highlighter.js + +- **`Icon`**: Icon wrapper component + - **Purpose**: Render Ant Design icons with consistent API + - **Props**: `type`, `className`, `onClick`, `style` + - **Use Cases**: Consistent icon rendering across app + - **File Reference**: src/features/common/Icon.js + +- **`LoadingMask`**: Inline loading indicator + - **Purpose**: Display loading state for components + - **Use Cases**: Inline component loading states + - **File Reference**: src/features/common/LoadingMask.js + +- **`PageNotFound`**: 404 error page component + - **Purpose**: Display "page not found" error page + - **Use Cases**: React Router fallback route + - **File Reference**: src/features/common/PageNotFound.js + +- **`RequestStatus`**: Request state management component + - **Purpose**: Handle loading, error, and success states for async requests + - **Props**: `pending`, `loading`, `error`, `errorMode`, `loadingMode`, `dismissError` + - **Use Cases**: Wrap async components to handle loading/error states + - **File Reference**: src/features/common/RequestStatus.js + +- **`StatusLabel`**: Status badge component + - **Purpose**: Display status with colored labels + - **Props**: `label`, `type` (SUCCESS, FAILURE, PROCESSING, etc.) + - **Use Cases**: Display plugin status, request status, app status + - **File Reference**: src/features/common/StatusLabel.js + +- **`TableBar`**: Table toolbar with search + - **Purpose**: Provide search bar and actions for tables + - **Props**: `onSearch`, `search`, `placeholder`, `children` + - **Use Cases**: Table filtering, bulk actions + - **File Reference**: src/features/common/TableBar.js + +- **`TagInput`**: Tag input widget + - **Purpose**: Input component for entering multiple tags + - **Props**: `max`, `value`, `onChange` + - **Use Cases**: Form fields for arrays of strings (tags, labels, keywords) + - **File Reference**: src/features/common/TagInput.js + +- **`ConfigProviderWrapper`**: Ant Design ConfigProvider with dark mode + - **Purpose**: Wrap app with Ant Design theme provider and dark mode support + - **Props**: `children` + - **Use Cases**: Root-level provider for Ant Design theming + - **File Reference**: src/features/common/ConfigProviderWrapper.js + +### Utilities (from src/utils.js) + +- **`extendFormMeta(meta, extBase, ...args)`**: Make Nice Form metadata extensible + - **Purpose**: Allow plugins to extend form definitions via extension points + - **Parameters**: + - `meta`: FormMeta object with `fields` array + - `extBase`: String - base name for extension points (e.g., 'am.createAppForm') + - `...args`: Additional arguments passed to extension point handlers + - **Returns**: `{ watchingFields: Array, meta: FormMeta }` + - **Extension Points Invoked**: + - `${extBase}.preProcessMeta` + - `${extBase}.getFields` + - `${extBase}.processMeta` + - `${extBase}.postProcessMeta` + - `${extBase}.getWatchingFields` + - **Use Cases**: Create extensible forms in manager UIs where plugins can add custom fields + - **File Reference**: src/utils.js:11-23 + +- **`extendArray(arr, extName, extBase, ...args)`**: Make arrays extensible + - **Purpose**: Allow plugins to extend arrays (columns, actions, tabs, etc.) via extension points + - **Parameters**: + - `arr`: Array - the array to extend + - `extName`: String - name of the array type (e.g., 'columns', 'actions') + - `extBase`: String - base name for extension points (e.g., 'pm.pluginList') + - `...args`: Additional arguments passed to extension point handlers + - **Returns**: The modified array + - **Extension Points Invoked**: + - `${extBase}.preProcess${CapitalizedExtName}` + - `${extBase}.get${CapitalizedExtName}` + - `${extBase}.process${CapitalizedExtName}` + - `${extBase}.postProcess${CapitalizedExtName}` + - **Use Cases**: Create extensible table columns, action menus, tabs, etc. + - **File Reference**: src/utils.js:36-45 + +### Other Resources + +- **History Object**: Browser history singleton for programmatic navigation + - **Access**: Import from '@ebay/muse-lib-antd/src/common/history' + - **Type**: `BrowserHistory` from history v5 + - **Global Access**: Also available at `window.MUSE_ANTD_HISTORY` + - **Methods**: `push(path)`, `replace(path)`, `go(n)`, `goBack()`, `goForward()` + - **Use Cases**: Navigate programmatically without React Router hooks, imperative navigation in non-component code + - **File Reference**: src/common/history.js:1-6 + +- **Table Configuration**: Default Ant Design table props and utilities + - **Access**: Import from '@ebay/muse-lib-antd/src/features/common/tableConfig' + - **Exports**: `defaultProps`, `defaultSorter`, `defaultFilter` + - **Use Cases**: Consistent table configuration across app + - **File Reference**: src/features/common/tableConfig.js:1-42 + +- **Redux State**: Dark mode state management + - **Hook**: `useSetIsDarkMode()` - Returns `{ isDarkMode, setIsDarkMode }` + - **Action**: `setIsDarkMode(isDarkMode)` - Dispatch action to toggle dark mode + - **Use Cases**: Theme switching, dark mode preferences + - **File Reference**: src/features/common/redux/setIsDarkMode.js:1-31 + +- **Modals**: Pre-registered Nice Modal React modals + - **Modal ID**: `'muse-lib-antd.loading-modal'` + - **Component**: LoadingModal + - **Use Cases**: Show loading dialogs via Nice Modal React + - **File Reference**: src/modals.js:1-4 + +- **Nice Form Widgets**: Custom form widgets registered with Nice Form + - **Widgets**: + - `'tag'`: TagInput component + - `'tag-view'`: BlockView component + - `'date-view'`: DateView (dateOnly) + - `'time-view'`: DateView (timeOnly) + - `'datetime-view'`: DateView (full) + - **Use Cases**: Use in Nice Form field definitions + - **File Reference**: src/initNiceForm.jsx:1-16 + +### Using Exported Functionality + +```javascript +// Accessing shared Ant Design components (automatic via module federation) +import { Button, Table, Form } from 'antd'; +import { UserOutlined, SearchOutlined } from '@ant-design/icons'; + +// Using custom components +import { + MetaMenu, + DropdownMenu, + ErrorBox, + CodeViewer, + DateView +} from '@ebay/muse-lib-antd/src/features/common'; + +// Using utilities +import { extendFormMeta, extendArray } from '@ebay/muse-lib-antd/src/utils'; + +// Example: Make a form extensible +const meta = { + fields: [ + { key: 'name', label: 'Name', widget: 'input' } + ] +}; + +const { watchingFields, meta: extendedMeta } = extendFormMeta( + meta, + 'myPlugin.myForm', + { context: 'additional data' } +); + +// Example: Make a table columns array extensible +const columns = [ + { key: 'name', title: 'Name', dataIndex: 'name' } +]; + +extendArray(columns, 'columns', 'myPlugin.myTable', tableData); + +// Example: Using dark mode +import { useSetIsDarkMode } from '@ebay/muse-lib-antd/src/features/common/redux/hooks'; + +function MyComponent() { + const { isDarkMode, setIsDarkMode } = useSetIsDarkMode(); + + return ( + + ); +} + +// Example: Using MetaMenu +const menuMeta = { + items: [ + { key: 'home', label: 'Home', link: '/', icon: 'home' }, + { key: 'settings', label: 'Settings', link: '/settings', icon: 'setting' } + ], + autoActive: true, + mode: 'inline' +}; + + + +// Example: Using history for programmatic navigation +import history from '@ebay/muse-lib-antd/src/common/history'; + +// Navigate to a different route +function handleCreateSuccess(newId) { + history.push(`/items/${newId}`); +} + +// Or access via global +window.MUSE_ANTD_HISTORY.push('/dashboard'); +``` + +**Note**: As a lib plugin, the primary value is providing shared Ant Design modules. The custom components and utilities are secondary exports that build on top of the shared foundation. + +--- + +## 5. Integration Examples + +**CRITICAL**: Extension points are **nested object properties**, NOT string paths! + +### ✅ CORRECT Syntax +```javascript +plugin.register({ + name: 'my-plugin', + pluginName: { + extensionPoint: (context) => { + // Your implementation + } + } +}); +``` + +### ❌ INCORRECT Syntax +```javascript +plugin.register({ + name: 'my-plugin', + 'pluginName.extensionPoint': (context) => { // WRONG! + // This will NOT work! + } +}); +``` + +--- + +### Example 1: Using Shared Ant Design Components + +Since `@ebay/muse-lib-antd` is a lib plugin, consuming plugins automatically use its shared Ant Design instance: + +```javascript +// In your plugin - automatically uses shared antd from muse-lib-antd +import React from 'react'; +import { Button, Table, Form, Modal } from 'antd'; +import { UserOutlined, PlusOutlined } from '@ant-design/icons'; + +export default function MyComponent() { + return ( +
+ + + + ); +} +``` + +### Example 2: Extending Forms with Custom Fields + +```javascript +import plugin from 'js-plugin'; + +// ✅ CORRECT - nested object properties +plugin.register({ + name: 'my-custom-plugin', + + // Add fields to the create app form + am: { + createAppForm: { + getFields: (context) => { + return [ + { + key: 'githubRepo', + label: 'GitHub Repository', + widget: 'input', + placeholder: 'org/repo', + order: 100, + required: true, + }, + { + key: 'slackChannel', + label: 'Slack Channel', + widget: 'input', + placeholder: '#channel-name', + order: 101, + } + ]; + }, + + // Modify form after all fields are added + processMeta: (context) => { + const { meta } = context; + + // Make appName field required + const appNameField = meta.fields.find(f => f.key === 'appName'); + if (appNameField) { + appNameField.required = true; + appNameField.rules = [ + { required: true, message: 'App name is required' }, + { pattern: /^[a-z][a-z0-9-]*$/, message: 'Must start with letter, use lowercase and hyphens only' } + ]; + } + } + } + } +}); +``` + +### Example 3: Customizing Ant Design Theme + +```javascript +import plugin from 'js-plugin'; + +// ✅ CORRECT - nested object properties +plugin.register({ + name: 'my-theme-plugin', + + museLibAntd: { + configProvider: { + processProps: (configProps) => { + // Customize theme tokens + configProps.theme.token = { + colorPrimary: '#1890ff', // Primary color + colorSuccess: '#52c41a', // Success color + colorWarning: '#faad14', // Warning color + colorError: '#f5222d', // Error color + borderRadius: 4, // Border radius for components + fontSize: 14, // Base font size + fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial', + }; + + // Add component-level configurations + configProps.button = { + defaultProps: { + size: 'middle' + } + }; + + // Add locale if needed + // configProps.locale = enUS; + } + } + } +}); +``` + +### Example 4: Creating Extensible UI with MetaMenu + +```javascript +import React from 'react'; +import { MetaMenu } from '@ebay/muse-lib-antd/src/features/common'; +import plugin from 'js-plugin'; + +// Define base menu +function MySidebar() { + const menuMeta = { + items: [ + { + key: 'dashboard', + label: 'Dashboard', + link: '/dashboard', + icon: 'dashboard', + order: 0 + }, + { + key: 'users', + label: 'Users', + link: '/users', + icon: 'user', + order: 10 + }, + ], + mode: 'inline', + autoActive: true, + }; + + return ( + + ); +} + +// Other plugins can extend the menu +// ✅ CORRECT - nested object properties +plugin.register({ + name: 'my-extension-plugin', + + myApp: { + siderMenu: { + getItems: (meta) => { + return [ + { + key: 'custom-feature', + label: 'Custom Feature', + link: '/custom', + icon: 'star', + order: 20, + } + ]; + }, + + processItems: (meta, items, itemByKey) => { + // Hide users menu if user doesn't have permission + const usersItem = itemByKey['users']; + if (usersItem && !userHasPermission('view_users')) { + items.splice(items.indexOf(usersItem), 1); + } + } + } + } +}); +``` + +### Example 5: Using Extensibility Utilities + +```javascript +import React from 'react'; +import { extendFormMeta, extendArray } from '@ebay/muse-lib-antd/src/utils'; +import NiceForm from '@ebay/nice-form-react'; + +function MyForm({ record }) { + // Create extensible form + const baseMeta = { + fields: [ + { key: 'name', label: 'Name', widget: 'input', required: true }, + { key: 'email', label: 'Email', widget: 'input', required: true }, + ] + }; + + const { meta, watchingFields } = extendFormMeta( + baseMeta, + 'myPlugin.userForm', + { record, mode: 'create' } + ); + + return ; +} + +// Create extensible table columns +function MyTable({ dataSource }) { + const columns = [ + { key: 'name', title: 'Name', dataIndex: 'name', sorter: true }, + { key: 'email', title: 'Email', dataIndex: 'email' }, + ]; + + // Allow plugins to add columns + extendArray(columns, 'columns', 'myPlugin.userTable', { dataSource }); + + return
; +} + +// Other plugins can extend these +// ✅ CORRECT - nested object properties +plugin.register({ + name: 'my-extension', + + myPlugin: { + userForm: { + getFields: ({ record, mode }) => { + if (mode === 'create') { + return [ + { + key: 'role', + label: 'Role', + widget: 'select', + options: ['admin', 'user'], + order: 50 + } + ]; + } + return []; + } + }, + + userTable: { + getColumns: ({ dataSource }) => { + return [ + { + key: 'role', + title: 'Role', + dataIndex: 'role', + filters: [ + { text: 'Admin', value: 'admin' }, + { text: 'User', value: 'user' } + ], + onFilter: (value, record) => record.role === value, + order: 50 + } + ]; + } + } + } +}); +``` + +### Example 6: Using Custom Components + +```javascript +import React from 'react'; +import { + ErrorBox, + CodeViewer, + DateView, + Highlighter, + StatusLabel, + DropdownMenu +} from '@ebay/muse-lib-antd/src/features/common'; + +function MyComponent({ data, error, searchTerm }) { + if (error) { + return ( + + ); + } + + return ( +
+ {/* Display formatted code */} + + + {/* Display dates */} + + + {/* Highlight search terms */} + + + {/* Display status */} + + + {/* Dropdown menu with actions */} + +
+ ); +} +``` diff --git a/ui-plugins/muse-lib-antd/package.json b/ui-plugins/muse-lib-antd/package.json index 9401fe0a..55796f67 100644 --- a/ui-plugins/muse-lib-antd/package.json +++ b/ui-plugins/muse-lib-antd/package.json @@ -1,6 +1,6 @@ { "name": "@ebay/muse-lib-antd", - "version": "1.3.2", + "version": "1.3.3", "repository": "", "muse": { "type": "lib", diff --git a/ui-plugins/muse-lib-react/CLAUDE.md b/ui-plugins/muse-lib-react/CLAUDE.md new file mode 100644 index 00000000..aef499a9 --- /dev/null +++ b/ui-plugins/muse-lib-react/CLAUDE.md @@ -0,0 +1,235 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +`@ebay/muse-lib-react` is a **lib plugin** for the MUSE micro-frontends framework. It serves as the foundational React library plugin that: + +1. **Bootstraps the React application** - Creates the React root and renders the app +2. **Provides shared modules** - Bundles popular React libraries (React Router, Redux, React Query, lodash, Nice Modal) as shared dependencies for other plugins +3. **Defines extension points** - Allows other plugins to extend routing, Redux store, providers, and UI components + +As a lib plugin (`muse.type: "lib"` in package.json:6), this plugin loads before normal plugins and exports shared modules to avoid duplicate dependencies across the application. + +## Common Commands + +### Development + +```bash +# Start development server (uses CRACO) +pnpm start + +# Build for production +pnpm build + +# Build for development (faster, no optimization) +pnpm build:dev + +# Build for testing (with coverage instrumentation) +pnpm build:test +``` + +### Testing + +```bash +# Run tests in CI mode (single run with coverage) +pnpm test + +# Run tests in watch mode with coverage +pnpm test:dev + +# View test configuration +pnpm test:config +``` + +### Deployment + +```bash +# Build both prod and dev, then release and deploy +pnpm deploy + +# Generate TypeDoc documentation for extension points +pnpm gen-muse-doc +``` + +## Architecture Overview + +### Bootstrap Flow + +The plugin follows this initialization sequence: + +1. **Plugin registration** (`src/index.js:14`) - Registers plugin with js-plugin +2. **App entry push** (`src/index.js:36`) - Adds `renderApp` function to `window.MUSE_GLOBAL.appEntries` +3. **Root rendering** (`src/index.js:18-34`) - When triggered: + - Invokes `root.beforeRender` extension point + - Creates React root and renders `` component + - Invokes `root.afterRender` and `onReady` extension points + +### Core Components + +#### Root Component (`src/Root.js`) + +The Root component is responsible for: + +- **Provider stack** (`src/Root.js:144-179`) - Creates ordered provider chain: + 1. React Query Provider (order: 10) + 2. Redux Provider (order: 20) + 3. SubApp Context Provider (order: 30) + 4. Nice Modal Provider (order: 40) + 5. React Router Provider (order: 50) + +- **Extension point integration** - Uses `extendArray()` helper (`src/utils.js:15`) to allow plugins to: + - Add/modify/remove providers via `root.preProcessProviders`, `root.getProviders`, `root.processProviders`, `root.postProcessProviders` + - Customize router props via `routerProps` extension point + +- **Router configuration** - Supports three router types via `window.MUSE_GLOBAL.appVariables.routerType`: + - `browser` (default) - BrowserRouter with history object + - `hash` - HashRouter + - `memory` - MemoryRouter + +- **Route rendering** - Converts React Router v3-style route config to v6 elements using `renderRouteConfigV3()` (`src/Root.js:35-102`) + +#### Route Configuration (`src/common/routeConfig.js`) + +Routes are assembled from multiple sources: + +1. **Built-in feature routes** - `homeRoute`, `commonRoute`, `subAppRoute` (`src/common/routeConfig.js:12`) +2. **Plugin routes** - Collected via `plugin.invoke('!route')` (`src/common/routeConfig.js:87`) +3. **Homepage resolution** - Finds homepage component via `home.homepage` extension point (`src/common/routeConfig.js:94-111`) +4. **Route normalization** - Handles absolute paths, parent references, and path arrays (`src/common/routeConfig.js:32-82`) + +**Important route handling**: +- Routes starting with `/` are moved to top level +- Routes with `parent` property are moved to correct parent's `childRoutes` +- Routes with array paths are expanded to multiple route objects + +#### Redux Store (`src/common/configStore.js`, `src/common/rootReducer.js`) + +Redux setup: + +- **Middlewares** - Redux Thunk (always) + Redux Logger (dev only) +- **DevTools** - Enabled in development if extension installed +- **Hot reloading** - Supports reducer hot replacement in dev mode +- **Plugin reducers** - Plugins can contribute reducers via two extension points: + - `reducer` - Adds plugin-level reducer under auto-generated key `plugin-` + - `reducers` - Adds root-level reducers with custom keys + +### Sub-App System + +The sub-app feature (`src/features/sub-app/`) enables embedding external MUSE apps in iframes: + +- **Configuration** - Sub-apps defined in `window.MUSE_GLOBAL.plugins` under `subApps` array +- **Route generation** (`src/features/sub-app/route.js:22-36`) - Creates routes for each sub-app with `mountPoint: 'default'` +- **Container component** (`src/features/sub-app/SubAppContainer.js`) - Detects URL changes and loads matching sub-app in iframe +- **Context communication** - Parent-child messaging via `window.MUSE_GLOBAL.msgEngine` and `SubAppContext` (`src/features/sub-app/SubAppContext.js`) + +**Important**: Sub-app implementation details are documented in `DEV.md`. + +## Extension Points + +This plugin defines extensive extension points for customization. Full documentation is auto-generated in `MUSE.md` via `pnpm gen-muse-doc`. + +### Key Extension Points + +**Root lifecycle**: +- `root.beforeRender` - Called before root element renders +- `root.afterRender` - Called after root element renders +- `onReady` - Called after app is mounted to DOM + +**Provider customization**: +- `root.preProcessProviders` - Pre-process providers before collection +- `root.getProviders` - Contribute additional providers +- `root.processProviders` - Process collected providers +- `root.postProcessProviders` - Final provider processing + +**Routing**: +- `route` - Contribute route definitions (can return single route or array) +- `routerProps` - Merge props into Router component +- `home.homepage` - Provide custom homepage component +- `home.mainLayout` - Provide custom main layout + +**Redux**: +- `reducer` - Contribute plugin-level reducer +- `reducers` - Contribute root-level reducers + +**Other**: +- `rootComponent` - Render initialization component (should return null, no UI) +- `root.renderChildren` - Wrap root element with custom components + +## Build System + +### CRACO (Create React App Configuration Override) + +Uses CRACO to customize Create React App (`craco.config.js`): + +- **Plugins**: + - `CracoLessPlugin` - Adds Less support + - `MuseCracoPlugin` - MUSE-specific webpack/babel configuration + +- **Jest configuration** - Custom test setup: + - Test environment: `jsdom` + - Test pattern: `tests/**/*.test.js` + - Setup file: `tests/setupAfterEnv.js` + - Mock files for styles and assets + +### Vite Support + +Also supports Vite as build tool (`vite.config.js`): + +- **Plugins**: + - `@vitejs/plugin-react` - React support + - `museVitePlugin` - MUSE integration for shared modules + +- **JSX in .js files** - Configured to treat `.js` files in `src/` as JSX + +### Shared Module Exports + +As a lib plugin, exports these modules (`src/index.js:41`): +- `Loadable` - react-loadable for code splitting +- `_` - lodash utilities +- `reactUse` - react-use hooks collection +- `reactRouterDom` - React Router v6 +- `reactQuery` - TanStack React Query v4 + +## Key Files + +- `src/index.js` - Plugin registration and app entry point +- `src/Root.js` - Root React component with provider stack and routing +- `src/common/routeConfig.js` - Route assembly and normalization +- `src/common/rootReducer.js` - Redux reducer combination with plugin integration +- `src/common/configStore.js` - Redux store configuration +- `src/utils.js` - `extendArray()` helper for extension point integration +- `MUSE.md` - Auto-generated extension point documentation +- `DEV.md` - Sub-app implementation details + +## Testing + +Tests use Jest with React Testing Library: + +- **Test location**: `tests/` directory (mirrors `src/` structure) +- **Setup**: `tests/setupAfterEnv.js` - Configures testing library and mocks +- **Mocks**: + - `tests/__mocks__/msgEngine.js` - Mock message engine + - `tests/__mocks__/fileMock.js` - Mock static assets + - `tests/__mocks__/styleMock.js` - Mock CSS/Less imports + +**Important**: `MUSE_GLOBAL` object must be mocked in tests as it's required by many components. + +## Package Configuration + +Key `package.json` fields: + +- `muse.type: "lib"` - Identifies this as a lib plugin +- `muse.isAppEntry: true` - Marks plugin as app entry point (bootstrap) +- `muse.appConfig` - Default app configuration (routerType, noSSO) +- `muse.devConfig` - Development server configuration +- `files: ["build", "src"]` - Files included in published package + +## Important Notes + +1. **Extension point invocation** - Use `plugin.invoke('!extensionPoint')` with `!` prefix to get array of all contributions +2. **Provider order** - Lower order numbers render outer in the component tree +3. **Router types** - Router type is determined by `window.MUSE_GLOBAL.appVariables.routerType` +4. **Module federation** - Shared modules are exposed via MUSE build plugins (webpack/vite), not direct exports +5. **CRACO patch** - `muse-cra-patch` command must run before CRACO commands to apply necessary patches diff --git a/ui-plugins/muse-lib-react/MUSE_README.md b/ui-plugins/muse-lib-react/MUSE_README.md new file mode 100644 index 00000000..069f6a6d --- /dev/null +++ b/ui-plugins/muse-lib-react/MUSE_README.md @@ -0,0 +1,621 @@ +# Plugin Integration Guide: @ebay/muse-lib-react + +**Generated**: 2026-03-28 +**Plugin Type**: lib + +--- + +## 1. Plugin Purpose & Overview + +`@ebay/muse-lib-react` is the **foundational React library plugin** for MUSE applications. As a lib-type plugin with app entry capabilities, it serves three critical roles: + +1. **React Application Bootstrap** - Creates the React root element, initializes the application, and renders the main component tree with all providers (Redux, React Router, React Query, Nice Modal) + +2. **Shared Module Provider** - Exports commonly-used React libraries as shared modules to prevent duplicate dependencies across plugins: + - `react-loadable` - Code splitting and lazy loading + - `lodash` - Utility functions + - `react-use` - Collection of React hooks + - `react-router-dom` v6 - Client-side routing + - `@tanstack/react-query` v4 - Server state management + +3. **Extension Point Foundation** - Defines comprehensive extension points enabling other plugins to: + - Customize routing and homepage + - Extend Redux store with plugin-specific reducers + - Modify the provider stack + - Define main layout and root-level components + - Hook into application lifecycle events + +### Key Features + +- **Provider Stack Management**: Ordered provider chain (React Query → Redux → SubApp Context → Nice Modal → React Router) with extension points at every stage +- **Flexible Routing**: Supports Browser, Hash, and Memory routers with React Router v6, backwards-compatible with v3-style route configuration +- **Redux Integration**: Pre-configured Redux store with Thunk middleware, Redux Logger (dev), and DevTools support +- **Sub-App Support**: Built-in iframe-based sub-application system for embedding external MUSE apps +- **Lifecycle Hooks**: Extension points for initialization (`beforeRender`, `afterRender`, `onReady`) + +--- + +## 2. Extension Points Exposed + +This plugin exposes extension points that allow other plugins to customize and extend the React application. Extension points are organized into four categories: + +### 2.1 Application Lifecycle + +Extension points for hooking into application initialization: + +#### `root.beforeRender` +- **Type**: `Function` +- **Purpose**: Execute logic before the React root element is created and rendered +- **Use Case**: Early initialization tasks like setting up global state, registering event listeners, or configuring third-party libraries +- **Invocation**: Called in `src/index.js:26` before `createRoot()` +- **Context**: Runs after all plugins are loaded but before any React rendering + +#### `root.afterRender` +- **Type**: `Function` +- **Purpose**: Execute logic immediately after the React root element is rendered +- **Use Case**: Post-render initialization like focusing elements, starting analytics, or triggering initial data fetches +- **Invocation**: Called in `src/index.js:31` after `root.render()` +- **Context**: Runs before `onReady`, React tree is mounted but may not be fully painted + +#### `onReady` +- **Type**: `Function` +- **Purpose**: Execute logic after the application is fully mounted to the DOM +- **Use Case**: Final initialization tasks that require the full component tree to be ready +- **Invocation**: Called in `src/index.js:33` as the last step of `renderApp()` +- **Context**: Complete application ready, all components mounted + +### 2.2 Routing & Navigation + +Extension points for customizing application routes and routing behavior: + +#### `route` +- **Type**: `MuseRoute | MuseRoute[]` +- **Purpose**: Register route definitions for plugin-specific pages +- **Use Case**: Add new pages/routes to the application +- **Collection**: Routes collected in `src/common/routeConfig.js:87` via `plugin.invoke('!route')` +- **Route Structure**: + - `path`: URL path (string or array of strings) + - `component`: React component to render + - `childRoutes`: Nested routes + - `parent`: ID of parent route (moves route to parent's `childRoutes`) + - `isIndex`: Mark as index route (duplicated to path='') + - `id`: Unique identifier for route (enables `parent` references) +- **Special Behavior**: + - Routes with `path` starting with `/` are moved to top level + - Routes with `parent` property are relocated to parent's `childRoutes` + - Array paths are expanded to multiple route objects +- **File Reference**: `src/common/routeConfig.js:32-132` + +#### `routerProps` +- **Type**: `Record` +- **Purpose**: Merge additional props into the React Router `RouterProvider` component +- **Use Case**: Customize router behavior (e.g., future flags, error handlers) +- **Collection**: First contribution used via `plugin.invoke('!routerProps')[0]` in `src/Root.js:140` +- **Note**: Props are merged with defaults (`basename`, `router`, `navigator`) +- **File Reference**: `src/Root.js:140-177` + +### 2.3 Provider Stack Customization + +Extension points for modifying the provider chain that wraps the application. Providers execute in order from outer to inner (lower order = outer wrapper). + +#### `root.preProcessProviders` +- **Type**: `(context: { providers: ProviderType[] }) => void` +- **Purpose**: Modify or remove built-in providers before custom providers are collected +- **Use Case**: Disable default providers (e.g., remove Redux if using alternative state management) +- **Invocation Order**: 1st - called by `extendArray()` in `src/Root.js:181` +- **Context**: `{ providers }` array of default providers (React Query, Redux, SubApp Context, Nice Modal, React Router) +- **File Reference**: `src/utils.js:17`, `src/Root.js:144-181` + +#### `root.getProviders` +- **Type**: `(context: { providers: ProviderType[] }) => ProviderType | ProviderType[] | void` +- **Purpose**: Contribute additional providers to wrap the application +- **Use Case**: Add custom providers (e.g., theme provider, i18n provider, custom context) +- **Invocation Order**: 2nd - called by `extendArray()` in `src/Root.js:181` +- **Return**: Single provider, array of providers, or nothing +- **Provider Structure**: + - `order`: Rendering order (lower = outer, default sort) + - `key`: Unique identifier + - `provider`: React component (must accept `children` prop) + - `props`: Props object for provider + - `renderProvider`: Custom render function `(children) => ReactNode` (alternative to `provider`) +- **File Reference**: `src/utils.js:18`, `src/Root.js:144-192` + +#### `root.processProviders` +- **Type**: `(context: { providers: ProviderType[] }) => void` +- **Purpose**: Modify the collected providers array after all contributions +- **Use Case**: Reorder providers, modify provider props, or conditionally add/remove providers +- **Invocation Order**: 3rd - called by `extendArray()` in `src/Root.js:181` +- **Context**: `{ providers }` includes both built-in and contributed providers +- **File Reference**: `src/utils.js:20`, `src/Root.js:181` + +#### `root.postProcessProviders` +- **Type**: `(context: { providers: ProviderType[] }) => void` +- **Purpose**: Final opportunity to modify providers before rendering +- **Use Case**: Last-minute adjustments or validations +- **Invocation Order**: 4th (final) - called by `extendArray()` in `src/Root.js:181` +- **Note**: After this, providers are sorted by `order` and rendered +- **File Reference**: `src/utils.js:21`, `src/Root.js:181` + +#### `root.renderChildren` +- **Type**: `(children: ReactNode) => ReactNode` +- **Purpose**: Wrap the root element with custom components +- **Use Case**: Alternative to `getProviders` for wrapping the app (less recommended) +- **Collection**: All contributions applied sequentially in `src/Root.js:105-110` +- **Note**: Prefer `getProviders` for adding providers; this is for non-provider wrappers +- **File Reference**: `src/Root.js:104-111` + +### 2.4 Layout & UI + +Extension points for customizing the application's visual structure: + +#### `home.homepage` +- **Type**: `ComponentType` +- **Purpose**: Provide a custom homepage component for the root route `/` +- **Use Case**: Replace default "Welcome to Muse" page with application-specific homepage +- **Resolution**: Single contribution used; if multiple, checks `window.MUSE_GLOBAL.homepage` for preference +- **Collection**: Via `plugin.getPlugins('home.homepage')` in `src/common/routeConfig.js:96-111` +- **Default**: If not provided, uses built-in `Homepage` component (`src/features/home/Homepage.js`) +- **File Reference**: `src/common/routeConfig.js:94-116` + +#### `home.mainLayout` +- **Type**: `ComponentType<{ children: ReactNode }>` +- **Purpose**: Provide a main layout component that wraps all routes +- **Use Case**: Define application shell (header, sidebar, footer) that persists across pages +- **Collection**: Via `plugin.invoke('!home.mainLayout')` in `src/features/home/App.js:10` +- **Constraint**: Only one layout allowed; multiple contributions will show error message +- **Note**: When using custom layout, undeploy `@ebay/muse-layout-antd` if present +- **File Reference**: `src/features/home/App.js:9-22` + +#### `rootComponent` +- **Type**: `ComponentType` +- **Purpose**: Render initialization components at app root (should return `null`, no UI) +- **Use Case**: Global initialization logic that needs component lifecycle (e.g., modal managers, global listeners) +- **Collection**: All contributions rendered in `src/features/home/App.js:26-29` +- **Important**: Components should not render visible UI, only invisible initialization/context +- **File Reference**: `src/features/home/App.js:6-30` + +### 2.5 State Management + +Extension points for extending the Redux store: + +#### `reducer` +- **Type**: `Reducer` +- **Purpose**: Contribute a plugin-level reducer to the Redux store +- **Use Case**: Add plugin-specific state management to Redux +- **Storage Key**: Auto-generated as `camelCase('plugin-' + pluginName)` +- **Example**: Plugin `@ebay/my-plugin` → state key `pluginEbayMyPlugin` +- **Collection**: Via `plugin.getPlugins('reducer')` in `src/common/rootReducer.js:24-31` +- **File Reference**: `src/common/rootReducer.js:23-31` + +#### `reducers` +- **Type**: `Record>` +- **Purpose**: Contribute root-level reducers with custom state keys +- **Use Case**: Add multiple reducers or control exact state key names +- **Example**: `{ myData: myDataReducer, config: configReducer }` → `state.myData`, `state.config` +- **Collection**: Via `plugin.getPlugins('reducers')` in `src/common/rootReducer.js:33-37` +- **Note**: Provides more control than `reducer` extension point +- **File Reference**: `src/common/rootReducer.js:33-39` + +--- + +## 3. Extension Points Contributed + +This plugin does not contribute to extension points from other plugins. As a foundational lib plugin, it defines the extension point architecture but does not extend external plugins. + +--- + +## 4. Exported Functionality + +As a lib-type plugin, `@ebay/muse-lib-react` exports shared modules and utilities for use by other plugins. + +### 4.1 Shared Modules (Module Federation) + +These modules are exported via the default export in `src/index.js:41` and made available to other plugins through MUSE's module sharing system (webpack/vite plugin externalization): + +#### `Loadable` +- **Source**: `react-loadable` package +- **Purpose**: Code splitting and lazy loading of React components +- **Use Case**: Improve bundle size and load time by dynamically importing components +- **File Reference**: `src/index.js:7,41` + +#### `_` (lodash) +- **Source**: `lodash` package +- **Purpose**: Utility functions for arrays, objects, strings, etc. +- **Use Case**: Common data manipulation without importing lodash separately +- **File Reference**: `src/index.js:8,41` + +#### `reactUse` +- **Source**: `react-use` package +- **Purpose**: Collection of essential React hooks +- **Use Case**: Access common hooks (useMount, useToggle, etc.) without separate package +- **File Reference**: `src/index.js:9,41` + +#### `reactRouterDom` +- **Source**: `react-router-dom` v6 +- **Purpose**: React Router v6 routing library +- **Use Case**: Navigation components (`Link`, `Navigate`) and hooks (`useNavigate`, `useParams`) +- **File Reference**: `src/index.js:11,41` + +#### `reactQuery` +- **Source**: `@tanstack/react-query` v4 +- **Purpose**: Server state management (data fetching, caching, synchronization) +- **Use Case**: Manage API calls and server state with hooks (`useQuery`, `useMutation`) +- **File Reference**: `src/index.js:12,41` + +### 4.2 Utility Components + +These components can be imported from the plugin's `build/` directory by other plugins: + +#### `Nodes` +- **Source**: `src/features/common/Nodes.js` +- **Type**: `ComponentType<{ items: any[], extName: string, extBase: string, extArgs: any }>` +- **Purpose**: Render a list of components with extension point integration +- **Use Case**: Create extendable UI component lists (e.g., toolbar buttons, menu items) +- **Behavior**: Uses `extendArray()` to allow plugins to contribute additional nodes +- **Exported**: `src/features/common/index.js:3` +- **File Reference**: `src/features/common/Nodes.js:1-21` + +#### `useExtPoint` +- **Source**: `src/features/common/useExtPoint.js` +- **Type**: `(extPointName: string, extArgs?: any) => { extNode: ReactNode, values: any[] }` +- **Purpose**: React hook for consuming extension points within components +- **Use Case**: Render extension point contributions as React elements and collect callback values +- **Returns**: + - `extNode`: React fragment containing all extension point components + - `values`: Array of values passed to callbacks +- **Exported**: `src/features/common/index.js:4` +- **File Reference**: `src/features/common/useExtPoint.js:1-29` + +#### `ErrorBoundary` +- **Source**: `src/features/common/ErrorBoundary.js` +- **Type**: `ComponentType<{ children: ReactNode }>` +- **Purpose**: React error boundary component +- **Use Case**: Catch and handle React errors gracefully +- **Exported**: `src/features/common/index.js:2` + +#### `PageNotFound` +- **Source**: `src/features/common/PageNotFound.js` +- **Type**: `ComponentType` +- **Purpose**: Default 404 page component +- **Use Case**: Fallback for unmatched routes +- **Exported**: `src/features/common/index.js:1` + +### 4.3 Sub-App Utilities + +Components and utilities for the sub-application feature: + +#### `SubAppContainer` +- **Source**: `src/features/sub-app/SubAppContainer.js` +- **Type**: `ComponentType<{ subApp: SubAppConfig, subApps: SubAppConfig[] }>` +- **Purpose**: Renders an external MUSE app in an iframe +- **Use Case**: Embed other MUSE applications within the main app +- **Exported**: `src/features/sub-app/index.js:1` + +#### `FixedSubAppContainer` +- **Source**: `src/features/sub-app/FixedSubAppContainer.js` +- **Type**: `ComponentType` +- **Purpose**: Fixed-position sub-app container +- **Use Case**: Persistent sub-app that doesn't follow routing +- **Exported**: `src/features/sub-app/index.js:2` + +#### `SubAppContext` +- **Source**: `src/features/sub-app/SubAppContext.js` +- **Type**: `React.Context` +- **Purpose**: React context for parent-child sub-app communication +- **Use Case**: Share data between parent app and embedded sub-apps +- **Exported**: `src/features/sub-app/index.js:4` + +#### `LoadingSkeleton` +- **Source**: `src/features/sub-app/LoadingSkeleton.js` +- **Type**: `ComponentType` +- **Purpose**: Loading placeholder for sub-apps +- **Use Case**: Display loading state while sub-app loads +- **Exported**: `src/features/sub-app/index.js:3` + +#### `C2SProxyFailed` +- **Source**: `src/features/sub-app/C2SProxyFailed.js` +- **Type**: `ComponentType` +- **Purpose**: Error component for sub-app proxy failures +- **Use Case**: Display when C2S proxy configuration fails +- **Exported**: `src/features/sub-app/index.js:5` + +#### `useSetSubAppState` +- **Source**: `src/features/sub-app/redux/setSubAppState.js` +- **Type**: `() => (state: any) => void` +- **Purpose**: Hook for setting sub-app state from Redux +- **Use Case**: Update sub-app context from parent app +- **Exported**: `src/features/sub-app/redux/hooks.js:1` + +### 4.4 Common Utilities + +#### `extendArray` +- **Source**: `src/utils.js:15-24` +- **Type**: `(arr: any[], extName: string, extBase: string, ...args: any[]) => any[]` +- **Purpose**: Makes an array extensible via js-plugin extension points +- **Use Case**: Internal utility for provider/route extension, can be used by other plugins for custom extension points +- **Behavior**: Invokes `preProcess`, `get`, `process`, `postProcess` extension points, flattens contributions, sorts by order +- **Example**: + ```javascript + const items = []; + extendArray(items, 'items', 'myPlugin.customExt', { context: 'data' }); + // Invokes: myPlugin.customExt.preProcessItems + // myPlugin.customExt.getItems + // myPlugin.customExt.processItems + // myPlugin.customExt.postProcessItems + ``` +- **Exported**: `src/utils.js:26` +- **File Reference**: `src/utils.js:15-24` + +### 4.5 Redux Store Access + +#### `store` +- **Source**: `src/common/store.js` +- **Type**: Redux Store wrapper +- **Purpose**: Access to the global Redux store +- **Use Case**: Dispatch actions or access state outside React components +- **Methods**: + - `getStore()`: Returns the Redux store instance + - `getState()`: Returns current state + - `dispatch(action)`: Dispatches an action + - `subscribe(listener)`: Subscribes to store changes + - `replaceReducer(reducer)`: Hot-swaps reducers +- **File Reference**: `src/common/store.js:1-21` + +#### `history` +- **Source**: `src/common/history.js` +- **Type**: History object (from `history` package) +- **Purpose**: Programmatic navigation outside React components +- **Use Case**: Navigate to routes from Redux actions or non-React code +- **File Reference**: `src/common/history.js` + +--- + +## 5. Integration Examples + +**CRITICAL**: Extension points are **nested object properties**, NOT string paths! + +### Example 1: Adding a Custom Route + +```javascript +// ✅ CORRECT - nested object properties +plugin.register({ + name: 'my-plugin', + route: { + path: '/my-page', + component: MyPageComponent, + childRoutes: [ + { + path: 'details/:id', + component: DetailsComponent + } + ] + } +}); + +// ❌ INCORRECT - DO NOT use string paths +plugin.register({ + name: 'my-plugin', + 'route': { ... } // Works, but 'route' doesn't need quotes +}); +``` + +### Example 2: Contributing Multiple Routes + +```javascript +plugin.register({ + name: 'my-plugin', + route: [ + { path: '/page1', component: Page1 }, + { path: '/page2', component: Page2 }, + { path: '/page3', component: Page3, parent: 'some-route-id' } + ] +}); +``` + +### Example 3: Customizing Homepage + +```javascript +import MyHomepage from './MyHomepage'; + +plugin.register({ + name: 'my-plugin', + home: { + homepage: MyHomepage + } +}); +``` + +### Example 4: Adding Custom Layout + +```javascript +import MyLayout from './MyLayout'; + +plugin.register({ + name: 'my-plugin', + home: { + mainLayout: MyLayout // Should accept children prop + } +}); +``` + +### Example 5: Adding Redux Reducer (Plugin-Level) + +```javascript +import myReducer from './redux/reducer'; + +plugin.register({ + name: '@ebay/my-plugin', + reducer: myReducer +}); + +// Redux state will be accessible at: +// state.pluginEbayMyPlugin +``` + +### Example 6: Adding Redux Reducers (Root-Level) + +```javascript +import userReducer from './redux/userReducer'; +import configReducer from './redux/configReducer'; + +plugin.register({ + name: 'my-plugin', + reducers: { + user: userReducer, + appConfig: + } +}); + +// Redux state accessible at: +// state.user +// state.appConfig +``` + +### Example 7: Adding a Custom Provider + +```javascript +import { ThemeProvider } from 'my-theme-library'; + +plugin.register({ + name: 'my-plugin', + root: { + getProviders: () => ({ + order: 15, // Between React Query (10) and Redux (20) + key: 'theme-provider', + provider: ThemeProvider, + props: { theme: myTheme } + }) + } +}); +``` + +### Example 8: Modifying Existing Providers + +```javascript +plugin.register({ + name: 'my-plugin', + root: { + preProcessProviders: ({ providers }) => { + // Remove Redux provider + const reduxIndex = providers.findIndex(p => p.key === 'redux-provider'); + if (reduxIndex >= 0) { + providers.splice(reduxIndex, 1); + } + } + } +}); +``` + +### Example 9: Lifecycle Hooks + +```javascript +plugin.register({ + name: 'my-plugin', + root: { + beforeRender: () => { + console.log('App is about to render'); + // Initialize analytics, set up listeners, etc. + }, + afterRender: () => { + console.log('App has rendered'); + // Post-render tasks + } + }, + onReady: () => { + console.log('App is fully ready'); + // Final initialization + } +}); +``` + +### Example 10: Using Shared Modules + +```javascript +// In your plugin code +const { exports } = plugin.getPlugin('@ebay/muse-lib-react'); +const { _, reactRouterDom, reactQuery } = exports; + +// Use lodash +const uniqueItems = _.uniq(items); + +// Use React Router +const { useNavigate, Link } = reactRouterDom; + +// Use React Query +const { useQuery, useMutation } = reactQuery; +``` + +### Example 11: Using Utility Components + +```javascript +import { Nodes, useExtPoint } from '@ebay/muse-lib-react/build/features/common'; + +// Using Nodes component +function MyToolbar() { + const items = [ + { key: 'btn1', component: Button1, props: { label: 'Click' } } + ]; + + return ( + + ); +} + +// Using useExtPoint hook +function MyComponent() { + const { extNode, values } = useExtPoint('myPlugin.customPoint', { data: 123 }); + + return
{extNode}
; +} +``` + +### Example 12: Root Component for Initialization + +```javascript +function MyInitComponent() { + useEffect(() => { + // Initialize something globally + window.myGlobalState = { ... }; + }, []); + + return null; // IMPORTANT: No UI rendering +} + +plugin.register({ + name: 'my-plugin', + rootComponent: MyInitComponent +}); +``` + +### Example 13: Custom Router Props + +```javascript +plugin.register({ + name: 'my-plugin', + routerProps: { + future: { + v7_startTransition: true + } + } +}); +``` + +--- + +## Integration Checklist + +When integrating with `@ebay/muse-lib-react`: + +- [ ] Ensure plugin type is compatible (normal or lib plugins can extend this) +- [ ] Use nested object properties for extension points, not string paths +- [ ] Only one plugin should provide `home.homepage` and `home.mainLayout` +- [ ] Provider `order` values: lower = outer wrapper (10, 20, 30...) +- [ ] Redux reducer keys via `reducer` are auto-generated; use `reducers` for custom keys +- [ ] `rootComponent` must return `null` (no UI) +- [ ] Routes with absolute paths (`/`) are moved to top level +- [ ] Shared modules accessed via `plugin.getPlugin('@ebay/muse-lib-react').exports` +- [ ] Sub-app configuration goes in `window.MUSE_GLOBAL.plugins` under this plugin's `subApps` array diff --git a/ui-plugins/muse-lib-react/package.json b/ui-plugins/muse-lib-react/package.json index fc193360..dbe08baa 100644 --- a/ui-plugins/muse-lib-react/package.json +++ b/ui-plugins/muse-lib-react/package.json @@ -1,6 +1,6 @@ { "name": "@ebay/muse-lib-react", - "version": "1.3.2", + "version": "1.3.3", "muse": { "isAppEntry": true, "type": "lib", diff --git a/ui-plugins/muse-manager/CLAUDE.md b/ui-plugins/muse-manager/CLAUDE.md new file mode 100644 index 00000000..e4bd21d8 --- /dev/null +++ b/ui-plugins/muse-manager/CLAUDE.md @@ -0,0 +1,281 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is `@ebay/muse-manager`, a Vite-based React UI plugin for the MUSE micro-frontends platform. It provides a web-based management interface for MUSE apps, environments, and plugins. This plugin is part of the larger `muse-next` monorepo (located at `/Users/pwang7/muse/muse-next/`) - refer to the root CLAUDE.md for overall architecture. + +**Important**: This directory (`ui-plugins/muse-manager`) is a MUSE plugin, not a standalone app. It runs within the MUSE ecosystem and follows MUSE plugin conventions. + +## Common Commands + +```bash +# Development +pnpm start # Start Vite dev server (localhost:5173 by default) + +# Building +pnpm build # Production build to build/dist/ +pnpm build:dev # Development build to build/dev/ +pnpm build:test # E2E test build + +# Deploy to MUSE +pnpm deploy # Build both dist + dev, release, and deploy to musemanager/staging + +# Testing +pnpm test # Run tests (uses react-scripts test) +``` + +## Project Structure + +``` +src/ +├── index.js # Plugin entry point - registers with js-plugin +├── route.js # React Router routes +├── modals.jsx # Central modal registration via nice-modal-react +├── ext/ # Extension point implementations +│ └── museLayout.js # Header/sider configuration +├── features/ # Feature modules +│ ├── am/ # App Management (apps, environments, variables) +│ ├── pm/ # Plugin Management (plugins, releases, deployments) +│ ├── req/ # Request tracking +│ ├── common/ # Shared components +│ └── sub-app/ # Sub-application support +├── hooks/ # Custom React hooks +└── ext-points.d.ts # TypeScript definitions for extension points + +build/ # Build output (generated) +├── dist/ # Production build +└── dev/ # Development build +``` + +## Architecture + +### Plugin Registration System + +This plugin uses `js-plugin` for extensibility. The main entry point (`src/index.js:24-30`) registers the plugin with: + +```js +plugin.register({ + ...ext, // Extension point implementations + name: '@ebay/muse-manager', + route, // React Router routes + exports: { hooks, utils, pm, common, ability, museClient }, + rootComponent: InitAbilityComp, +}); +``` + +### Extension Points + +The plugin exposes numerous extension points defined in `src/ext-points.d.ts:321-330`. Other plugins can extend muse-manager's functionality via these extension points: + +- `museManager.am.*` - App management extension points +- `museManager.pm.*` - Plugin management extension points +- `museManager.req.*` - Request tracking extension points +- `museManager.appPage.*` - App page customization +- `museManager.pluginListBar.*` - Plugin list UI customization + +**Pattern**: Extension points use a lifecycle pattern: +1. `preProcessX` - Before processing +2. `getX` - Gather contributions from all plugins +3. `processX` - Transform items +4. `postProcessX` - Final modifications + +Example from `src/ext-points.d.ts:111-116`: +```ts +interface NodesExtPoints { + preProcessNodes?: (args: Context) => void; + getNodes?: (args: Context) => NodeType | NodeType[]; + processNodes?: (args: Context) => void; + postProcessNodes?: (args: Context) => void; +} +``` + +### Modal Management + +All modals are registered centrally in `src/modals.jsx` using `@ebay/nice-modal-react`. To show a modal from anywhere in the app: + +```js +import NiceModal from '@ebay/nice-modal-react'; +NiceModal.show('muse-manager.create-app-modal'); +``` + +Modal IDs follow the pattern `muse-manager.-modal`. + +### API Client + +The `museClient` (`src/museClient.js:5-14`) is configured to communicate with the MUSE backend API. It: + +- Reads endpoint from plugin variables or app variables (defaults to `/api/v2`) +- Includes authentication token from `window.MUSE_GLOBAL.getUser().museSession` +- Has 120 second timeout +- Is based on `@ebay/muse-client` + +### Feature Modules + +**App Management (`src/features/am/`)**: +- `AppList.jsx` - List all apps +- `AppPage.jsx` - Main app detail page with tabs +- `AppOverview.jsx` - App overview tab +- `Environments.jsx` - Environment management table +- Variable management modals for apps, environments, and plugins +- App icon creator with canvas-based icon generation + +**Plugin Management (`src/features/pm/`)**: +- `PluginList.jsx` - List all plugins with filtering +- `PluginActions.jsx` - Action buttons for plugins +- `ReleasesDrawer.jsx` - Shows plugin release history +- Deploy/undeploy modals for individual and group deployments +- `PluginStatus.jsx` - Real-time deployment status tracking +- Version/release selection components + +**Shared Components (`src/features/common/`)**: +Components shared across features. + +### Routing + +Routes defined in `src/route.js:4-18`: + +- `/plugins` → Plugin list page +- `/apps` → App list page +- `/app/:appName/:tabKey?/:scope?` → App detail page with optional tab and scope + +### Custom Hooks + +Key hooks in `src/hooks/`: + +- `useExtPoint` - Consume extension points with React hooks support +- `useAbility` - Access control / permissions +- `useSyncStatus` - Track sync status across operations +- `useEnvFilter` - Environment filtering logic +- `useValidateDeployment` - Validation before deployment +- `usePendingError` - Error state management +- `useSearchState` - Search state management with URL sync +- `museHooks` - MUSE-specific hooks (from `@ebay/muse-client`) + +### Layout Configuration + +Header and sidebar are configured in `src/ext/museLayout.js:2-74`: + +- **Header**: Black background, includes "+ Create" menu (app/plugin), theme switcher +- **Sider**: Collapsable mode, shows "Apps" and "Plugins" menu items +- Mode switches to `none` when running as sub-app (`window.MUSE_GLOBAL.isSubApp`) + +## Build Configuration + +Uses Vite with `@ebay/muse-vite-plugin` for MUSE integration (`vite.config.js:5-16`): + +- **Server**: Runs on `local.cloud.ebay.com` (eBay internal domain) +- **Plugins**: + - `@vitejs/plugin-react-swc` - Fast React refresh + - `@ebay/muse-vite-plugin` - MUSE shared module integration +- **Environment variables**: `REACT_APP_MUSE_API_ENDPOINT` injected via `loadEnv` + +### Build Output + +Following MUSE plugin conventions: + +- `build/dist/` - Production optimized build +- `build/dev/` - Development build with faster rebuild times +- Both outputs required for MUSE deployment + +### Package Configuration + +Key `package.json` fields: + +- `"type": "module"` - ES modules +- `"muse.devConfig"` - Local dev configuration (app: "musemanager", env: "staging") +- `"files": ["build"]` - Only build output is published + +## Development Workflow + +### Local Development + +1. **Standalone mode** (Vite dev server): + ```bash + pnpm start + ``` + Opens on `http://local.cloud.ebay.com:5173` + +2. **Within MUSE ecosystem** (preferred for full integration): + ```bash + # From muse-next root + muse serve musemanager@staging + ``` + +### Adding New Features + +1. **Add new modal**: + - Create modal component in `src/features//` + - Register in `src/modals.jsx` + - Use `NiceModal.show('muse-manager.your-modal')` to display + +2. **Add new extension point**: + - Define TypeScript interface in `src/ext-points.d.ts` + - Implement extension point logic in relevant feature component + - Use `jsPlugin.invoke('museManager.your.extPoint', context)` to invoke + +3. **Add new route**: + - Create component in `src/features/` + - Add route to `src/route.js` + - Update sidebar in `src/ext/museLayout.js` if needed + +### Deployment + +```bash +pnpm deploy +``` + +This command: +1. Builds production bundle (`pnpm build`) +2. Builds dev bundle (`pnpm build:dev`) +3. Releases to MUSE registry (`muse release`) +4. Deploys to musemanager/staging (`muse deploy musemanager staging`) + +## Important Notes + +### MUSE Plugin Context + +This plugin runs in the MUSE runtime with access to: + +- `window.MUSE_GLOBAL` - Global MUSE context (user, variables, app config) +- Shared dependencies from lib plugins (`@ebay/muse-lib-react`, `@ebay/muse-lib-antd`) +- MUSE layout system (header, sider configured via `museLayout` ext point) + +### Dependencies + +All dependencies in `package.json` are `devDependencies` because: +- React, Ant Design, and other shared libs come from MUSE lib plugins at runtime +- Build tools are only needed during development +- MUSE's shared module system handles runtime dependencies + +### Extension Point Pattern + +When adding extension points, follow the established pattern in `src/ext-points.d.ts`: + +1. Define context type with all data needed by extensions +2. Use lifecycle hooks: `preProcess` → `get` → `process` → `postProcess` +3. Export TypeScript interfaces for type safety +4. Invoke with `jsPlugin.invoke('museManager.feature.extPoint', context)` + +### API Integration + +When adding API calls: +- Use `museClient` from `src/museClient.js` +- Follow REST conventions: `/api/v2/apps`, `/api/v2/plugins`, etc. +- Handle loading/error states consistently +- Use React Query (`@tanstack/react-query`) for data fetching when appropriate + +### State Management + +- No Redux - uses React hooks and context +- `useAbility` for permissions/access control +- Modal state managed by `nice-modal-react` +- Form state managed by Ant Design Form (`FormInstance`) + +### Styling + +- Uses Tailwind CSS (`tailwind.config.js` - not shown but referenced in package.json) +- Ant Design components for UI primitives +- Custom styles in `src/style.less` +- Font files in `src/fonts/` diff --git a/ui-plugins/muse-manager/MUSE_README.md b/ui-plugins/muse-manager/MUSE_README.md new file mode 100644 index 00000000..89ce7cf4 --- /dev/null +++ b/ui-plugins/muse-manager/MUSE_README.md @@ -0,0 +1,849 @@ +# Plugin Integration Guide: @ebay/muse-manager + +**Generated**: 2026-03-28 +**Plugin Type**: normal + +--- + +## 1. Plugin Purpose & Overview + +### What This Plugin Does + +The `@ebay/muse-manager` plugin provides a comprehensive web-based UI for managing MUSE apps, environments, and plugins. It serves as the central administrative interface for the MUSE micro-frontends platform, enabling users to create, configure, deploy, and monitor MUSE applications and their plugins through an intuitive dashboard. + +### Key Features + +- **Application Management**: Create apps, manage environments, configure app settings, and control plugin deployments +- **Plugin Management**: Register plugins, release versions, deploy/undeploy plugins across environments, and track deployment status +- **Request Tracking**: Monitor deployment requests and system operations with real-time status updates +- **Access Control Integration**: Implements permission-based UI controls using CASL/ACL system +- **Environment Variables**: Manage app-level, environment-level, and plugin-level configuration variables +- **Version Tracking**: Visual diff indicators showing version drift between deployed and latest plugin releases + +### Plugin Type: normal + +This is a normal-type plugin providing business features within the MUSE ecosystem. It runs after library plugins and consumes shared modules (React, Ant Design) from lib plugins at runtime. + +--- + +## 2. Extension Points Exposed + +This plugin exposes 70+ extension points that OTHER plugins can implement to extend its functionality. These extension points allow customization of forms, tables, modals, actions, and UI components throughout the muse-manager interface. + +### Summary + +- **Total Extension Points**: 70+ individual extension points +- **Categories**: + - Application Management Extensions (AM) + - Plugin Management Extensions (PM) + - Request Management Extensions (Req) + - App Page Extensions + - Form Extensions + - Table Extensions + - Modal Extensions + - Action Extensions + +### Extension Point List + +#### Application Management (AM) Extensions + +##### `museManager.am.createAppForm.getWatchingFields` + +- **Purpose**: Specify form fields to watch for changes in the create app form +- **When Invoked**: During form initialization +- **Context Parameters**: + - `meta`: `NiceFormMeta` - Form metadata + - `form`: `FormInstance` - Ant Design form instance +- **Expected Return**: `string[]` - Array of field names to watch +- **Use Case Example**: Add custom field validation that depends on other field values +- **File Reference**: src/features/am/CreateAppModal.jsx:54 + +##### `museManager.am.createAppForm.getFields` + +- **Purpose**: Add custom fields to the create app form +- **When Invoked**: During form field collection +- **Context Parameters**: + - `meta`: `NiceFormMeta` - Form metadata + - `form`: `FormInstance` - Ant Design form instance +- **Expected Return**: `NiceFormFieldType | NiceFormFieldType[]` - Field definitions +- **Use Case Example**: Add organization or cost center fields for app creation +- **File Reference**: src/features/am/CreateAppModal.jsx:54 + +##### `museManager.am.createAppForm.preProcessMeta` + +- **Purpose**: Modify form metadata before rendering in create app form +- **When Invoked**: Before form renders +- **Context Parameters**: + - `meta`: `NiceFormMeta` - Form metadata object + - `form`: `FormInstance` - Ant Design form instance +- **Expected Return**: `void` - Mutates meta object directly +- **Use Case Example**: Dynamically adjust field properties based on user permissions +- **File Reference**: src/features/am/CreateAppModal.jsx:54 + +##### `museManager.am.createAppForm.processMeta` + +- **Purpose**: Process form metadata during rendering in create app form +- **When Invoked**: During form rendering +- **Context Parameters**: + - `meta`: `NiceFormMeta` - Form metadata + - `form`: `FormInstance` - Ant Design form instance +- **Expected Return**: `void` - Mutates meta object +- **Use Case Example**: Add conditional field visibility rules +- **File Reference**: src/features/am/CreateAppModal.jsx:54 + +##### `museManager.am.createAppForm.postProcessMeta` + +- **Purpose**: Final metadata processing after all extensions in create app form +- **When Invoked**: After all other metadata processing +- **Context Parameters**: + - `meta`: `NiceFormMeta` - Form metadata + - `form`: `FormInstance` - Ant Design form instance +- **Expected Return**: `void` - Mutates meta object +- **Use Case Example**: Ensure custom field ordering or final validation rules +- **File Reference**: src/features/am/CreateAppModal.jsx:54 + +##### `museManager.am.editAppForm.getWatchingFields` +##### `museManager.am.editAppForm.getFields` +##### `museManager.am.editAppForm.preProcessMeta` +##### `museManager.am.editAppForm.processMeta` +##### `museManager.am.editAppForm.postProcessMeta` + +- **Purpose**: Extend edit app form (similar lifecycle to createAppForm) +- **When Invoked**: During edit app modal rendering +- **Context Parameters**: `meta`, `form`, `app`, `setLoading`, `setError` +- **Expected Return**: Varies by lifecycle method +- **Use Case Example**: Add custom configuration fields for app editing +- **File Reference**: src/features/am/EditAppModal.jsx:54 + +##### `museManager.am.appBasicInfo.getWatchingFields` +##### `museManager.am.appBasicInfo.getFields` +##### `museManager.am.appBasicInfo.preProcessMeta` +##### `museManager.am.appBasicInfo.processMeta` +##### `museManager.am.appBasicInfo.postProcessMeta` + +- **Purpose**: Extend app basic information display form +- **When Invoked**: During app overview rendering +- **Context Parameters**: `meta`, `app` +- **Expected Return**: Varies by lifecycle method +- **Use Case Example**: Add custom app metadata fields to basic info section +- **File Reference**: src/features/am/AppBasicInfo.jsx:60 + +##### `museManager.am.environments.preProcessColumns` +##### `museManager.am.environments.getColumns` +##### `museManager.am.environments.processColumns` +##### `museManager.am.environments.postProcessColumns` + +- **Purpose**: Customize environment table columns +- **When Invoked**: During environment table rendering +- **Context Parameters**: `app`, `columns` +- **Expected Return**: Column definitions or void +- **Use Case Example**: Add deployment status or health check columns +- **File Reference**: src/features/am/Environments.jsx:32 + +##### `museManager.am.appPage.getNodes` + +- **Purpose**: Add custom UI nodes to app page layout +- **When Invoked**: During app page rendering +- **Context Parameters**: `app`, `nodes` +- **Expected Return**: `NodeType | NodeType[]` - Node definitions +- **Use Case Example**: Add custom alerts, notices, or action buttons to app page +- **File Reference**: src/features/am/AppPage.jsx:99 + +##### `museManager.am.appPage.getAppNameActions` + +- **Purpose**: Add action buttons/nodes next to app name header +- **When Invoked**: During app page header rendering +- **Context Parameters**: `app`, `appNameActions` +- **Expected Return**: `NodeType | NodeType[]` - Action node definitions +- **Use Case Example**: Add quick action buttons like "Export Config" or "Clone App" +- **File Reference**: src/features/am/AppPage.jsx:44 + +##### `museManager.am.appOverview.preProcessNodes` +##### `museManager.am.appOverview.getNodes` +##### `museManager.am.appOverview.processNodes` +##### `museManager.am.appOverview.postProcessNodes` + +- **Purpose**: Extend app overview tab with custom sections +- **When Invoked**: During overview tab rendering +- **Context Parameters**: `app`, `nodes` +- **Expected Return**: `NodeType | NodeType[]` for getNodes, `void` for process methods +- **Use Case Example**: Add custom app statistics, dashboards, or monitoring widgets +- **File Reference**: src/features/am/AppOverview.jsx:49 + +#### Plugin Management (PM) Extensions + +##### `museManager.pm.pluginInfoForm.getWatchingFields` +##### `museManager.pm.pluginInfoForm.getFields` +##### `museManager.pm.pluginInfoForm.preProcessMeta` +##### `museManager.pm.pluginInfoForm.processMeta` +##### `museManager.pm.pluginInfoForm.postProcessMeta` + +- **Purpose**: Extend plugin information/edit form +- **When Invoked**: During plugin info modal rendering +- **Context Parameters**: `meta`, `form`, `app`, `plugin`, `viewMode` +- **Expected Return**: Varies by lifecycle method +- **Use Case Example**: Add repository URL, CI/CD status, or quality metrics fields +- **File Reference**: src/features/pm/PluginInfoModal.jsx:110 + +##### `museManager.pm.pluginInfoView.preProcessNodes` +##### `museManager.pm.pluginInfoView.getNodes` +##### `museManager.pm.pluginInfoView.processNodes` +##### `museManager.pm.pluginInfoView.postProcessNodes` + +- **Purpose**: Add custom sections to plugin info modal body +- **When Invoked**: During plugin info modal rendering +- **Context Parameters**: `viewMode`, `plugin`, `app` +- **Expected Return**: `NodeType | NodeType[]` for getNodes, `void` for process methods +- **Use Case Example**: Add plugin dependency graph or usage statistics +- **File Reference**: src/features/pm/PluginInfoModal.jsx:137 + +##### `museManager.pm.pluginInfoModal.footer.preProcessItems` +##### `museManager.pm.pluginInfoModal.footer.getItems` +##### `museManager.pm.pluginInfoModal.footer.processItems` +##### `museManager.pm.pluginInfoModal.footer.postProcessItems` + +- **Purpose**: Customize footer buttons in plugin info modal +- **When Invoked**: During modal footer rendering +- **Context Parameters**: `items` (button definitions) +- **Expected Return**: Footer item definitions or void +- **Use Case Example**: Add custom actions like "Build Plugin" or "Run Tests" +- **File Reference**: src/features/pm/PluginInfoModal.jsx:180 + +##### `museManager.pm.pluginList.preProcessColumns` +##### `museManager.pm.pluginList.getColumns` +##### `museManager.pm.pluginList.processColumns` +##### `museManager.pm.pluginList.postProcessColumns` + +- **Purpose**: Customize plugin list table columns +- **When Invoked**: During plugin list rendering +- **Context Parameters**: `app`, `columns`, `plugins`, `searchValue`, `latestReleases` +- **Expected Return**: Column definitions or void +- **Use Case Example**: Add test coverage, build time, or bundle size columns +- **File Reference**: src/features/pm/PluginList.jsx:278 + +##### `museManager.pm.pluginList.pluginBadges.preProcessNodes` +##### `museManager.pm.pluginList.pluginBadges.getNodes` +##### `museManager.pm.pluginList.pluginBadges.processNodes` +##### `museManager.pm.pluginList.pluginBadges.postProcessNodes` + +- **Purpose**: Add badges/tags next to plugin names in the list +- **When Invoked**: During plugin name cell rendering +- **Context Parameters**: `app`, `plugin` +- **Expected Return**: `NodeType | NodeType[]` for getNodes, `void` for process methods +- **Use Case Example**: Add status badges like "Deprecated", "Beta", or build status icons +- **File Reference**: Via src/features/pm/PluginList.jsx:132 → PluginBadges component + +##### `museManager.pm.pluginList.preProcessActions` +##### `museManager.pm.pluginList.getActions` +##### `museManager.pm.pluginList.processActions` +##### `museManager.pm.pluginList.postProcessActions` + +- **Purpose**: Customize plugin action dropdown menu +- **When Invoked**: During plugin actions rendering +- **Context Parameters**: `app`, `plugin`, `ability`, `actions`, `appByName` +- **Expected Return**: `ActionType | ActionType[]` for getActions, `void` for process methods +- **Use Case Example**: Add "Build", "Test", or "Publish to NPM" actions +- **File Reference**: src/features/pm/PluginActions.jsx:126 + +##### `museManager.pm.pluginList.getEnvFilters` + +- **Purpose**: Add custom environment filters to plugin list +- **When Invoked**: During filter menu rendering +- **Context Parameters**: Filter context +- **Expected Return**: `EnvFilter | EnvFilter[]` - Filter definitions +- **Use Case Example**: Add filters like "Has Errors" or "Needs Update" +- **File Reference**: src/features/pm/EnvFilterMenu.jsx:61 + +##### `museManager.pm.pluginList.getEnvFilterFns` + +- **Purpose**: Provide filter functions for environment-based filtering +- **When Invoked**: When applying environment filters +- **Context Parameters**: `filterKey`, `app`, `envName` +- **Expected Return**: `Function[]` - Filter functions to apply +- **Use Case Example**: Custom logic to filter plugins by deployment health +- **File Reference**: src/features/pm/PluginList.jsx:101 + +##### `museManager.pm.pluginList.getScopeFilterFns` + +- **Purpose**: Provide filter functions for scope-based filtering (all/deployed) +- **When Invoked**: When applying scope filters +- **Context Parameters**: `scope` (string) +- **Expected Return**: `Function` - Filter function +- **Use Case Example**: Filter plugins by custom scopes like "mine" or "team" +- **File Reference**: src/features/pm/PluginList.jsx:63 + +##### `museManager.pm.pluginList.processPluginList` + +- **Purpose**: Process/transform the entire plugin list before rendering +- **When Invoked**: After initial filtering, before display +- **Context Parameters**: `pluginList` (array) +- **Expected Return**: `void` - Mutates pluginList +- **Use Case Example**: Sort plugins by custom criteria or add computed properties +- **File Reference**: src/features/pm/PluginList.jsx:71 + +##### `museManager.pm.releaseList.preProcessColumns` +##### `museManager.pm.releaseList.getColumns` +##### `museManager.pm.releaseList.processColumns` +##### `museManager.pm.releaseList.postProcessColumns` + +- **Purpose**: Customize release list table columns +- **When Invoked**: During releases drawer rendering +- **Context Parameters**: `plugin`, `app`, `releases` +- **Expected Return**: Column definitions or void +- **Use Case Example**: Add commit SHA, branch info, or build artifacts columns +- **File Reference**: src/features/pm/ReleasesDrawer.jsx:190 + +##### `museManager.pm.releaseList.preProcessActions` +##### `museManager.pm.releaseList.getActions` +##### `museManager.pm.releaseList.processActions` +##### `museManager.pm.releaseList.postProcessActions` + +- **Purpose**: Customize release action dropdown +- **When Invoked**: During release row actions rendering +- **Context Parameters**: `app`, `plugin`, `ability`, `items` +- **Expected Return**: Action definitions or void +- **Use Case Example**: Add "Download Assets" or "View Build Log" actions +- **File Reference**: src/features/pm/ReleasesDrawer.jsx:159 + +##### `museManager.pm.releaseList.preProcessNodes` +##### `museManager.pm.releaseList.getNodes` +##### `museManager.pm.releaseList.processNodes` +##### `museManager.pm.releaseList.postProcessNodes` + +- **Purpose**: Add custom sections to releases drawer +- **When Invoked**: During releases drawer rendering +- **Context Parameters**: `items`, `plugin`, `app`, `releases` +- **Expected Return**: Nodes or void +- **Use Case Example**: Add release timeline visualization or statistics +- **File Reference**: src/features/pm/ReleasesDrawer.jsx:214 + +##### `museManager.pm.releaseList.expandRow.preProcessNodes` +##### `museManager.pm.releaseList.expandRow.getNodes` +##### `museManager.pm.releaseList.expandRow.processNodes` +##### `museManager.pm.releaseList.expandRow.postProcessNodes` + +- **Purpose**: Customize expanded row content for each release +- **When Invoked**: When release row is expanded +- **Context Parameters**: `items`, `release` +- **Expected Return**: Nodes or void +- **Use Case Example**: Show commit details, file changes, or test results +- **File Reference**: src/features/pm/ReleasesDrawer.jsx:186 + +##### `museManager.pm.deployPluginModal.form.getWatchingFields` +##### `museManager.pm.deployPluginModal.form.getFields` +##### `museManager.pm.deployPluginModal.form.preProcessMeta` +##### `museManager.pm.deployPluginModal.form.processMeta` +##### `museManager.pm.deployPluginModal.form.postProcessMeta` + +- **Purpose**: Extend deploy plugin form +- **When Invoked**: During deploy modal rendering +- **Context Parameters**: `meta`, `ability`, `app`, `form`, `plugin`, `version`, plus state setters +- **Expected Return**: Varies by method +- **Use Case Example**: Add deployment options like "run smoke tests" or "notify team" +- **File Reference**: src/features/pm/DeployPluginModal.jsx:152 + +##### `museManager.pm.deployPluginModal.footer.preProcessItems` +##### `museManager.pm.deployPluginModal.footer.getItems` +##### `museManager.pm.deployPluginModal.footer.processItems` +##### `museManager.pm.deployPluginModal.footer.postProcessItems` + +- **Purpose**: Customize deploy modal footer buttons +- **When Invoked**: During modal footer rendering +- **Context Parameters**: `items`, plus full extArgs context +- **Expected Return**: Footer items or void +- **Use Case Example**: Add "Deploy & Validate" or "Schedule Deployment" buttons +- **File Reference**: src/features/pm/DeployPluginModal.jsx:181 + +##### `museManager.pm.undeployPluginModal.form.getWatchingFields` +##### `museManager.pm.undeployPluginModal.form.getFields` +##### `museManager.pm.undeployPluginModal.form.preProcessMeta` +##### `museManager.pm.undeployPluginModal.form.processMeta` +##### `museManager.pm.undeployPluginModal.form.postProcessMeta` + +- **Purpose**: Extend undeploy plugin form +- **When Invoked**: During undeploy modal rendering +- **Context Parameters**: Similar to deployPluginModal +- **Expected Return**: Varies by method +- **Use Case Example**: Add cleanup options or rollback configurations +- **File Reference**: src/features/pm/UndeployPluginModal.jsx:146 + +##### `museManager.pm.undeployPluginModal.footer.preProcessItems` +##### `museManager.pm.undeployPluginModal.footer.getItems` +##### `museManager.pm.undeployPluginModal.footer.processItems` +##### `museManager.pm.undeployPluginModal.footer.postProcessItems` + +- **Purpose**: Customize undeploy modal footer +- **When Invoked**: During footer rendering +- **Context Parameters**: `items` plus extArgs +- **Expected Return**: Footer items or void +- **File Reference**: src/features/pm/UndeployPluginModal.jsx:175 + +##### `museManager.pm.groupDeployModal.form.getWatchingFields` +##### `museManager.pm.groupDeployModal.form.getFields` +##### `museManager.pm.groupDeployModal.form.preProcessMeta` +##### `museManager.pm.groupDeployModal.form.processMeta` +##### `museManager.pm.groupDeployModal.form.postProcessMeta` + +- **Purpose**: Extend group deployment form (deploy/undeploy multiple plugins at once) +- **When Invoked**: During group deploy modal rendering +- **Context Parameters**: `meta`, `ability`, `form`, `app`, plus state management functions +- **Expected Return**: Varies by method +- **Use Case Example**: Add deployment strategies like "sequential" vs "parallel" +- **File Reference**: src/features/pm/GroupDeployModal.jsx:184 + +##### `museManager.pm.groupDeployModal.footer.preProcessItems` +##### `museManager.pm.groupDeployModal.footer.getItems` +##### `museManager.pm.groupDeployModal.footer.processItems` +##### `museManager.pm.groupDeployModal.footer.postProcessItems` + +- **Purpose**: Customize group deploy modal footer +- **When Invoked**: During footer rendering +- **Context Parameters**: `items` plus extArgs +- **Expected Return**: Footer items or void +- **File Reference**: src/features/pm/GroupDeployModal.jsx:231 + +##### `museManager.pm.pluginConfigForm.getWatchingFields` +##### `museManager.pm.pluginConfigForm.getFields` +##### `museManager.pm.pluginConfigForm.preProcessMeta` +##### `museManager.pm.pluginConfigForm.processMeta` +##### `museManager.pm.pluginConfigForm.postProcessMeta` +##### `museManager.pm.pluginConfigForm.processPayload` + +- **Purpose**: Extend plugin configuration form (app-level plugin settings) +- **When Invoked**: During plugin config modal rendering +- **Context Parameters**: `meta`, `form`, `app`, `plugin` +- **Expected Return**: Varies by method +- **Use Case Example**: Add plugin-specific config fields like feature flags or API keys +- **File Reference**: src/features/pm/PluginConfigModal.jsx:66 + +##### `museManager.pm.createPluginModal.form.getWatchingFields` +##### `museManager.pm.createPluginModal.form.getFields` +##### `museManager.pm.createPluginModal.form.preProcessMeta` +##### `museManager.pm.createPluginModal.form.processMeta` +##### `museManager.pm.createPluginModal.form.postProcessMeta` + +- **Purpose**: Extend create plugin form +- **When Invoked**: During create plugin modal rendering +- **Context Parameters**: `meta`, `form` +- **Expected Return**: Varies by method +- **Use Case Example**: Add fields for repository template, initial version, or build configuration +- **File Reference**: src/features/pm/CreatePluginModal.jsx:115 + +##### `museManager.pm.pluginStatus.relatedToPlugin` + +- **Purpose**: Determine if a request is related to a specific plugin +- **When Invoked**: During plugin status check +- **Context Parameters**: `plugin`, `app`, `request` +- **Expected Return**: `boolean | void` - True if request relates to this plugin +- **Use Case Example**: Filter custom request types related to plugin operations +- **File Reference**: Via src/features/pm/PluginStatus.jsx + +##### `museManager.pm.pluginStatus.processRequest` + +- **Purpose**: Process/transform request data for plugin status display +- **When Invoked**: During plugin status rendering +- **Context Parameters**: `request`, `app`, `plugin` +- **Expected Return**: `void` - Mutates request object +- **Use Case Example**: Add custom status messages or progress indicators +- **File Reference**: src/features/pm/PluginStatus.jsx:46 + +#### Request Management Extensions + +##### `museManager.req.requestDetailModal.processModalProps` + +- **Purpose**: Customize request detail modal properties +- **When Invoked**: Before modal renders +- **Context Parameters**: Modal props object +- **Expected Return**: `void` - Mutates props +- **Use Case Example**: Adjust modal width or behavior based on request type +- **File Reference**: src/features/req/RequestDetailModal.jsx:215 + +##### `museManager.req.requestDetailModal.form.getWatchingFields` +##### `museManager.req.requestDetailModal.form.getFields` +##### `museManager.req.requestDetailModal.form.preProcessMeta` +##### `museManager.req.requestDetailModal.form.processMeta` +##### `museManager.req.requestDetailModal.form.postProcessMeta` +##### `museManager.req.requestDetailModal.body.preProcessItems` +##### `museManager.req.requestDetailModal.body.getItems` +##### `museManager.req.requestDetailModal.body.processItems` +##### `museManager.req.requestDetailModal.body.postProcessItems` +##### `museManager.req.requestDetailModal.footer.preProcessItems` +##### `museManager.req.requestDetailModal.footer.getItems` +##### `museManager.req.requestDetailModal.footer.processItems` +##### `museManager.req.requestDetailModal.footer.postProcessItems` + +- **Purpose**: Extend request detail modal form, body, and footer +- **When Invoked**: During request modal rendering +- **Context Parameters**: Varies by section +- **Expected Return**: Varies by method +- **Use Case Example**: Add custom request fields, logs, or action buttons +- **File Reference**: Via src/features/req/RequestDetailModal.jsx + +#### App Page Extensions + +##### `museManager.appPage.getTabs` + +- **Purpose**: Add custom tabs to app detail page +- **When Invoked**: During app page rendering +- **Context Parameters**: `tabs`, `app` +- **Expected Return**: `TabsProps['items']` - Tab item definitions +- **Use Case Example**: Add "Analytics", "Logs", or "Settings" tabs +- **File Reference**: src/features/am/AppPage.jsx:39 + +#### List Bar Extensions + +##### `museManager.pluginListBar.getScopes` + +- **Purpose**: Add scope filters to plugin list bar (e.g., "all", "deployed", custom) +- **When Invoked**: During plugin list bar rendering +- **Context Parameters**: `setScope`, `scope` +- **Expected Return**: `ScopeType | ScopeType[]` - Scope definitions +- **Use Case Example**: Add "My Plugins" or "Team Plugins" scope +- **File Reference**: src/features/pm/PluginListBar.jsx:59 + +##### `museManager.appListBar.getScopes` +##### `museManager.appListBar.getDropdownItems` +##### `museManager.appListBar.processDropdownItems` + +- **Purpose**: Customize app list bar scopes and dropdown menu +- **When Invoked**: During app list bar rendering +- **Context Parameters**: Varies +- **Expected Return**: Scopes or dropdown items +- **Use Case Example**: Add bulk operations or export functionality +- **File Reference**: src/features/am/AppListBar.jsx + +#### Environment Forms + +##### `museManager.addEnvForm.getWatchingFields` +##### `museManager.addEnvForm.getFields` +##### `museManager.addEnvForm.preProcessMeta` +##### `museManager.addEnvForm.processMeta` +##### `museManager.addEnvForm.postProcessMeta` +##### `museManager.editEnvForm.getWatchingFields` +##### `museManager.editEnvForm.getFields` +##### `museManager.editEnvForm.preProcessMeta` +##### `museManager.editEnvForm.processMeta` +##### `museManager.editEnvForm.postProcessMeta` + +- **Purpose**: Extend add/edit environment forms +- **When Invoked**: During environment modal rendering +- **Context Parameters**: Form metadata and context +- **Expected Return**: Varies by method +- **Use Case Example**: Add environment-specific config like CDN URLs or API endpoints +- **File Reference**: Via src/features/am/AddEnvironmentModal.jsx, EditEnvironmentModal.jsx + +#### Configuration Extension + +##### `museManager.setConfig` + +- **Purpose**: Set/override muse-manager configuration +- **When Invoked**: During plugin initialization +- **Context Parameters**: Config setter function +- **Expected Return**: `void` +- **Use Case Example**: Customize default page sizes, API endpoints, or UI behavior +- **File Reference**: src/config.js:13 + +### Usage Example + +**CRITICAL**: Extension points are **nested object properties**, NOT string paths! + +```javascript +// ✅ CORRECT - nested object properties +plugin.register({ + name: 'my-custom-plugin', + museManager: { + pm: { + pluginList: { + getColumns: (context) => { + // Add custom column + return { + dataIndex: 'myCustomField', + title: 'Custom Data', + order: 85, + render: (val, plugin) => plugin.myCustomField + }; + } + } + } + } +}); + +// ❌ INCORRECT - DO NOT use string paths +plugin.register({ + name: 'my-custom-plugin', + 'museManager.pm.pluginList.getColumns': (context) => { // WRONG! + // This will NOT work! + } +}); +``` + +--- + +## 3. Extension Points Contributed + +This plugin implements the following extension points from OTHER plugins to integrate with the MUSE ecosystem. + +### Summary + +- **Total Contributions**: 6 +- **Host Plugins**: @ebay/muse-lib-react, @ebay/muse-layout-antd + +### By Host Plugin + +#### Contributes to: @ebay/muse-lib-react + +##### `route` + +- **Invoked By**: @ebay/muse-lib-react routing system +- **What This Plugin Provides**: Three routes: + - `/plugins` - Plugin list page + - `/apps` - Application list page + - `/app/:appName/:tabKey?/:scope?` - App detail page with optional tab and scope +- **Why Needed**: Integrates muse-manager pages into the MUSE application router +- **File Reference**: src/route.js:4-18 + +##### `rootComponent` + +- **Invoked By**: @ebay/muse-lib-react plugin initialization system +- **What This Plugin Provides**: `InitAbilityComp` component that initializes the CASL ability object for access control +- **Why Needed**: Sets up permission system before any muse-manager components render +- **File Reference**: src/index.js:17-20, src/index.js:29 + +#### Contributes to: @ebay/muse-layout-antd + +##### `museLayout.header.getConfig` + +- **Invoked By**: @ebay/muse-layout-antd header component +- **What This Plugin Provides**: Header configuration: + - Black background (#000000) + - Title: "Muse Managers" + - Subtitle: "Muse app and plugin manager" + - Theme switcher enabled +- **Why Needed**: Configures the application header appearance +- **File Reference**: src/ext/museLayout.js:4-11 + +##### `museLayout.header.getItems` + +- **Invoked By**: @ebay/muse-layout-antd header items collection +- **What This Plugin Provides**: Header menu items: + - "+ Create" dropdown menu with "Create App" and "Create Plugin" options +- **Why Needed**: Provides quick access to app/plugin creation from header +- **File Reference**: src/ext/museLayout.js:13-43 + +##### `museLayout.sider.getConfig` + +- **Invoked By**: @ebay/muse-layout-antd sidebar component +- **What This Plugin Provides**: Sidebar configuration: + - Mode: `collapsable` (or `none` if running as sub-app) + - Default collapsed: true + - Home menu: enabled + - Width: 200px +- **Why Needed**: Configures sidebar behavior and appearance +- **File Reference**: src/ext/museLayout.js:47-53 + +##### `museLayout.sider.getItems` + +- **Invoked By**: @ebay/muse-layout-antd sidebar menu +- **What This Plugin Provides**: Sidebar menu items: + - "Apps" menu item (link to /apps) + - "Plugins" menu item (link to /plugins) +- **Why Needed**: Provides main navigation menu for muse-manager +- **File Reference**: src/ext/museLayout.js:55-70 + +--- + +## 4. Exported Functionality + +This plugin exports the following functionality for use by other plugins. + +**Access via**: `plugin.getPlugin('@ebay/muse-manager').exports` + +### Hooks + +- **`useAbility`**: Returns the CASL ability object for permission checks. Use this to check if the current user can perform actions like create/edit/delete apps or plugins. Returns: `Ability` object with `can()` and `cannot()` methods. + +- **`useSyncStatus`**: Hook to manually trigger data refresh for a specific data key. Takes a data key (e.g., 'muse.apps', 'muse.plugins') and returns a function to trigger sync. Useful after mutations to refresh cached data. + +- **`useSearchState`**: Manages search state synchronized with URL query parameters. Returns search state and setter function. Use when building searchable UI components. + +- **`useEnvFilter`**: Hook for environment filtering logic in plugin lists. Returns `{ getEnvFilterConfig, envFilterMap }` with filter configuration and current filter state. Use for building custom environment filter UI. + +- **`useValidateDeployment`**: Hook that validates plugin deployments (checks shared module compatibility). Returns `{ validateDeployment, validateDeploymentError, validateDeploymentPending }`. Use before deploying plugins to ensure module compatibility. + +- **`usePendingError`**: Combines multiple loading and error states into single pending/error state. Takes arrays of pending states and errors, returns unified `{ pending, error, setPending, setError }`. Useful for forms with multiple async operations. + +- **`useExtPoint`**: Hook to consume extension points in React components. Takes extension point name and args, returns array of contributed components. Use when building extensible UI components. + +- **`useMuseData`**: (from museHooks) React Query hook for fetching MUSE data. Takes data key (e.g., 'muse.apps'), returns `{ data, isLoading, error }`. Auto-polling support. + +- **`usePollingMuseData`**: (from museHooks) Similar to useMuseData but with auto-polling enabled. Use for data that needs real-time updates like deployment status. + +- **`useMuseMutation`**: (from museHooks) React Query mutation hook for MUSE API operations. Takes mutation key (e.g., 'am.createApp'), returns `{ mutateAsync, error, isLoading }`. Use for write operations. + +### Components (from common features) + +Components are exported via the `common` export object. + +### PM (Plugin Management) Exports + +- **`VersionSelect`**: Version selector component for plugin releases. Use in forms that need version selection. + +- **`PluginReleaseSelect`**: Plugin release selector with version dropdown. Props: `{ plugin, app }`. Use in deployment forms. + +- **`EnvFilterMenu`**: Environment filter menu component with version diff indicators. Use for building custom plugin list filters. + +- **`LightOnIcon`**: Icon component for UI decoration. Use in info messages or alerts. + +- **`MultiPluginSelector`**: Component for selecting multiple plugins with version. Props: `{ app }`. Use in group deployment forms. + +- **`GroupDeployModal`**: Modal component for deploying/undeploying multiple plugins. Props: `{ app }`. Use via NiceModal.show(). + +- **`PluginStatus`**: Component showing real-time plugin deployment status. Props: `{ plugin, app }`. Use to display ongoing deployment operations. + +- **`PluginBadges`**: Component rendering plugin badges/tags. Props: `{ app, plugin }`. Use to display plugin status indicators. + +### Utils + +- **`getPluginId`**: Converts scoped package names to ID format (e.g., `@ebay/foo` → `ebay.foo`). Use when working with plugin identifiers. + +- **`versionDiff`**: Calculates semantic version difference between two versions. Returns: `'major' | 'minor' | 'patch' | 'null'`. Use for version comparison logic. + +### Other Exports + +- **`ability`**: The global CASL ability object for permission checking. Direct access to the ability singleton. Prefer using `useAbility()` hook in React components. + +- **`museClient`**: Configured axios client for MUSE API v2. Pre-configured with authentication, timeout, and base URL. Use for custom API calls not covered by museHooks. + +### Using Exported Functionality + +```javascript +// Accessing exports from muse-manager +const museManager = plugin.getPlugin('@ebay/muse-manager'); +if (!museManager) { + console.warn('@ebay/muse-manager plugin not found'); + return; +} + +const { hooks, pm, utils, ability, museClient } = museManager.exports; + +// Using a hook in React component +const { useAbility } = hooks; +function MyComponent({ app }) { + const ability = useAbility(); + const canEdit = ability.can('update', 'App', app); + return canEdit ? : null; +} + +// Using a component +const { PluginStatus } = pm; + + +// Using a utility +const { versionDiff } = utils; +const diff = versionDiff('2.0.0', '1.5.3'); // Returns 'major' + +// Using museClient for custom API calls +const { data } = await museClient.get('/api/v2/custom-endpoint'); +``` + +**Note**: Exports create tight coupling between plugins. Prefer using extension points for loose coupling when possible. Use exports when you need to reuse complex components, hooks, or utilities across plugins. + +--- + +## 5. Integration Examples + +### Example 1: Adding a Custom Column to Plugin List (CORRECT Syntax) + +```javascript +plugin.register({ + name: 'my-monitoring-plugin', + museManager: { + pm: { + pluginList: { + getColumns: ({ app, plugins }) => { + return { + dataIndex: 'healthScore', + title: 'Health', + order: 55, + width: 100, + render: (_, plugin) => { + const score = calculateHealthScore(plugin); + return 80 ? 'green' : 'orange'}>{score}; + } + }; + } + } + } + } +}); +``` + +### Example 2: Adding Custom App Page Tab + +```javascript +plugin.register({ + name: 'my-analytics-plugin', + museManager: { + appPage: { + getTabs: ({ app }) => { + return { + key: 'analytics', + label: 'Analytics', + order: 50, + children: + }; + } + } + } +}); +``` + +### Example 3: Extending Create Plugin Form + +```javascript +plugin.register({ + name: 'my-ci-cd-plugin', + museManager: { + pm: { + createPluginModal: { + form: { + getFields: () => { + return { + key: 'cicdPipeline', + label: 'CI/CD Pipeline', + order: 25, + widget: 'select', + options: ['GitHub Actions', 'Jenkins', 'GitLab CI'], + tooltip: 'Select the CI/CD system for this plugin' + }; + } + } + } + } + } +}); +``` + +### Example 4: Using Exported Hooks + +```javascript +// In your plugin's component +import plugin from 'js-plugin'; + +function MyCustomComponent({ app, plugin }) { + const museManager = plugin.getPlugin('@ebay/muse-manager'); + const { useAbility, useMuseData } = museManager.exports.hooks; + + const ability = useAbility(); + const { data: releases } = useMuseData(`muse.plugin-releases.${plugin.name}`); + + const canDeploy = ability.can('deploy', 'App', app); + + return ( +
+

Latest: {releases?.[0]?.version}

+ {canDeploy && } +
+ ); +} +``` diff --git a/ui-plugins/muse-manager/package.json b/ui-plugins/muse-manager/package.json index c81745e9..39230b8b 100644 --- a/ui-plugins/muse-manager/package.json +++ b/ui-plugins/muse-manager/package.json @@ -1,6 +1,6 @@ { "name": "@ebay/muse-manager", - "version": "1.3.3", + "version": "1.3.5", "private": false, "type": "module", "muse": { @@ -24,7 +24,7 @@ "@ebay/muse-lib-antd": "^1.3.2", "@ebay/muse-lib-react": "^1.3.2", "@ebay/muse-plugin-acl": "^1.0.19", - "@ebay/muse-vite-plugin": "^1.0.55", + "@ebay/muse-vite-plugin": "^1.0.56", "@tanstack/react-query": "^4.28.0", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", diff --git a/ui-plugins/muse-manager/pnpm-lock.yaml b/ui-plugins/muse-manager/pnpm-lock.yaml index fa403533..da747f78 100644 --- a/ui-plugins/muse-manager/pnpm-lock.yaml +++ b/ui-plugins/muse-manager/pnpm-lock.yaml @@ -27,8 +27,8 @@ devDependencies: specifier: ^1.0.19 version: 1.0.19 '@ebay/muse-vite-plugin': - specifier: ^1.0.55 - version: 1.0.55(@ebay/muse-modules-analyzer@1.0.18)(express@4.22.1)(vite@5.4.21) + specifier: ^1.0.56 + version: 1.0.56(@ebay/muse-modules-analyzer@1.0.18)(express@4.22.1)(vite@5.4.21) '@tanstack/react-query': specifier: ^4.28.0 version: 4.42.0(react-dom@18.3.1)(react@18.3.1) @@ -232,8 +232,8 @@ packages: '@babel/generator': 7.29.1 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.29.0 + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 @@ -262,7 +262,7 @@ packages: resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 @@ -377,8 +377,8 @@ packages: '@babel/types': 7.28.5 dev: true - /@babel/helpers@7.28.6: - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + /@babel/helpers@7.29.2: + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.28.6 @@ -393,8 +393,8 @@ packages: '@babel/types': 7.28.5 dev: true - /@babel/parser@7.29.0: - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + /@babel/parser@7.29.2: + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} engines: {node: '>=6.0.0'} hasBin: true dependencies: @@ -555,8 +555,8 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/runtime@7.28.6: - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + /@babel/runtime@7.29.2: + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} dev: true @@ -574,7 +574,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 dev: true @@ -600,7 +600,7 @@ packages: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/template': 7.28.6 '@babel/types': 7.29.0 debug: 4.4.3 @@ -860,8 +860,8 @@ packages: typescript: 4.9.3 dev: true - /@ebay/muse-vite-plugin@1.0.55(@ebay/muse-modules-analyzer@1.0.18)(express@4.22.1)(vite@5.4.21): - resolution: {integrity: sha512-ajH2QPPhV5mVbiqNwNDLUfBvjInY8mftt1/A41ednlUMe388GZPhLgWj9GFjI4PHfmoRjbicOz1r+OlI1bwOSg==} + /@ebay/muse-vite-plugin@1.0.56(@ebay/muse-modules-analyzer@1.0.18)(express@4.22.1)(vite@5.4.21): + resolution: {integrity: sha512-LTvqHQSBWJzxnHsbVAjlNRSAxpC3/pmYtKZ4hsXV/U0T15VO8JGimfeEh0WVz88lZsRr+cLIMjKECq8TkvBz5Q==} peerDependencies: vite: ^5.2.11 dependencies: @@ -1204,7 +1204,7 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/types': 28.1.3 - '@types/node': 25.3.5 + '@types/node': 25.5.0 chalk: 4.1.2 jest-message-util: 28.1.3 jest-util: 28.1.3 @@ -1225,14 +1225,14 @@ packages: '@jest/test-result': 28.1.3 '@jest/transform': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 25.3.5 + '@types/node': 25.5.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 28.1.3 - jest-config: 28.1.3(@types/node@25.3.5) + jest-config: 28.1.3(@types/node@25.5.0) jest-haste-map: 28.1.3 jest-message-util: 28.1.3 jest-regex-util: 28.0.2 @@ -1265,7 +1265,7 @@ packages: dependencies: '@jest/fake-timers': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 25.3.5 + '@types/node': 25.5.0 jest-mock: 28.1.3 dev: true @@ -1299,7 +1299,7 @@ packages: dependencies: '@jest/types': 28.1.3 '@sinonjs/fake-timers': 9.1.2 - '@types/node': 25.3.5 + '@types/node': 25.5.0 jest-message-util: 28.1.3 jest-mock: 28.1.3 jest-util: 28.1.3 @@ -1344,7 +1344,7 @@ packages: '@jest/transform': 28.1.3 '@jest/types': 28.1.3 '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 25.3.5 + '@types/node': 25.5.0 chalk: 4.1.2 collect-v8-coverage: 1.0.3 exit: 0.1.2 @@ -1440,7 +1440,7 @@ packages: '@jest/schemas': 28.1.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.3.5 + '@types/node': 25.5.0 '@types/yargs': 17.0.35 chalk: 4.1.2 dev: true @@ -2021,7 +2021,7 @@ packages: engines: {node: '>=18'} dependencies: '@babel/code-frame': 7.29.0 - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -2110,7 +2110,7 @@ packages: /@types/babel__core@7.20.5: resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 @@ -2126,7 +2126,7 @@ packages: /@types/babel__template@7.4.4: resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 dev: true @@ -2143,7 +2143,7 @@ packages: /@types/graceful-fs@4.1.9: resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} dependencies: - '@types/node': 25.3.5 + '@types/node': 25.5.0 dev: true /@types/hast@2.3.10: @@ -2223,8 +2223,8 @@ packages: undici-types: 7.16.0 dev: true - /@types/node@25.3.5: - resolution: {integrity: sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==} + /@types/node@25.5.0: + resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} dependencies: undici-types: 7.18.2 dev: true @@ -4173,7 +4173,7 @@ packages: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flatted: 3.4.0 + flatted: 3.4.2 keyv: 4.5.4 rimraf: 3.0.2 dev: true @@ -4183,8 +4183,8 @@ packages: hasBin: true dev: true - /flatted@3.4.0: - resolution: {integrity: sha512-kC6Bb+ooptOIvWj5B63EQWkF0FEnNjV2ZNkLMLZRDDduIiWeFF4iKnslwhiWxjAdbg4NzTNo6h0qLuvFrcx+Sw==} + /flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} dev: true /fn.name@1.1.0: @@ -5036,7 +5036,7 @@ packages: '@jest/expect': 28.1.3 '@jest/test-result': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 25.3.5 + '@types/node': 25.5.0 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -5072,7 +5072,7 @@ packages: exit: 0.1.2 graceful-fs: 4.2.11 import-local: 3.2.0 - jest-config: 28.1.3(@types/node@25.3.5) + jest-config: 28.1.3(@types/node@25.5.0) jest-util: 28.1.3 jest-validate: 28.1.3 prompts: 2.4.2 @@ -5083,7 +5083,7 @@ packages: - ts-node dev: true - /jest-config@28.1.3(@types/node@25.3.5): + /jest-config@28.1.3(@types/node@25.5.0): resolution: {integrity: sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: @@ -5098,7 +5098,7 @@ packages: '@babel/core': 7.29.0 '@jest/test-sequencer': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 25.3.5 + '@types/node': 25.5.0 babel-jest: 28.1.3(@babel/core@7.29.0) chalk: 4.1.2 ci-info: 3.9.0 @@ -5177,7 +5177,7 @@ packages: '@jest/environment': 28.1.3 '@jest/fake-timers': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 25.3.5 + '@types/node': 25.5.0 jest-mock: 28.1.3 jest-util: 28.1.3 dev: true @@ -5198,7 +5198,7 @@ packages: dependencies: '@jest/types': 28.1.3 '@types/graceful-fs': 4.1.9 - '@types/node': 25.3.5 + '@types/node': 25.5.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -5284,7 +5284,7 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/types': 28.1.3 - '@types/node': 25.3.5 + '@types/node': 25.5.0 dev: true /jest-mock@30.2.0: @@ -5352,7 +5352,7 @@ packages: '@jest/test-result': 28.1.3 '@jest/transform': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 25.3.5 + '@types/node': 25.5.0 chalk: 4.1.2 emittery: 0.10.2 graceful-fs: 4.2.11 @@ -5438,7 +5438,7 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/types': 28.1.3 - '@types/node': 25.3.5 + '@types/node': 25.5.0 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -5475,7 +5475,7 @@ packages: dependencies: '@jest/test-result': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 25.3.5 + '@types/node': 25.5.0 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.10.2 @@ -5487,7 +5487,7 @@ packages: resolution: {integrity: sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@types/node': 25.3.5 + '@types/node': 25.5.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -5640,7 +5640,7 @@ packages: image-size: 0.5.5 make-dir: 2.1.0 mime: 1.6.0 - needle: 3.3.1 + needle: 3.5.0 source-map: 0.6.1 dev: true @@ -6078,8 +6078,8 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /needle@3.3.1: - resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} + /needle@3.5.0: + resolution: {integrity: sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==} engines: {node: '>= 4.4.x'} hasBin: true requiresBuild: true