# @juanpprieto/lexicon-client

![CI](https://github.com/juanpprieto/lexicon-client/workflows/CI/badge.svg)
![npm version](https://badge.fury.io/js/%40juanpprieto%2Flexicon-client.svg)
![License](https://img.shields.io/npm/l/@juanpprieto/lexicon-client)
![Node.js](https://img.shields.io/node/v/@juanpprieto/lexicon-client)

> **TypeScript-first client for [Lexicon DJ's](https://lexicondj.com/) REST API with comprehensive type safety and unified control interface**

Build professional DJ applications with confidence. This library provides complete type-safe access to [Lexicon DJ's](https://lexicondj.com/) local API, with automatic validation, intelligent error handling, and a unified control interface for all DJ operations.

## What is Lexicon DJ?

**[Lexicon DJ](https://lexicondj.com/)** is professional DJ software for managing music libraries, creating playlists, and performing live DJ sets. It provides a built-in REST API that allows developers to programmatically control the software, access music metadata, and integrate DJ functionality into custom applications.

**Perfect for building:**

- 🎛️ Custom DJ controllers and interfaces
- 📊 Music library analysis and management tools
- 🤖 Automated playlist generation and DJ workflows
- 📱 Mobile DJ applications and remote controls

## ✨ Key Features

- **🔒 Complete Type Safety** - Full TypeScript support with automatic type inference
- **⚡ Zero Configuration** - Works out of the box with sensible defaults
- **🎛️ Unified Control API** - 128+ DJ operations under `client.control.*`
- **📊 Comprehensive Library Management** - Tracks, playlists, tags with full CRUD operations
- **🚀 Modern Architecture** - Built with Zod validation, neverthrow error handling, and undici HTTP client
- **🧪 Production Ready** - Extensive test suite with both unit and integration tests

## ⚡ Requirements

**This library requires [Lexicon DJ](https://lexicondj.com/) to be installed and running with the Local API enabled.**

### 1. Install Lexicon DJ

Download and install [Lexicon DJ](https://lexicondj.com/) - professional DJ software with local API support.

### 2. Enable the Local API

**⚠️ CRITICAL:** The Lexicon Local API is **disabled by default** and must be enabled manually:

1. **Open Lexicon DJ**
2. **Go to Settings → Integrations**
3. **Enable "Local API"**
4. **Verify it's running on `localhost:48624`**

The Local API is a REST API that you can send requests to as long as Lexicon is running. See the [official API documentation](https://lexicondj.com/docs/developers/api) for complete details.

### 3. Verify Connection

```bash
# Test if the API is accessible
curl http://localhost:48624/api/tracks?limit=1
```

## 🚀 Quick Start

### Installation

```bash
npm install @juanpprieto/lexicon-client
```

### Basic Usage

```typescript
import { createClient } from '@juanpprieto/lexicon-client'

// Connect to Lexicon DJ (localhost:48624)
const client = createClient()

// List tracks from your library
const tracks = await client.tracks.list({ limit: 10 })
if (tracks.isOk()) {
  console.log(`Found ${tracks.value.tracks.length} tracks`)
  tracks.value.tracks.forEach(track => {
    console.log(`${track.artist} - ${track.title}`)
  })
}

// Control playback
await client.control.player.play()
await client.control.player.pause()
```

> ⚠️ **Important:** This library uses functional error handling. API methods return `Result<T, E>` instead of throwing errors. Always check `.isOk()` before accessing data.

## 🎮 Try the Demos

**Learn by example** with interactive demos that show exactly how to use the library:

### React + Vite Demo (Web Applications)

```bash
cd demo/react-vite
npm install && npm run demo
```

Perfect for building web DJ applications with modern React.

### Node.js Demo (Scripts & Backend)

```bash
cd demo/node-script
npm install && npm run demo
```

Perfect for automation scripts and backend DJ tools.

**Both demos include:**

- 🔌 Connection testing
- 📚 API examples for all namespaces
- 🎛️ Player control demonstrations
- 📋 AI documentation access examples

## 📚 API Documentation

### Library Management

#### Tracks API

```typescript
// List tracks with pagination
const tracks = await client.tracks.list({ limit: 25 })
if (tracks.isOk()) {
  console.log(`Found ${tracks.value.tracks.length} tracks`)
}

// Search tracks by genre
const houseTracks = await client.tracks.search({
  filter: { genre: 'house' },
  limit: 10,
})

// Get a specific track
const track = await client.tracks.get({ id: 123 })
if (track.isOk()) {
  console.log(`${track.value.artist} - ${track.value.title}`)
}
```

#### Playlists API

```typescript
// List all playlists
const playlists = await client.playlists.list()
if (playlists.isOk()) {
  console.log(`Found ${playlists.value.playlists.length} playlists`)
}

// Get a specific playlist
const playlist = await client.playlists.get({ id: 1 })
if (playlist.isOk()) {
  console.log(`Playlist: ${playlist.value.name}`)
  console.log(`Tracks: ${playlist.value.trackIds?.length || 0}`)
}
```

#### Tags API

```typescript
// List all tags and categories
const tagsResult = await client.tags.list()
if (tagsResult.isOk()) {
  const { tags, categories } = tagsResult.value
  console.log(`Found ${tags.length} tags in ${categories.length} categories`)
}

// Get a specific tag
const tag = await client.tags.get({ id: 1 })
if (tag.isOk()) {
  console.log(`Tag: ${tag.value.label}`)
}
```

#### Player Control API

```typescript
// Basic playback control
await client.control.player.play()
await client.control.player.pause()
await client.control.player.stop()

// Get current player state
const state = await client.control.player.getCurrentState()
if (state.isOk()) {
  console.log(`Playing: ${state.value.currentTrack?.title}`)
  console.log(`Progress: ${Math.round(state.value.progress * 100)}%`)
}

// Seek to position
await client.control.player.seek({ progress: 0.5 }) // 50% through track
```

## 🔧 Advanced Usage

### Advanced Track Operations

```typescript
// Search tracks with complex filters
const tracks = await client.tracks.search({
  genre: ['house', 'techno'],
  bpmMin: 120,
  bpmMax: 140,
  rating: { min: 4 },
  limit: 50,
})

// Update track metadata
await client.tracks.update({
  id: 123,
  edits: {
    rating: 5,
    energy: '+1',
    tags: [1, 2, 3],
  },
})

// Memory-efficient iteration over large libraries
for await (const track of client.tracks.iterate({ genre: 'house' })) {
  console.log(`${track.artist} - ${track.title}`)
}
```

### Advanced Playlist Management

```typescript
// Create smart playlists with rules
const smartPlaylist = await client.playlists.create({
  name: 'High Energy House',
  type: 'smart',
  smartlist: {
    rules: [
      { field: 'genre', operator: 'is', value: 'house' },
      { field: 'energy', operator: 'greater_than', value: 7 },
    ],
  },
})

// Add tracks to existing playlist
if (playlist.isOk()) {
  await client.playlists.addTracks({
    id: playlist.value.id,
    trackIds: [1, 2, 3],
    index: 0, // Insert at beginning
  })
}
```

### Advanced Player Control

```typescript
// Advanced DJ features
await client.control.player.hotcue.trigger({ index: 1 })
await client.control.player.loop.create({ amount: 4 })
await client.control.player.jump.beats({ beats: 16 })
await client.control.player.tempomarker.doubleBpm()

// Interface control
await client.control.appearance.toggleTheme()
await client.control.navigation.toggleSidebar()
await client.control.trackBrowser.analyze()
```

### Advanced Tag Management

```typescript
// Create tag categories with colors
const category = await client.tagCategories.create({
  name: 'Mood',
  color: '#FF6B6B',
})

// Create tags within categories
const tag = await client.tags.create({
  label: 'Energetic',
  categoryId: category.value.id,
  position: 0,
})
```

### Error Handling with neverthrow

```typescript
import { match } from 'ts-pattern'

const result = await client.tracks.get({ id: 1 })

match(result)
  .with({ isOk: () => true }, ({ value: track }) => {
    console.log(`Found: ${track.artist} - ${track.title}`)
  })
  .with({ isErr: () => true }, ({ error }) => {
    match(error)
      .with({ type: 'NotFound' }, () => {
        console.log('Track not found')
      })
      .with({ type: 'NetworkError' }, () => {
        console.log('Connection failed - is Lexicon DJ running?')
      })
      .with({ type: 'ValidationError' }, err => {
        console.log(`Invalid request: ${err.message}`)
      })
      .otherwise(() => {
        console.log(`Unexpected error: ${error.message}`)
      })
  })
```

### Batch Operations with Queue Management

```typescript
import PQueue from 'p-queue'

// Built-in concurrency management prevents API overload
const queue = new PQueue({ concurrency: 5 })

// Process large track collections efficiently
const trackIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const tracks = await Promise.all(
  trackIds.map(id => queue.add(() => client.tracks.get({ id })))
)

// Filter successful results
const validTracks = tracks
  .filter(result => result.isOk())
  .map(result => result.value)

console.log(
  `Successfully loaded ${validTracks.length}/${trackIds.length} tracks`
)
```

### Custom Configuration

```typescript
import { createClient } from '@juanpprieto/lexicon-client'

const client = createClient({
  baseUrl: 'http://localhost:48624', // Custom Lexicon API URL
  timeout: 30000, // 30 second timeout
  retries: 3, // Retry failed requests
  logger: {
    level: 'debug', // Enable debug logging
    pretty: true, // Pretty-print logs
  },
})
```

## 🏗️ TypeScript Integration

### Automatic Type Inference

```typescript
// No manual type annotations needed
const client = createClient() // → LexiconClient

const tracks = await client.tracks.search({
  genre: 'house',
  bpmMin: 128,
}) // → Result<PaginatedTracksResponse, ApiError>

if (tracks.isOk()) {
  tracks.value.tracks.forEach(track => {
    // track is automatically inferred as Track
    console.log(track.artist) // Full IntelliSense support
  })
}
```

### Custom Type Guards

```typescript
import { type Track } from '@juanpprieto/lexicon-client'

function isHighEnergyTrack(track: Track): boolean {
  return track.energy >= 8 && track.bpm >= 128
}

const tracks = await client.tracks.list()
if (tracks.isOk()) {
  const highEnergyTracks = tracks.value.tracks.filter(isHighEnergyTrack)
  console.log(`Found ${highEnergyTracks.length} high-energy tracks`)
}
```

## 🤖 AI-Friendly Documentation

### For AI Tools and Development

```bash
# Copy AI documentation to your project
npx @juanpprieto/lexicon-client copy-ai-docs
```

This creates:

- `docs/lexicon-client-llms.txt` - Comprehensive API documentation for AI prompts
- `docs/lexicon-client-claude.md` - Development context for Claude Code sessions

**Usage with AI tools:**

```
"Please read docs/lexicon-client-llms.txt for complete API context and examples"
```

**Alternative:** Point AI directly to node_modules:

```
"Reference node_modules/@juanpprieto/lexicon-client/llms.txt for API documentation"
```

## 🧪 Testing

### Running Tests

```bash
# All tests
npm test

# Unit tests only
npm run test:unit

# Integration tests (requires running Lexicon DJ)
npm run test:integration

# Coverage report
npm run test:coverage

# Watch mode for development
npm run test:watch
```

### Integration Test Requirements

Integration tests require a running Lexicon DJ instance with the Local API enabled:

1. Open Lexicon DJ
2. Go to **Settings → Integrations**
3. Enable **"Local API"**
4. Run `npm run lexicon:check` to verify connection

## 📦 Publishing & Releases

This package uses [Changesets](https://github.com/changesets/changesets) for version management:

```bash
# Add a changeset for your changes
npm run changeset

# Release (maintainers only)
npm run release
```

### Automated Releases

- **GitHub Actions** handle automated testing and publishing
- **Semantic versioning** based on changeset types
- **NPM provenance** for supply chain security
- **GitHub releases** with automatically generated changelogs

## 🛡️ Security

### Vulnerability Reporting

If you discover a security vulnerability, please report it privately via [Security Advisories](https://github.com/juanpprieto/lexicon-client/security/advisories/new).

### Supply Chain Security

- **NPM Provenance** - All published packages include build provenance
- **SLSA Level 3** - Supply chain security attestation
- **Dependency Scanning** - Automated vulnerability detection
- **CodeQL Analysis** - Static security analysis

## 🤝 Contributing

We welcome contributions! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for detailed guidelines.

### Development Setup

```bash
# Clone and install
git clone https://github.com/juanpprieto/lexicon-client.git
cd lexicon-client
npm install

# Run quality checks
npm run validate

# Start development
npm run dev
```

### Project Structure

```
src/
├── client/          # HTTP client and namespace implementations
├── schemas/         # Zod validation schemas (modular structure)
├── types/           # TypeScript type definitions
├── utils/           # Shared utilities
└── __tests__/       # Comprehensive test suite
```

## 📋 API Reference

| Namespace        | Methods | Description                                                  |
| ---------------- | ------- | ------------------------------------------------------------ |
| `tracks`         | 7       | Track library management (CRUD, search, iteration)           |
| `playlists`      | 9       | Playlist operations (create, manage tracks, smart playlists) |
| `tags`           | 4       | Tag management and assignment                                |
| `tagCategories`  | 3       | Tag category organization with colors                        |
| `control.player` | 49      | Complete music player control and state                      |
| `control.*`      | 79      | DJ interface control (navigation, appearance, analysis)      |

**Total**: 151+ methods providing comprehensive Lexicon DJ integration.

## 🔗 Related Projects

- **[Lexicon DJ](https://lexicondj.com/)** - Professional DJ software and music library management
- **[Lexicon DJ API Documentation](https://lexicondj.com/docs/developers/api)** - Official REST API reference
- **[OpenAPI Specification](https://www.lexicondj.com/developer/api-docs.yaml)** - Lexicon's API schema

## 📄 License

MIT © [Juan Pablo Prieto](https://github.com/juanpprieto)

---

**🎧 Ready to build amazing DJ applications? [Get started now](#-quick-start) →**
