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

@vkid/captcha

v2.0.0

Published

VKID Captcha

Readme

Web Captcha

SDK каптчи VK ID: монтирует приложение каптчи в страницу сервиса и возвращает токен, который сервис предъявляет своему бэкенду.

Каптча проходится одним из двух способов, и выбирает его интегратор:

  • невидимо — рамка скрыта, проверка идёт в фоне и запускается методом execute() на целевом действии (обычно отправка формы);
  • чекбоксом «Я не робот» — привычная каптча, встроенная в вёрстку или поднятая модалкой поверх страницы.

Код отправки формы у обоих способов одинаков: execute() возвращает токен и в невидимом режиме, и в видимом — там он просто отдаёт уже полученный по клику.

Установка

pnpm add @vkid/captcha

Быстрый старт

Ссылку на приложение каптчи (link) выдаёт ваш бэкенд в ответе createSession.

import { CaptchaWidget, CaptchaPublicEvents } from '@vkid/captcha';

const captcha = new CaptchaWidget();

// Невидимая каптча: монтируем на загрузке страницы
await captcha.render({ link, invisible: true });

form.addEventListener('submit', async (event) => {
  event.preventDefault();

  try {
    const token = await captcha.execute();

    await sendForm({ ...formData, captchaToken: token });
  } catch (code) {
    // Токена не будет — решайте по коду, см. «Исходы execute()»
  }
});

Тот же код с чекбоксом в вёрстке — меняется только вызов render():

await captcha.render({ link, container: '#captcha' });

И с чекбоксом, который поднимается модалкой на целевом действии, — контейнера просто нет:

await captcha.render({ link });

Методы

| Метод | Назначение | |---|---| | render(params) | Монтирует каптчу. Промис резолвится, когда приложение загружено — сколько бы та ни длилась | | execute(params?) | Запускает проверку и возвращает токен | | close() | Убирает каптчу со страницы | | destroy() | Убирает каптчу и возвращает виджет в исходное состояние: можно смонтировать следующую | | on(event, handler) / off(event, handler) | Подписка на события |

render() вызывается один раз на загрузку страницы; повторный вызов отклоняется кодом 'already_initialized'. Исключение — неудача: если монтаж или загрузка сорвались, каптчу поднимают повторным render().

Одностраничным приложениям, где экраны сменяются без перезагрузки, между каптчами нужен destroy():

await captcha.render({ link, invisible: true });
// ... переход на следующий экран
captcha.destroy();
await captcha.render({ link: nextLink, invisible: true });

nextLink — свежая ссылка от вашего бэкенда: сессию каптчи гасит первая же проверка, и старая ссылка новую каптчу не поднимет. Незавершённые render() и execute() destroy() отклоняет кодом 'destroyed' и о своей уборке событий не шлёт.

После close() капча со страницы снята, но виджет остаётся занятым: execute() отвечает 'close', render() — 'already_initialized'. Поднять капчу заново можно тем же путём: destroy() и render(). Если проверка не состоялась (пользователь закрыл модалку до клика), подойдёт прежняя ссылка — сессия не погашена.

Основные параметры render(): link, invisible (без параметра режим берётся из query присланной ссылки: invisible=true включает невидимый режим, иначе капча видимая), container (в невидимом режиме не участвует, рамка монтируется в document.body), lang, scheme, notice (плашка уведомления об обработке данных: положение, скрытие, перетаскивание draggable), slowLoadAfter (через сколько миллисекунд сообщить о затянувшейся загрузке, по умолчанию 10 000). Полное описание — в jsdoc типов CaptchaRenderParams и CaptchaExecuteParams.

События

captcha.on(CaptchaPublicEvents.SUCCESS, ({ token }) => { /* ... */ });

| Событие | Когда | Аргумент | |---|---|---| | captcha: success | Проверка пройдена (в видимом чекбоксе — в момент клика) | { token } | | captcha: challenge visible | Невидимая проверка развернулась в видимое задание | — | | captcha: challenge hidden | Видимое задание свёрнуто | — | | captcha: token expired | Полученный токен протух | — | | captcha: load slow | Загрузка приложения идёт дольше порога. Не отказ: каптча продолжает грузиться | { slowLoadAfter } |

Исходы execute()

Промис отклоняется строковым кодом:

| Код | Что произошло | Что делать | |---|---|---| | not_passed | Чекбокс не нажат | Попросить пользователя пройти проверку | | timeout | Ответ не пришёл в срок с момента вызова, ожидание загрузки входит в срок; каптча остаётся на странице | Повторить execute() или применить свою политику деградации | | close | Пользователь закрыл каптчу | Целевое действие не выполнять | | failed | Проверка провалена терминально либо приложение сломалось | Целевое действие не выполнять | | token_expired | Сессия каптчи умерла | См. ниже | | already_checked | Проверка на этой загрузке страницы уже пройдена | Новая проверка — только с новой загрузкой страницы | | not_initialized | render() не вызывали или он не резолвился | Смонтировать каптчу повторным render() | | destroyed | Каптчу сняли вызовом destroy() | Целевое действие не выполнять |

Протухший токен

Сессия каптчи одноразовая: её гасит первая же проверка, а запросить новую с фронта нельзя. Поэтому 'token_expired' (и парное ему событие 'captcha: token expired') означает, что полученный токен предъявлять бэкенду бессмысленно, — но пройти проверку заново на этой же странице нечем.

Автоматической переинициализации SDK не делает. Рекомендуемая обработка: запросить у своего бэкенда свежий link и перезагрузить страницу либо сообщить пользователю, что нужно повторить действие после обновления.

Попасть в этот исход можно, если страница долго не получала процессорного времени — вкладка ушла в bfcache, была заморожена или устройство спало, — и сессия истекла раньше, чем приложение успело её продлить.

Миграция с Yandex SmartCaptcha и Google reCAPTCHA

Схема работы совпадает: смонтировать каптчу на загрузке страницы, вызвать execute() на целевом действии, отправить токен на бэкенд.

| Yandex SmartCaptcha / reCAPTCHA | VK ID Captcha | |---|---| | render(container, { sitekey, invisible }) | render({ link, container?, invisible? }) | | execute() | execute() | | subscribe('success', cb) / callback | on('captcha: success', cb) | | subscribe('challenge-visible', cb) | on('captcha: challenge visible', cb) | | subscribe('token-expired', cb) | on('captcha: token expired', cb) | | destroy() | destroy() | | Статический sitekey | Живой link из createSession вашего бэкенда |

Главное отличие — вместо постоянного sitekey каптча получает одноразовую ссылку сессии, которую выдаёт ваш бэкенд. Из этого следует и ограничение, которого нет у SmartCaptcha: проверка одна на загрузку страницы.

Легаси-путь show()

Метод show() продолжает работать: он показывает каптчу сразу и отдаёт токен промисом. execute() и публичные события доступны только после render(). Что изменилось на этом пути:

  • без ссылки (link или iframeSrc) промис отклоняется кодом 'link_required', после render() — 'already_initialized'; раньше это были исключения;
  • destroy() отклоняет незавершённый промис кодом 'destroyed';
  • повторный close() ничего не делает: common: close и onClose приходят один раз;
  • в типах параметров iframeSrc и captchaType стали необязательными.

Разработка

Демо-страница поднимается одной командой:

pnpm demo:dev   # сборка SDK, демо и мока приложения + дев-сервер

Все режимы, сценарии проверки и мок приложения описаны в demo/README.md. Бэкенд каптчи для демо не нужен.

Прод-сборка SDK — pnpm sdk:prod, тесты — pnpm tests.