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

@dits-sa/license-client

v0.1.0

Published

Official TypeScript SDK for the DITS License Server — activation, sync, session management, and offline verification.

Readme

@dits-sa/license-client

Official TypeScript SDK for the DITS License Server. Handles license activation, sync, session management, offline verification, and feature flags.

Works in any JavaScript runtime (browser, Node.js, Electron, Tauri, Deno, Bun). React bindings available via @dits-sa/license-client/react.

Install

npm install @dits-sa/license-client
# or
pnpm add @dits-sa/license-client

Quick Start

Inertia + Laravel (server-provided license)

import { createLicenseClient } from '@dits-sa/license-client'
import { LicenseProvider } from '@dits-sa/license-client/react'

// Laravel passes license + fingerprint via Inertia shared props
function App({ licenseToken, machineHash, ...pageProps }) {
  const client = useMemo(() => createLicenseClient({
    baseUrl: 'https://license.yourcompany.com',
    apiKey: import.meta.env.VITE_LICENSE_API_KEY,
    license: licenseToken,
    fingerprint: { hash: machineHash },
  }), [licenseToken, machineHash])

  return (
    <LicenseProvider client={client}>
      <YourApp {...pageProps} />
    </LicenseProvider>
  )
}

Standalone SPA

import { createLicenseClient, LocalStorageLicenseStore } from '@dits-sa/license-client'
import { LicenseProvider, LicenseGuard } from '@dits-sa/license-client/react'

const client = createLicenseClient({
  baseUrl: 'https://license.yourcompany.com',
  apiKey: import.meta.env.VITE_LICENSE_API_KEY,
  store: new LocalStorageLicenseStore(),
})

function App() {
  return (
    <LicenseProvider client={client}>
      <LicenseGuard fallback={<ActivationScreen />}>
        <YourApp />
      </LicenseGuard>
    </LicenseProvider>
  )
}

Node.js / Electron (no React)

import { createLicenseClient, MemoryLicenseStore } from '@dits-sa/license-client'

const client = createLicenseClient({
  baseUrl: 'https://license.yourcompany.com',
  apiKey: process.env.LICENSE_API_KEY,
  store: new MemoryLicenseStore(),
})

const { license } = await client.activate({
  serial: 'XXXX-XXXX-XXXX',
  activationCode: '123456',
  fingerprint: { cpu: 'Intel', board: 'ASUS' },
  hash: 'sha256-of-fingerprint',
  instanceKey: 'unique-instance-id',
})

API Reference

Core Client

createLicenseClient(config)

| Option | Type | Required | Description | |--------|------|----------|-------------| | baseUrl | string | Yes | License server URL | | apiKey | string | Yes | API key (X-Api-Key header) | | timeout | number | No | Request timeout in ms (default: 30000) | | retry | { maxRetries, backoff } | No | Retry config (default: 3 retries, exponential) | | fingerprint | object \| function | No | Server-provided { hash } or async collector function | | license | string | No | Pre-loaded base64 license token (Inertia/Tauri) | | store | LicenseStore | No | Persistent storage for SPAs | | onError | function | No | Error callback | | fetch | function | No | Custom fetch implementation |

Methods

client.activate(params)                    // Activate a license
client.sync({ serial, hash })              // Sync / refresh license token
client.sessions.start({ serial, hash })    // Start concurrent session
client.sessions.heartbeat({ sessionToken }) // Keep session alive
client.sessions.end({ sessionToken })      // End session
client.parseLicense(token?)                // Parse cached license token
client.getLicense()                        // Get raw cached token

License Parsing

import { parseLicenseToken, isLicenseExpired } from '@dits-sa/license-client'

const license = parseLicenseToken(base64Token)
// { id, type, expiresAt, maxDevices, customer, features, deviceMode, ... }

isLicenseExpired(license) // boolean

Storage

import { LocalStorageLicenseStore, MemoryLicenseStore } from '@dits-sa/license-client'

