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

@lukso/core

v1.2.11

Published

Core utilities, services, and mixins for LUKSO web components and applications

Downloads

3,110

Readme

@lukso/core

npm version

Core utilities, services, and mixins for LUKSO web components and applications.

Installation

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

Peer Dependencies

This package requires lit as a peer dependency (for the Lit mixins):

pnpm add lit

If you're using chain definitions with viem types, you'll also need:

pnpm add viem

Features

Package contains some basic elements:

  • Services
  • Utility functions
  • Lit Mixins
  • Chain Definitions
  • Types
  • Configurations

Services

Device

Detect device type, OS, and browser:

import { deviceService } from '@lukso/core/services/device'

const device = deviceService()
if (device.isMobile) {
  console.log('Mobile device detected')
}

console.log({
  os: device.isMacOS ? 'macOS' : device.isWindows ? 'Windows' : 'Unknown',
  browser: device.isSafari ? 'Safari' : device.isChrome ? 'Chrome' : 'Unknown',
})

Intl

Format messages, numbers, and dates:

import { createIntlService, setIntlService } from '@lukso/core/services/intl'

const intl = createIntlService({
  locale: 'en-US',
  messages: {
    'app.welcome': 'Welcome',
    'app.hello': 'Hello, {name}!',
  },
})
setIntlService(intl)

console.log(intl.formatMessage('app.welcome')) // 'Welcome'
console.log(intl.formatMessage('app.hello', { name: 'John' })) // 'Hello, John!'
console.log(intl.formatNumber(1234.56)) // '1,234.56'

// Enable warnings for missing translations (useful for debugging)
const intlWithWarnings = createIntlService({
  locale: 'en-US',
  messages: {
    'app.welcome': 'Welcome',
  },
  onError: (err) => {
    console.error('Translation error:', err)
  },
})

Note: By default, missing translation warnings are suppressed since translations are optional. To enable warnings during development, pass a custom onError handler.

Utility functions

browserInfo

Detect browser type and get browser-specific metadata (name, icon, store link):

import { browserInfo, type BrowserInfo } from '@lukso/core/utils'
import { deviceService } from '@lukso/core/services'

const device = deviceService()
const browser: BrowserInfo = browserInfo(device)

console.log(browser.id) // 'chrome', 'firefox', 'safari', 'edge', 'brave', 'opera'
console.log(browser.name) // 'Chrome', 'Firefox', 'Safari', etc.
console.log(browser.icon) // 'logo-chrome', 'logo-firefox', etc.
console.log(browser.storeLink) // URL to browser's extension store for Universal Profile

The browserInfo function is useful for:

  • Showing browser-specific Universal Profile extension installation links
  • Displaying browser icons in UI
  • Determining browser-specific features or limitations
  • Customizing UX based on browser capabilities

slug

Convert text to slug format (lowercase with hyphens):

import { slug } from '@lukso/core/utils'

console.log(slug('Hello World')) // 'hello-world'
console.log(slug('TEST')) // 'test'
console.log(slug('some-text')) // 'some-text'
console.log(slug('')) // ''

The slug function is useful for:

  • Creating URL-friendly identifiers
  • Generating CSS class names
  • Normalizing user input

Signed QR Codes (Check-in URLs)

Create and verify signed QR codes for Universal Profile check-ins. This is useful for proving ownership of a Universal Profile without on-chain transactions.

URI Format: ethereum:<address>@<chainId>?ts=<unixTimestamp>&sig=<signature>

import {
  createSignedQR,
  verifySignedQR,
  parseSignedQR,
  isSignedQRFormat,
  getControllerAddress,
  getEIP1271Data,
  EIP1271_MAGIC_VALUE,
} from '@lukso/core/utils'

Creating a signed QR code:

// Using a private key directly
const uri = await createSignedQR(
  '0x1234...', // controller private key
  '0xabcd...', // profile address
  42,          // LUKSO mainnet (or 4201 for testnet)
  { generationOffsetSeconds: 2 } // optional: account for QR display latency
)
// Returns: ethereum:0xabcd...@42?ts=1706345678&sig=0x...

// Using a custom signer (WalletConnect, hardware wallet, etc.)
const uri = await createSignedQR(
  null,        // not needed when signer is provided
  '0xabcd...', // profile address
  42,
  {
    signer: async (message) => await wallet.signMessage(message)
  }
)

Verifying a signed QR code:

const result = await verifySignedQR(uri, {
  maxAgeSeconds: 60, // default: 60 seconds
})

if (result.isValid) {
  console.log('Profile:', result.profileAddress)
  console.log('Chain ID:', result.chainId)
  console.log('Signed by:', result.recoveredAddress)
  console.log('Timestamp:', result.timestamp)
  console.log('Is expired:', result.isExpired)
}

EIP-1271 verification (for smart contract wallets):

import { createPublicClient, http } from 'viem'
import { lukso } from 'viem/chains'

const result = await verifySignedQR(uri)
const { hash, signature } = getEIP1271Data(uri, result)

// Call isValidSignature on the Universal Profile contract
const client = createPublicClient({ chain: lukso, transport: http() })
const magicValue = await client.readContract({
  address: result.profileAddress,
  abi: [{
    name: 'isValidSignature',
    type: 'function',
    stateMutability: 'view',
    inputs: [
      { name: 'dataHash', type: 'bytes32' },
      { name: 'signature', type: 'bytes' },
    ],
    outputs: [{ name: '', type: 'bytes4' }],
  }],
  functionName: 'isValidSignature',
  args: [hash, signature],
})

