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

@verifiables/verifier-sdk

v0.2.0

Published

Framework-agnostic SDK to embed a verifier with QR code creation and API integration

Readme

@verifiables/verifier-sdk

A framework-agnostic SDK for embedding credential verification flows in web applications. Supports both OID4VP (remote/QR-based) and ISO 18013-5 Proximity (BLE-based in-person) verification.

Features

  • OID4VP Flow: Remote verification via QR codes and wallet deep links
  • Proximity Flow: In-person BLE verification per ISO 18013-5 (mDoc)
  • API Client: Low-level client for interacting with the verifier backend
  • QR Code Generation: Highly customizable QR code rendering with 40+ styling options
  • Web Component: Drop-in <verifier-qr> custom element for quick integration
  • TypeScript First: Full type safety with comprehensive type definitions
  • Framework Agnostic: Works with any JavaScript framework or vanilla JS

Installation

npm install @verifiables/verifier-sdk
# or
pnpm add @verifiables/verifier-sdk

Quick Start

Option 1: Web Component (Simplest)

<verifier-qr
  api-url="https://your-verifier-api.com"
  namespace="eu.europa.ec.eudi.pid.1"
  attributes="given_name,family_name,age_over_18"
  show-link
></verifier-qr>

<script type="module">
  import { registerVerifierElement } from '@verifiables/verifier-sdk/component'

  registerVerifierElement()

  document.addEventListener('verifier:success', (event) => {
    console.log('Verification succeeded:', event.detail)
  })
</script>

Option 2: Programmatic API (OID4VP)

import { createVerifierClient } from '@verifiables/verifier-sdk'
import { generateQRCode, createQRConfig } from '@verifiables/verifier-sdk/qr'

// 1. Create a client
const client = createVerifierClient({
  apiUrl: 'https://your-verifier-api.com'
})

// 2. Create an authorization request
const response = await client.createAuthorizationRequest({
  namespace: 'eu.europa.ec.eudi.pid.1',
  attributes: ['given_name', 'family_name', 'age_over_18'],
  version: 'draft18'
})

// 3. Render QR code
const canvas = document.getElementById('qr-canvas') as HTMLCanvasElement
const config = createQRConfig(response.url, { scale: 8, margin: 2 })
await generateQRCode(canvas, config)

// 4. Poll for completion
const status = await client.pollAuthorizationStatus(response.id, {
  timeout: 180000,  // 3 minutes
  interval: 2000    // Check every 2 seconds
})

if (status.status === 'valid') {
  console.log('Verification successful:', status.presentation)
}

Option 3: Proximity Flow (BLE/In-Person)

For in-person verification using Web Bluetooth (ISO 18013-5):

import {
  createMdocProximityReader,
  MdocProximityReader,
  type ProximityRequestConfig,
  type ParsedDeviceEngagement,
} from '@verifiables/verifier-sdk/proximity'

// 1. Create a proximity reader
const reader = createMdocProximityReader({
  apiClient: { baseUrl: 'https://your-verifier-api.com' },
  logger: console, // Optional: for debug output
})

// 2. Check browser support
if (!MdocProximityReader.isSupported()) {
  console.error('Web Bluetooth not supported')
}

// 3. Subscribe to state changes
reader.onStateChange((state) => {
  console.log('Status:', state.status)
  if (state.error) console.error('Error:', state.error)
  if (state.result) console.log('Result:', state.result)
})

// 4. Parse device engagement from wallet's QR code
// (Use a QR scanner library like jsQR to scan the wallet's QR)
const qrData = 'mdoc:owBjMS4...' // QR code data starting with 'mdoc:'
const deviceEngagement = reader.parseDeviceEngagement(qrData)

// 5. Connect via Bluetooth
await reader.connect(deviceEngagement)

// 6. Request credentials
const result = await reader.requestCredentials({
  docType: 'eu.europa.ec.eudi.pid.1',
  namespace: 'eu.europa.ec.eudi.pid.1',
  attributes: ['family_name', 'given_name', 'birth_date', 'issuing_country'],
})

if (result.status === 'valid') {
  console.log('Verification successful:', result.presentation)
  // Access data: result.presentation['eu.europa.ec.eudi.pid.1']['eu.europa.ec.eudi.pid.1']
}

// 7. Disconnect when done
reader.disconnect()

Important: When using Vue/React with reactive state, use shallowRef (Vue) or avoid wrapping deviceEngagement in reactive wrappers, as the mDoc library uses private class fields that don't work with Proxy objects.

API Reference

Main Module (@verifiables/verifier-sdk)

createVerifierClient(config)

Creates a new verifier client instance.

import { createVerifierClient } from '@verifiables/verifier-sdk'

