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

@ygo-build-x/plugin-sdk

v0.1.0

Published

YGO Build X 插件开发 SDK - 为游戏王卡组构筑工具开发插件

Downloads

15

Readme

YGO Build X 插件系统

概述

YGO Build X 插件系统允许开发者扩展应用功能,支持侧边栏面板和独立页面两种展示模式。

快速开始

1. 安装依赖

npm install @ygo-build-x/plugin-sdk

2. 创建插件

import { definePlugin } from '@ygo-build-x/plugin-sdk'
import MyComponent from './MyComponent.vue'

export default definePlugin({
  meta: {
    id: '@your-name/plugin-name',
    name: '插件名称',
    version: '1.0.0',
    description: '插件描述',
    author: 'Your Name',
    icon: '🎯',
    type: 'calculator',
    permissions: ['cards.read', 'ui.sidebar'],
    displayMode: 'sidebar',
  },

  setup(context) {
    return {
      views: [
        {
          id: 'my-view',
          title: '我的视图',
          component: MyComponent,
          displayMode: 'sidebar',
          sidebar: { icon: '🎯', order: 10 },
        },
      ],
    }
  },
})

插件类型

| 类型 | 说明 | | ------------- | ----------------------------- | | calculator | 计算器类 (概率计算、分析工具) | | tool | 工具类 (导入导出、数据处理) | | view | 视图类 (新页面、面板) | | theme | 主题类 (样式、外观) | | integration | 集成类 (第三方服务) |

展示模式

| 模式 | 说明 | | --------- | ---------------- | | sidebar | 在侧边栏显示面板 | | page | 作为独立路由页面 |

权限系统

可用权限

| 权限 | 风险等级 | 说明 | | ------------ | -------- | -------------- | | cards.read | 低 | 读取卡片数据库 | | deck.read | 中 | 读取用户卡组 | | deck.write | 高 | 修改用户卡组 | | storage | 低 | 本地存储数据 | | network | 高 | 访问网络 | | ui.notify | 低 | 显示通知消息 | | ui.modal | 中 | 显示弹窗 | | ui.sidebar | 低 | 在侧边栏显示 | | ui.route | 中 | 添加新页面 |

权限请求

插件安装时会请求所需权限,用户可以选择授予或拒绝部分权限。

Context API

cards - 卡片数据 API

// 获取单张卡片
const card = await context.cards.getById(12345)

// 批量获取卡片 (最多 500 张)
const cards = await context.cards.getByIds([12345, 67890])

// 搜索卡片
const results = await context.cards.search('灰流丽', { limit: 10 })

deck - 卡组数据 API

// 获取当前卡组(包含主卡组、额外卡组、副卡组)
const deck = context.deck.getCurrent()
console.log(deck.main)   // 主卡组卡片 ID 数组
console.log(deck.extra)  // 额外卡组卡片 ID 数组
console.log(deck.side)   // 副卡组卡片 ID 数组

// 获取所有卡组列表
const decks = context.deck.getList()

// 监听卡组变化
const unsubscribe = context.deck.onChange(deck => {
  console.log('卡组已更新:', deck)
})

storage - 存储 API

// 存储数据
context.storage.set('my-key', { data: 'value' })

// 读取数据
const data = context.storage.get<MyData>('my-key')

// 删除数据
context.storage.remove('my-key')

// 清空插件存储
context.storage.clear()

ui - 界面 API

// 显示通知
context.ui.notify({
  type: 'success', // 'success' | 'info' | 'warning' | 'error'
  message: '操作成功',
  duration: 3000,
})

// 显示确认框
const confirmed = await context.ui.confirm({
  title: '确认操作',
  message: '确定要执行此操作吗?',
  confirmText: '确定',
  cancelText: '取消',
})

events - 事件 API

// 监听事件
const unsubscribe = context.events.on('deck:changed', deck => {
  console.log('卡组变化:', deck)
})

