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

@zaplink/web

v0.2.3

Published

Browser SDK for Zaplink WhatsApp Authentication

Readme

@zaplink/web

Browser SDK for Zaplink WhatsApp Authentication. Provides PKCE-secured authentication flows, session management, and WhatsApp deep link helpers for client-side applications.

Installation

npm install @zaplink/web
# or
pnpm add @zaplink/web
# or
yarn add @zaplink/web

Features

  • PKCE Flow - Secure OAuth 2.0 PKCE implementation using Web Crypto API
  • Session Management - Automatic token storage and refresh
  • WhatsApp Integration - Deep link helpers for seamless WhatsApp handoff
  • Type-safe - Full TypeScript support
  • Browser-only - Uses Web APIs, no Node.js dependencies
  • Auto-refresh - Optional automatic token refresh
  • Storage Options - localStorage or sessionStorage

Quick Start

import { ZaplinkWeb } from '@zaplink/web';

// Initialize the client
const zaplink = new ZaplinkWeb({
  publicKey: import.meta.env.VITE_ZAPLINK_PUBLIC_KEY,
  baseUrl: import.meta.env.VITE_ZAPLINK_API_URL,
  version: '2025-10',
  autoRefresh: true, // Enable automatic token refresh
});

// Start login flow
const { attemptId } = await zaplink.startLogin({
  phone: '+5215555555555',
  locale: 'es-MX',
});

// Open WhatsApp to receive OTP
zaplink.openWhatsApp({
  phone: '+5215555555555',
});

// After user receives OTP, verify it
const session = await zaplink.verifyOtp({
  attemptId,
  otp: '123456',
});

console.log('Logged in!', session.accessToken);

// Check authentication status
if (zaplink.isAuthenticated()) {
  console.log('User is logged in');
}

// Get current access token
const token = zaplink.getAccessToken();

// Refresh session
await zaplink.refreshSession();

// Logout
await zaplink.revokeSession();

Complete Authentication Flow

import { ZaplinkWeb } from '@zaplink/web';

// 1. Initialize client
const zaplink = new ZaplinkWeb({
  publicKey: 'pk_xxx',
  autoRefresh: true,
});

// 2. Start login
async function login(phoneNumber: string) {
  try {
    // Start the login flow
    const { attemptId } = await zaplink.startLogin({
      phone: phoneNumber,
    });

    // Open WhatsApp so user can receive OTP
    zaplink.openWhatsApp({ phone: phoneNumber });

    // Store attemptId for OTP verification
    return attemptId;
  } catch (error) {
    console.error('Login failed:', error);
    throw error;
  }
}

// 3. Verify OTP
async function verifyOTP(attemptId: string, otp: string) {
  try {
    const session = await zaplink.verifyOtp({
      attemptId,
      otp,
    });

    console.log('Login successful!');
    return session;
  } catch (error) {
    console.error('OTP verification failed:', error);
    throw error;
  }
}

// 4. Check if user is logged in
function isLoggedIn(): boolean {
  return zaplink.isAuthenticated();
}

// 5. Logout
async function logout() {
  await zaplink.revokeSession();
  console.log('Logged out');
}

// Example usage
const attemptId = await login('+5215555555555');
// User receives OTP via WhatsApp
const session = await verifyOTP(attemptId, '123456');

Session Management

Automatic Session Storage

Sessions are automatically saved to browser storage (localStorage by default):

const zaplink = new ZaplinkWeb({
  publicKey: 'pk_xxx',
  storage: {
    prefix: 'myapp', // Custom storage prefix
    useSessionStorage: false, // Use localStorage (default)
  },
});

// Session is automatically saved after verifyOtp
await zaplink.verifyOtp({ attemptId, otp });

// Session is automatically loaded on page refresh
if (zaplink.isAuthenticated()) {
  console.log('User is still logged in');
}

Manual Session Access

// Get current session
const session = zaplink.getSession();
if (session) {
  console.log('Access token:', session.accessToken);
  console.log('Expires at:', new Date(session.expiresAt));
}

// Get access token only
const token = zaplink.getAccessToken();

// Access storage directly
const storage = zaplink.getStorage();
storage.clearAll(); // Clear all data

Automatic Token Refresh

const zaplink = new ZaplinkWeb({
  publicKey: 'pk_xxx',
  autoRefresh: true, // Enable automatic refresh
});

// Token will be automatically refreshed when it's about to expire
// (less than 5 minutes remaining)

Manual Token Refresh

