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

@pollar/core

v0.8.0

Published

Core authentication functions for Pollar services

Readme

@pollar/core

Core SDK for Pollar — authentication and transaction utilities for Stellar-based applications.

0.8.0 routes every submitTx (custodial and external wallets) through /tx/submit so the dashboard sees every transaction and idempotency is tracked end-to-end. Adds proactive token refresh with a visibility-aware scheduler, an onStorageDegrade telemetry hook, and a timeout around the wallet adapter resolver. External-wallet callers may now observe pending outcomes where they only ever got success / error before. Read the CHANGELOG before upgrading.

0.7.0 ships sender-constrained tokens via DPoP (RFC 9449), pluggable storage and key managers, automatic refresh-on-401, and removes PII from persisted storage. Requires HTTPS and sdk-api ≥ Phase 5.

Installation

npm install @pollar/core
# or
pnpm add @pollar/core
# or
yarn add @pollar/core

For React Native / Expo, also install one of the storage adapter peer deps:

# Expo
npx expo install expo-secure-store react-native-get-random-values

# Bare React Native
npm i react-native-keychain react-native-get-random-values

Overview

@pollar/core provides the PollarClient class and utilities to:

  • Authenticate users via Google, GitHub, Email (OTP), or Stellar wallets (Freighter, Albedo)
  • Sign every authenticated request with DPoP (RFC 9449), making stolen tokens useless to an attacker without the per-session keypair
  • Build and submit Stellar transactions
  • Fetch Stellar account balances
  • React to real-time authentication state changes

Quick Start (web)

import { PollarClient } from '@pollar/core';

const client = new PollarClient({ apiKey: 'your-api-key' });
// Storage and KeyManager autodetect:
//   storage  → localStorage with in-memory fallback
//   keypair  → WebCrypto ECDSA P-256, non-extractable, persisted in IndexedDB

React Native (Expo)

// At your app entry — `crypto.getRandomValues` polyfill
import 'react-native-get-random-values';

import { PollarClient } from '@pollar/core';
import { createSecureStoreAdapter } from '@pollar/core/adapters/expo';

// `await`: SecureStore is loaded via dynamic import.
const storage = await createSecureStoreAdapter({
  // Default: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY
  // Prevents iCloud Keychain from carrying the key to another device.
});

const client = new PollarClient({ apiKey: 'your-api-key', storage });
// KeyManager autodetects → NobleKeyManager (pure-JS @noble/curves p256).

React Native (react-native-keychain)

import 'react-native-get-random-values';
import { PollarClient } from '@pollar/core';
import { createKeychainAdapter } from '@pollar/core/adapters/react-native-keychain';

const storage = await createKeychainAdapter();
const client = new PollarClient({ apiKey: 'your-api-key', storage });

Preserved-on-disk storage shape

0.7.0 persists exactly:

clientSessionId, userId, status,
token { accessToken, refreshToken, expiresAt },
user { id?, ready },
wallet { publicKey, existsOnStellar?, createdAt? }

PII (mail, first_name, last_name, avatar, providers.*) lives in memory only on the PollarClient instance and is fetched after auth. Reach it via:

const profile = client.getUserProfile();
// { mail, first_name, last_name, avatar, providers } | null

Storage keys are namespaced by apiKeyHash (first 8 hex chars of SHA-256 of your API key) so multiple SDK instances on the same origin don't cross-contaminate.

End-to-end example

import { PollarClient } from '@pollar/core';

const client = new PollarClient({ apiKey: 'your-api-key' });

// React to auth state
const unsubscribe = client.onAuthStateChange((state) => {
  console.log(state.step, state.errorCode ?? '');
});

// Wait until the keypair is ready and any persisted session has been restored
await client.ready();

// Start an email login
client.login({ provider: 'email', email: '[email protected]' });

// Submit the OTP — clientSessionId is tracked internally
client.verifyEmailCode('123456');

// After success
const profile = client.getUserProfile(); // PII (memory-only)
const sessions = await client.listSessions();

API Reference

new PollarClient(config)

