npm package: @rm-graph/core
Description: RM-Graphs Core - Charting library built on SciChart
Latest version: 0.1.13

# @rm-graph/core

RM-Graphs Core - Framework-agnostic charting library built on SciChart.js. This package provides the core functionality for creating high-performance 2D and 3D charts, including license management with encrypted storage.

## Installation

```bash
npm install @rm-graph/core
# or
pnpm add @rm-graph/core
# or
yarn add @rm-graph/core
```

> `scichart` is automatically installed as a dependency!

## Quick Start

```typescript
import { createColumn3DChart, createPRPDChart, loadLicense } from '@rm-graph/core';

// Load license on app startup (optional - removes watermark)
await loadLicense();

// Create a 3D column chart
const chart = await createColumn3DChart('container-id', {
  theme: 'dark',
  data: [[45, 1500, 1], [90, 2200, 2], [180, 1800, 3]],
  xAxisTitle: 'Phase Angle (°)',
  yAxisTitle: 'Amplitude (mVp)',
  zAxisTitle: 'Cycle',
});

// Update data dynamically
chart.setData([[50, 1600, 1], [95, 2300, 2]]);

// Change theme
chart.setOptions({ theme: 'light' });

// Cleanup when done
chart.destroy();
```

---

## Chart Types

### Column3DChart

3D column/bar charts for phase-resolved data visualization.

```typescript
import { createColumn3DChart, type Column3DDataPoint } from '@rm-graph/core';

// Data format: [phaseAngle, amplitude, cycle][]
const data: Column3DDataPoint[] = [
  [45, 1500, 1],   // 45° phase, 1500mV amplitude, cycle 1
  [90, 2200, 2],   // 90° phase, 2200mV amplitude, cycle 2
  [180, 1800, 3],  // 180° phase, 1800mV amplitude, cycle 3
];

const chart = await createColumn3DChart('container', {
  data,
  theme: 'dark',
  xAxisTitle: 'Phase Angle (°)',
  yAxisTitle: 'Amplitude (mVp)',
  zAxisTitle: 'Cycle',
  xRange: { min: 0, max: 360 },
  yRange: { min: 0, max: 5000 },
  zRange: { min: 0, max: 50 },
  barFill: '#52aaf2',
  barOpacity: 0.95,
  showSineWave: true,
});
```

### PRPDChart

Phase Resolved Partial Discharge heatmap visualization.

```typescript
import { createPRPDChart, type PRPDDataPoint } from '@rm-graph/core';

// Data format: [phaseAngle, amplitude, count][]
const data: PRPDDataPoint[] = [
  [45, 1500, 10],   // 45° phase, 1500mV amplitude, 10 pulses
  [90, 2200, 25],   // 90° phase, 2200mV amplitude, 25 pulses
  [180, -1800, 15], // 180° phase, -1800mV amplitude, 15 pulses
];

const chart = await createPRPDChart('container', {
  data,
  maxPeak: 5000,
  minPeak: -5000,
  maxPhaseAngle: 360,
  showSineWave: true,
  showColorPalette: true,
});
```

### Surface3DChart

3D surface visualization for continuous data.

```typescript
import { createSurface3DChart } from '@rm-graph/core';

const chart = await createSurface3DChart('container', {
  yValues: surfaceData, // 2D array of height values
  theme: 'dark',
  colorMin: 0,
  colorMax: 100,
});
```

---

## Theming

Built-in themes: `light`, `dark`, `modern`, `midnight`

```typescript
import { getThemeManager, lightTheme, darkTheme } from '@rm-graph/core';

// Use built-in theme
const chart = await createColumn3DChart('container', {
  theme: 'dark',
  // ...
});

// Register custom theme
getThemeManager().registerTheme({
  name: 'custom',
  backgroundColor: '#1a1a2e',
  textColor: '#ffffff',
  gridLineColor: '#333366',
  colorPalette: ['#ff6b6b', '#4ecdc4', '#45b7d1'],
});

// Use custom theme
chart.setOptions({ theme: 'custom' });
```

### Theme Configuration

```typescript
interface ThemeConfig {
  name: string;
  backgroundColor: string;
  textColor: string;
  gridLineColor: string;
  axisColor: string;
  colorPalette: string[];
  // ... more options
}
```

---

## 🔑 License Management System

The core package includes a complete license management system for SciChart.js with **encrypted storage** support.

### Overview

SciChart.js requires a valid license key to remove watermarks from charts. The `@rm-graph/core` package provides:

- **Encrypted Storage**: License keys are encrypted before storing in localStorage
- **Multiple Configuration Methods**: UI component, programmatic, or environment variables
- **Automatic Loading**: Load license on app startup
- **Status Tracking**: Check if license is stored and applied

### How Licensing Works Internally