// Manually refresh the session
try {
  const newSession = await zaplink.refreshSession();
  console.log('Token refreshed:', newSession.accessToken);
} catch (error) {
  console.error('Refresh failed:', error);
  // Session expired, need to login again
}

WhatsApp Integration

Opening WhatsApp

// Simple deep link (auto-detects platform)
zaplink.openWhatsApp({
  phone: '+5215555555555',
});

// With platform specification
zaplink.openWhatsApp({
  phone: '+5215555555555',
  platform: 'web', // 'web' | 'mobile' | 'ios' | 'android'
});

// Using utility functions directly
import { openWhatsApp, createWhatsAppDeepLink, detectPlatform } from '@zaplink/web';

// Detect platform
const platform = detectPlatform();
console.log('Platform:', platform); // 'web' | 'mobile' | 'ios' | 'android'

// Create deep link
const link = createWhatsAppDeepLink({
  phone: '+5215555555555',
  platform: 'web',
});

// Open WhatsApp
openWhatsApp({ phone: '+5215555555555' });

Storage Options

Using sessionStorage

const zaplink = new ZaplinkWeb({
  publicKey: 'pk_xxx',
  storage: {
    useSessionStorage: true, // Session cleared when tab closes
  },
});

Custom Storage Prefix

const zaplink = new ZaplinkWeb({
  publicKey: 'pk_xxx',
  storage: {
    prefix: 'myapp', // Keys will be: myapp:session, myapp:pkce, etc.
  },
});

Custom Storage Implementation

import { createClientStorage } from '@zaplink/web';

const customStorage = createClientStorage({
  prefix: 'custom',
  useSessionStorage: false,
});

const zaplink = new ZaplinkWeb({
  publicKey: 'pk_xxx',
  customStorage,
});

Error Handling

import { ZaplinkWeb, type Problem } from '@zaplink/web';
import { isHttpError } from '@zaplink/core';

try {
  await zaplink.verifyOtp({ attemptId, otp: '000000' });
} catch (error) {
  if (isHttpError(error)) {
    // Handle API errors
    console.error(`Error ${error.code}:`, error.message);

    switch (error.code) {
      case 'otp_invalid':
        console.error('Invalid OTP code');
        break;
      case 'otp_expired':
        console.error('OTP has expired');
        break;
      case 'rate_limit_exceeded':
        console.error('Too many attempts');
        break;
      default:
        console.error('Unknown error:', error.detail);
    }
  } else {
    // Network or other errors
    console.error('Unexpected error:', error);
  }
}

PKCE Utilities

The SDK uses PKCE (Proof Key for Code Exchange) for secure authentication:

import { generatePKCE, createPKCEParams } from '@zaplink/web';

// Generate PKCE parameters
const pkce = await generatePKCE();
console.log('Verifier:', pkce.verifier);
console.log('Challenge:', pkce.challenge);

// Generate complete PKCE params including state
const params = await createPKCEParams();
console.log('Verifier:', params.verifier);
console.log('Challenge:', params.challenge);
console.log('State:', params.state);

Phone Utilities

import { normalizePhone, isValidE164 } from '@zaplink/web';

// Validate phone numbers
if (isValidE164('+5215555555555')) {
  console.log('Valid E.164 format');
}

// Normalize phone numbers
const normalized = normalizePhone('(521) 555-555-5555');
console.log(normalized); // '+5215555555555'

React Integration

For React applications, use @zaplink/react which provides hooks and components built on top of this SDK.

npm install @zaplink/react

Configuration

interface WebClientOptions {
  // Required: Public key for browser authentication
  publicKey: string;

  // Optional: API base URL (default: https://api.zaplink.so)
  baseUrl?: string;

  // Optional: API version header
  version?: string;

  // Optional: Storage configuration
  storage?: {
    prefix?: string;
    useSessionStorage?: boolean;
  };

  // Optional: Enable automatic token refresh
  autoRefresh?: boolean;

  // Optional: Custom storage implementation
  customStorage?: ClientStorage;

  // Optional: Additional headers
  headers?: Record<string, string>;

  // Optional: Request timeout (default: 30000ms)
  timeout?: number;

  // Optional: Enable debug logging
  debug?: boolean;
}

Browser Support

  • Modern browsers with ES2020 support
  • Web Crypto API (for PKCE)
  • Fetch API
  • localStorage or sessionStorage

Security Notes

  • Never use API secret keys in the browser - Use publicKey only
  • PKCE is used to secure the authentication flow
  • Tokens are stored in browser storage - consider the security implications
  • Use HTTPS in production
  • Implement proper CORS configuration on your backend

License

MIT