// 触发自定义事件 (仅在插件命名空间内)
context.events.emit('my-event', { data: 'value' })

可用事件

| 事件 | 说明 | | ----------------------- | ------------ | | deck:changed | 卡组内容变化 | | deck:saved | 卡组已保存 | | deck:loaded | 卡组已加载 | | card:selected | 卡片被选中 | | calculation:started | 开始计算 | | calculation:completed | 计算完成 |

扩展点

views - 视图扩展

{
  views: [{
    id: 'unique-view-id',
    title: '视图标题',
    component: VueComponent,
    displayMode: 'sidebar', // 'sidebar' | 'page'
    sidebar: {              // 仅 sidebar 模式
      icon: '🎯',
      order: 10,
      group: 'calculator',  // 'calculator' | 'tool' | 'other'
    },
    route: {                // 仅 page 模式
      path: '/my-page',
      meta: { title: '页面标题' },
    },
  }],
}

calculators - 计算器扩展

{
  calculators: [{
    id: 'my-calculator',
    name: '我的计算器',
    description: '计算器描述',
    icon: '🧮',
    component: CalculatorComponent,
  }],
}

menuItems - 菜单项扩展

{
  menuItems: [{
    id: 'my-menu',
    label: '菜单项',
    icon: '📁',
    location: 'sidebar', // 'sidebar' | 'toolbar' | 'context'
    order: 10,
    onClick: () => { /* 处理点击 */ },
    viewId: 'my-view', // 关联的视图 ID
  }],
}

importFormats / exportFormats - 导入导出格式

{
  importFormats: [{
    id: 'my-format',
    name: 'My Format',
    extensions: ['.myf'],
    parse: async (content) => [/* 返回卡片 ID 数组 */],
  }],
  exportFormats: [{
    id: 'my-format',
    name: 'My Format',
    extension: '.myf',
    generate: async (cardIds) => '导出内容',
  }],
}

安全机制

代码隔离

  • 插件代码在 Web Worker 沙箱中执行
  • 阻止访问危险全局对象 (eval, Function, document 等)
  • 原型链冻结保护

签名验证

  • RSA-2048 数字签名
  • SHA-256 代码哈希验证
  • 常量时间比较 (防时序攻击)

资源限制

  • 执行超时: 30 秒
  • API 调用限制: 每秒 100 次
  • 存储配额: 5MB/插件
  • 事件监听器: 50 个/插件

网络代理

  • 域名白名单/黑名单
  • 内网地址阻止
  • 请求频率限制
  • 敏感头过滤

内容安全策略 (CSP)

default-src 'self';
script-src 'self' 'unsafe-inline' blob:;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self';
frame-ancestors 'none';

Vue 组件开发

获取插件上下文

<script setup lang="ts">
import { inject } from 'vue'
import type { PluginContext } from '@ygo-build-x/plugin-sdk'

const context = inject<PluginContext>('pluginContext')
const pluginId = inject<string>('pluginId')

// 使用 context API
const deck = context?.deck.getCurrent()
</script>

UI 样式规范

插件应遵循 YGO Build X 的 Notion 风格设计哲学:简洁、清爽、内容优先。

CSS 变量

使用项目提供的 CSS 变量而非硬编码值:

/* ✅ 正确 */
.my-element {
  background: var(--bg-secondary);
  color: var(--text-primary);
  border: 1px solid var(--border);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  transition: all var(--transition-normal);
}

/* ❌ 错误 */
.my-element {
  background: #f7f6f3;
  color: #37352f;
  padding: 12px;
}

可用 CSS 变量

背景色:

  • --bg-primary: #ffffff - 主要背景
  • --bg-secondary: #f7f6f3 - 次要背景
  • --bg-tertiary: #f1f1ef - 第三级背景
  • --bg-hover: rgba(55, 53, 47, 0.08) - 悬停背景
  • --bg-active: rgba(55, 53, 47, 0.16) - 激活背景

