@lukso/core
v1.2.11
Published
Core utilities, services, and mixins for LUKSO web components and applications
Downloads
3,110
Readme
@lukso/core
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/corePeer Dependencies
This package requires lit as a peer dependency (for the Lit mixins):
pnpm add litIf you're using chain definitions with viem types, you'll also need:
pnpm add viemFeatures
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
onErrorhandler.
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 ProfileThe 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_VALUEQuick 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-rootattribute - Tailwind integration: The wrapper div receives the
darkclass 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