new LocalStorageLicenseStore()    // Browser (AES-GCM encrypted at rest)
new MemoryLicenseStore()          // Node.js / testing

Sync Manager

import { createSyncManager } from '@dits-sa/license-client'

const manager = createSyncManager({
  client,
  serial: 'XXXX',
  hash: 'machine-hash',
  store,
  interval: 30 * 60_000,  // 30 min
  onSync: (license) => console.log('Synced'),
  onError: (err) => console.error(err),
})

manager.start()   // Begin periodic sync
manager.stop()    // Stop
manager.syncNow() // Force immediate sync

React Hooks

useActivateLicense()

const { mutateAsync: activate, isPending } = useActivateLicense()
await activate({ serial, activationCode, fingerprint, hash, instanceKey })

useSyncLicense({ serial, hash, interval? })

Auto-refreshing query with configurable interval (default 30 min).

useSessionManager({ serial, hash, heartbeatInterval? })

Full session lifecycle: auto-start, heartbeat, cleanup on unmount/beforeunload.

const { status, sessionToken, start, end } = useSessionManager({ serial, hash })
// status: 'idle' | 'starting' | 'active' | 'expired' | 'error'

useLicense() / useLicenseStatus() / useFeatureFlag(name)

const license = useLicense()           // ParsedLicense | null
const status = useLicenseStatus()       // 'valid' | 'expired' | 'none'
const hasAnalytics = useFeatureFlag('advanced-analytics') // boolean

React Guards

Mirror Laravel middleware — conditionally render based on license state.

<LicenseGuard fallback={<ActivationScreen />}>
  <FeatureGuard feature="analytics" fallback={<UpgradePrompt />}>
    <SessionGuard serial={s} hash={h} fallback={<SessionLimitScreen />}>
      <App />
    </SessionGuard>
  </FeatureGuard>
</LicenseGuard>

Error Handling

All API errors throw typed exceptions:

| Error | Code | HTTP | |-------|------|------| | LicenseNotFoundError | LICENSE_NOT_FOUND | 404 | | LicenseExpiredError | LICENSE_EXPIRED | 403 | | LicenseRevokedError | LICENSE_REVOKED | 403 | | InvalidActivationCodeError | INVALID_ACTIVATION_CODE | 403 | | DeviceLimitError | LICENSE_DEVICE_LIMIT | 409 | | SessionLimitError | SESSION_LIMIT | 409 | | SessionExpiredError | SESSION_EXPIRED | 401 | | RateLimitError | RATE_LIMIT_EXCEEDED | 429 | | ValidationError | VALIDATION_ERROR | 422 | | SignatureVerificationError | SIGNATURE_INVALID | - | | NetworkError | NETWORK_ERROR | - |

try {
  await client.activate(params)
} catch (err) {
  if (err instanceof DeviceLimitError) {
    // Handle max device limit
  }
}

Security Model

The SDK is built with defense-in-depth against common license cracking techniques:

  • Embedded public keys: ECDSA verification keys are hardcoded in the SDK, not configurable via env vars. Prevents key replacement attacks.
  • Signed API responses: Every response includes an ECDSA signature (X-Signature header) verified against the embedded key. Prevents server spoofing.
  • Nonce + timestamp: Each request includes a unique nonce echoed in the signed response. Rejects replayed or stale responses (>5 min).
  • Anti-clock-rollback: Stores last verified server timestamp. Detects system clock manipulation (>24h drift).
  • Encrypted storage: LocalStorageLicenseStore uses AES-GCM encryption tied to the browser origin.
  • Integrity self-checks: Critical verification functions are SHA-256 hashed at build time and checked at runtime.
  • Key rotation: Supports multiple trusted keys via kid (key ID) for seamless server key rotation.
  • Server is the authority: All client-side checks are defense-in-depth. The server enforces device limits, session limits, and revocation.

License

MIT