npm package: @sentriqx/web-sdk
Description: Sentriqx Web SDK — Screen recording, face recognition, live location tracking, and transaction recording for web applications and games (Unity WebGL, Phaser.js, React)
Author: Sentriqx
Latest version: 1.0.1

# @sentriqx/web-sdk

[![npm version](https://img.shields.io/npm/v/@sentriqx/web-sdk.svg)](https://www.npmjs.com/package/@sentriqx/web-sdk)
[![license](https://img.shields.io/npm/l/@sentriqx/web-sdk.svg)](./LICENSE)
[![types](https://img.shields.io/npm/types/@sentriqx/web-sdk.svg)](https://www.npmjs.com/package/@sentriqx/web-sdk)

Web SDK for game platforms — **Screen Recording**, **Live Streaming (RTMP)**, **Live Location**, **Transaction Recording**, and **Face Recognition**.

Built for web games using **Unity WebGL**, **Phaser.js**, or any **React**-based app.

> This is the web equivalent of the Sentriqx Kotlin/Android SDK, ported to TypeScript using browser-native APIs.

---

## Installation

```bash
npm install @sentriqx/web-sdk
```

Or with yarn / pnpm:

```bash
yarn add @sentriqx/web-sdk
pnpm add @sentriqx/web-sdk
```

---

## Quick Start

```typescript
import { SentriqxSDK } from '@sentriqx/web-sdk';

// 1. Initialize
const sdk = new SentriqxSDK({
  appId: 'NX-992',
  apiSecretKey: 'sxk_live_xxxxx',
  mediaServerUrl: 'https://your-media-server.com', // optional, for streaming
  debug: true,
});

// 2. Fetch config from backend
await sdk.fetchConfig();

// 3. Register this device/browser
await sdk.registerDevice();

// 4. Check browser support
const support = SentriqxSDK.checkSupport();
console.log(support);
// { screenRecording: true, faceRecognition: true, geolocation: true, deviceMotion: true }
```

---

## Feature 1: Screen Recording & Live Streaming

Uses the browser's `getDisplayMedia()` + `MediaRecorder` APIs (replaces Android MediaProjection + RootEncoder).

### Option A — Full Pipeline (Recommended)

For streaming to your media server with automatic backend integration and chunked upload:

```typescript
// Must be called from a user gesture (button click)
document.getElementById('record-btn').onclick = async () => {

  // Step 1: Request screen capture permission
  await sdk.startScreenCapture({
    width: 1280,
    height: 720,
    fps: 30,
    enableAudio: true,
  });

  // Step 2: Start the complete streaming pipeline
  // This handles: backend stream creation + media server FFmpeg + chunked upload
  const { streamKey, sessionId, backendResult } = await sdk.startScreenStreaming({
    streamTitle: 'My Recording',
    streamerName: 'Player One',
    config: { videoBitrate: 2500000 },
  });

  console.log('Stream key:', streamKey);

  // Step 3: Listen for chunk events
  sdk.on('streaming:chunk', ({ index, size }) => {
    console.log(`Chunk #${index} sent (${size} bytes)`);
  });

  sdk.on('streaming:chunk-error', ({ index, error }) => {
    console.error(`Chunk #${index} failed:`, error);
  });

  // Step 4: Stop when done — handles all cleanup
  await sdk.stopScreenStreaming();
};
```

### Option B — Local Recording Only

For recording without a media server (download-only):

```typescript
// Step 1: Request screen capture
await sdk.startScreenCapture({ enableAudio: true });

// Step 2: Start local recording
const sessionId = sdk.startStreaming('game-123');

// Step 3: Stop and get the blob
const result = await sdk.stopStreaming();

// Download the recording
if (result.recordedUrl) {
  const a = document.createElement('a');
  a.href = result.recordedUrl;
  a.download = `recording-${result.sessionId}.webm`;
  a.click();
}

// Or upload the blob to your backend
if (result.recordedBlob) {
  const formData = new FormData();
  formData.append('video', result.recordedBlob, 'recording.webm');
  await fetch('/api/upload', { method: 'POST', body: formData });
}
```

### Pause / Resume

```typescript
sdk.pauseStreaming();
sdk.resumeStreaming();

// Check state
if (sdk.streaming.isSessionPaused()) {
  sdk.resumeStreaming();
}
```

---

## Feature 2: Live Location

Uses the browser's Geolocation API (replaces Android FusedLocationProvider).

```typescript
// One-shot location
const loc = await sdk.getCurrentLocation();
console.log(loc.latitude, loc.longitude);

// Continuous tracking
sdk.on('location:updated', (data) => {
  console.log('New position:', data.latitude, data.longitude);
});

sdk.startLocationTracking();

// Stop later
sdk.stopLocationTracking();

// Get location history
const history = sdk.getLocationHistory();
```

---

## Feature 3: Transaction Recording

Validates and sends crypto transaction data to your backend.

```typescript
// Validate locally (no API call)
const validation = sdk.validateTransaction({
  transaction_hash: '0xabc123...',
  receiver_wallet: '0xdef...',
  sender_wallet: '0x456...',
  amount: '100.5',
  chain: 'ethereum',
});

if (validation.validated) {
  // Record on backend
  const result = await sdk.recordTransaction({
    transaction_hash: '0xabc123...',
    receiver_wallet: '0xdef...',
    sender_wallet: '0x456...',
    amount: '100.5',
    chain: 'ethereum',
    token_symbol: 'usdt',
  });
  console.log('Recorded:', result.success);
}

// Fetch history
const txHistory = await sdk.getTransactions();
```

---

## Feature 4: Face Recognition

Uses the browser's `getUserMedia()` for webcam access + Canvas for image capture (replaces Android Camera2 API). Includes a built-in capture UI with overlay guide.

### Capture Only

```typescript
const result = await sdk.captureFace({
  useFrontCamera: true,
  maxImageWidth: 480,
  jpegQuality: 0.8,
  guideText: 'Position your face in the frame',
});

if (result.success) {
  console.log('Image blob:', result.imageBlob);
  console.log('Data URL:', result.imageDataUrl);

  // Upload separately
  await sdk.uploadFace(result.imageBlob, 'username123');
}
```

### Capture + Auto Upload

```typescript
const result = await sdk.captureFaceAndUpload({
  useFrontCamera: true,
  maxImageWidth: 480,
  jpegQuality: 0.8,
});

if (result.success) {
  console.log(result.message); // "Face registered successfully!"
}
```

---

## Events

Listen for SDK events:

```typescript
// Streaming events
sdk.on('session:started', (data) => console.log('Started:', data.sessionId));
sdk.on('session:stopped', (data) => console.log('Stopped:', data.durationMs));
sdk.on('session:paused', () => console.log('Paused'));
sdk.on('session:resumed', () => console.log('Resumed'));
sdk.on('streaming:started', (data) => console.log('Streaming:', data));
sdk.on('streaming:stopped', () => console.log('Stream stopped'));
sdk.on('streaming:error', (data) => console.log('Error:', data.reason));
sdk.on('streaming:chunk', (data) => console.log('Chunk sent:', data.index));
sdk.on('streaming:chunk-error', (data) => console.log('Chunk error:', data));

// Location events
sdk.on('location:updated', (data) => console.log('Location:', data));
sdk.on('location:error', (data) => console.log('Location error:', data));

// Face events
sdk.on('face:captured', () => console.log('Face captured'));
sdk.on('face:uploaded', (data) => console.log('Face uploaded:', data));
sdk.on('face:error', (data) => console.log('Face error:', data));

// Transaction events
sdk.on('transaction:recorded', (data) => console.log('TX recorded'));
sdk.on('transaction:error', (data) => console.log('TX error:', data));

// Device/config events
sdk.on('device:registered', (data) => console.log('Registered:', data));
sdk.on('config:loaded', (data) => console.log('Config:', data));
```

---

## React Integration Example

```tsx
import { useEffect, useRef, useState } from 'react';
import { SentriqxSDK } from '@sentriqx/web-sdk';

function GamePage() {
  const sdkRef = useRef<SentriqxSDK | null>(null);
  const [isRecording, setIsRecording] = useState(false);
  const [streamKey, setStreamKey] = useState<string | null>(null);

  useEffect(() => {
    const sdk = new SentriqxSDK({
      appId: 'NX-992',
      apiSecretKey: 'sxk_live_xxxxx',
      mediaServerUrl: 'https://your-media-server.com',
      debug: true,
    });
    sdkRef.current = sdk;

    sdk.fetchConfig().then(() => sdk.registerDevice());

    return () => sdk.destroy();
  }, []);

  const startRecording = async () => {
    const sdk = sdkRef.current!;
    await sdk.startScreenCapture({ enableAudio: true });
    const { streamKey } = await sdk.startScreenStreaming({
      streamTitle: 'Game Session',
    });
    setStreamKey(streamKey);
    sdk.startLocationTracking();
    setIsRecording(true);
  };

  const stopRecording = async () => {
    const sdk = sdkRef.current!;
    await sdk.stopScreenStreaming();
    sdk.stopLocationTracking();
    setIsRecording(false);
  };

  return (
    <div>
      {/* Your game canvas (Unity WebGL / Phaser.js) */}
      <canvas id="game-canvas" />

      <button onClick={isRecording ? stopRecording : startRecording}>
        {isRecording ? 'Stop Recording' : 'Start Recording'}
      </button>

      {streamKey && <p>Stream Key: {streamKey}</p>}
    </div>
  );
}
```

---

## Unity WebGL Integration

```html
<script type="module">
  import { SentriqxSDK } from 'https://unpkg.com/@sentriqx/web-sdk/dist/index.mjs';

  const sdk = new SentriqxSDK({
    appId: 'NX-992',
    apiSecretKey: 'sxk_live_xxxxx',
    mediaServerUrl: 'https://your-media-server.com',
  });

  await sdk.fetchConfig();
  await sdk.registerDevice();

  // Expose to Unity via window
  window.SentriqxSDK = sdk;

  // Unity C# can call these via Application.ExternalCall or jslib
  window.startRecording = async () => {
    await sdk.startScreenCapture();
    const { streamKey } = await sdk.startScreenStreaming({
      streamTitle: 'Unity Game',
    });
    return streamKey;
  };

  window.stopRecording = async () => {
    const result = await sdk.stopScreenStreaming();
    return JSON.stringify(result);
  };
</script>
```

---

## Phaser.js Integration

```typescript
import { SentriqxSDK } from '@sentriqx/web-sdk';

const sdk = new SentriqxSDK({
  appId: 'NX-992',
  apiSecretKey: 'sxk_live_xxxxx',
});

class GameScene extends Phaser.Scene {
  create() {
    // Start tracking when game scene loads
    sdk.startLocationTracking();

    sdk.on('location:updated', (loc) => {
      // Use location data in your game
      this.updatePlayerRegion(loc.latitude, loc.longitude);
    });
  }
}
```

---

## API Reference

### `new SentriqxSDK(config)`

| Property | Type | Required | Description |
|---|---|---|---|
| `appId` | string | Yes | Your app identifier |
| `apiSecretKey` | string | Yes | API secret key |
| `baseUrl` | string | No | Backend URL (default: built-in) |
| `mediaServerUrl` | string | No | Media server URL for streaming |
| `organizationId` | string | No | Organization ID (auto-set by fetchConfig) |
| `productId` | string | No | Product ID (auto-set by fetchConfig) |
| `debug` | boolean | No | Enable debug logs |

### Static Methods

| Method | Returns | Description |
|---|---|---|
| `SentriqxSDK.checkSupport()` | object | Check browser feature support |

### Instance Methods

| Method | Returns | Description |
|---|---|---|
| `fetchConfig()` | `Promise<ApiResponse>` | Fetch backend config |
| `registerDevice()` | `Promise<ApiResponse>` | Register device |
| `isServiceActive(name)` | `boolean` | Check if a service is active |
| `startScreenCapture(config?)` | `Promise<MediaStream>` | Request screen capture permission |
| `startScreenStreaming(options?)` | `Promise<{streamKey, sessionId, backendResult}>` | Full streaming pipeline |
| `stopScreenStreaming()` | `Promise<SessionResult>` | Stop full pipeline |
| `startStreaming(gameId, config?)` | `string` | Low-level: start MediaRecorder only |
| `stopStreaming()` | `Promise<SessionResult>` | Low-level: stop MediaRecorder only |
| `pauseStreaming()` | `void` | Pause recording |
| `resumeStreaming()` | `void` | Resume recording |
| `captureFace(config?)` | `Promise<FaceCaptureResult>` | Capture face image |
| `captureFaceAndUpload(config?)` | `Promise<FaceCaptureResult>` | Capture + upload |
| `uploadFace(blob, username)` | `Promise<ApiResponse>` | Upload face image |
| `recordTransaction(fields)` | `Promise<ApiResponse>` | Record transaction |
| `validateTransaction(fields)` | `TransactionValidation` | Validate locally |
| `getTransactions()` | `Promise<ApiResponse>` | Get transaction history |
| `getCurrentLocation()` | `Promise<LocationData>` | Get current location |
| `startLocationTracking()` | `void` | Start live location |
| `stopLocationTracking()` | `void` | Stop live location |
| `getLocationHistory()` | `LocationData[]` | Get location history |
| `sendSensorData(data)` | `Promise<ApiResponse>` | Send gyro/accel data |
| `getDeviceId()` | `string` | Get persistent device ID |
| `getDeviceInfo()` | `Promise<DeviceInfo>` | Get full device info |
| `on(event, listener)` | `() => void` | Subscribe to events |
| `off(event, listener)` | `void` | Unsubscribe from events |
| `destroy()` | `void` | Clean up all resources |

---

## TypeScript

Full type definitions are included. Import types directly:

```typescript
import type {
  SentriqxConfig,
  ApiResponse,
  StreamConfig,
  SessionResult,
  FaceRecognitionConfig,
  FaceCaptureResult,
  TransactionFields,
  TransactionValidation,
  LocationData,
  DeviceInfo,
  SdkEventType,
  SdkEventListener,
} from '@sentriqx/web-sdk';
```

---

## Kotlin → Web API Mapping

For developers familiar with the Sentriqx  Android SDK:

| Kotlin (Android) | Web (Browser) |
|---|---|
| `MediaProjection` + `ScreenSource` | `navigator.mediaDevices.getDisplayMedia()` |
| `RootEncoder` / `GenericStream` | `MediaRecorder` API |
| `Camera2` API | `navigator.mediaDevices.getUserMedia()` |
| `FusedLocationProvider` | `navigator.geolocation` |
| `SharedPreferences` | `localStorage` |
| `HttpURLConnection` | `fetch()` API |
| `SensorManager` (Gyroscope) | `DeviceMotionEvent` / `DeviceOrientationEvent` |
| `LocalBroadcastManager` | `EventEmitter` (custom pub/sub) |
| `Build.MODEL` / `Build.MANUFACTURER` | `navigator.userAgent` parsing |

---

## Browser Support

| Feature | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| Screen Recording | 72+ | 66+ | 13+ | 79+ |
| Face Recognition (Camera) | 53+ | 36+ | 11+ | 79+ |
| Geolocation | 5+ | 3.5+ | 5+ | 12+ |
| DeviceMotion | 31+ | 6+ | 4.2+ | 12+ |

**Note:** Screen recording requires a secure context (HTTPS or localhost).

---

## Troubleshooting

### `NotAllowedError` on `startScreenCapture()`
Screen capture must be initiated from a **user gesture** (click, tap). Don't call it on page load or from a timeout.

### `NotSupportedError` on Safari
Safari requires iOS 15+ and macOS 12+ for screen capture. Face recognition works on all supported versions.

### Recording blob is empty
Ensure you call `stopStreaming()` (or `stopScreenStreaming()`) and wait for it to resolve. The blob is only available after the MediaRecorder has flushed.

---

## License

MIT © Sentriqx