const client = createVerifierClient({
  apiUrl: 'https://your-verifier-api.com',
  timeout: 60000,  // Optional: request timeout in ms (default: 60000)
  headers: {}      // Optional: custom headers
})

VerifierClient

The main API client class.

createAuthorizationRequest(params?)

Creates a new authorization request with the verifier backend.

const response = await client.createAuthorizationRequest({
  namespace: 'eu.europa.ec.eudi.pid.1',  // Credential doctype
  attributes: ['given_name', 'family_name'],  // Requested attributes
  version: 'draft18',  // OID4VP version: 'v1' | 'draft18'
  dcql_query: { ... },  // Optional: custom DCQL query (overrides namespace/attributes)
  fin: false  // Optional: whether this is a final request
})

// Returns: { url: string, id: string, version: 'v1' | 'draft18' }
getAuthorizationStatus(requestId)

Gets the current status of an authorization request.

const status = await client.getAuthorizationStatus(response.id)

// Returns: AuthorizationStatus
// status.status: 'created' | 'claimed' | 'pending' | 'valid' | 'failed'
// status.verification: Record<string, VerificationCheck[]>
// status.presentation: Record<string, unknown>
// status.dcqlValidation: DcqlValidationResult
pollAuthorizationStatus(requestId, options?)

Polls for authorization status until it reaches a target status or times out.

const status = await client.pollAuthorizationStatus(response.id, {
  timeout: 120000,  // Max wait time in ms (default: 120000)
  interval: 2000,   // Poll interval in ms (default: 2000)
  targetStatuses: ['valid', 'failed'],  // Statuses to stop polling on
  signal: abortController.signal  // Optional: AbortSignal for cancellation
})

QR Module (@verifiables/verifier-sdk/qr)

createQRConfig(url, overrides?)

Creates a QR code configuration with sensible defaults.

import { createQRConfig } from '@verifiables/verifier-sdk/qr'

const config = createQRConfig('https://example.com/verify?id=123', {
  scale: 10,
  margin: 2,
  darkColor: '#000000',
  lightColor: '#ffffff',
  pixelStyle: 'rounded'
})

generateQRCode(canvas, config)

Renders a QR code to an HTML canvas element.

import { generateQRCode, createQRConfig } from '@verifiables/verifier-sdk/qr'

const canvas = document.getElementById('qr') as HTMLCanvasElement
const config = createQRConfig(url)
await generateQRCode(canvas, config)

QRCodeConfig

Full configuration options for QR code generation:

| Property | Type | Default | Description | |----------|------|---------|-------------| | text | string | - | The URL/text to encode | | ecc | 'L' \| 'M' \| 'Q' \| 'H' | 'M' | Error correction level | | margin | number \| MarginObject | 2 | Margin around QR code | | scale | number | 20 | Size of each module/pixel | | lightColor | string | '#ffffff' | Background color | | darkColor | string | '#000000' | Foreground color | | pixelStyle | PixelStyle | 'rounded' | Style for data pixels | | markerStyle | PixelStyle \| 'auto' | 'rounded' | Style for position markers | | markerShape | MarkerShape | 'square' | Shape of position markers | | markerInnerShape | MarkerInnerShape \| 'auto' | 'auto' | Inner marker shape | | rotate | 0 \| 90 \| 180 \| 270 | 0 | Rotation angle | | invert | boolean | false | Invert colors | | marginNoise | boolean | false | Add noise in margins | | marginNoiseRate | number | 0.5 | Noise density | | marginNoiseOpacity | number \| [number, number] | 1 | Noise opacity | | effect | 'none' \| 'crystalize' \| 'liquidify' | 'none' | Visual effect | | seed | number | 608496 | Random seed for consistent rendering |

Pixel Styles: 'square', 'rounded', 'dot', 'squircle', 'row', 'column'

Marker Shapes: 'square', 'circle', 'plus', 'box', 'octagon', 'random', 'tiny-plus'

Marker Inner Shapes: 'square', 'circle', 'plus', 'diamond', 'eye'

Component Module (@verifiables/verifier-sdk/component)

registerVerifierElement(tagName?)

Registers the <verifier-qr> custom element.

import { registerVerifierElement } from '@verifiables/verifier-sdk/component'

// Use default tag name
registerVerifierElement()

// Or use custom tag name
registerVerifierElement('my-verifier')

<verifier-qr> Element

A custom HTML element for embedding verification flows.

Attributes:

| Attribute | Type | Default | Description | |-----------|------|---------|-------------| | api-url | string | required | Base URL of the verifier API | | namespace | string | - | Credential namespace (e.g., eu.europa.ec.eudi.pid.1) | | attributes | string | - | Comma-separated list of requested attributes | | version | 'v1' \| 'draft18' | - | OID4VP protocol version | | poll-interval | number | 3000 | Polling interval in milliseconds | | auto-start | 'true' \| 'false' | 'true' | Auto-start verification on mount | | show-link | boolean attr | - | Show "Open in wallet" link |

