npm package: @akedly/shield
Description: Client-side PoW solver and Turnstile helper for Akedly Shield V1.2
Latest version: 1.0.1

# @akedly/shield

Client-side Proof-of-Work solver and Turnstile helper for Akedly Shield V1.2.

## Installation

```bash
npm install @akedly/shield
```

Or via CDN:

```html
<script src="https://unpkg.com/@akedly/shield/dist/akedly-shield.min.js"></script>
```

## Quick Start

```javascript
import { solvePow } from '@akedly/shield';

// 1. Get challenge from your server
const res = await fetch('/api/v1.2/transactions/challenge?APIKey=...&pipelineID=...');
const { data } = await res.json();

// 2. Solve PoW
const { nonce } = await solvePow(data.challenge, data.difficulty);

// 3. Send OTP with solution
await fetch('/api/v1.2/transactions/send', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    APIKey: '...',
    pipelineID: '...',
    verificationAddress: { phoneNumber: '+201234567890' },
    powSolution: { challengeToken: data.challengeToken, nonce }
  })
});
```

## API Reference

### `solvePow(challenge, difficulty, options?)`

Convenience function that creates a solver, finds the nonce, and cleans up.

- `challenge` — 64-char hex string from the server
- `difficulty` — integer (number of leading hex zeros required)
- `options.onProgress(hashesChecked)` — progress callback
- `options.useWorker` — `true`, `false`, or `'auto'` (default: `'auto'`)
- Returns `Promise<{ nonce: number }>`

### `new AkedlyShield(options?)`

Reusable solver instance.

- `options.useWorker` — `true`, `false`, or `'auto'` (default: `'auto'`)

#### `.solve(challenge, difficulty, options?)`

Same as `solvePow` but reuses the instance.

#### `.terminate()`

Kills the active Web Worker and releases the Blob URL.

### `getTurnstileToken(siteKey, options?)`

Browser-only. Creates a hidden Turnstile widget, resolves with the token, and cleans up.

- `siteKey` — Cloudflare Turnstile site key
- `options.theme` — `'light'`, `'dark'`, or `'auto'`
- `options.size` — `'normal'` or `'compact'`
- Returns `Promise<string>`

### `renderTurnstile(siteKey, container, options?)`

Renders a Turnstile widget into a DOM element.

- `container` — DOM element to render into
- Returns `Promise<string>` (token)

## Platform Support

| Platform | PoW Solver | Turnstile |
|----------|-----------|-----------|
| Browser (Worker) | Web Worker (off-thread) | `getTurnstileToken()` |
| Browser (no Worker) | Batched main-thread | `getTurnstileToken()` |
| React Native | Batched main-thread | Use bridge page |
| Node.js | Sync with native crypto | N/A |

## Web Worker Behavior

When `useWorker` is `'auto'` (default), the library:
1. Checks if `Worker`, `Blob`, and `URL.createObjectURL` are available
2. If yes, bundles the solver code into a Blob URL Worker (no separate file needed)
3. If no (e.g., React Native), falls back to batched main-thread solving with `setTimeout(fn, 0)` between batches to keep the UI responsive

## Algorithm

```
hash = SHA256(challenge + ":" + String(nonce))   // hex digest
valid = hash.startsWith("0".repeat(difficulty))   // leading hex zeros
```

The solver increments `nonce` from 0 until a valid hash is found.

## Full Integration Example

```javascript
import { solvePow, getTurnstileToken } from '@akedly/shield';

async function sendOTP(phoneNumber, apiKey, pipelineID) {
  // Step 1: Get challenge
  const challengeRes = await fetch(
    `https://api.akedly.io/api/v1.2/transactions/challenge?APIKey=${apiKey}&pipelineID=${pipelineID}`
  );
  const { data } = await challengeRes.json();

  // Step 2: Solve PoW (if required)
  let powSolution;
  if (data.challengeRequired) {
    const { nonce } = await solvePow(data.challenge, data.difficulty, {
      onProgress: (checked) => console.log(`${checked} hashes checked...`)
    });
    powSolution = { challengeToken: data.challengeToken, nonce };
  }

  // Step 3: Get Turnstile token (if required)
  let turnstileToken;
  if (data.turnstile?.required) {
    turnstileToken = await getTurnstileToken(data.turnstile.siteKey);
  }

  // Step 4: Send OTP
  const sendRes = await fetch('https://api.akedly.io/api/v1.2/transactions/send', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      APIKey: apiKey,
      pipelineID,
      verificationAddress: { phoneNumber },
      powSolution,
      turnstileToken
    })
  });

  return sendRes.json();
}
```

## Related Packages

- **Dart/Flutter**: [`akedly_shield`](https://github.com/Akedly-Org/akedly-shield-dart)
- **Swift (iOS)**: [`AkedlyShield`](https://github.com/Akedly-Org/akedly-shield-swift)
- **Kotlin (Android)**: [`com.akedly.shield`](https://github.com/Akedly-Org/akedly-shield-kotlin)