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 🙏

© 2025 – Pkg Stats / Ryan Hefner

iframe-comlink-sdk

v1.0.18

Published

A TypeScript SDK for iframe communication using Comlink

Readme

Simple Iframe SDK

简单易用的 iframe 通信 SDK,提供六个核心 API,支持连接失败自动重试。

安装

npm install iframe-comlink-sdk

使用方法

父页面

import { AIAssistantSDK } from 'iframe-comlink-sdk'

const sdk = new AIAssistantSDK({
  onSubmit: (data) => {
    console.log('收到子页面数据:', data)
  },
  onSelect: (data) => {
    console.log('收到子页面选择数据:', data)
    // data 结构:{ customString?, businessType?, data: [{ label, value, originData? }] }
    console.log('自定义回复内容:', data.customString)
    console.log('业务类型:', data.businessType)
    console.log('选择的项:', data.data)
  },
  getToken: () => {
    return {
      /** 商龙云token */
      sly_token: 'xxx',
      /** 组织ID */
      orgId: 'yyy',
      /** 组织类型: 0-门店, 1-集团 */
      orgType: 0,
      /** 账户ID */
      accountId: 'zzz'
    }
  },
  onOpenLink: (data) => {
    console.log('收到子页面打开链接请求:', data)
    // 处理打开链接逻辑,比如在新标签页中打开
    if (data.url) {
      window.open(data.url, '_blank')
    }
  }
})

OpenLinkData数据结构

SDK使用的OpenLinkData数据结构如下:

interface OpenLinkData {
  /** 必传,固定值 */
  code: 'menuJump'
  /** 必传 业务线跳自己菜单或者表单上时,对应着他们自己的菜单 code,sourceModule 他们肯定知道,业务线跳商龙云菜单时,需要商龙云同学告诉业务线同学对应的 menuId */
  id: string
  /** 非必传 业务线自己的菜单 id,打开业务线自己表单时,需要传 */
  sourceId?: string
  /** 必传 add:新建,edit:详情, menu:正常菜单跳转 */
  type: 'add' | 'edit' | 'menu'
  /** 非必传 跳转详情或者新建的时候(业务线/商龙云),需要传此值, ---新建时为 add,详情时为 detailId */
  detailId?: string
  /** 非必传 跳转新建或者详情时(业务线/商龙云),需要传 */
  name?: string
  /** 必传 商龙云:000, 供应链:010 新餐饮:012 老餐饮: 005 */
  sourceCode: '000' | '010' | '012' | '005'
  /** 非必传 新建或者详情时(业务线/商龙云),需要传 */
  url?: string
  /** 非必传 跳转商龙云详情或者新建的时候,需要时传此值 */
  query?: Record<string, any>
  /** 在业务线的新建或者编辑标签页时,点击保存或者保存并新建成功后,进行通讯时需要传此值,true:需要替换当前标签页(新建->编辑、 编辑->新建) */
  replaceTab?: boolean
  /** 业务线的新建或者编辑页签,离开或者关闭时,是否需要通知业务线进行更改校验,需要为 true */
  formCheck?: boolean
  /** 是否为报表类型的菜单 */
  isReport?: boolean
}

SelectionData数据结构

SDK使用的SelectionData数据结构如下(用于 onSelect 方法):

interface SelectionItem {
  /** 显示标签 */
  label: string
  /** 选择值(通常是id) */
  value: string | number
  /** 原始数据(业务方的完整数据) */
  originData?: any
}

interface SelectionData {
  /** 自定义字符串(用于自定义显示回复内容) */
  customString?: string
  /** 业务类型标识 */
  businessType?: string
  /** 选择的数据(支持单选和多选) */
  data: SelectionItem[]
}

onSelect 使用示例

单选场景

// 选择单个商品
await sdk.onSelect({
  customString: '已选择商品:iPhone 15 Pro',
  businessType: 'product',
  data: [
    { 
      label: 'iPhone 15 Pro', 
      value: 'P001',
      originData: { 
        id: 'P001', 
        name: 'iPhone 15 Pro', 
        price: 8999,
        stock: 100 
      }
    }
  ]
})

// 选择单个客户
await sdk.onSelect({
  customString: '已选择客户:张三',
  businessType: 'customer',
  data: [
    {
      label: '张三',
      value: 'C001',
      originData: {
        id: 'C001',
        name: '张三',
        phone: '13800138000',
        address: '北京市朝阳区'
      }
    }
  ]
})

