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

astro-tokenkit

v1.0.17

Published

A powerful API client for Astro with automatic token rotation, session management, and seamless context integration.

Readme

Astro TokenKit

A powerful, type-safe API client for Astro with automatic token rotation, session management, and seamless context integration.

Features

  • 🚀 Built for Astro: Deep integration with Astro's middleware and context.
  • 🔄 Automatic Token Rotation: Handles access and refresh tokens automatically behind the scenes.
  • 🔒 Secure by Default: Uses HttpOnly cookies for token storage.
  • 🧩 Flexible Context: Supports both internal AsyncLocalStorage and external context management.
  • 🛠 Type-Safe: Built with TypeScript for a first-class developer experience.
  • 📡 Powerful Interceptors: Easily add custom logic for requests, responses, and errors.

Installation

pnpm add astro-tokenkit

Quick Start

1. Add the Integration

Configure TokenKit in your astro.config.mjs. This sets the global configuration for the entire app.

// astro.config.mjs
import { defineConfig } from 'astro/config';
import { tokenKit } from 'astro-tokenkit';

export default defineConfig({
  integrations: [
    tokenKit({
      baseURL: 'https://api.yourserver.com',
      auth: {
        login: '/auth/login',
        refresh: '/auth/refresh',
      }
    })
  ],
});

2. Setup Middleware

Create src/middleware.ts to automatically handle context binding and token rotation. You can use the exported api singleton's middleware:

// src/middleware.ts
import { api } from 'astro-tokenkit';

export const onRequest = api.middleware();

3. Use in Pages

Now you can use the api client anywhere in your Astro pages or components without worrying about passing context.

---
// src/pages/profile.astro
import { api } from 'astro-tokenkit';

// Request methods return an APIResponse object
const { data: user } = await api.get('/me');
---

<h1>Welcome, {user.name}</h1>

Global Configuration

TokenKit supports a global configuration via the tokenKit integration or setConfig. All ClientConfig properties can be set globally.

import { setConfig } from 'astro-tokenkit';

setConfig({
  baseURL: 'https://api.example.com',
  auth: {
    login: '/auth/login',
    refresh: '/auth/refresh',
  }
});

API Singleton

The library exports a global api instance that is automatically synchronized with your configuration.

  • Dynamic Sync: If you update the configuration via setConfig(), the api instance immediately reflects these changes.
  • Shared Manager: The api instance uses a global TokenManager which ensures that token refreshes are synchronized across all requests (preventing race conditions).
  • Middleware Integration: Use api.middleware() for a seamless setup in Astro.

If you need a specialized client with a different configuration, you can still create one:

import { createClient } from 'astro-tokenkit';

const specializedClient = createClient({
  baseURL: 'https://another-api.com'
});

Configuration

Client Configuration

| Property | Type | Description | | :--- | :--- | :--- | | baseURL | string | Required. Base URL for all requests. | | auth | AuthConfig | Optional authentication configuration. | | headers | Record<string, string> | Default headers for all requests. | | timeout | number | Request timeout in milliseconds (default: 30000). | | retry | RetryConfig | Retry strategy for failed requests. | | interceptors| InterceptorsConfig | Request/Response/Error interceptors. | | context | AsyncLocalStorage | External AsyncLocalStorage instance. | | getContextStore| () => TokenKitContext| Custom method to retrieve the context store. | | setContextStore| (ctx) => void| Custom method to set the context store. | | runWithContext| Function| Custom runner to bind context. |

Auth Configuration

| Property | Type | Description | | :--- | :--- | :--- | | login | string | Endpoint path for login (POST). | | refresh | string | Endpoint path for token refresh (POST). | | logout | string | Endpoint path for logout (POST). | | contentType | 'application/json' \| 'application/x-www-form-urlencoded' | Content type for auth requests (default: application/json). | | headers | Record<string, string> | Extra headers for login/refresh requests. | | loginData | Record<string, any> | Extra data to be sent with login request. | | refreshData | Record<string, any> | Extra data to be sent with refresh request. | | refreshRequestField | string | Field name for the refresh token in the refresh request (default: refreshToken). | | fields | FieldMapping | Custom mapping for token fields in API responses (accessToken, refreshToken, expiresAt, expiresIn, tokenType, sessionPayload). | | parseLogin | Function | Custom parser for login response: (body: any) => TokenBundle. | | parseRefresh| Function | Custom parser for refresh response: (body: any) => TokenBundle. | | injectToken | Function | Custom token injection: (token: string, type?: string) => string (default: Bearer). | | cookies | CookieConfig | Configuration for auth cookies. | | policy | RefreshPolicy | Strategy for when to trigger token refresh. |

Login Options

| Property | Type | Description | | :--- | :--- | :--- | | onLogin | Function | Callback after successful login: (bundle, body, ctx) => void. | | onError | Function | Callback after failed login: (error, ctx) => void. | | headers | Record<string, string> | Extra headers for this specific login request. | | data | Record<string, any> | Extra data for this specific login request. |

Request Auth Overrides

When calling api.get(), api.post(), etc., you can override auth configuration (e.g., for multi-tenancy). Headers provided in the request options are automatically propagated to any automatic token refresh operations:

await api.get('/data', {
  headers: { 'x-tenant-name': 'lynx' },
  auth: {
    data: { extra_refresh_param: 'value' }
  }
});

Advanced Usage

Manual Context

If you prefer not to use middleware, you can bind the Astro context manually for a specific scope:

import { runWithContext } from 'astro-tokenkit';

const { data } = await runWithContext(Astro, () => api.get('/data'));

Interceptors

const api = createClient({
  baseURL: '...',
  interceptors: {
    request: [
      (config, ctx) => {
        config.headers = { ...config.headers, 'X-Custom': 'Value' };
        return config;
      }
    ]
  }
});

Login and Logout

// In an API route or server-side component
const { data: bundle } = await api.login({ username, password }, {
  onLogin: (bundle, body, ctx) => {
    // Post-login logic (e.g., sync session to another store)
    console.log('User logged in!', bundle.sessionPayload);
  },
  onError: (error, ctx) => {
    // Handle error (e.g., log it or perform cleanup)
    console.error('Login failed:', error.message);
  }
});

await api.logout();

Using Promises (.then, .catch, .finally)

All API methods return a Promise that resolves to an APIResponse object. You can use traditional promise chaining:

// Example with GET request
api.get('/me')
  .then(({ data: user, status }) => {
    console.log(`User ${user.name} fetched with status ${status}`);
  })
  .catch(err => {
    console.error('Failed to fetch user:', err.message);
  })
  .finally(() => {
    console.log('Request finished');
  });

// Example with login
api.login(credentials)
  .then(({ data: token }) => {
    console.log('Successfully logged in!', token.accessToken);
  })
  .catch(err => {
    if (err instanceof AuthError) {
      console.error('Authentication failed:', err.message);
    } else {
      console.error('An unexpected error occurred:', err.message);
    }
  })
  .finally(() => {
    // E.g. stop loading state
  });

Note: Since all methods return an APIResponse object, you can use destructuring in .then() to access the data directly, which allows for clean syntax like .then(({ data: token }) => ... ).

License

MIT © oamm