| Option | Type | Required | Description | | ------------------ | ------------------------ | -------- | ----------------------------------------------------------------------------------------------------------- | | apiKey | string | Yes | Your Pollar API key | | baseUrl | string | No | Override the default API endpoint | | stellarNetwork | 'mainnet' \| 'testnet' | No | Target Stellar network (default: testnet) | | storage | Storage | No | Pluggable storage adapter. Web autodetects localStorage with in-memory fallback; RN must inject one | | keyManager | KeyManager | No | Pluggable DPoP key manager. Web picks WebCryptoKeyManager; otherwise NobleKeyManager | | walletAdapter | WalletAdapterResolver | No | External wallet stack (e.g. Stellar Wallets Kit). Falls back to built-in FreighterAdapter/AlbedoAdapter | | deviceLabel | string | No | UI-friendly device label sent at /auth/login time and shown in listSessions() rows | | onStorageDegrade | OnStorageDegrade | No | Notified the first time localStorage falls back to in-memory mode (SSR, private browsing, quota, …) |


Authentication

client.login(options)

Initiates a login flow. Returns { cancelLogin } to abort the flow at any point.

// Social providers
client.login({ provider: 'google' });
client.login({ provider: 'github' });

// Email OTP
client.login({ provider: 'email', email: '[email protected]' });

// Stellar wallet
import { WalletType } from '@pollar/core';
client.login({ provider: 'wallet', type: WalletType.FREIGHTER });
client.login({ provider: 'wallet', type: WalletType.ALBEDO });

client.verifyEmailCode(code)

Submits the OTP code for email authentication. The active clientSessionId is tracked internally — no need to pass it.

client.loginWallet(walletId)

Lower-level entry point for wallet flows. Accepts any WalletId (WalletType.FREIGHTER, WalletType.ALBEDO, or an opaque string id like 'xbull' / 'lobstr' resolved by walletAdapter).

client.cancelLogin()

Aborts any in-flight login flow and resets authState to idle. Safe to call from any step (including error).

client.logout(options?): Promise<void>

Server-side revokes the refresh-token family via POST /v1/auth/logout, then clears local storage and resets the keypair. Server revocation is best-effort: a failed POST still clears local state.

await client.logout(); // sign out this device
await client.logout({ everywhere: true }); // revoke every active session for this user

Returns Promise<void> (was void pre-0.7.0). Existing fire-and-forget call sites keep working, but await it if you want to observe server-side revocation.

client.logoutEverywhere(): Promise<void>

Shorthand for logout({ everywhere: true }).

client.isAuthenticated()

Returns true if a valid session with a wallet public key is present.

client.getUserProfile(): PollarUserProfile | null

Returns the in-memory profile (mail, first_name, last_name, avatar, providers). null until /auth/login completes. This is the only way to read PII as of 0.7.0 — PII is no longer persisted to storage.

client.ready(): Promise<void>

Resolves once the keypair is initialized and any persisted session has been restored. Useful in tests and server-side rendering.

client.destroy(): void

Detaches the cross-tab storage listener, aborts in-flight logins, and releases the keypair. Call this on unmount in environments that re-instantiate PollarClient.

client.refresh(): Promise<void>

Forces an access-token refresh. Race-safe: concurrent calls coalesce into a single /v1/auth/refresh request. Request middleware also calls this automatically on 401 with DPoP-Nonce rotation.


Sessions

client.listSessions(): Promise<SessionInfo[]>

Returns one row per active refresh-token family for the authenticated user:

interface SessionInfo {
  familyId: string;
  createdAt: string;
  lastUsedAt: string;
  userAgent: string;
  ipHash: string;
  deviceLabel?: string;
  expiresAt: string;
  current: boolean; // true for the family backing this client
}

client.revokeSession(familyId): Promise<void>

Revokes a specific refresh-token family. Revoking the current family does not immediately clear local state — the next 401 triggers an auto-refresh, which fails (family revoked) and clears the session. Call logout() for an immediate teardown.


Transactions

client.buildTx(operation, params, options?)

Builds a Stellar transaction via the Pollar API.

await client.buildTx('payment', {
  destination: 'G...',
  amount: '10',
  asset: 'XLM',
});

client.submitTx(signedXdr)

Submits a signed XDR transaction to the network.

await client.submitTx(signedXdr);

State

Each state domain has its own typed subscriber. All on*StateChange methods return an unsubscribe function.

const unsubAuth = client.onAuthStateChange((state) => {
  // state.step    — 'idle' | 'oauth' | 'email' | 'wallet' | 'success' | 'error'
  // state.session — PollarPersistedSession (when step === 'success')
  // state.errorCode — AuthErrorCode (when step === 'error')
});

