npm package: @realnation/builder-shared-sdk
Description: 一个为Vite模块联邦微前端设计的轻量级SDK，旨在简化Host（宿主应用）与Remote（远程模块）之间的通信和流程控制。
Latest version: 1.0.4

# @realnation/builder-shared-sdk

一个为Vite模块联邦微前端设计的轻量级SDK，旨在简化Host（宿主应用）与Remote（远程模块）之间的通信和流程控制。

## ✨ 功能

- **HTTP客户端共享**: 提供统一的`axios`实例，自动处理鉴权Token，避免在每个Remote中重复配置。
- **全局事件总线**: 一个简单的发布/订阅系统，用于在Host和Remotes之间或Remotes彼此之间进行解耦的通信。
- **动态流程控制**: 一个极其灵活的事件驱动流程控制机制，允许Host动态注册回调，由任意Remote在特定时机触发。

## 📦 安装

```bash
npm install @realnation/builder-shared-sdk
# or
yarn add @realnation/builder-shared-sdk
# or
pnpm add @realnation/builder-shared-sdk
```

## 🚀 快速上手

### 1. 初始化HTTP客户端 (在Host中)

在您的Host应用启动时，配置共享的HTTP客户端。

```typescript
import { configureHttpClient, setAuthToken } from '@realnation/builder-shared-sdk';

// 登录后，从您的用户会话中获取Token
const userToken = 'your-jwt-token-here';
setAuthToken(userToken);

// 配置API基础URL和超时
configureHttpClient({
  baseURL: 'https://api.example.com',
  timeout: 15000,
});
```

### 2. 在Remote中使用HTTP客户端

在任何Remote模块中，直接获取已配置好的`axios`实例来发起请求。

```typescript
import { getHttpClient } from '@realnation/builder-shared-sdk';

async function fetchSomeData() {
  try {
    const httpClient = getHttpClient();
    const response = await httpClient.get('/data');
    console.log(response.data);
  } catch (error) {
    console.error('Request failed:', error);
  }
}
```

### 3. 使用全局事件总线

`SdkEventBus`可用于在不同模块间传递消息，而无需直接依赖。

**在Host或某个Remote中监听事件:**

```typescript
import { getGlobalEventBus } from '@realnation/builder-shared-sdk';

const eventBus = getGlobalEventBus();

// 监听模块导航事件
const unsubscribe = eventBus.on('module:navigate', (payload) => {
  console.log('Navigating to:', payload.path);
  // 在这里执行路由跳转逻辑...
});

// 在组件卸载或不再需要时取消监听
// unsubscribe();
```

**在另一个Remote中触发事件:**

```typescript
import { getGlobalEventBus } from '@realnation/builder-shared-sdk';

const eventBus = getGlobalEventBus();

function onButtonClick() {
  eventBus.emit('module:navigate', { path: '/new-page' });
}
```

## 🔗 动态流程控制 (Flow Control)

这是SDK的核心亮点之一，提供了一种比传统模块联邦更灵活的模块间协作方式。Host可以定义一组具名事件（例如 `success`, `failure`, `nextStep`），并将它们与具体的操作（例如加载下一个Remote模块）关联起来。Remote模块在完成其任务后，只需触发这些预定义的事件，而无需关心接下来会发生什么。

### 系统事件与自定义事件

Flow Control区分两种事件类型：

1.  **系统事件 (System Events)**: 以`sys:`为前缀，由SDK或框架约定，用于处理通用的生命周期钩子。例如 `sys:ready`。这些事件**不会**被`clearEvents()`清除。
2.  **自定义事件 (Custom Events)**: 不带前缀，由业务逻辑定义，例如 `welcome:completed` 或 `profile:submitted`。这些事件会被`clearEvents()`清除。

### 工作流程

1.  **Host注册事件**: Host应用使用`registerEvent`注册系统事件和自定义事件的回调。
    *   注册`sys:ready`来处理Remote模块加载完成后的显示逻辑。
    *   注册自定义事件来编排业务流程。
2.  **Remote触发事件**:
    *   Remote模块加载并初始化完成后，应立即触发`sys:ready`事件，通知Host可以将其UI插入DOM并显示。
    *   在用户交互或内部状态改变后，触发相应的自定义事件。
3.  **执行与回退**:
    *   如果Host注册了该事件，对应的回调函数将被执行。
    *   如果Host**没有**注册该事件，`emitEvent`会执行一个可选的本地回退函数（最后一个函数类型的参数），确保Remote在独立运行时也能正常工作。
4.  **清理**: 在业务流程结束时，Host调用`clearEvents()`来清理所有**自定义事件**，而系统事件保持不变。

### 代码示例

#### 在Host中编排流程