```
┌─────────────────────────────────────────────────────────────────┐
│                    LICENSE FLOW DIAGRAM                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  User enters license key                                        │
│           │                                                     │
│           ▼                                                     │
│  ┌─────────────────┐                                           │
│  │ validateFormat()│ ──── Invalid ───► Show error message      │
│  └────────┬────────┘                                           │
│           │ Valid                                               │
│           ▼                                                     │
│  ┌─────────────────────────────────────────┐                   │
│  │ encryptLicense(key, passphrase)         │                   │
│  │  - Uses AES-256-GCM (Web Crypto API)    │                   │
│  │  - Falls back to Base64 if no HTTPS     │                   │
│  └────────┬────────────────────────────────┘                   │
│           │                                                     │
│           ▼                                                     │
│  ┌─────────────────────────────────────────┐                   │
│  │ localStorage.setItem(                   │                   │
│  │   'RMGRAPH_LICENSE_DATA',               │                   │
│  │   encryptedData                         │                   │
│  │ )                                       │                   │
│  └────────┬────────────────────────────────┘                   │
│           │                                                     │
│           ▼                                                     │
│  ┌─────────────────────────────────────────┐                   │
│  │ SciChartSurface.setRuntimeLicenseKey()  │                   │
│  │  - Applies license to SciChart          │                   │
│  │  - Removes watermark from charts        │                   │
│  └─────────────────────────────────────────┘                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

### Encryption Details

The license system uses **two-tier encryption**:

#### Primary: AES-256-GCM (Secure Contexts)

When Web Crypto API is available (HTTPS or localhost):

```typescript
// Encryption process:
1. Derive key from passphrase using PBKDF2 (100,000 iterations, SHA-256)
2. Generate random 12-byte IV (Initialization Vector)
3. Encrypt license key using AES-256-GCM
4. Combine IV + encrypted data
5. Encode as Base64 for storage

// Storage format in localStorage:
"RMGRAPH_LICENSE_DATA": "base64(IV + AES-GCM-encrypted-data)"
```

#### Fallback: Base64 Obfuscation (Insecure Contexts)

When Web Crypto API is NOT available (HTTP over IP):

```typescript
// Simple obfuscation:
1. Prepend "rmgraph:" prefix
2. Encode as Base64

// Storage format:
"RMGRAPH_LICENSE_DATA": "base64('rmgraph:' + licenseKey)"
```

> **Security Note**: Base64 is NOT encryption - it only provides basic obfuscation. For production, always use HTTPS and combine with SciChart's domain-locking.

### License Management API

#### Core Functions

```typescript
import {
  // High-level operations
  setLicense,           // Encrypt, store, and apply license
  loadLicense,          // Load from storage and apply
  removeLicense,        // Remove stored license
  applyLicenseKey,      // Apply directly without storing
  
  // Status checks
  hasStoredLicense,     // Check if license exists in storage
  isLicenseApplied,     // Check if license is active in session
  getLicenseStatus,     // Get full status object
  validateLicenseFormat, // Validate key format (basic)
  
  // Configuration
  configureLicenseEncryption, // Set custom passphrase
  
  // Low-level crypto (advanced)
  encryptLicense,
  decryptLicense,
  saveEncryptedLicense,
  loadEncryptedLicense,
  clearStoredLicense,
  getLicenseStorageKey,
  
  // Types
  type LicenseConfig,
  type LicenseStatus,
} from '@rm-graph/core';
```

#### Function Reference

##### `setLicense(licenseKey: string): Promise<boolean>`

Encrypts and stores a license key, then applies it to SciChart.

```typescript
const success = await setLicense('your-scichart-license-key');
if (success) {
  console.log('License saved and applied!');
} else {
  console.log('Failed to save license');
}
```

##### `loadLicense(): Promise<boolean>`

Loads license from encrypted storage and applies to SciChart.

```typescript
// Call on app startup
const loaded = await loadLicense();
if (loaded) {
  console.log('License loaded from storage');
} else {
  console.log('No license found - charts will show watermark');
}
```

##### `removeLicense(): void`

Removes the stored license from localStorage.

```typescript
removeLicense();
// Note: Page reload required to remove license from SciChart
```

##### `hasStoredLicense(): boolean`

Checks if a license exists in localStorage (without decrypting).

```typescript
if (hasStoredLicense()) {
  console.log('License found in storage');
}
```

##### `isLicenseApplied(): boolean`

Checks if a license has been applied to SciChart in the current session.

```typescript
if (isLicenseApplied()) {
  console.log('License is active');
}
```

##### `getLicenseStatus(): LicenseStatus`

Returns complete license status.

```typescript
const status = getLicenseStatus();
// { hasLicense: true, isApplied: true }
```

##### `validateLicenseFormat(licenseKey: string): boolean`

Basic format validation (length check only - doesn't verify with SciChart).

```typescript
if (validateLicenseFormat(userInput)) {
  await setLicense(userInput);
}
```

##### `configureLicenseEncryption(config: LicenseConfig): void`

Configure custom passphrase for encryption (optional).

```typescript
// Call before setLicense/loadLicense if using custom passphrase
configureLicenseEncryption({
  passphrase: 'my-app-secret-key'
});
```

### Usage Examples

#### Example 1: Basic Setup

```typescript
import { loadLicense, createColumn3DChart } from '@rm-graph/core';

async function initApp() {
  // Load license on startup
  await loadLicense();
  
  // Create charts (will be watermark-free if license is valid)
  const chart = await createColumn3DChart('container', {
    data: myData,
  });
}

