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

@zixinit/vue-captcha

v0.1.2

Published

Extensible Vue 3 image and slider CAPTCHA components with server adapters and SSR support.

Readme

@zixinit/vue-captcha

可扩展的 Vue 3 验证码组件,包含图片验证码与滑块拼图。不依赖 UI 框架,支持 SSR、键盘操作、触屏、过期处理和请求取消。

安装

pnpm add @zixinit/vue-captcha

需要 Vue 3.5+,提供 ESM 和 TypeScript 声明。所有入口均需在应用中引入一次样式:

import '@zixinit/vue-captcha/style.css'

Nuxt 可将上述样式路径加入 nuxt.config.ts 的 css 数组。组件仅在客户端挂载后加载挑战;无需全局插件或 Pinia。

图片验证码

<script setup lang="ts">
import { ref } from 'vue'
import { ImageCaptcha } from '@zixinit/vue-captcha/image'
import type { ChallengeLoader, ImageChallenge } from '@zixinit/vue-captcha/core'

const challengeId = ref('')
const code = ref('')
const load: ChallengeLoader<ImageChallenge> = async ({ signal }) => {
  const response = await fetch('/api/captcha/image', { signal })
  if (!response.ok) throw new Error('加载失败')
  return response.json()
}
function invalidate() {
  challengeId.value = ''
  code.value = ''
}
</script>

<template>
  <input v-model="code" aria-label="验证码" autocomplete="off">
  <ImageCaptcha :load="load" @loaded="challengeId = $event.id" @invalidated="invalidate" />
</template>

服务端返回 { id, imageUrl, expiresAt? }。expiresAt 是 Unix 毫秒时间戳。业务提交时将 challengeId 与用户输入一起交给服务端校验;组件不持有正确答案。

滑块拼图

<script setup lang="ts">
import { ref } from 'vue'
import { SliderCaptcha } from '@zixinit/vue-captcha/slider'
import type { ChallengeLoader, SliderChallenge, SliderVerifier } from '@zixinit/vue-captcha/core'

const token = ref('')
const load: ChallengeLoader<SliderChallenge> = async ({ signal }) => {
  const response = await fetch('/api/captcha/slider', { signal })
  if (!response.ok) throw new Error('加载失败')
  return response.json()
}
const verify: SliderVerifier = async (answer, { signal }) => {
  const response = await fetch('/api/captcha/verify', {
    method: 'POST', signal, headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(answer),
  })
  if (!response.ok) throw new Error('验证失败')
  return response.json()
}
</script>

<template>
  <SliderCaptcha :load="load" :verify="verify"
    @success="token = $event.token" @invalidated="token = ''" />
</template>

挑战结构:

interface SliderChallenge {
  id: string
  backgroundUrl: string
  pieceUrl: string
  width: number
  height: number
  pieceWidth: number
  pieceHeight: number
  pieceTop: number
  expiresAt?: number
}

尺寸与 pieceTop 使用原始图片坐标单位,组件会按显示宽度缩放。独立拼块图片不应包含整个背景画布。提交参数为 { challengeId, offsetX, durationMs },offsetX 为拼块左边缘在原始背景中的横坐标;成功返回 { token, expiresAt? }。没有有效 token 不触发成功事件。刷新、重置、禁用、失败和过期均触发 invalidated,消费方应清空已保存的凭证。

服务端负责生成挑战、验证位置、控制尝试次数与有效期、签发并一次性消费凭证。durationMs 等客户端数据不可作为可信依据。业务接口必须核验 token,不能信任浏览器的成功事件。

公共 API

| 属性 | 说明 | | --- | --- | | load | 必填异步加载函数,接收 { signal } | | verify | 仅滑块必填,接收答案与 { signal } | | autoLoad | 默认 true;关闭后通过组件 ref 的 refresh() 加载 | | disabled | 默认 false;禁用时取消请求并使当前挑战失效 | | messages | Partial<CaptchaMessages>,覆盖默认中文文案和图片描述 |

公共事件:loaded(challenge)、invalidated()、expired()、error(Error);滑块额外提供 success(proof)。独立组件通过 ref 暴露 refresh(): Promise<void> 和 reset(): void,类型为 CaptchaHandle。

原生滑块支持方向键,左右按钮提供单击微调,确认按钮提交验证。视觉验证码仍需要业务提供非视觉验证路径,可通过 #alternative 插槽放置其他验证方式。

通过 .vc-captcha 上的 --vc-accent、--vc-surface、--vc-text、--vc-muted、--vc-border 等 CSS 变量调整主题,完整变量见 src/style.css。

扩展其他验证码

import { Captcha, createCaptchaRegistry } from '@zixinit/vue-captcha'
import ClickCaptcha from './ClickCaptcha.vue'
const registry = createCaptchaRegistry({ click: ClickCaptcha })
// 后续也可 registry.register('rotate', RotateCaptcha)
<Captcha type="click" :registry="registry" :load="load" @success="onSuccess" />

内置类型为 image 和 slider。注册表属于应用实例,不使用跨请求的全局注册状态;自定义组件决定自己的 props/events。统一入口转发属性、事件和 alternative 插槽,未知类型显示不支持提示,也可用 unsupported 插槽替换。需要命令式控制时直接使用独立组件。core 导出无 UI 的 createCaptchaController、类型和校验函数,可复用请求竞态与过期管理。

开发与发布

fnm use
pnpm install
make dev
make preflight

打开 http://localhost:5173。Playground 的演示 API 仅由 Vite 开发插件提供,不包含在 npm 包中。它使用内存数据及演示凭证,图片验证码没有业务提交接口,不适用于生产防刷。build:demo 只生成静态页面,部署时需自行提供对应 API。

pnpm test:browser 运行浏览器交互测试(先 pnpm exec playwright install chromium)。发布流程见 deploy/DEPLOY.md。GitHub 保存完整源码,不执行其他包的文档镜像清理流程。

兼容已有 client-slider 协议

@zixinit/vue-captcha/client 导出浏览器端 createClientSliderChallenge({ imageUrl, challenge: { id, expiresAt }, signal })。返回 { challenge, checkPosition(offsetX) },可将 challenge 交给 SliderCaptcha;verify 适配器先调用 checkPosition,再调用应用的服务端验证接口并返回真实凭证。图片需要同源、data URL 或允许 CORS。

本地位置检查只提供交互反馈,不能抵御伪造请求,也不生成可信 token。该入口用于兼容已有的 client-slider 业务协议;服务端滑块直接使用服务端生成的 challenge。

原版客户端拼图

ClientSliderCaptcha(或 <Captcha type="client-slider">)内部使用 [email protected],保留原组件的弹窗、拖动、刷新与失败反馈。该依赖由本包统一管理,两端无需直接安装。组件仅在浏览器挂载后加载,适用于 Nuxt SSR。

<ClientSliderCaptcha v-model="visible" :imgs="images" :load="loadChallenge" :verify="verifyClient" @success="onProof" @invalidated="clearProof" />

load 返回 { id, expiresAt? };verify 接收含 challengeId 的答案和 { signal },由业务适配器请求服务端验证,返回 { token, expiresAt? }。客户端拼图不提供服务端位移答案,兼容 SliderVerifier 的 offsetX、durationMs 固定为 0。本地拼图通过后才调用 verify;服务端凭证过期、关闭未完成验证或刷新都会清空旧状态。imgs 与原组件一致,接收图片 URL 数组。图片验证码、服务端拼图仍使用 ImageCaptcha、SliderCaptcha。