{"_id":"@atgcentry/safe-capture-verifier","_rev":"2-622a5363a7781acf10d7016f01de749c","name":"@atgcentry/safe-capture-verifier","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@atgcentry/safe-capture-verifier","version":"1.0.0","license":"MIT","_id":"@atgcentry/safe-capture-verifier@1.0.0","maintainers":[{"name":"mbu_gerald","email":"mbugeraldjunior@gmail.com"}],"dist":{"shasum":"a87eda534af0ac0035a6a185087af913c020b753","tarball":"https://registry.npmjs.org/@atgcentry/safe-capture-verifier/-/safe-capture-verifier-1.0.0.tgz","fileCount":9,"integrity":"sha512-bKmK4eywCdKSfAT275mQDToQIP/Ec/1gtUVWcsWop7gQAIP29/K8nsHVt/NL4y6WDR/rh2xfC8MeHhIltTQ7oQ==","signatures":[{"sig":"MEYCIQChLdA0OWhXH2GGeYFld2gqwVnO1sraPq4kDRAjtmsk0AIhALcsTZCvuq2IBqEcPe32qnBDKBR4mj8jFCGgks2PMjUB","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":139758},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.mjs","scripts":{"build":"tsup src/index.ts --format cjs,esm --dts --minify","prepare":"npm run build"},"_npmUser":{"name":"mbu_gerald","email":"mbugeraldjunior@gmail.com"},"_npmVersion":"10.9.3","description":"Identity verification camera component with TF FaceMesh, document ROI, quality gating and remote validation.","directories":{},"_nodeVersion":"22.20.0","dependencies":{"lucide-react":"^0.553.0","@mediapipe/face_mesh":"^0.4.1633559619","@tensorflow-models/face-landmarks-detection":"^1.0.6"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.1","react":"^19.1.0","react-dom":"^19.1.0","typescript":"^5.8.3","@types/node":"^22.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/safe-capture-verifier_1.0.0_1763018288195_0.7226719548357841","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@atgcentry/safe-capture-verifier","version":"1.0.1","description":"Identity verification camera component with TF FaceMesh, document ROI, quality gating and remote validation.","main":"dist/index.cjs","module":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"type":"module","scripts":{"build":"tsup src/index.ts --format cjs,esm --dts --minify","prepare":"npm run build"},"keywords":["identity","kyc","react","face","tensorflow","facemesh","camera","verification"],"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0"},"dependencies":{"@tensorflow-models/face-landmarks-detection":"^1.0.6","@mediapipe/face_mesh":"^0.4.1633559619","lucide-react":"^0.553.0"},"devDependencies":{"react":"^19.1.0","react-dom":"^19.1.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@types/node":"^22.0.0","tsup":"^8.0.1","typescript":"^5.8.3"},"license":"MIT","_id":"@atgcentry/safe-capture-verifier@1.0.1","_nodeVersion":"22.20.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-Z/ZlLi8e63kJA/tP1Lv0CDvYN4iOaog0B9zd30tramaRSnVvbev1peFpF3/5DnKqQftjhpTq9T9kDKLX/Yzy2Q==","shasum":"c21d1e6256b34cca55e02818b15f17be76511e1d","tarball":"https://registry.npmjs.org/@atgcentry/safe-capture-verifier/-/safe-capture-verifier-1.0.1.tgz","fileCount":9,"unpackedSize":140042,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCifU/kGQONYa+1SuFMQKYbodeJNQZjEtMOrGFWDwHmnAIhAOkvUEKyUBRfkAGn3WLWKD78A3MOmvzpbT0TXvSnyg4h"}]},"_npmUser":{"name":"mbu_gerald","email":"mbugeraldjunior@gmail.com"},"directories":{},"maintainers":[{"name":"mbu_gerald","email":"mbugeraldjunior@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/safe-capture-verifier_1.0.1_1763018993773_0.06105832936775446"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-13T07:18:08.097Z","modified":"2025-11-13T07:29:54.155Z","1.0.0":"2025-11-13T07:18:08.404Z","1.0.1":"2025-11-13T07:29:53.956Z"},"license":"MIT","description":"Identity verification camera component with TF FaceMesh, document ROI, quality gating and remote validation.","maintainers":[{"name":"mbu_gerald","email":"mbugeraldjunior@gmail.com"}],"readme":"# SafeCaptureVerifier React Component\n\n`SafeCaptureVerifier` is a validation‑first identity capture widget for React applications.  \nIt combines **live camera capture**, **face landmarks + micro‑liveness (MediaPipe FaceMesh)**, and **remote image analysis** into a single, easy‑to‑embed component.\n\nThe component is designed for KYC / onboarding / identity flows where:\n\n- A **live face** is required inside the frame\n- The user holds an **identity document** in a guide box\n- The app performs **local quality checks** (exposure, blur, edge density)\n- A remote service performs **SafeSearch + document detection**\n- Capture is only allowed when the **face is detected and “armed”**\n\n---\n\n## Features\n\n- ✔️ **Live camera stream** with device switching and optional torch toggle\n- ✔️ **MediaPipe FaceMesh** via `@tensorflow-models/face-landmarks-detection`\n    - Face bounding box and landmarks overlay\n    - Sticky **face gate**: once a good face is seen, capture stays enabled while any face remains\n    - Simple **micro‑liveness** heuristic (movement + size change)\n- ✔️ **Static document guide** (dotted white frame) for document positioning\n- ✔️ **Local image quality gates** before any upload:\n    - Exposure\n    - Blur (Laplacian variance)\n    - Edge density\n- ✔️ **Remote validation** via `endpointUrl` + `X_API_KEY` header\n- ✔️ **SafeSearch parsing**, face & document confidence, and telemetry hook\n- ✔️ **EN / FR built‑in translations**, optional overrides, and future‑proof for additional locales\n- ✔️ **Responsive layout**: mobile & desktop\n- ✔️ **Optional auto‑capture** when face is stable & live\n- ✔️ **Optional consent checkbox** before enabling capture\n\n---\n\n## Installation\n\nMake sure you have React and TypeScript set up.\n\n```bash\n# Core dependencies\nyarn add react react-dom\n\n# TensorFlow / FaceMesh stack\nyarn add @tensorflow-models/face-landmarks-detection @mediapipe/face_mesh\n\n# Icons (used by this component)\nyarn add lucide-react\n```\n\nIf you use `npm`:\n\n```bash\nnpm install \\\n  react react-dom \\\n  @tensorflow-models/face-landmarks-detection @mediapipe/face_mesh \\\n  lucide-react\n```\n\n> **Note**: The component itself is just a `.tsx` file; you can drop it into your project (e.g. `src/components/SafeCaptureVerifier.tsx`).\n\n---\n\n## Basic Usage\n\n```tsx\nimport React, { useCallback } from \"react\";\nimport SafeCaptureVerifier, {\n  ValidationPayload,\n  TelemetryPayload,\n} from \"./SafeCaptureVerifier\";\n\nexport const KycPage: React.FC = () => {\n  const handleValidated = useCallback(\n    (isSafe: boolean, analysis: ValidationPayload) => {\n      console.log(\"Validated?\", isSafe, analysis);\n      // e.g. update local state or show summary to the user\n    },\n    []\n  );\n\n  const handleConfirm = useCallback(\n    (file: File, analysis: ValidationPayload) => {\n      // This is called only when:\n      // - the image is marked as `safe`\n      // - there is a valid analysis payload\n      // The parent can then upload `file` to its own backend.\n      console.log(\"Confirmed snapshot:\", file, analysis);\n    },\n    []\n  );\n\n  const handleFailure = useCallback((err: unknown) => {\n    console.error(\"Validation error:\", err);\n  }, []);\n\n  const handleTelemetry = useCallback((payload: TelemetryPayload) => {\n    console.debug(\"Telemetry:\", payload);\n  }, []);\n\n  return (\n    <SafeCaptureVerifier\n      apiKey={import.meta.env.VITE_CENTRY_API_KEY}\n      endpointUrl=\"https://api.centry.uk/v1/ocr/api/analyze\"\n      requestId=\"signup-kyc-12345\"\n      locale=\"en\"\n      minDocConfidence={89}\n      countdownSeconds={5}\n      autoCapture\n      onValidated={handleValidated}\n      onConfirm={handleConfirm}\n      onFailure={handleFailure}\n      onTelemetry={handleTelemetry}\n      brandImageUrl=\"/assets/brand-logo.svg\"\n    />\n  );\n};\n```\n\nAt a minimum you **must** provide:\n\n- `apiKey` – the API key sent as `X_API_KEY` header to your backend / analysis service.\n\nIf you don’t pass `endpointUrl`, it defaults to:\n`https://api.centry.uk/v1/ocr/api/analyze`\n\n---\n\n## Component API\n\n### `SafeCaptureVerifier` Props\n\n```ts\nexport type SafeSearchLikelihood =\n  | \"UNKNOWN\"\n  | \"VERY_UNLIKELY\"\n  | \"UNLIKELY\"\n  | \"POSSIBLE\"\n  | \"LIKELY\"\n  | \"VERY_LIKELY\";\n\nexport interface SafeSearchAnnotation {\n  Adult: SafeSearchLikelihood;\n  Medical: SafeSearchLikelihood;\n  Racy: SafeSearchLikelihood;\n  Spoof: SafeSearchLikelihood;\n  Violence: SafeSearchLikelihood;\n}\n\nexport interface ValidationPayload {\n  SafeSearch: string;             // Raw SafeSearch string (parsed internally)\n  Faces?: unknown[];              // Face detections from backend (if any)\n  Labels: Record<string, number>; // Label -> score map\n}\n\nexport interface TelemetryPayload {\n  requestId?: string | number;\n  faceDetected: boolean;\n  faceQualityPct: number;         // 0..100\n  livenessScore: number;\n  localChecks: {\n    exposureOk: boolean;\n    blurOk: boolean;\n    edgesOk: boolean;\n  };\n  remote?: {\n    safe: boolean;\n    docConfidence: number;\n    safeSearch?: SafeSearchAnnotation | null;\n  };\n}\n```\n\n#### Main Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `apiKey` | `string` | **required** | API key sent as `X_API_KEY` header to the validation endpoint. |\n| `endpointUrl` | `string` | `\"https://api.centry.uk/v1/ocr/api/analyze\"` | Remote image analysis endpoint. Receives a `FormData` with `image` and optional `title`. |\n| `requestId` | `string \\| number` | `undefined` | Optional identifier attached to the validation request as `title`. Useful for correlating backend logs. |\n| `locale` | `\"en\" \\| \"fr\"` | `\"en\"` | Current UI language. |\n| `labels` | `Partial<LabelSchema>` | `undefined` | Override or extend the built‑in EN/FR translations. |\n| `brandImageUrl` | `string` | `undefined` | Optional logo shown above the analysis panel. |\n| `countdownSeconds` | `number` | `10` | Countdown duration before the snapshot is taken once the user clicks the capture button. |\n| `minDocConfidence` | `number` | `89` | Minimum `\"Identity document\"` label score (in %) required for the capture to be considered safe. |\n| `acceptedLikelihoods` | `SafeSearchLikelihood[]` | `[\"VERY_UNLIKELY\",\"UNLIKELY\"]` | Allowed SafeSearch likelihoods for all categories. Any category outside this set fails the validation. |\n| `autoCapture` | `boolean` | `false` | If `true`, starts the countdown automatically when the face is stable and liveness is OK. |\n| `autoCaptureStableFrames` | `number` | `10` | Number of stable frames required before auto‑capture triggers. |\n| `requireConsent` | `boolean` | `false` | If `true`, shows a consent checkbox and **keeps the capture button disabled** until the user agrees. |\n| `onConsentChange` | `(consented: boolean) => void` | `undefined` | Called whenever the consent checkbox changes. |\n| `enableTfFace` | `boolean` | `true` | Enables MediaPipe FaceMesh for face detection / landmarks / liveness. |\n| `detectionFps` | `number` | `8` | Target detection FPS for the FaceMesh loop. |\n| `livenessWindow` | `number` | `12` | Number of frames in the liveness history window. |\n| `livenessThreshold` | `number` | `0.015` | Threshold on movement + area change; above this, liveness is considered OK. |\n| `showOverlay` | `boolean` | `true` | If `true`, draws the face bounding box and sparse landmarks on a canvas overlay. |\n| `enableTorch` | `boolean` | `true` | If supported by the device, shows a torch toggle button. |\n| `docRoiInset` | `number` | `0.12` | Creates a white **dotted static guide box** inset by this fraction from each border. Only visual – no document detection. |\n\n#### Callback Props\n\n| Prop | Signature | Description |\n|------|-----------|-------------|\n| `onValidated` | `(isSafe: boolean, analysis: ValidationPayload) => void` | Called after the remote validator responds, whether the snapshot is safe or not. |\n| `onConfirm` | `(file: File, analysis: ValidationPayload) => void` | Called when the user submits the form and the capture is considered safe (meets SafeSearch, face, and document confidence criteria). |\n| `onFailure` | `(error: unknown) => void` | Called when remote validation fails (network or server error). |\n| `onTelemetry` | `(payload: TelemetryPayload) => void` | Optional analytics hook for logging local quality metrics, liveness score, and remote result. |\n\n---\n\n## i18n and Label Overrides\n\nThe component ships with a `LabelSchema` for **English (`en`)** and **French (`fr`)** and merges your overrides on top.\n\n```ts\nimport { LabelSchema } from \"./SafeCaptureVerifier\";\n\nconst customLabels: Partial<LabelSchema> = {\n  title: \"Identity Check\",\n  subtitle: \"Please hold your ID and align your face in the frame.\",\n  errors: {\n    // only override what you want\n    apiValidation: \"Unable to validate at this time. Try again later.\",\n  },\n};\n```\n\n```tsx\n<SafeCaptureVerifier\n  apiKey={apiKey}\n  locale=\"fr\"\n  labels={customLabels}\n/>\n```\n\nAny missing field falls back to the internal defaults.\n\n---\n\n## Consent‑Gated Capture\n\nTo require explicit consent before capture is possible:\n\n```tsx\n<SafeCaptureVerifier\n  apiKey={apiKey}\n  requireConsent\n  onConsentChange={(consented) => {\n    console.log(\"User consented?\", consented);\n  }}\n/>\n```\n\nUntil the user checks **“I understand and agree”**, the capture button remains disabled.\n\n---\n\n## Auto‑Capture Flow\n\nYou can enable auto‑capture so that the user does not need to click the camera button explicitly; the system will start the countdown once the face is stable and liveness is OK:\n\n```tsx\n<SafeCaptureVerifier\n  apiKey={apiKey}\n  autoCapture\n  autoCaptureStableFrames={12} // number of stable frames (~1–2 seconds)\n/>\n```\n\nThe component still shows the countdown overlay. When the countdown reaches `0`, it takes a snapshot, runs local quality checks, and sends it to your backend.\n\n---\n\n## Handling the Confirmed Snapshot\n\nThe component wraps everything in a `<form>`; it calls `onConfirm` only when the latest snapshot is considered **safe**:\n\n```tsx\nconst handleConfirm = (file: File, analysis: ValidationPayload) => {\n  const formData = new FormData();\n  formData.append(\"image\", file);\n  formData.append(\"safeScore\", String(analysis.Labels[\"Identity document\"] ?? 0));\n\n  fetch(\"/api/kyc/upload\", {\n    method: \"POST\",\n    body: formData,\n  })\n    .then((res) => res.json())\n    .then((res) => console.log(\"Uploaded to our backend:\", res))\n    .catch((err) => console.error(err));\n};\n\n<SafeCaptureVerifier\n  apiKey={apiKey}\n  onConfirm={handleConfirm}\n/>;\n```\n\nYour backend is free to store the original file; the component itself never persists it.\n\n---\n\n## Telemetry and Observability\n\n`onTelemetry` is useful for dashboards, debugging or offline tuning. Example:\n\n```tsx\nconst handleTelemetry = (p: TelemetryPayload) => {\n  // Example: send to your logging / metrics service\n  console.debug(\"[SafeCapture] telemetry\", p);\n};\n\n<SafeCaptureVerifier\n  apiKey={apiKey}\n  onTelemetry={handleTelemetry}\n/>;\n```\n\nYou’ll get both **local** metrics (exposure, blur, edge density) and **remote** verdicts (SafeSearch, doc confidence) in a single object.\n\n---\n\n## Styling & Layout\n\nThe component uses **inline styles** only, no external CSS. It is:\n\n- **Responsive by design** – uses a simple breakpoint hook (`useBreakpoint`) to switch between single‑column and two‑column layout.\n- Uses only neutral colors and clean visual hierarchy:\n    - Left: **live camera** with face overlay and dotted document guide\n    - Right: **latest snapshot preview** with retake button\n    - Below: **analysis panel** and optional error alert\n\nIf you prefer, you can wrap it in your own layout container or place it inside a modal / dialog component. The width scales with its parent node.\n\n---\n\n## Notes & Best Practices\n\n1. **HTTPS & Permissions**  \n   Browsers require HTTPS for camera access in production. Make sure your app runs over HTTPS, or use `localhost` in development.\n\n2. **Device Support**  \n   Torch support depends on the browser & device. The component fails gracefully if torch is not available.\n\n3. **Backend Contract**  \n   The remote endpoint is expected to return a JSON payload with either:\n    - `payload: ValidationPayload`, or\n    - `ValidationPayload` at the top level.\n\n   The relevant parts are:\n    - `SafeSearch`: a string like `\"Adult: VERY_UNLIKELY, Violence: UNLIKELY, ...\"`\n    - `Faces`: an array (non‑empty if faces detected)\n    - `Labels[\"Identity document\"]`: a numeric 0–100 score\n\n4. **Liveness**  \n   Liveness is a **lightweight heuristic**, not a full anti‑spoof system. For high‑risk use cases, combine it with stronger server‑side checks.\n\n---\n\n## License\n\nThis component is provided as part of a private project integration.  \nAdapt, extend, and integrate it into your own codebase according to your project’s license and compliance requirements.","readmeFilename":"README.md","keywords":["identity","kyc","react","face","tensorflow","facemesh","camera","verification"]}