多选场景

// 选择多个商品
await sdk.onSelect({
  customString: '已选择3个商品',
  businessType: 'product',
  data: [
    { 
      label: 'iPhone 15 Pro', 
      value: 'P001',
      originData: { id: 'P001', name: 'iPhone 15 Pro', price: 8999 }
    },
    { 
      label: 'MacBook Pro', 
      value: 'P002',
      originData: { id: 'P002', name: 'MacBook Pro', price: 12999 }
    },
    { 
      label: 'AirPods Pro', 
      value: 'P003',
      originData: { id: 'P003', name: 'AirPods Pro', price: 1999 }
    }
  ]
})

// 选择多个供应商
await sdk.onSelect({
  customString: '已选择2个供应商',
  businessType: 'supplier',
  data: [
    {
      label: '供应商A',
      value: 'S001',
      originData: { id: 'S001', name: '供应商A', contact: '李四' }
    },
    {
      label: '供应商B',
      value: 'S002',
      originData: { id: 'S002', name: '供应商B', contact: '王五' }
    }
  ]
})

使用示例

// 打开新建页面
await sdk.onOpenLink({
  code: 'menuJump',
  id: 'productModule',
  type: 'add',
  detailId: 'add',
  name: '新建商品',
  sourceCode: '000',
  url: 'https://example.com/product/new',
  formCheck: true
})

// 打开编辑页面
await sdk.onOpenLink({
  code: 'menuJump',
  id: 'productModule',
  type: 'edit',
  detailId: '12345',
  name: '编辑商品',
  sourceCode: '000',
  url: 'https://example.com/product/edit/12345',
  query: { readonly: false },
  replaceTab: true,
  formCheck: true
})

// 普通菜单跳转
await sdk.onOpenLink({
  code: 'menuJump',
  id: 'reportModule',
  type: 'menu',
  name: '销售报表',
  sourceCode: '000',
  isReport: true
})

子页面

import { AIAssistantSDK } from 'iframe-comlink-sdk'

const sdk = new AIAssistantSDK({
  onSetToken: (tokenData) => {
    console.log('收到父页面发送的token:', tokenData)
    // 处理接收到的token数据
    localStorage.setItem('currentTokens', JSON.stringify(tokenData))
  }
})

// 🎯 智能等待:以下方法会自动等待SDK初始化完成,无需担心时序问题

// 发送数据给另一个子页面(双iframe模式)或父页面(单iframe模式)
await sdk.onSubmit({ message: 'hello' })

// 发送选择数据给另一个子页面(双iframe模式)或父页面(单iframe模式)
await sdk.onSelect({
  customString: '已选择商品:iPhone 15 Pro',
  businessType: 'product',
  data: [{ label: 'iPhone 15 Pro', value: 'P001', originData: { id: 'P001', name: 'iPhone 15 Pro', price: 8999 } }]
})

// 请求父页面打开链接(自动等待初始化完成)
const result = await sdk.onOpenLink({
  code: 'menuJump',        // 必传,固定值
  id: 'sourceModule',      // 必传,菜单code或目标模块ID
  sourceId: 'myMenuId',    // 非必传,业务线自己的菜单ID
  type: 'add',             // 必传,操作类型:add-新建,edit-详情,menu-菜单跳转
  detailId: 'add',         // 非必传,新建时为'add',详情时为具体ID
  name: '新建商品',         // 非必传,标签页名称
  sourceCode: '000',       // 必传,系统代码:000-商龙云,010-供应链,012-新餐饮,005-老餐饮
  url: 'https://example.com/form', // 非必传,跳转URL
  query: { productId: 123 }, // 非必传,查询参数
  replaceTab: false,       // 非必传,是否替换当前标签页
  formCheck: true,         // 非必传,离开时是否需要表单校验
  isReport: false          // 非必传,是否为报表类型
})
console.log('新页面返回的数据:', result)

// 获取父页面的token数据(自动等待初始化完成)
const tokens = await sdk.getToken()

// 获取另一个子页面或父页面的表单数据(自动等待初始化完成)
// 需要URL包含openlink_id参数
const formData = await sdk.getFormData()
console.log('获取到的表单数据:', formData)

## API