```typescript
import { registerEvent, clearEvents } from '@realnation/builder-shared-sdk/flow';
import { loadRemoteModule, showRemote, hideRemote } from './remoteLoader'; // 假设的加载器

// 注册系统事件，用于显示已加载的模块
registerEvent('sys:ready', (remoteId) => {
  console.log(`Remote module [${remoteId}] is ready to be displayed.`);
  showRemote(remoteId);
});

// 注册自定义业务事件
registerEvent('welcome:completed', () => {
  hideRemote('welcome'); // 隐藏旧模块
  loadRemoteModule('user-profile'); // 加载新模块
});

registerEvent('profile:submitted', (userData) => {
  hideRemote('user-profile');
  loadRemoteModule('summary-view', { user: userData });
});

// 当整个业务流程结束时，清理自定义事件
function cleanupBusinessFlow() {
  clearEvents();
}
```

#### 在Remote中触发事件

**`welcome-module` (远程模块):**

```typescript
import { emitEvent } from '@realnation/builder-shared-sdk/flow';
import React, { useEffect } from 'react';

function WelcomeComponent({ remoteId }) {
  // 1. 组件挂载后，触发 sys:ready 事件
  useEffect(() => {
    emitEvent('sys:ready', remoteId, () => {
      console.log(`Standalone mode: [${remoteId}] is ready.`);
    });
  }, [remoteId]);

  const handleStartClick = () => {
    // 2. 用户交互，触发自定义事件
    emitEvent('welcome:completed', () => {
      console.log('Standalone mode: Welcome journey would start here.');
    });
  };

  return <button onClick={handleStartClick}>开始</button>;
}
```

**`user-profile-module` (远程模块):**

```typescript
import { emitEvent } from '@realnation/builder-shared-sdk/flow';

function UserProfileForm() {
  const handleSubmit = (formData) => {
    // 触发 'profile:submitted' 事件，并传递表单数据
    emitEvent('profile:submitted', formData, () => {
      console.log('In standalone mode: Form submitted locally.', formData);
      // 在独立模式下可以导航到本地的下一页
    });
  };

  // ... 表单逻辑
}
```

这种模式的优势在于：

-   **高度解耦**: Remote模块不关心下一个模块是什么，只关心在何时完成了自己的任务。
-   **灵活编排**: Host可以随时改变流程，例如在A/B测试中，通过注册不同的回调来加载不同的下一个模块，而无需修改任何Remote模块的代码。
-   **易于测试**: Remote模块可以独立开发和测试，只需为其`emitEvent`提供本地回退函数即可。

## 🛠️ API参考

### HTTP

-   `setAuthToken(token: string | null)`: 设置全局JWT Token。
-   `setTokenResolver(resolver: () => string | null)`: (高级) 设置一个函数来动态解析Token，优先级高于`setAuthToken`。
-   `configureHttpClient(options: HttpClientOptions)`: 配置共享的`axios`实例。
-   `getHttpClient(): AxiosInstance`: 获取配置好的`axios`实例。

### Events

-   `getGlobalEventBus(): SdkEventBus`: 获取全局事件总线实例。
-   `eventBus.on(name, handler)`: 监听一个事件。
-   `eventBus.emit(name, payload)`: 触发一个事件。
-   `eventBus.off(name, handler)`: 取消监听。

### Flow

-   `registerEvent(type: string, callback: Function)`: Host注册一个流程事件。以`sys:`为前缀的事件为系统事件，不会被`clearEvents`清除。
-   `removeEvent(type: string)`: 移除一个已注册的事件。
-   `clearEvents()`: 移除所有**非系统级**的自定义事件。
-   `emitEvent(type: string, ...args: any[])`: Remote触发一个流程事件。参数会透传给回调，如果最后一个参数是函数，则被视作本地回退`callback`。

### OSS

-   `fetchOssCredential(options: OssCredentialRequestOptions)`: 获取固定接口的OSS临时凭证。
-   `createOssClient(credential: OssCredential, options?)`: 使用临时凭证创建OSS客户端。
-   `uploadOssFile(options: OssUploadOptions)`: 上传文件并返回对象地址与Key，支持进度回调和上传目录配置。
-   `clearOssCache(projectId?)`: 清理OSS凭证与客户端缓存，可按项目或全量清理。

说明：SDK会基于接口返回的`expireAtUnix/expireAt/expiration`自动缓存并复用凭证与客户端，默认提前60秒刷新；可通过`cacheSkewMs`调整。`uploadOssFile` 默认在凭证失效时重试1次，可用 `retryTimes` 控制，并支持 `allowedExtensions`、`maxSizeBytes`、`minSizeBytes` 限制上传文件。

#### OSS 快速示例

```typescript
import { uploadOssFile } from '@realnation/builder-shared-sdk';

const result = await uploadOssFile({
  projectId: 'project-id',
  file,
  directory: 'uploads/images',
  onProgress: (ratio) => console.log('progress:', ratio)
});

console.log(result.url);
```

## 🔧 构建

```bash
npm run build
```

该命令会使用`tsc`将`src`目录下的TypeScript源文件编译到`dist`目录。