Events:

| Event | Detail | Description | |-------|--------|-------------| | verifier:status | AuthorizationStatus | Emitted when status changes | | verifier:success | AuthorizationStatus | Emitted when verification succeeds | | verifier:error | Error | Emitted when an error occurs |

Methods:

const element = document.querySelector('verifier-qr')

element.start()    // Start the verification flow
element.stop()     // Stop polling and cleanup
element.refresh()  // Create new request and QR code

CSS Variables for Theming:

verifier-qr {
  --verifier-font: system-ui, -apple-system, sans-serif;
  --verifier-text: inherit;
  --verifier-text-muted: #6b7280;
  --verifier-success: #22c55e;
  --verifier-error: #ef4444;
  --verifier-warning: #f59e0b;
}

Proximity Module (@verifiables/verifier-sdk/proximity)

The proximity module implements ISO 18013-5 mDoc credential verification over BLE (Bluetooth Low Energy) for in-person verification scenarios.

createMdocProximityReader(options)

Creates a new proximity reader instance.

import { createMdocProximityReader } from '@verifiables/verifier-sdk/proximity'

const reader = createMdocProximityReader({
  apiClient: {
    baseUrl: 'https://your-verifier-api.com',  // Required: API endpoint for verification
  },
  logger: {                    // Optional: custom logger
    log: (msg) => console.log(msg),
    warn: (msg) => console.warn(msg),
    error: (msg) => console.error(msg),
  },
  defaultMtu: 512,             // Optional: BLE MTU size (default: negotiated)
})

MdocProximityReader

The main class for proximity verification.

Static Methods
// Check if Web Bluetooth is supported in the current browser
MdocProximityReader.isSupported()  // Returns: boolean
Instance Methods
// Parse device engagement from wallet's QR code
// Input: string starting with 'mdoc:' or raw bytes
const deviceEngagement = reader.parseDeviceEngagement(qrData)

// Connect to wallet via BLE
await reader.connect(deviceEngagement)

// Request credentials from connected wallet
const result = await reader.requestCredentials({
  docType: 'eu.europa.ec.eudi.pid.1',
  namespace: 'eu.europa.ec.eudi.pid.1',
  attributes: ['family_name', 'given_name', 'birth_date'],
})

// Get current state
const state = reader.getState()

// Subscribe to state changes (returns unsubscribe function)
const unsubscribe = reader.onStateChange((state) => {
  console.log(state.status, state.error, state.result)
})

// Check connection status
reader.isConnected()  // Returns: boolean

// Disconnect from wallet
reader.disconnect()

Supported Document Types

The module includes predefined document types and their attributes:

import { DOC_TYPES, NAMESPACES, DOCUMENT_DEFINITIONS } from '@verifiables/verifier-sdk/proximity'

// Document types
DOC_TYPES.EU_PID      // 'eu.europa.ec.eudi.pid.1'
DOC_TYPES.MDL         // 'org.iso.18013.5.1.mDL'
DOC_TYPES.AGE_VERIFICATION  // 'eu.europa.ec.av.1'
DOC_TYPES.PHOTO_ID    // 'org.iso.23220.photoID.1'
DOC_TYPES.MVC         // 'org.iso.7367.1.mVC' (Vehicle Registration)

// Namespaces
NAMESPACES.EU_PID     // 'eu.europa.ec.eudi.pid.1'
NAMESPACES.MDL        // 'org.iso.18013.5.1'

// Full document definitions with attributes
DOCUMENT_DEFINITIONS  // Array of DocumentDefinition with localized labels

Browser Support

Web Bluetooth is required for proximity verification. Supported browsers:

  • Chrome (desktop and Android)
  • Edge (desktop)
  • Opera (desktop)

Not supported: Safari, Firefox, iOS browsers (due to Web Bluetooth restrictions).

Types

Core Types

// OID4VP protocol version
type Oid4VpVersion = 'v1' | 'draft18'

// Authorization request parameters
interface AuthorizationRequestParams {
  namespace?: string        // Credential doctype
  attributes?: string[]     // Requested attributes
  version?: Oid4VpVersion   // Protocol version
  dcql_query?: DcqlQuery    // Custom DCQL query
  fin?: boolean             // Final request flag
}

// Authorization request response
interface AuthorizationRequestResponse {
  url: string               // URL to encode in QR code
  id: string                // Request ID for polling
  version: Oid4VpVersion    // Protocol version used
}

// Authorization status
interface AuthorizationStatus {
  status: 'created' | 'claimed' | 'pending' | 'valid' | 'failed'
  verification?: Record<string, VerificationCheck[]>
  presentation?: Record<string, unknown>
  dcqlValidation?: DcqlValidationResult
}