initApp();
```

#### Example 2: Programmatic License Configuration

```typescript
import { setLicense, loadLicense, hasStoredLicense } from '@rm-graph/core';

async function configureLicense(licenseKey: string) {
  // Check if already configured
  if (hasStoredLicense()) {
    console.log('License already stored');
    await loadLicense();
    return;
  }
  
  // Save new license
  const success = await setLicense(licenseKey);
  if (success) {
    console.log('License configured successfully');
  }
}
```

#### Example 3: Environment Variable Setup

```typescript
import { setupSciChartLicense } from '@rm-graph/core';

// .env file:
// VITE_SCICHART_LICENSE_KEY=your-license-key

// In main.ts:
await setupSciChartLicense({
  fromEnv: true,           // Check VITE_SCICHART_LICENSE_KEY
  fromLocalStorage: true,  // Also check encrypted storage
});
```

#### Example 4: Custom Passphrase

```typescript
import { 
  configureLicenseEncryption, 
  setLicense, 
  loadLicense 
} from '@rm-graph/core';

// Use custom passphrase for extra security
configureLicenseEncryption({
  passphrase: 'my-unique-app-identifier-2024'
});

// Now save/load will use this passphrase
await setLicense('your-license-key');
await loadLicense();
```

### Types

```typescript
interface LicenseConfig {
  /** Custom passphrase for encryption/decryption */
  passphrase?: string;
}

interface LicenseStatus {
  /** Whether a license is stored in localStorage */
  hasLicense: boolean;
  /** Whether the license was successfully applied to SciChart */
  isApplied: boolean;
  /** Error message if license operation failed */
  error?: string;
}
```

### Storage Details

| Key | Value | Description |
|-----|-------|-------------|
| `RMGRAPH_LICENSE_DATA` | Base64 encoded encrypted data | Encrypted license key |

### Security Considerations

1. **Client-Side Encryption**: The encryption happens in the browser. While AES-256-GCM is strong, the decryption key (passphrase) is in the client code.

2. **Domain Locking**: For production security, always use SciChart's domain-locking feature. Register your domains at [SciChart License Portal](https://www.scichart.com/licensing-scichart-js/).

3. **HTTPS Required**: Full AES encryption requires HTTPS or localhost. HTTP over IP will use Base64 fallback.

4. **Don't Commit Keys**: Never commit license keys to source control. Use environment variables.

---

## Utilities

```typescript
import {
  generateId,        // Generate unique IDs
  parseSize,         // Parse CSS size values
  deepMerge,         // Deep merge objects
  debounce,          // Debounce function calls
  throttle,          // Throttle function calls
  hexToRgba,         // Convert hex to RGBA
  formatNumber,      // Format numbers
  clamp,             // Clamp value to range
  lerp,              // Linear interpolation
} from '@rm-graph/core';
```

---

## WebAssembly Configuration

SciChart uses WebAssembly for high performance. The package auto-configures to use CDN:

```typescript
import { configureSciChart } from '@rm-graph/core';

// Default: Uses CDN (auto-configured)

// Custom WASM location:
configureSciChart({
  wasmUrl: '/path/to/scichart2d.wasm'
});
```

---

## Framework Integrations

This core package is framework-agnostic. For framework-specific components:

- **React**: `@rm-graph/react` - React components and hooks
- **Angular**: `@rm-graph/angular` - Angular components

Both packages re-export all core functionality plus framework-specific UI components like `LicenseManager`.

---

## API Reference

### Chart Factory Functions

| Function | Description |
|----------|-------------|
| `createColumn3DChart(container, config)` | Create 3D column chart |
| `createPRPDChart(container, config)` | Create PRPD heatmap |
| `createSurface3DChart(container, config)` | Create 3D surface |

### Chart Instance Methods

| Method | Description |
|--------|-------------|
| `setData(data)` | Update chart data |
| `setOptions(options)` | Update chart options |
| `destroy()` | Cleanup and remove chart |

### Theme Functions

| Function | Description |
|----------|-------------|
| `getThemeManager()` | Get theme manager instance |
| `lightTheme` | Built-in light theme |
| `darkTheme` | Built-in dark theme |
| `modernTheme` | Built-in modern theme |
| `midnightTheme` | Built-in midnight theme |

### License Functions

| Function | Description |
|----------|-------------|
| `setLicense(key)` | Save and apply license |
| `loadLicense()` | Load license from storage |
| `removeLicense()` | Remove stored license |
| `hasStoredLicense()` | Check if license exists |
| `isLicenseApplied()` | Check if license is active |
| `getLicenseStatus()` | Get full status |

---

## Troubleshooting

### Charts show watermark

1. Verify license is loaded: `console.log(getLicenseStatus())`
2. Check browser console for license errors
3. Ensure domain is registered in SciChart portal
4. Try reloading page after setting license

### Crypto errors on HTTP

Web Crypto API requires secure context (HTTPS or localhost). The system will fallback to Base64 encoding automatically.

### License not persisting

Check if localStorage is available and not blocked by browser settings.

---

## License

MIT

---

See framework-specific packages for UI components and additional documentation.