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

@siact/ai-chatbot-bridge

v0.0.10

Published

用于在父容器页面与嵌入的 iframe 之间建立双向指令通信的轻量桥接库。基于 [Comlink](https://github.com/GoogleChromeLabs/comlink) 的 RPC 机制,通过 `HostBridge` 和 `ClientBridge`,宿主应用可以暴露受控的命令,iframe 客户端可发现并调用这些命令,实现对父容器的安全控制。

Readme

AI Chatbot Bridge

用于在父容器页面与嵌入的 iframe 之间建立双向指令通信的轻量桥接库。基于 Comlink 的 RPC 机制,通过 HostBridgeClientBridge,宿主应用可以暴露受控的命令,iframe 客户端可发现并调用这些命令,实现对父容器的安全控制。

安装

pnpm add @siact/ai-chatbot-bridge
# 或
npm install @siact/ai-chatbot-bridge

基本原理

┌─────────────────────────────┐
│   宿主页面 (Host)           │
│  ┌───────────────────────┐  │
│  │   HostBridge          │  │
│  │  - expose()           │  │
│  │  - CommandProvider    │  │
│  └───────────────────────┘  │
└────────────┬────────────────┘
             │ postMessage
             │ (Comlink RPC)
             │
┌────────────┴────────────────┐
│   iframe 客户端 (Client)    │
│  ┌───────────────────────┐  │
│  │   ClientBridge        │  │
│  │  - wrap()             │  │
│  │  - discover()         │  │
│  │  - execute()          │  │
│  └───────────────────────┘  │
└─────────────────────────────┘

快速开始

宿主页面(父容器)

import { HostBridge } from "@siact/ai-chatbot-bridge";

const hostBridge = new HostBridge({
  iframe: document.querySelector("#app-iframe"),
  allowedOrigins: ["https://your-client-origin"],
  debug: true,
});

hostBridge.register({
  project: "host-demo",
  description: "父容器控制指令",
  version: "1.0.0",
  commands: [
    {
      name: "open-modal",
      description: "在宿主中打开一个弹窗",
      handler: (title: string) => {
        console.log(`打开弹窗: ${title}`);
        return { success: true };
      },
    },
    {
      name: "navigate",
      description: "切换宿主路由",
      handler: async (path: string) => {
        console.log(`导航到: ${path}`);
        return { success: true, path };
      },
    },
    {
      name: "get-user-info",
      description: "获取当前用户信息",
      handler: async () => {
        return {
          id: "user-123",
          name: "张三",
          role: "admin",
        };
      },
    },
  ],
});

// 页面卸载时清理资源
window.addEventListener("beforeunload", () => hostBridge.destroy());

iframe 客户端

import { ClientBridge } from "@siact/ai-chatbot-bridge";

const clientBridge = new ClientBridge({
  targetOrigin: "https://your-host-origin",
  debug: true,
});

async function init() {
  // 发现宿主提供的命令
  const provider = await clientBridge.discover();
  console.log("可用命令:", provider.commands);

  // 执行宿主命令
  const userInfo = await clientBridge.execute("get-user-info");
  console.log("用户信息:", userInfo);

  await clientBridge.execute("open-modal", ["来自 iframe 的弹窗"]);
  await clientBridge.execute("navigate", ["/dashboard"]);
}

init();

window.addEventListener("beforeunload", () => clientBridge.destroy());

让客户端向宿主暴露命令

vX.Y.Z 起,客户端也可以通过 clientBridge.register() 注册自身命令,供宿主发现与调用:

import { ClientBridge } from "@siact/ai-chatbot-bridge";

const clientBridge = new ClientBridge({
  targetOrigin: "https://your-host-origin",
  debug: true,
});

clientBridge.register({
  project: "client-demo",
  description: "iframe 暴露给宿主的能力",
  version: "1.0.0",
  commands: [
    {
      name: "get-client-state",
      description: "返回 iframe 内部的会话状态",
      handler: () => {
        return { theme: "dark", unreadCount: 2 };
      },
    },
    {
      name: "focus-input",
      description: "聚焦聊天输入框",
      handler: () => {
        const input = document.querySelector<HTMLInputElement>("#chat-input");
        input?.focus();
        return { focused: !!input };
      },
    },
  ],
});

宿主侧可调用新增的 discoverClient()executeOnClient() 方法:

import { HostBridge } from "@siact/ai-chatbot-bridge";

const hostBridge = new HostBridge({
  iframe: document.querySelector("#app-iframe"),
  allowedOrigins: ["https://your-client-origin"],
  debug: true,
});

async function useClientCommands() {
  const clientProvider = await hostBridge.discoverClient();
  console.log("客户端暴露的命令:", clientProvider.commands);

  const clientState = await hostBridge.executeOnClient("get-client-state");
  console.log("客户端状态:", clientState);

  await hostBridge.executeOnClient("focus-input");
}

useClientCommands();

配置说明

HostBridge 选项

  • iframe (可选): 指定目标 iframe 元素。如果不指定,Comlink 会自动使用 window.parent
  • allowedOrigins (可选): 允许接收消息的来源,默认 ['*']推荐在生产环境中填写具体域名以提高安全性
  • debug (可选): 启用调试模式,在控制台输出通信日志。

ClientBridge 选项

  • targetWindow (可选): 目标窗口对象,默认 window.parent
  • targetOrigin (可选): 宿主页面的 origin,默认 '*'推荐在生产环境中指定具体 origin
  • debug (可选): 启用调试模式。

高级用法

错误处理

async function executeWithErrorHandling() {
  try {
    const result = await clientBridge.execute("non-existent-command");
  } catch (error) {
    if (error instanceof Error) {
      console.error("错误信息:", error.message);
      // 输出: 错误信息: Command "non-existent-command" not found
    }
  }
}

缓存 discover 结果

ClientBridge 会自动缓存第一次 discover() 的结果,后续调用会直接返回缓存:

const provider1 = await clientBridge.discover(); // 发送请求到宿主
const provider2 = await clientBridge.discover(); // 返回缓存结果

支持复杂数据类型

Comlink 支持传递和返回复杂的数据类型(对象、数组、Promise 等):

// 宿主侧
{
  name: "get-data",
  handler: async () => {
    return {
      users: [
        { id: 1, name: "Alice" },
        { id: 2, name: "Bob" },
      ],
      metadata: {
        total: 2,
        timestamp: new Date(),
      },
    };
  },
}

// 客户端侧
const data = await clientBridge.execute("get-data");
console.log(data.users); // [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]

完整示例

宿主页面 (host.html)

<!DOCTYPE html>
<html>
  <head>
    <title>宿主页面</title>
  </head>
  <body>
    <h1>宿主页面</h1>
    <iframe id="app-iframe" src="http://localhost:3000/client.html"></iframe>

    <script type="module">
      import { HostBridge } from "@siact/ai-chatbot-bridge";

      const hostBridge = new HostBridge({
        iframe: document.querySelector("#app-iframe"),
        debug: true,
      });

      hostBridge.register({
        project: "host-app",
        version: "1.0.0",
        commands: [
          {
            name: "alert",
            handler: (message) => {
              alert(message);
              return { shown: true };
            },
          },
        ],
      });

      window.addEventListener("beforeunload", () => hostBridge.destroy());
    </script>
  </body>
</html>

客户端页面 (client.html)

<!DOCTYPE html>
<html>
  <head>
    <title>客户端页面</title>
  </head>
  <body>
    <h1>客户端页面</h1>
    <button id="btn">点击我</button>

    <script type="module">
      import { ClientBridge } from "@siact/ai-chatbot-bridge";

      const clientBridge = new ClientBridge({
        debug: true,
      });

      document.getElementById("btn").addEventListener("click", async () => {
        const result = await clientBridge.execute("alert", [
          "来自 iframe 的问候!",
        ]);
        console.log(result);
      });

      window.addEventListener("beforeunload", () => clientBridge.destroy());
    </script>
  </body>
</html>

类型定义

CommandHandler

type CommandHandler = (...args: any[]) => any | Promise<any>;

CommandDefinition

interface CommandDefinition {
  name: string;
  description?: string;
  handler: CommandHandler;
}

DiscoveredProvider

interface DiscoveredProvider {
  project: string;
  description?: string;
  version?: string;
  commands: DiscoveredCommand[];
}

最佳实践

安全性

  • 在生产环境中,始终在 allowedOrigins 中指定具体的域名
  • ClientBridge 中指定 targetOrigin 而不是使用 '*'
  • 只暴露必要的命令,避免暴露敏感操作

性能

  • 利用 discover() 的自动缓存机制
  • 避免频繁创建和销毁 Bridge 实例
  • 使用 timeoutMs 设置合理的超时时间

调试

  • 在开发环境启用 debug: true 以便追踪通信过程
  • 在生产环境禁用 debug 模式以减少日志输出

资源清理

  • 页面卸载时调用 destroy() 方法,避免内存泄漏
  • 清理事件监听器和定时器

常见问题

Q: 为什么使用 Comlink? A: Comlink 提供了一个简洁的 RPC 接口,自动处理 postMessage 通信的复杂性,使代码更易读易维护。

Q: 支持哪些数据类型? A: Comlink 支持大多数可序列化的 JavaScript 类型,包括对象、数组、Date、Map、Set 等。不支持函数和循环引用。

Q: 如何处理长时间运行的命令? A: 可以增加 timeoutMs 的值,或者在命令中使用 WebSocket 等实时通信方式来推送进度更新。

Q: 能否在多个 iframe 之间通信? A: 可以。每个 iframe 创建独立的 ClientBridge 实例,连接到同一个宿主。

构建与发布

pnpm install
pnpm build

pnpm build 将生成 dist/ 产物和类型声明,方便发布到私有或公共 npm 镜像。