npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

host-app-bridge-sdk

v1.0.0

Published

Host-App 桥接 SDK - 实现 Host 和 Iframe App 之间的双向通信

Readme

host-app-bridge-sdk

Host-App 桥接 SDK - 实现 Host(宿主对话界面)和 App(运行在 Iframe 中的插件)之间的双向通信。

特性

  • 🚀 极简 API 设计
  • 🔒 内置安全校验(Origin 验证、消息格式验证)
  • 🤝 自动握手机制
  • 📦 兼容 OpenAI Apps SDK 标准
  • 💪 完整的 TypeScript 支持
  • 🔧 MCP 工具调用结果通知

安装

pnpm add host-app-bridge-sdk

快速开始

App 端使用

import { Bridge, MESSAGE_TYPES } from "host-app-bridge-sdk";

// 创建 Bridge 实例(自动发送 APP_READY)
const bridge = new Bridge({ debug: true });

// 监听来自 Host 的消息
bridge.on(MESSAGE_TYPES.CONTEXT_UPDATE, data => {
  console.log("收到上下文更新:", data);
});

bridge.on(MESSAGE_TYPES.INIT_DATA, data => {
  console.log("收到初始化数据:", data);
});

// 向 Host 发送 MCP 结果通知
bridge.notifyMCPSuccess("search", { items: [...] });

Host 端使用

import { HostProvider, MESSAGE_TYPES } from "host-app-bridge-sdk";

// 创建 HostProvider 实例
const hostProvider = new HostProvider({ debug: true });

// 注册 Iframe App
const iframe = document.getElementById("app-iframe") as HTMLIFrameElement;
hostProvider.registerApp(iframe);

// 监听 App 就绪事件
hostProvider.onAppReady((iframe, data) => {
  console.log("App 已就绪:", data);
  // 推送初始化数据
  hostProvider.pushInitData(iframe, { theme: "dark", token: "xxx" });
});

// 推送上下文数据
hostProvider.pushContext(iframe, { conversationId: "123" });

架构概述

┌─────────────────────────────────────────────────────────────┐
│                      Host 应用                               │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                  HostProvider                        │   │
│  │  - 注册/注销 Iframe App                              │   │
│  │  - 监听 App 消息                                     │   │
│  │  - 推送上下文/指令/初始化数据                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                           │                                  │
│                    window.postMessage                        │
│                           │                                  │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                   Iframe App                         │   │
│  │  ┌─────────────────────────────────────────────┐    │   │
│  │  │                  Bridge                      │    │   │
│  │  │  - 监听 Host 消息                            │    │   │
│  │  │  - 向 Host 发送通知                          │    │   │
│  │  │  - MCP 结果通知                              │    │   │
│  │  └─────────────────────────────────────────────┘    │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

通信流程

App                                    Host
 │                                      │
 │  ──────── APP_READY ────────────►   │  1. App 加载完成,发送就绪消息
 │                                      │
 │  ◄─────── INIT_DATA ────────────    │  2. Host 推送初始化数据
 │                                      │
 │  ◄────── CONTEXT_UPDATE ────────    │  3. Host 推送上下文更新
 │                                      │
 │  ◄─────── AI_COMMAND ───────────    │  4. Host 推送 AI 指令
 │                                      │
 │  ──────── MCP_RESULT ───────────►   │  5. App 通知 MCP 调用结果
 │                                      │

API 参考

Bridge 类

App 端使用的桥接 SDK,在 Iframe 中运行。

构造函数

new Bridge(options?: BridgeOptions)

BridgeOptions:

| 参数 | 类型 | 默认值 | 说明 | | ---------------- | ---------- | ------- | ------------------ | | allowedOrigins | string[] | ['*'] | 允许的 origin 列表 | | debug | boolean | false | 是否开启调试模式 |

方法

on(type: string, callback: (payload: any) => void): void

监听来自 Host 的消息。

bridge.on(MESSAGE_TYPES.CONTEXT_UPDATE, data => {
  console.log("收到上下文:", data.context);
});
notifyHost(type: string, payload?: any): void

向 Host 发送通知消息。

bridge.notifyHost("custom_event", { action: "click", target: "button" });
notifyMCPResult(toolName: string, success: boolean, result?: any, error?: string, callId?: string): void

通知 Host MCP 工具调用结果(标准化格式)。

// 成功调用
bridge.notifyMCPResult("search", true, { items: [...] });

// 失败调用
bridge.notifyMCPResult("search", false, undefined, "搜索服务不可用");

// 带调用 ID
bridge.notifyMCPResult("search", true, { items: [...] }, undefined, "call_123");
notifyMCPSuccess(toolName: string, result: any, callId?: string): void

便捷方法,通知 Host MCP 工具调用成功。

const result = await mcpClient.call("search", { query: "test" });
bridge.notifyMCPSuccess("search", result);
notifyMCPError(toolName: string, error: string, callId?: string): void

便捷方法,通知 Host MCP 工具调用失败。