const isAuthorizedController = magicValue === EIP1271_MAGIC_VALUE

Quick format check:

if (isSignedQRFormat(uri)) {
  // Valid format, proceed with verification
  const parsed = parseSignedQR(uri)
  console.log(parsed?.profileAddress, parsed?.chainId)
}

Get controller address from private key:

const controllerAddress = getControllerAddress('0x1234...')
console.log('Will sign as:', controllerAddress)

The signed QR code functionality is useful for:

  • Event check-ins without on-chain transactions
  • Proving Universal Profile ownership
  • Time-limited access tokens
  • QR-based authentication flows

Lit Mixins

withDeviceService

Add device detection to your component:

import { LitElement, html } from 'lit'
import { customElement } from 'lit/decorators.js'
import { withDeviceService } from '@lukso/core/mixins'

@customElement('my-component')
export class MyComponent extends withDeviceService(LitElement) {
  render() {
    return html`
      <div>
        Is mobile: ${this.device?.isMobile}
        Is Safari: ${this.device?.isSafari}
      </div>
    `
  }
}

withIntlService

Add internationalization to your component:

import { LitElement, html } from 'lit'
import { customElement } from 'lit/decorators.js'
import { withIntlService } from '@lukso/core/mixins'

@customElement('my-component')
export class MyComponent extends withIntlService(LitElement) {
  render() {
    return html`
      <h1>${this.formatMessage('app.title')}</h1>
      <p>${this.formatMessage('app.welcome')}</p>
    `
  }
}

withTheme

Add theme management with automatic dark mode support:

import { LitElement, html } from 'lit'
import { customElement } from 'lit/decorators.js'
import { withTheme } from '@lukso/core/mixins'

@customElement('my-component')
export class MyComponent extends withTheme(LitElement) {
  render() {
    return html`
      <div class="bg-neutral-100 dark:bg-neutral-10">
        <h1 class="text-neutral-10 dark:text-neutral-100">
          Hello World
        </h1>
        <p>Current theme: ${this.theme}</p>
        <p>Is dark mode: ${this.isDark}</p>
      </div>
    `
  }
}

Features:

  • Manual theme selection: Set theme to 'light', 'dark', or 'auto'
  • System preference detection: Automatically follows OS dark mode when theme is 'auto'
  • Reactive updates: Listens for system theme changes and updates automatically
  • Automatic wrapping: All rendered content is wrapped in a div with data-theme-root attribute
  • Tailwind integration: The wrapper div receives the dark class for Tailwind's dark mode utilities

API:

  • theme ('light' | 'dark' | 'auto'): The theme mode (reflects to attribute)
  • isDark (boolean): Read-only computed state indicating if dark mode is active

HTML Structure:

<!-- Light mode -->
<my-component theme="light">
  #shadow-root
    <div data-theme-root>
      <!-- Your component's rendered content -->
    </div>
</my-component>

<!-- Dark mode -->
<my-component theme="dark">
  #shadow-root
    <div data-theme-root class="dark">
      <!-- Your component's rendered content -->
    </div>
</my-component>

Chain Definitions

Extended chain configurations for LUKSO networks with custom contracts and metadata.

import { luksoMainnet, luksoTestnet } from '@lukso/core/chains'

Types

Package contain some basic types.

import type { ChainExtended, NetworkSlug, Address } from '@lukso/core'

Configurations

Package provides some basic constants.

import { SUPPORTED_NETWORK_IDS, GRAPHQL_ENDPOINT_MAINNET, GRAPHQL_ENDPOINT_TESTNET } from '@lukso/core/config'

Using Without a Bundler (Import Maps)

If you need to use @lukso/core in environments without a bundler (e.g., plain HTML, WordPress, static sites), you can use import maps to resolve bare module specifiers.

Since this package uses Lit as a peer dependency and marks it as external, it outputs bare imports like import { html } from 'lit'. Browsers can't resolve these without help.

Example: Using Import Maps

<!DOCTYPE html>
<html>
<head>
  <script type="importmap">
    {
      "imports": {
        "lit": "https://cdn.jsdelivr.net/npm/[email protected]/+esm",
        "lit/": "https://cdn.jsdelivr.net/npm/[email protected]/",
        "@lit/reactive-element": "https://cdn.jsdelivr.net/npm/@lit/reactive-element@2/+esm",
        "@lit/reactive-element/": "https://cdn.jsdelivr.net/npm/@lit/reactive-element@2/",
        "@preact/signals-core": "https://cdn.jsdelivr.net/npm/@preact/signals-core@1/+esm",
        "@lukso/core": "https://cdn.jsdelivr.net/npm/@lukso/core/+esm",
        "@lukso/core/": "https://cdn.jsdelivr.net/npm/@lukso/core/"
      }
    }
  </script>
</head>
<body>
  <script type="module">
    import { deviceService } from '@lukso/core/services/device'
    import { luksoMainnet } from '@lukso/core/chains'

    const device = deviceService()
    console.log('Is mobile:', device.isMobile)
    console.log('Chain ID:', luksoMainnet.id)
  </script>
</body>
</html>

Notes on Import Maps

  • Browser Support: Import maps are supported in all modern browsers (Chrome 89+, Safari 16.4+, Firefox 108+)
  • Must be before scripts: The <script type="importmap"> must appear before any <script type="module">
  • Trailing slashes matter: Use "@lukso/core/" to map subpath imports like @lukso/core/services

License

MIT