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

@latentsearch/license-client

v0.2.0

Published

TypeScript client for Táillí License Manager - offline seat-based licensing

Readme

Táillí License Client for TypeScript

A friendly TypeScript/JavaScript client for the Táillí License Manager. Manage seats and API keys for offline/air-gapped deployments with minimal code.

Installation

npm install @latentsearch/license-client

Or with yarn:

yarn add @latentsearch/license-client

Quick Start

import { LicenseClient } from '@latentsearch/license-client';

// Connect to your License Manager
const client = new LicenseClient('http://localhost:8787', {
  productId: 'my-product'
});

// Use a seat with automatic cleanup
await client.withSeat(async (seat) => {
  console.log(`Allocated seat: ${seat.seatId}`);
  // Do your licensed work here
  await runMyApplication();
});
// Seat automatically released when done

Features

  • Simple callback API - Seats are automatically released, even on exceptions
  • Auto-refresh - Tokens are refreshed in the background
  • Full TypeScript support - Complete type definitions included
  • Zero dependencies - Uses only the Fetch API
  • Works everywhere - Node.js, browsers, Deno, Bun, edge runtimes

Usage Examples

Check License Status

import { LicenseClient } from '@latentsearch/license-client';

const client = new LicenseClient('http://localhost:8787', {
  productId: 'video-processor'
});

const status = await client.status();
console.log(`Product: ${status.productId}`);
console.log(`Seats: ${status.seatsInUse}/${status.seats} in use`);
console.log(`Expires: ${status.expiry}`);
console.log(`Features: ${status.features.join(', ')}`);

Use a Seat (Recommended)

The withSeat method is the cleanest way to use seats:

import { LicenseClient, SeatLimitError, LicenseExpiredError } from '@latentsearch/license-client';

const client = new LicenseClient('http://localhost:8787', {
  productId: 'video-processor'
});

try {
  const result = await client.withSeat(async (seat) => {
    console.log(`Got seat ${seat.seatId}`);

    // Your licensed code here
    return await processVideos();
  });

  console.log('Processing complete:', result);

} catch (e) {
  if (e instanceof SeatLimitError) {
    console.log('All seats are in use. Please try again later.');
  } else if (e instanceof LicenseExpiredError) {
    console.log('Your license has expired. Please contact support.');
  } else {
    throw e;
  }
}

Add Context for Auditing

Track who's using seats for compliance and debugging:

await client.withSeat(
  async (seat) => {
    await runAnalysis();
  },
  {
    context: {
      user: 'alice',
      workstation: 'lab-pc-42'
    }
  }
);

Manual Seat Management

For more control, manage seats directly:

// Allocate a seat
const seat = await client.allocate({
  context: { user: 'bob' }
});

try {
  // Do work...
  for (const batch of dataBatches) {
    await process(batch);
  }
} finally {
  // Always release when done
  await seat.release();
}

Disable Auto-Refresh

For short operations, you might not need background refresh:

await client.withSeat(
  async (seat) => {
    await quickOperation(); // Done in < 1 minute
  },
  { autoRefresh: false }
);

Check Feature Entitlements

See what features your license enables:

const entitlements = await client.getEntitlements();

if (entitlements.features.includes('ai-enhancement')) {
  enableAiFeatures();
}

if (entitlements.features.includes('batch-processing')) {
  enableBatchMode();
}

console.log(`API keys: ${entitlements.keysIssued}/${entitlements.maxKeys} used`);

Verify User API Keys

Validate JWTs issued by the License Manager:

try {
  const payload = await client.verifyKey(userProvidedToken);
  console.log(`Valid token for product: ${payload.productId}`);
  console.log(`Features: ${payload.features}`);
} catch (e) {
  console.log('Invalid or expired token');
}

Error Handling

The client provides specific error classes for common scenarios:

import {
  LicenseClient,
  LicenseError,       // Base class for all errors
  SeatLimitError,     // All seats are in use
  LicenseExpiredError, // License has expired
  ConnectionError,    // Can't reach License Manager
} from '@latentsearch/license-client';

const client = new LicenseClient('http://localhost:8787', {
  productId: 'my-product'
});

try {
  await client.withSeat(async (seat) => {
    await doWork();
  });
} catch (e) {
  if (e instanceof SeatLimitError) {
    // All seats taken - maybe queue the request
    await queueForLater();
  } else if (e instanceof LicenseExpiredError) {
    // License expired - notify admin
    await notifyAdmin('License expired!');
  } else if (e instanceof ConnectionError) {
    // Can't reach LM - maybe it's down
    await useCachedStateOrFail();
  } else if (e instanceof LicenseError) {
    // Other license-related errors
    console.error(`License error: ${e.message}`);
  } else {
    throw e;
  }
}

Configuration

Client Options

const client = new LicenseClient('http://localhost:8787', {
  productId: 'my-product',  // Your product ID (required)
  timeout: 30000,           // Request timeout in ms (default: 30000)
});

Allocation Options

await client.withSeat(callback, {
  context: { user: 'alice' },  // Metadata for auditing
  autoRefresh: true,           // Keep token fresh (default: true)
  refreshInterval: 30000,      // Refresh interval in ms (default: 30000)
});

API Reference

LicenseClient

| Method | Description | |--------|-------------| | status() | Get license status (seats, expiry, features) | | allocate(options?) | Allocate a seat, returns Seat object | | withSeat(callback, options?) | Use a seat with automatic cleanup | | verifyKey(token) | Verify a user API key (JWT) | | getEntitlements() | Get feature entitlements |

Seat

| Property/Method | Description | |-----------------|-------------| | seatId | Unique identifier for this seat | | token | Current authentication token | | expiresAt | Token expiration time (ISO 8601) | | release() | Release the seat back to the pool | | refresh() | Manually refresh the token |

Types

interface LicenseStatus {
  productId: string;
  seats: number;
  seatsInUse: number;
  seatsAvailable: number;
  expiry: string;
  features: string[];
  valid: boolean;
}

interface Entitlements {
  features: string[];
  scopes: string[];
  maxKeys: number;
  keysIssued: number;
  keysAvailable: number;
  expiry: string;
}

interface SeatContext {
  user?: string;
  workstation?: string;
  [key: string]: unknown;
}

Browser Usage

The client works in browsers out of the box:

<script type="module">
  import { LicenseClient } from 'https://unpkg.com/@latentsearch/license-client';

  const client = new LicenseClient('http://localhost:8787', {
    productId: 'my-product'
  });

  const status = await client.status();
  console.log(status);
</script>

Node.js Usage

Works with Node.js 18+ (which has built-in fetch):

import { LicenseClient } from '@latentsearch/license-client';

const client = new LicenseClient('http://localhost:8787', {
  productId: 'my-product'
});

await client.withSeat(async (seat) => {
  // Your code here
});

For Node.js < 18, you'll need a fetch polyfill like node-fetch.

License

MIT License - see the main Táillí repository for details.