# FrankieOne OneSDK Library

FrankieOne OneSDK - A comprehensive frontend SDK library for identity verification, onboarding, and document verification flows.

**Version:** 1.9.13
**Node Version:** >=18 (npm >=10)
**Published as:** `@frankieone/one-sdk` on NPM

---

## Table of Contents

-   [Overview](#overview)
-   [Architecture](#architecture)
-   [Module Overview](#module-overview)
-   [Vendor Integrations](#vendor-integrations)
    -   [IDV (Identity Verification)](#idv-identity-verification)
    -   [OCR (Document Recognition)](#ocr-document-recognition)
    -   [Biometrics](#biometrics)
    -   [Device Fingerprinting](#device-fingerprinting)
    -   [Federation](#federation)
    -   [Form](#form)
-   [Integration Method Reference](#integration-method-reference)
-   [Build & Development](#build--development)
-   [Deployment](#deployment)

---

## Overview

This is a **hybrid monorepo** containing:

1. **TypeScript/React Frontend SDK** - The main OneSDK library with multiple vendor integrations
2. **Go CLI Tool** - Interactive CLI for generating OneSDK flows and local HTML preview

### Key Technologies

| Category     | Technologies                                                         |
| ------------ | -------------------------------------------------------------------- |
| **Frontend** | React 18.2, Redux Toolkit 2.2.1, TypeScript 5.7, Webpack 5, Vite 7.2 |
| **Styling**  | TailwindCSS 3.4, Sass, PostCSS                                       |
| **Testing**  | Jest 28, ts-jest                                                     |
| **Reactive** | RxJS 7.5, Zod 3.19                                                   |
| **CLI**      | Go 1.21.5, Charmbracelet (huh, lipgloss, bubbles)                    |

---

## Architecture

```
OneSDK (index.ts)
       │
   ────┴────
  │         │
 SDK     Common Utils
  │         │
  └── Modules
       ├── Individual (F2)
       ├── Business
       ├── Device (Sardine, ThreatMetrix)
       ├── IDV (Onfido, Sumsub, Incode, Daon, Mastercard, Truuth, OcrLabs, IDVerse)
       ├── OCR (Onfido, Sumsub, Incode, Daon, IDVerse)
       ├── Biometrics (Onfido, Sumsub, Incode, Daon, OcrLabs, IDVerse)
       ├── Form (Formio, React, Legacy)
       ├── Federation (Singpass)
       ├── Session
       └── Frankie-Client (API)
```

---

## Module Overview

| Module         | Purpose                             | Vendors                                                            |
| -------------- | ----------------------------------- | ------------------------------------------------------------------ |
| **IDV**        | Full identity verification flow     | Onfido, Sumsub, Incode, Daon, Mastercard, Truuth, OcrLabs, IDVerse |
| **OCR**        | Document scanning & data extraction | Onfido, Sumsub, Incode, Daon, IDVerse                              |
| **Biometrics** | Face capture & liveness detection   | Onfido, Sumsub, Incode, Daon, OcrLabs, IDVerse                     |
| **Device**     | Device fingerprinting               | Sardine, ThreatMetrix                                              |
| **Federation** | Federated identity (OAuth)          | Singpass                                                           |
| **Form**       | Data entry & review forms           | Formio, React, Legacy                                              |

---

## Vendor Integrations

### IDV (Identity Verification)

#### Onfido

| Aspect                 | Details                                     |
| ---------------------- | ------------------------------------------- |
| **Integration Method** | Native SDK loaded from CDN                  |
| **SDK Source**         | `https://sdk.onfido.com/v{version}`         |
| **UI Type**            | Vendor-provided UI with theme customization |
| **Version Support**    | v14+ (modern) and <v14 (legacy modal-based) |

**Modes:**

-   **OneStep Mode** (`useOneStep: true`): Combined document + face capture in single SDK instance
-   **Dual Component Mode**: Separate OCR → Biometrics chain

**Key Features:**

-   Theme support: `'light'` or `'dark'` with full `customUI` configuration
-   Cross-device linking: QR code, copy link, SMS options
-   Exit button support
-   Analytics disabled by default
-   Document type configuration: Passport, Drivers License, Medicare

**Events Emitted:**

-   `ready`, `loading`, `error`, `detection_complete`, `detection_failed`
-   `results`, `session_closed`, `session_interrupted`, `vendor_event`

**Files:**

-   `modules/idv/vendors/Onfido/index.ts`
-   `modules/ocr/vendors/Onfido/` (onfido.init.ts, onfidoLegacy.init.ts)
-   `modules/biometrics/vendors/Onfido/`
-   `modules/common/vendors/onfido/`

---

#### Sumsub

| Aspect                 | Details                                                          |
| ---------------------- | ---------------------------------------------------------------- |
| **Integration Method** | SDK Library with iframe UI                                       |
| **SDK Source**         | `https://static.sumsub.com/idensic/static/sns-websdk-builder.js` |
| **UI Type**            | Vendor-provided iframe                                           |
| **Products**           | `'idv'`, `'ocr'`, `'poa'`                                        |

**Initialization Flow:**

```javascript
snsWebSdk
    .init(token, refreshTokenCallback)
    .withConf({ theme: 'light' })
    .withOptions({ addViewportTag: false, adaptIframeHeight: true })
    .on('idCheck.onError', errorHandler)
    .onMessage(messageHandler)
    .build()
    .launch(mountElement);
```

**Message Types:**

-   `idCheck.onReady` - SDK loading
-   `idCheck.onInitialized` - UI ready
-   `idCheck.onApplicantStatusChanged` - Review status changes (pending → completed)
-   `idCheck.onUploadError` - Upload failures

**Files:**

-   `modules/idv/vendors/Sumsub/index.ts`
-   `modules/ocr/vendors/Sumsub/index.ts`
-   `modules/biometrics/vendors/Sumsub/index.ts`
-   `modules/common/vendors/sumsub/`

---

#### Incode

| Aspect                 | Details                                              |
| ---------------------- | ---------------------------------------------------- |
| **Integration Method** | Native SDK + Camera (OCR/Biometrics) or Iframe (IDV) |
| **SDK Source**         | `https://sdk.incode.com/sdk/onBoarding-{version}.js` |
| **Default Version**    | 1.75.2                                               |
| **UI Type**            | Custom camera UI with document selector              |

**Camera Types:**

-   `'front'` - Document front side
-   `'back'` - Document back side
-   `'selfie'` - Face capture

**Document Type Mapping:**

```javascript
Passport → 'PASSPORT'
DriversLicense → 'DRIVERS_LICENCE'
MedicalCard → 'NATIONAL_HEALTH_ID'
```

**IDV Iframe Mode:**

-   PostMessage protocol with workflow messages
-   Success: `{ workflow: 'INCODE_COMPLETE' }`
-   Error: `{ workflow: 'INCODE_ERROR' }`
-   Session key: `interviewId`

**Special Handling:**

-   Memory out of bounds error detection via console.error override
-   Country/region document validation
-   Consent reflow fix for iOS WebKit

**Files:**

-   `modules/idv/vendors/Incode/index.ts`
-   `modules/ocr/vendors/Incode/index.ts`
-   `modules/biometrics/vendors/Incode/index.ts`
-   `modules/common/vendors/incode/`

---

#### Daon

| Aspect                 | Details                       |
| ---------------------- | ----------------------------- |
| **Integration Method** | Iframe + PostMessage protocol |
| **UI Type**            | Vendor-provided iframe        |
| **Modules**            | IDV, OCR, Biometrics          |

**Message Format:**

```javascript
// Success
{
    workflow: 'DAON_COMPLETE';
}

// Error
{
    workflow: 'DAON_ERROR';
}
```

**Post-Iframe Actions:**

-   IDV: `initProcess` (default)
-   OCR: `initProcessAndEvaluateResults`
-   Biometrics: `initProcessBiometrics` (with window event listener)

**Check Processing Pool:**

-   Enabled by default for Daon
-   `wait_time: 5` seconds
-   `max_attempts: 3`

**Files:**

-   `modules/idv/vendors/Daon/index.ts`
-   `modules/ocr/vendors/Daon/index.ts`
-   `modules/biometrics/vendors/Daon/index.ts`

---

#### Mastercard

| Aspect                 | Details                   |
| ---------------------- | ------------------------- |
| **Integration Method** | Iframe + JSON PostMessage |
| **UI Type**            | Vendor-provided iframe    |
| **Session Key**        | `workflowExecutionId`     |

**Message Format (Stringified JSON):**

```javascript
// Loaded
{ "payload": { "value": "loaded" } }

// Success
{ "payload": { "value": "success" }, "workflowExecutionId": "..." }

// Error
{ "payload": { "value": "error", "metainfo": { "msg": "..." } } }
```

**Key Difference:** Mastercard emits its own 'loaded' message (`emitOnLoad: false`)

**Files:**

-   `modules/idv/vendors/Mastercard/index.ts`

---

#### Truuth

| Aspect                 | Details                                                           |
| ---------------------- | ----------------------------------------------------------------- |
| **Integration Method** | Web Component SDK (NOT iframe)                                    |
| **SDK Source**         | `https://cdn.truuth.id/kyc/websdk/{version}/truuth-kyc-websdk.js` |
| **UI Type**            | `<truuth-kyc>` custom element                                     |

**Event Handling:**

```javascript
window.addEventListener('forwardedTruuthEventForOneSDK', eventListener);
```

**Event Types:**

-   `VERIFICATION_PROCESSING_STARTED` → loading + detection_complete + polling
-   `VERIFICATION_COMPLETED` → initProcess + results
-   `TERMS_AND_CONDITION_DECLINED` → input_required
-   `VERIFICATION_SUBMISSION_ABORTED` → input_required
-   `VERIFICATION_PROCESSING_TIMEOUT` → error
-   `ERROR` → detection_failed + error

**Files:**

-   `modules/idv/vendors/Truuth/index.ts`

---

#### OcrLabs

| Aspect                     | Details                                             |
| -------------------------- | --------------------------------------------------- |
| **IDV Integration**        | Server-side action workflow with persistent storage |
| **Biometrics Integration** | WebSocket + WebRTC (liveness detection)             |
| **WebSocket URL**          | `wss://engine.au.v3.liveness.idkit.io`              |

**IDV Flow:**

1. Check StorageClient for stored action details
2. Workflow states: `IDV_SUCCESS` or `IDV_ERROR`
3. Redirect to OcrLabs or start dummy flow

**Biometrics Liveness Detection:**

```
Face Detection → "Position your face within the frame"
     ↓
Smile Detection → "Please give us a BIG Smile!"
     ↓
detection_complete → initProcess
```

**UI Elements Created:**

-   `onesdk_idv_container` - Main container
-   `onesdk_idv_video` - Live video feed (mirrored)
-   `onesdk_idv_overlay` - Overlay container
-   `onesdk_idv_mask` - Oval face frame with shadow
-   `onesdk_idv_instructions` - Dynamic instructions

**Detection Timeout:** 7.5 seconds per action

**Files:**

-   `modules/idv/vendors/OcrLabs/`
-   `modules/biometrics/vendors/OcrLabs/`

---

#### IDVerse

| Aspect                 | Details                            |
| ---------------------- | ---------------------------------- |
| **Integration Method** | Web Component + Web Worker         |
| **SDK Source**         | ESM module from CDN                |
| **UI Type**            | `<idverse-sdk-ui>` custom element  |
| **Build ID**           | `build_01K7RRTN6RTH4S3MGHEBDDJ3FV` |

**Recognizer Types:**

-   `'id-scan'` - Document scanning with front/back capture
-   `'face-scan'` - Biometric face capture with liveness

**Web Worker:**

-   Cross-origin blob worker for image processing
-   Created via `URL.createObjectURL(blob)`
-   Manually terminated on SDK destroy

**Event Types:**

-   `authenticated` - SDK initialized
-   `ready` - Web component ready
-   `scanSuccess` - Scan completed successfully
-   `scanFail` - Scan failed (with error codes)
-   `validDocumentConfirmed` - User confirmed document
-   `fatalError` - Unrecoverable error
-   `faceScanClosed` - Face scan modal closed

**Retry Logic:**

-   Default max retries: 3 (configurable via recipe)
-   `scanFail` increments counter
-   SDK destroyed when max retries exceeded

**Asset Bundle (~2.2 MB):**

-   Main bundle: 935 KB
-   CSS: 113 KB
-   Lottie animations: 700+ KB (20 animations)
-   Audio feedback: 75+ KB
-   WorkSans font: 354 KB

**Files:**

-   `modules/common/vendors/idverse/index.ts`
-   `modules/ocr/vendors/IDVerse/index.ts`
-   `modules/biometrics/vendors/IDVerse/index.ts`

---

### OCR (Document Recognition)

OCR vendors share implementations with IDV where applicable. Key differences:

| Vendor  | Post-Process Action             | Review Screen            |
| ------- | ------------------------------- | ------------------------ |
| Onfido  | `initProcessAndEvaluateResults` | Feature flag controlled  |
| Sumsub  | `initProcess` (product='ocr')   | Built-in                 |
| Incode  | Native camera capture           | Validation callback      |
| Daon    | `initProcessAndEvaluateResults` | Optional form attachment |
| IDVerse | `initProcessAndEvaluateResults` | Feature flag controlled  |

---

### Biometrics

| Vendor      | Capture Method            | Liveness Detection                  |
| ----------- | ------------------------- | ----------------------------------- |
| **Onfido**  | SDK face step             | Motion, video, or standard variants |
| **Sumsub**  | SDK (product='idv')       | Built-in                            |
| **Incode**  | Native camera selfie      | SDK-managed                         |
| **Daon**    | Iframe with window events | Vendor-managed                      |
| **OcrLabs** | WebRTC + WebSocket        | Face + Smile detection              |
| **IDVerse** | Web Component face-scan   | DFA + face matching                 |

---

### Device Fingerprinting

#### Sardine

| Aspect                 | Details                                                           |
| ---------------------- | ----------------------------------------------------------------- |
| **Integration Method** | Remote SDK with context-based configuration                       |
| **SDK Source**         | `https://api.{domain}.sardine.ai/assets/loader.min.js`            |
| **Environments**       | `api.sardine.ai` (production), `api.sandbox.sardine.ai` (sandbox) |

**Configuration:**

```javascript
window._Sardine.createContext({
    clientId,
    sessionKey,
    environment: 'production' | 'sandbox',
    parentElement: document.body,
    flow: activityType,
    onDeviceResponse: (data) => {
        /* fingerprint data */
    },
});
```

**Activity Types:**

-   `REGISTRATION` → mapped to `SIGNUP`
-   `LOGIN`, `CRYPTO_DEPOSIT`, `CRYPTO_WITHDRAWAL`, `FIAT_DEPOSIT`, `FIAT_WITHDRAWAL`

**Events:**

-   `session_data_generated`
-   `device_characteristics_extracted`
-   `value_set`

**Files:**

-   `modules/device/vendors/Sardine.ts`

---

#### ThreatMetrix

| Aspect                 | Details                                        |
| ---------------------- | ---------------------------------------------- |
| **Integration Method** | Obfuscated SDK (intentionally hidden)          |
| **SDK Source**         | Inline vendor file `nolint_fp-clientlib-v4.js` |
| **Domain**             | `h.online-metrix.net`                          |

**Initialization:**

```javascript
threatmetrixSdk.profile('h.online-metrix.net', clientID, vendorSessionID);
```

**Technical Details:**

-   Uses RTCPeerConnection with TURN servers for data collection
-   ClientID must be exactly 8 characters
-   CSP nonce handling for Content Security Policy compliance

**Files:**

-   `modules/device/vendors/Threatmetrix/index.ts`
-   `modules/device/vendors/Threatmetrix/nolint_fp-clientlib-v4.js`

---

### Federation

#### Singpass (MyInfo)

| Aspect                 | Details                                          |
| ---------------------- | ------------------------------------------------ |
| **Integration Method** | OAuth 2.0 Authorization Code Flow                |
| **Protocol**           | Browser redirect to Singpass, then code exchange |

**Environment URLs:**

```
Sandbox:    https://sandbox.api.myinfo.gov.sg/com/v3/authorise
Test:       https://test.api.myinfo.gov.sg/com/v3/authorise
Production: https://api.myinfo.gov.sg/com/v3/authorise
```

**OAuth Flow:**

1. Build authorization URL with `client_id`, `attributes`, `state`, `redirect_uri`, `purpose`
2. Redirect to Singpass
3. User authenticates and consents
4. Redirect back with `code` and `state`
5. Validate state (CSRF protection)
6. Submit code to backend via `FederationClient.provideAuthorisation()`
7. Receive user data
8. User approves → `FederationClient.submitCachedData()`

**Events:**

-   `results` - User data with approve callback
-   `data_extracted_successfully` - After approval
-   `error` - On failures

**Validation:**

-   State parameter must match session ID
-   Production `redirect_uri` must use HTTPS
-   No preloaded entities allowed

**Files:**

-   `modules/federation/vendors/Singpass.ts`

---

### Form

#### Form Vendors Overview

| Vendor     | Technology              | Use Case                 |
| ---------- | ----------------------- | ------------------------ |
| **Formio** | Form.io SDK             | Cloud-based form builder |
| **React**  | Custom React components | Modern, flexible forms   |
| **Legacy** | Legacy UI widgets       | Backward compatibility   |

---

#### Formio

| Aspect               | Details                                       |
| -------------------- | --------------------------------------------- |
| **SDK Source**       | `https://cdn.form.io/formiojs/formio.full.js` |
| **CSS Dependencies** | Bootstrap 4.6.1, Formio CSS, FontAwesome 4.7  |

**Wrapping Modes (feature flag controlled):**

-   **Shadow DOM** (`'shadow'`): Isolated styles via `attachShadow({ mode: 'open' })`
-   **Iframe** (`'iframe'`): Complete DOM/CSS isolation
-   **Div** (`'div'`): Direct DOM insertion (default)

**Data Transformation:**

```
Individual Data ←→ Formio Schema
     │                    │
  name, DOB          basicPiiForm
  addresses          addressComponentForm
  documents          documents[]
```

**Document Type Mapping:**

-   `PASSPORT` → `pass_idNumber`, `pass_idExpiry`
-   `DRIVERS_LICENCE` → `dl_region`, `dl_idNumber`, `dl_cardNumber`
-   `NATIONAL_HEALTH_ID` → `medi_idNumber`, `medi_cardPosition`, `medi_cardColour`, `medi_idExpiry`

**Files:**

-   `modules/form/vendors/formio/`

---

#### React Form

| Aspect         | Details                                                                                   |
| -------------- | ----------------------------------------------------------------------------------------- |
| **Framework**  | React 18 + Redux Toolkit + React Hook Form                                                |
| **Components** | 60+ form components                                                                       |
| **Screens**    | Welcome, Consent, IdSelection, IdDetails, PersonalDetails, Review, DocumentUpload, Result |

**Key Hooks:**

-   `useEventContext` - Event hub context provider
-   `useConfigFactory` - Configuration merging
-   `useFormMapper` - Field component mapping
-   `useAddressSearch` - Google Maps autocomplete integration

**Field Configuration by Document Type:**

**Australian Drivers Licence (by state):**
| State | License Pattern | Card Pattern |
|-------|-----------------|--------------|
| NSW | 6-8 alphanumeric | 10 numeric |
| VIC | 1-10 numeric | 8 alphanumeric |
| QLD | 8-9 alphanumeric | 10 alphanumeric |
| SA | 6 alphanumeric | 9 alphanumeric |
| WA | 7 numeric | 8-10 alphanumeric |

**Validation Rules:**

```javascript
ValidationRulesForForm = {
    onlyAlphabets: /^[a-zA-Z]+(\s+[a-zA-Z]+)*$/,
    onlyNumbers: /^[0-9]+$/,
    alphaNumeric: /^[a-zA-Z0-9]+$/,
    emailPattern: /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/,
};
```

**Screen Events:**

-   `form:{screenId}:ready` - Screen mounted
-   `form:{screenId}:loaded` - Screen loaded
-   `form:{screenId}:failed` - Screen error
-   `form:{screenId}:success` - Screen completed

**Files:**

-   `modules/form/vendors/react/`

---

## Integration Method Reference

| Method                   | Vendors                        | Characteristics                               |
| ------------------------ | ------------------------------ | --------------------------------------------- |
| **Native SDK**           | Onfido, Incode                 | Full control, version management, CDN loading |
| **Iframe + PostMessage** | Daon, Mastercard, Incode (IDV) | Sandboxed, vendor-managed UI                  |
| **Web Component**        | IDVerse, Truuth                | Custom elements, event-based                  |
| **WebSocket + WebRTC**   | OcrLabs                        | Real-time liveness, canvas/video rendering    |
| **OAuth 2.0**            | Singpass                       | Server-side, browser redirect flow            |
| **Remote SDK**           | Sardine, Sumsub                | Dynamically loaded from vendor CDN            |
| **Obfuscated SDK**       | ThreatMetrix                   | Inline vendor code, hidden implementation     |
| **Form Builder**         | Formio                         | Cloud service SDK with schema                 |
| **React Components**     | React Form                     | Custom component library                      |

---

## Build & Development

### NPM Scripts

```bash
# Development
npm run dev:cli          # Go CLI development mode

# Build
npm run build:umd        # Webpack UMD bundle
npm run build:esm        # TypeScript ESM compilation
npm run build:css        # TailwindCSS generation
npm run build:cli        # Cross-platform Go binaries
npm run build:npm        # Full ESM+CSS+CLI build

# Testing
npm run test             # Jest test suite
```

### Build Outputs

| Output | Location                       | Description                   |
| ------ | ------------------------------ | ----------------------------- |
| UMD    | `dist/umd/oneSdk.umd.js`       | Browser global bundle         |
| ESM    | `dist/esm/modules/index.js`    | ES Module with types          |
| CSS    | `dist/esm/styles/f1-style.css` | TailwindCSS output            |
| CLI    | `dist/cli/{platform}/`         | Platform-specific Go binaries |

### CLI Platforms

-   `darwin-amd64` (macOS Intel)
-   `darwin-arm64` (macOS Apple Silicon)
-   `linux-amd64`
-   `linux-arm64`
-   `windows-amd64`

---

## Deployment

Deployments are managed via GitHub Actions using reusable workflows.

### Workflows

| Workflow             | Trigger                                 | Purpose                                                            |
| -------------------- | --------------------------------------- | ------------------------------------------------------------------ |
| `main.yml`           | Push to `main`                          | Full pipeline: CI → build → deploy (QA → UAT → prod) → NPM publish |
| `develop.yml`        | Push to `develop`                       | Development branch CI                                              |
| `feature_pr.yml`     | PR to `main`/`develop`                  | Feature branch CI                                                  |
| `publish_npm.yml`    | Called by `main.yml`                    | Publish production release to NPM                                  |
| `publish_npm_rc.yml` | Called by `main.yml` or manual dispatch | Publish RC (release candidate) to NPM                              |
| `rollback.yml`       | Manual dispatch                         | Deployment rollback                                                |

### Main Pipeline (`main.yml`)

```
ci → build → publish_artifact → bless-qa → publish-rc → deploy-qa → bless-uat → deploy-uat → bless-prod → deploy-prod → publish_npm
```

Each `bless-*` stage requires manual environment approval before proceeding.

### RC Publishing (`publish_npm_rc.yml`)

Publishes a release candidate to NPM under the `rc` dist-tag **before** QA deployment. This is required because the QA automated tests (`qe-ui-tests`) install the RC package to run against.

**How it works:**

1. Reads the base version from `package.json` (e.g. `1.9.13`)
2. Queries the NPM registry for existing RC versions matching that base
3. Computes the next RC version:
    - No existing RCs → `1.9.13-rc`
    - Existing `1.9.13-rc` → `1.9.13-rc.1`
    - Existing `1.9.13-rc.1` → `1.9.13-rc.2`
4. Sets the version, builds via `scripts/build-to-npm.sh`, and publishes with `npm publish --tag rc`

**Installing an RC version:**

```bash
npm install @frankieone/one-sdk@rc
```

**Manual trigger:** The workflow can also be dispatched manually from the Actions tab for ad-hoc RC publishes.

For detailed deployment instructions, see:

-   [Reusable CICD Github Actions Workflows](https://frankieone.atlassian.net/wiki/x/doCMZw)
-   [Rollback Frontend Deployment](https://frankieone.atlassian.net/wiki/x/FAGgZw)

---

## License

Proprietary - FrankieOne