const unsubTx = client.onTransactionStateChange((s) => {
  /* build → sign → submit */
});
const unsubHistory = client.onTxHistoryStateChange((s) => {
  /* paginated rows */
});
const unsubBalance = client.onWalletBalanceStateChange((s) => {
  /* balances */
});
const unsubNetwork = client.onNetworkStateChange((s) => {
  /* mainnet / testnet */
});

unsubAuth();

Snapshot getters are also available: getAuthState(), getTransactionState(), getTxHistoryState(), getWalletBalanceState(), getNetworkState().

Error codes for the auth flow are surfaced via AUTH_ERROR_CODES / AuthErrorCode:

import { AUTH_ERROR_CODES, type AuthErrorCode } from '@pollar/core';

// AUTH_ERROR_CODES.EMAIL_CODE_INVALID
// AUTH_ERROR_CODES.EMAIL_CODE_EXPIRED
// AUTH_ERROR_CODES.SESSION_CREATE_FAILED
// AUTH_ERROR_CODES.WALLET_CONNECT_FAILED
// …see types.ts for the full list

StellarClient

Lightweight client to query Stellar account balances via Horizon.

import { StellarClient } from '@pollar/core';

const stellar = new StellarClient('testnet');
// or: new StellarClient({ horizonUrl: 'https://horizon.stellar.org' })

const result = await stellar.getBalances('GABC...');

if (result.success) {
  console.log(result.balances);
  // [{ asset: 'XLM', balance: '100.0000000' }, ...]
} else {
  console.error(result.errorCode); // 'ACCOUNT_NOT_FOUND' | 'HORIZON_ERROR' | 'NETWORK_ERROR'
}

Wallet Adapters

For direct wallet interaction outside the login flow:

import { FreighterAdapter, AlbedoAdapter } from '@pollar/core';

const adapter = new FreighterAdapter();
const available = await adapter.isAvailable();
if (available) {
  const { publicKey } = await adapter.connect();
}

To plug in external wallet stacks (e.g. Stellar Wallets Kit) without @pollar/core having to depend on them, pass a WalletAdapterResolver to the client:

import { PollarClient, WalletType } from '@pollar/core';
import { stellarWalletsKit } from '@pollar/stellar-wallets-kit-adapter';
import { Networks } from '@creit.tech/stellar-wallets-kit';

const client = new PollarClient({
  apiKey: 'pk_...',
  walletAdapter: stellarWalletsKit({ network: Networks.PUBLIC }),
});

client.loginWallet('xbull'); // any string id the kit understands

The resolver signature is:

type WalletAdapterResolver = (id: WalletId) => WalletAdapter | Promise<WalletAdapter>;
type WalletId = WalletType | (string & {});

Custom adapters (AdapterFn / PollarAdapter)

Generic adapter contract for wrapping external signing flows (e.g. Trustless Work SDK). Adapter functions receive params and return an unsigned XDR; the client handles signing and submission.

import type { AdapterFn, PollarAdapter, PollarAdapters } from '@pollar/core';

const trustlessWork: PollarAdapter = {
  initialize: (async (params) => ({ unsignedTransaction: '...' })) satisfies AdapterFn,
  release: (async (params) => ({ unsignedTransaction: '...' })) satisfies AdapterFn,
};

const adapters: PollarAdapters = { trustlessWork };

Renamed in 0.7.0EscrowFnAdapterFn and EscrowAdapterPollarAdapter. Runtime contract is unchanged; rename your imports.

TypeScript

@pollar/core is written in TypeScript and ships full type declarations.

Key exported types:

import type {
  // Client
  PollarClientConfig,
  PollarLoginOptions,
  PollarPersistedSession,
  PollarUserProfile,
  AuthState,
  AuthErrorCode,

  // Storage / keys / DPoP
  Storage,
  OnStorageDegrade,
  StorageDegradeReason,
  KeyManager,
  PublicEcJwk,
  BuildProofArgs,

  // Sessions
  SessionInfo,

  // Wallets
  WalletType,
  WalletId,
  WalletAdapter,
  WalletAdapterResolver,

  // Adapters (renamed from Escrow*)
  AdapterFn,
  PollarAdapter,
  PollarAdapters,

  // Stellar
  StellarNetwork,
  StellarClientConfig,
  StellarBalance,
} from '@pollar/core';

import { AUTH_ERROR_CODES } from '@pollar/core';

License

MIT