try {
  await mcpClient.call("search", { query: "test" });
} catch (err) {
  bridge.notifyMCPError("search", err.message);
}
off(type: string, callback?: Function): void

移除消息监听器。

// 移除特定回调
bridge.off(MESSAGE_TYPES.CONTEXT_UPDATE, myCallback);

// 移除该类型所有监听器
bridge.off(MESSAGE_TYPES.CONTEXT_UPDATE);
getIsReady(): boolean

获取 Bridge 是否已就绪。

if (bridge.getIsReady()) {
  // Bridge 已就绪,可以正常通信
}
setAllowedOrigins(origins: string[]): void

更新允许的 origin 列表。

bridge.setAllowedOrigins(["https://trusted-host.com"]);
addAllowedOrigin(origin: string): void

添加允许的 origin。

bridge.addAllowedOrigin("https://another-trusted-host.com");
destroy(): void

销毁 Bridge 实例,清理资源。

// 在组件卸载时调用
bridge.destroy();

HostProvider 类

Host 端使用的管理类,负责管理与 Iframe App 的通信。

构造函数

new HostProvider(options?: HostProviderOptions)

HostProviderOptions:

| 参数 | 类型 | 默认值 | 说明 | | ---------------- | ---------- | ------- | -------------------------- | | allowedOrigins | string[] | ['*'] | 允许的 origin 列表 | | debug | boolean | false | 是否开启调试模式 | | initTimeout | number | 10000 | APP_READY 超时时间(毫秒) |

方法

registerApp(iframe: HTMLIFrameElement, expectedOrigin?: string): void

注册 Iframe App。

const iframe = document.getElementById("app-iframe") as HTMLIFrameElement;
hostProvider.registerApp(iframe, "https://app-domain.com");
unregisterApp(iframe: HTMLIFrameElement): void

注销 Iframe App。

hostProvider.unregisterApp(iframe);
sendToApp(iframe: HTMLIFrameElement, type: string, payload?: any): boolean

向指定 App 发送消息,返回是否发送成功。

const success = hostProvider.sendToApp(iframe, "custom_message", { data: "test" });
pushContext(iframe: HTMLIFrameElement, context: any): void

推送上下文数据给 App。

hostProvider.pushContext(iframe, {
  conversationId: "conv_123",
  userId: "user_456",
  messages: [...]
});
pushCommand(iframe: HTMLIFrameElement, command: any): void

推送 AI 指令给 App。

hostProvider.pushCommand(iframe, {
  action: "search",
  params: { query: "test" }
});
pushInitData(iframe: HTMLIFrameElement, data: any): void

推送初始化数据给 App。

hostProvider.pushInitData(iframe, {
  theme: "dark",
  token: "auth_token_xxx",
  config: { apiBaseUrl: "/api" }
});
getAppStatus(iframe: HTMLIFrameElement): AppStatus

获取 App 状态。

const status = hostProvider.getAppStatus(iframe);
// 返回: "ready" | "loading" | "error" | "unknown"
onAppReady(callback: AppReadyCallback): void

监听 App 就绪事件。

hostProvider.onAppReady((iframe, data) => {
  console.log("App 已就绪:", data.version, data.capabilities);
});
onMessage(callback: MessageCallback): void

监听所有来自 App 的消息。

hostProvider.onMessage((iframe, type, payload) => {
  console.log(`收到消息 [${type}]:`, payload);
});
offAppReady(callback: AppReadyCallback): void

移除 App 就绪事件监听。

offMessage(callback: MessageCallback): void

移除消息事件监听。

getRegisteredApps(): HTMLIFrameElement[]

获取所有已注册的 App。

const apps = hostProvider.getRegisteredApps();
console.log(`已注册 ${apps.length} 个 App`);
getReadyApps(): HTMLIFrameElement[]

获取所有已就绪的 App。

const readyApps = hostProvider.getReadyApps();
readyApps.forEach(iframe => {
  hostProvider.pushContext(iframe, newContext);
});
setAllowedOrigins(origins: string[]): void

更新允许的 origin 列表。

addAllowedOrigin(origin: string): void

添加允许的 origin。

removeAllowedOrigin(origin: string): void

移除允许的 origin。

destroy(): void

销毁 HostProvider 实例,清理资源。


消息类型常量

import { MESSAGE_TYPES } from "@zhixing/host-app-bridge-sdk";

// 握手相关
MESSAGE_TYPES.APP_READY; // "app_ready" - App 就绪消息
MESSAGE_TYPES.INIT; // "init" - 初始化消息
MESSAGE_TYPES.INIT_DATA; // "init_data" - 初始化数据消息

// 上下文同步
MESSAGE_TYPES.CONTEXT_UPDATE; // "context_update" - 上下文更新消息

// AI 指令
MESSAGE_TYPES.AI_COMMAND; // "ai_command" - AI 指令消息

// MCP 结果
MESSAGE_TYPES.MCP_RESULT; // "mcp_result" - MCP 调用结果消息

类型定义