// Polling options
interface PollOptions {
  timeout?: number                           // Max wait time (default: 120000)
  interval?: number                          // Poll interval (default: 2000)
  targetStatuses?: AuthorizationStatus['status'][]  // Statuses to wait for
  signal?: AbortSignal                       // For cancellation
}

DCQL Types

// DCQL claim definition
interface DcqlClaim {
  path: [string, string]
  intent_to_retain?: boolean
}

// DCQL credential definition
interface DcqlCredential {
  id: string
  format: 'mso_mdoc'
  meta?: { doctype_value?: string }
  claims?: DcqlClaim[]
}

// DCQL query
interface DcqlQuery {
  credentials: DcqlCredential[]
}

Proximity Types

// Configuration for proximity credential requests
interface ProximityRequestConfig {
  docType: string      // e.g., 'eu.europa.ec.eudi.pid.1'
  namespace: string    // e.g., 'eu.europa.ec.eudi.pid.1'
  attributes: string[] // e.g., ['family_name', 'given_name', 'birth_date']
}

// Parsed device engagement from wallet's QR code
interface ParsedDeviceEngagement {
  version: string              // Protocol version (e.g., '1.0')
  bleServiceUuid?: string      // BLE service UUID for connection
  deviceEngagement: DeviceEngagement  // Raw device engagement object
  rawBytes: Uint8Array         // Original CBOR-encoded bytes
}

// Reader state for tracking connection status
interface ReaderState {
  status: 'disconnected' | 'connecting' | 'connected' | 'requesting' | 'processing' | 'complete'
  deviceEngagement?: ParsedDeviceEngagement
  error?: string
  result?: ProximityVerificationResult
}

// Verification result from proximity flow
interface ProximityVerificationResult {
  status: 'valid' | 'failed'
  verification: VerificationChecks  // Grouped verification checks
  presentation: Record<string, Record<string, Record<string, unknown>>>
  error?: string
}

// Verification check details
interface VerificationCheck {
  status: string   // 'passed', 'failed', etc.
  check: string    // Description of the check
}

// Logger interface for debug output
interface Logger {
  log: (message: string) => void
  warn: (message: string) => void
  error: (message: string) => void
}

// Document definition with localized labels
interface DocumentDefinition {
  docType: string
  namespace: string
  display: { lang: string; label: string }[]
  attributes: DocumentAttribute[]
}

interface DocumentAttribute {
  id: string
  display: { lang: string; label: string }[]
}

Advanced Usage

Cancelling Polling with AbortController

const abortController = new AbortController()

// Start polling
const statusPromise = client.pollAuthorizationStatus(requestId, {
  signal: abortController.signal
})

// Cancel after 30 seconds
setTimeout(() => abortController.abort(), 30000)

try {
  const status = await statusPromise
} catch (error) {
  if (error.message === 'Polling aborted') {
    console.log('User cancelled verification')
  }
}

Custom DCQL Query

const response = await client.createAuthorizationRequest({
  dcql_query: {
    credentials: [{
      id: 'pid',
      format: 'mso_mdoc',
      meta: { doctype_value: 'eu.europa.ec.eudi.pid.1' },
      claims: [
        { path: ['eu.europa.ec.eudi.pid.1', 'given_name'] },
        { path: ['eu.europa.ec.eudi.pid.1', 'family_name'] },
        { path: ['eu.europa.ec.eudi.pid.1', 'age_over_18'], intent_to_retain: true }
      ]
    }]
  }
})

Extracting Presented Credentials

const status = await client.pollAuthorizationStatus(requestId)

if (status.status === 'valid' && status.presentation) {
  // Access presented credential data
  const pid = status.presentation['eu.europa.ec.eudi.pid.1'] as Record<string, unknown>

  const givenName = pid?.given_name
  const familyName = pid?.family_name
  const isOver18 = pid?.age_over_18
}

Styled QR Code with Custom Colors

import { generateQRCode, createQRConfig } from '@verifiables/verifier-sdk/qr'

const config = createQRConfig(url, {
  scale: 12,
  margin: 3,
  darkColor: '#1e40af',      // Blue
  lightColor: '#f0f9ff',     // Light blue background
  pixelStyle: 'dot',
  markerShape: 'circle',
  markerInnerShape: 'circle',
  marginNoise: true,
  marginNoiseRate: 0.3,
  marginNoiseOpacity: 0.5
})

await generateQRCode(canvas, config)

Dependencies

Core:

  • ky - Modern HTTP client
  • seedrandom - Deterministic random number generation
  • uqr - QR code encoding

Proximity module:

  • @animo-id/mdoc - ISO 18013-5 mDoc/CBOR/COSE handling

License

MIT