文字色:

  • --text-primary: #37352f - 主要文字
  • --text-secondary: #6b6b6b - 次要文字
  • --text-tertiary: #9b9a97 - 第三级文字
  • --text-placeholder: rgba(55, 53, 47, 0.5) - 占位符

强调色:

  • --accent: #2eaadc - 主强调色
  • --accent-hover: #2496c4 - 悬停强调色
  • --accent-light: rgba(46, 170, 220, 0.15) - 浅强调色背景

语义色:

  • --success: #0f7b6c / --success-light - 成功
  • --warning: #d9730d / --warning-light - 警告
  • --error: #e03e3e / --error-light - 错误
  • --info: #2eaadc / --info-light - 信息

边框:

  • --border: rgba(55, 53, 47, 0.09) - 细边框
  • --border-strong: rgba(55, 53, 47, 0.16) - 强边框

间距:

  • --space-1: 4px ~ --space-12: 48px

圆角:

  • --radius-sm: 3px
  • --radius-md: 6px
  • --radius-lg: 8px
  • --radius-xl: 12px

过渡:

  • --transition-fast: 0.1s ease
  • --transition-normal: 0.15s ease
  • --transition-slow: 0.2s ease

样式禁忌

  • ❌ 纯黑文字 #000000
  • ❌ 重阴影或发光效果
  • ❌ 渐变背景
  • ❌ 饱和度过高的背景色
  • ❌ 大圆角 (> 8px) 用于容器
  • ❌ 粗边框 (> 1px)
  • ❌ 闪烁或弹跳动画
  • !important 声明

安全注意事项

禁止使用:

  • v-html 指令 (XSS 风险)
  • getCurrentInstance() (可能逃逸)
  • 直接 DOM 操作
  • window.open(), window.location
  • eval(), new Function()

推荐做法:

  • 使用 Context API 访问数据
  • 使用 Naive UI 组件库
  • 数据验证和错误处理

插件元数据

interface PluginMeta {
  id: string // 唯一标识 (@scope/name 格式)
  name: string // 显示名称
  version: string // 版本号 (semver)
  description: string // 描述
  author:
    | string
    | {
        // 作者
        name: string
        email?: string
        url?: string
      }
  icon?: string // 图标 (emoji 或 URL)
  homepage?: string // 主页
  repository?: string // 仓库
  keywords?: string[] // 关键词
  minHostVersion?: string // 最低宿主版本
  type: PluginType // 插件类型
  permissions: PluginPermission[] // 所需权限
  displayMode?: PluginDisplayMode // 展示模式
}

生命周期

definePlugin({
  meta: {
    /* ... */
  },

  // 插件启用时调用
  setup(context) {
    console.log('插件已启用')

    // 返回扩展点
    return {
      views: [
        /* ... */
      ],
    }
  },

  // 插件禁用时调用
  teardown() {
    console.log('插件已禁用')
    // 清理资源
  },
})

类型定义

完整类型定义请参考 @ygo-build-x/plugin-sdk/types.ts

示例插件

小世界计算器

import { definePlugin } from '@ygo-build-x/plugin-sdk'
import SmallWorldPlugin from './SmallWorldPlugin.vue'

export default definePlugin({
  meta: {
    id: '@ygo-build-x/small-world',
    name: '小世界计算器',
    version: '1.0.0',
    description: '分析怪兽之间的桥接关系',
    author: 'YGO Build X',
    icon: '🌐',
    type: 'calculator',
    permissions: ['cards.read', 'deck.read', 'storage', 'ui.sidebar', 'ui.notify'],
    displayMode: 'sidebar',
  },

  setup() {
    return {
      views: [
        {
          id: 'small-world-view',
          title: '小世界计算器',
          component: SmallWorldPlugin,
          displayMode: 'sidebar',
          sidebar: { icon: '🌐', order: 10, group: 'calculator' },
        },
      ],
    }
  },
})