// 消息结构
interface BridgeMessage {
  type: string;
  payload?: any;
  id?: string;
  timestamp?: number;
}

// App 状态
type AppStatus = "ready" | "loading" | "error" | "unknown";

// App 就绪数据
interface AppReadyData {
  version: string;
  capabilities: string[];
}

// 初始化数据
interface InitData {
  context?: any;
  theme?: "light" | "dark";
  token?: string;
  config?: any;
  timestamp?: number;
}

// MCP 结果数据
interface MCPResultData {
  success: boolean;
  result?: any;
  error?: string;
  toolName: string;
  callId?: string;
}

// 回调类型
type AppReadyCallback = (iframe: HTMLIFrameElement, data: any) => void;
type MessageCallback = (iframe: HTMLIFrameElement, type: string, payload: any) => void;

安全模块

import {
  validateOrigin,
  validateMessageFormat,
  SecurityValidator
} from "@zhixing/host-app-bridge-sdk";

// 验证 origin
const originResult = validateOrigin("https://example.com", ["https://example.com"]);
if (originResult.valid) {
  // origin 有效
}

// 验证消息格式
const messageResult = validateMessageFormat({ type: "test", payload: {} });
if (messageResult.valid) {
  // 消息格式有效
}

// 使用 SecurityValidator 类
const validator = new SecurityValidator({
  allowedOrigins: ["https://trusted.com"],
  strictMode: true
});

Vue 3 集成示例

App 端 (Iframe 内)

<script setup lang="ts">
import { onMounted, onUnmounted, ref } from "vue";
import { Bridge, MESSAGE_TYPES } from "@zhixing/host-app-bridge-sdk";

const bridge = ref<Bridge>();
const context = ref<any>(null);

onMounted(() => {
  bridge.value = new Bridge({
    allowedOrigins: ["http://localhost:3000"],
    debug: true
  });

  // 监听上下文更新
  bridge.value.on(MESSAGE_TYPES.CONTEXT_UPDATE, data => {
    context.value = data.context;
  });

  // 监听初始化数据
  bridge.value.on(MESSAGE_TYPES.INIT_DATA, data => {
    console.log("初始化数据:", data);
  });
});

onUnmounted(() => {
  bridge.value?.destroy();
});

// MCP 工具调用
async function callSearchTool(query: string) {
  try {
    const result = await fetch("/api/mcp/search", {
      method: "POST",
      body: JSON.stringify({ query })
    }).then(r => r.json());

    bridge.value?.notifyMCPSuccess("search", result);
    return result;
  } catch (error) {
    bridge.value?.notifyMCPError("search", error.message);
    throw error;
  }
}
</script>

Host 端

<template>
  <div class="host-container">
    <iframe ref="appIframe" :src="appUrl" @load="onIframeLoad" />
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";
import { HostProvider, MESSAGE_TYPES } from "@zhixing/host-app-bridge-sdk";

const appIframe = ref<HTMLIFrameElement>();
const appUrl = "https://your-app-domain.com";

let hostProvider: HostProvider;

onMounted(() => {
  hostProvider = new HostProvider({
    allowedOrigins: ["https://your-app-domain.com"],
    debug: true
  });

  // 监听 App 就绪
  hostProvider.onAppReady((iframe, data) => {
    console.log("App 已就绪:", data);
    // 推送初始化数据
    hostProvider.pushInitData(iframe, {
      theme: "dark",
      token: localStorage.getItem("token")
    });
  });

  // 监听 MCP 结果
  hostProvider.onMessage((iframe, type, payload) => {
    if (type === MESSAGE_TYPES.MCP_RESULT) {
      console.log("MCP 结果:", payload);
    }
  });
});

function onIframeLoad() {
  if (appIframe.value) {
    hostProvider.registerApp(appIframe.value);
  }
}

// 推送上下文更新
function updateContext(newContext: any) {
  if (appIframe.value) {
    hostProvider.pushContext(appIframe.value, newContext);
  }
}

onUnmounted(() => {
  hostProvider?.destroy();
});
</script>

安全最佳实践

  1. 配置具体的 origin 列表:生产环境不要使用 ['*'],应该配置具体的域名。
// ❌ 不推荐
new Bridge({ allowedOrigins: ["*"] });

// ✅ 推荐
new Bridge({ allowedOrigins: ["https://trusted-host.com"] });
  1. 验证消息来源:SDK 内置了 origin 验证,确保只接收来自可信来源的消息。

  2. 使用 HTTPS:生产环境应该使用 HTTPS 协议。

  3. 定期更新 origin 列表:根据业务需求及时更新允许的 origin 列表。

调试

开启调试模式可以在控制台查看详细的消息日志:

const bridge = new Bridge({ debug: true });
const hostProvider = new HostProvider({ debug: true });

调试模式会输出:

  • 接收到的消息内容
  • 发送的消息内容
  • Origin 验证结果
  • 消息格式验证结果
  • 错误信息

示例文件

完整的使用示例请参考:

许可证

MIT