### 连接管理
- `linkSingleFrame(iframe)` - 父页面连接单个iframe(支持onOpenLink功能)
- `linkFrame(iframe1, iframe2)` - 父页面连接两个 iframe(完整通信功能)
- `retryConnection()` - 父页面手动重试连接
- `getConnectionStatus()` - 获取当前连接状态

### 数据通信(自动等待初始化)
- `onSubmit(data)` - 子页面发送数据给另一个子页面或父页面
- `onSelect(data)` - 子页面发送选择数据(SelectionData对象)给另一个子页面或父页面
- `onOpenLink(data)` - 子页面请求父页面打开链接,传入OpenLinkData对象
- `getToken()` - 子页面向父页面请求token数据
- `getFormData()` - 子页面获取另一个子页面或父页面的表单数据(需要URL包含openlink_id参数)
- `setToken(iframe, tokenData)` - 父页面向指定子页面发送token数据
- `retryConnection()` - 父页面手动重试连接
- `getConnectionStatus()` - 获取当前连接状态

## 重试机制

SDK 内置了连接失败重试机制:

- 默认重试3次,每次间隔2秒
- 可通过构造函数参数自定义重试次数和间隔
- 支持手动调用 `retryConnection()` 重新尝试连接
- 所有Comlink连接操作都会自动重试

## AI助手环境检测

被AI助手嵌入或者打开的页面会有一个 `openlink_id` 的参数,可以用这个参数判断是否在AI助手中:

```javascript
// 检查是否在AI助手环境中
const urlParams = new URLSearchParams(window.location.search);
const isInAIAssistant = urlParams.has('openlink_id');

if (isInAIAssistant) {
  console.log('当前页面运行在AI助手中');
  const openlinkId = urlParams.get('openlink_id');
  console.log('OpenLink ID:', openlinkId);
}

重试机制

SDK 内置了连接失败重试机制:

  • 默认重试3次,每次间隔2秒
  • 可通过构造函数参数自定义重试次数和间隔
  • 支持手动调用 retryConnection() 重新尝试连接
  • 所有Comlink连接操作都会自动重试

getFormData API

getFormData() 方法用于在子页面中获取另一个子页面或父页面的表单数据。

🎯 新特性:自动等待初始化

现在 getFormData() 支持自动等待SDK初始化完成,确保在子页面完全初始化之前调用也能正常工作。

特性

  • URL 校验:只有当前页面URL包含 openlink_id 参数时才能调用此方法
  • 双向获取:可以获取另一个子页面的数据,或获取父页面的数据
  • 自动降级:如果对等连接不可用,会自动尝试从父页面获取数据

使用方法

父页面配置

const sdk = new AIAssistantSDK({
  getFormData: () => {
    // 返回父页面的表单数据
    return {
      parentField1: 'value1',
      parentField2: 'value2',
      currentUser: localStorage.getItem('username')
    }
  }
})

子页面配置

const sdk = new AIAssistantSDK({
  getFormData: () => {
    // 返回当前子页面的表单数据
    return {
      username: document.querySelector('#username').value,
      email: document.querySelector('#email').value,
      selectedOption: document.querySelector('#option').value
    }
  }
})

// 在需要的时候获取另一个页面的表单数据
// 注意:只有URL包含openlink_id参数时才能调用
try {
  const formData = await sdk.getFormData()
  console.log('获取到的表单数据:', formData)
  
  // 使用获取到的数据预填表单
  if (formData.username) {
    document.querySelector('#username').value = formData.username
  }
} catch (error) {
  console.error('获取表单数据失败:', error)
}

数据流向

  1. 双iframe模式:子页面A → 子页面B 的表单数据
  2. 单iframe模式:子页面 → 父页面 的表单数据
  3. 连接降级:如果对等连接断开,自动切换到父页面数据源

注意事项

  • 必须在URL包含 openlink_id 参数的页面中调用
  • 需要配置对应的 getFormData 回调函数
  • 数据会自动序列化,移除Vue等框架的Proxy包装
  • 如果目标页面未配置回调或连接断开,会抛出错误

SDK使用的Token数据结构如下:

interface TokenData {
  /** 商龙云token */
  sly_token: string;
  /** 组织ID */
  orgId: string;
  /**
   * orgType 可以是字符串或数字,表示组织类型
   * 0 - 门店
   * 1 - 集团
   */
  orgType: string | number;
  /** 账户ID */
  accountId: string;
}

示例

查看 examples/ 目录下的完整示例。