JSPM

@akedly/shield

1.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 48
  • Score
    100M100P100Q110085F
  • License MIT

Client-side PoW solver and Turnstile helper for Akedly Shield V1.2

Package Exports

  • @akedly/shield

Readme

@akedly/shield

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

Installation

npm install @akedly/shield

Or via CDN:

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

Quick Start

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.useWorkertrue, false, or 'auto' (default: 'auto')
  • Returns Promise<{ nonce: number }>

new AkedlyShield(options?)

Reusable solver instance.

  • options.useWorkertrue, 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

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();
}