npm package: @jez.luka/iver-sdk
Description: iVer SDK - Identity verification SDK for showcasing real world SDK architecture and integration.
Latest version: 0.1.9

# IVer SDK

Identity verification SDK built with StencilJS for framework-agnostic web component integration.

## Why StencilJS?

The goal was framework-agnostic components as close to vanilla JS as possible. While Svelte 5's minimal build size was attractive, it's designed for apps - using it would require wrapper containers and custom constructors around Svelte's rendering functions.

Web Components provide native browser support with excellent stability. After evaluating Lit and StencilJS, I chose StencilJS for superior DX and faster implementation within project constraints.

### Key advantages:
- **Tree shaking support:** StencilJS generates optimized, individually importable component definitions. Consumers only bundle what they use.
- **TypeScript-first:** Native TypeScript support with auto-generated type definitions `(components.d.ts)`
- **Build optimizations:** Lazy loading, code splitting, and minimal runtime overhead `(~5KB per component)`
- **Framework agnostic:** with minimal setup, works seamlessly with React

## Architecture

**Challenge**: Enable `getIdentityData()` to aggregate data from all 3 components without tight coupling.

**Solution**: Controller pattern with component registry instead of shared store. `IVerComponentController`

- Each component registers on init, providing its ID and `getData()` reference
- Controller instance passed from outside, giving consumers manual lifecycle control (inspired by React Hook Form)
- Handles multi-step forms where components mount/unmount frequently
- Components auto-unregister on unmount, but manual control available to preserve data flow across form steps

### Shadow DOM vs Light DOM Decision

### Why Light DOM?

This SDK uses Light DOM (shadow: false) rather than Shadow DOM encapsulation. This was a deliberate architectural choice:
Rationale:

- **Styling flexibility:** Host applications can apply their design system (Tailwind, CSS modules, styled-components) directly to SDK components without piercing shadow boundaries
- **Global styles integration:** Components inherit CSS custom properties and theming variables from the parent application
- **CSS framework compatibility:** Works seamlessly with utility-first frameworks like Tailwind without requiring custom element targeting
- **Simpler integration:** No need for ::part() selectors or CSS custom properties workarounds

### Trade-offs accepted:

- **No style isolation:** Host styles can potentially conflict with component internals (mitigated by BEM naming and scoped classes)
- **DOM structure exposure:** Component markup is accessible to parent selectors (acceptable for this use case where restyling is a feature, not a bug)

### Data Aggregation Strategy
The `getIdentityData()` method uses a probabilistic verification scoring approach rather than deterministic state management.

**Design rationale:**

**Probabilistic (current) vs Adaptive Deterministic:**
- **Probabilistic**: Each method invocation will calculate failure/success probability at submission time. I was thinking about this being mock for actual API. This approach doesn't have state tracking overhead.
- **Adaptive Deterministic** (not implemented): Would maintain real-time validation state, track failure rate and adapt to it. More complex, requires state management and thorough testing.

**Why probabilistic?**
- **Simpler mental model**: Verification happens when needed (submission), not continuously
- **Lower runtime cost**: No state management and runtime complex calculations

**Trade-off**: No actual 30/70 percent split on small sample. From 100 times, this could theoretically fail 50% of the times.

## Project Structure
```
src/
├── components/                     # SDK Components
│   ├── address-form/
│   │   ├── components/             # Sub-components
│   │   ├── test/                   # Component tests
│   │   ├── types/                  # TypeScript type definitions
│   │   ├── address-form.configs.ts # Constants and config factory fns
│   │   ├── address-form.css
│   │   ├── address-form.helpers.ts # Business or component specific logic
│   │   └── address-form.tsx        # entry file for SDK component
│   │
│   ├── phone-input/    # Phone number input component
│   │
│   └── selfie-capture/ # Selfie capture component
│
├── configs/            # Global configuration files
│   └── countries.json  # Country data
│
├── global/             # Global styles and utilities
│   └── global.css      # Application-wide styles
│
├── pages/              # Page components
│   ├── address-form.html
│   ├── phone.html
│   └── selfie.html
│
└── sdk/                # SDK utilities and types
    ├── types/          # Shared TypeScript definitions
    │   └── sdk.types.ts
    ├── iver-component-controller.ts
    ├── components.d.ts # Auto-generated component types
    ├── global.ts       # Global SDK configuration
    └── index.html      # SDK demo/test page
```

## Installation

```bash
npm i @jez.luka/iver-sdk
```

## Setup

### 1. TypeScript definitions (`global.d.ts`)

```typescript
import { IVerComponentController } from '@jez.luka/iver-sdk'

type SDKComponent = React.DetailedHTMLProps
  React.HTMLAttributes<HTMLElement> & { controller: IVerComponentController }, 
  HTMLElement
>

declare module 'react' {
  namespace JSX {
    interface IntrinsicElements {
      'address-form': SDKComponent
      'phone-input': SDKComponent
      'selfie-capture': SDKComponent
    }
  }
}

export {}
```

### 2. Register components (`iver-sdk.ts`)

```typescript
import { defineCustomElement as defineAddressForm } from '@jez.luka/iver-sdk/address-form'
import { defineCustomElement as definePhoneInput } from '@jez.luka/iver-sdk/phone-input'
import { defineCustomElement as defineSelfieCapture } from '@jez.luka/iver-sdk/selfie-capture'
import { IVerComponentController } from '@jez.luka/iver-sdk'

defineSelfieCapture()
definePhoneInput()
defineAddressForm()

export const iVerGlobalController = new IVerComponentController('global-controller')
```

### 3. Import in `main.tsx`

```typescript
import './toolkit/iver-sdk/iver-sdk.ts'
```

## Usage

### Component usage (no imports needed)

```tsx
<div className="flex flex-col mx-auto min-h-screen w-full gap-5">
  <selfie-capture controller={iVerGlobalController} />
  
  <div className="flex flex-col flex-1 gap-4 max-w-[350px] mx-auto w-full">
    <phone-input controller={iVerGlobalController} />
    <address-form controller={iVerGlobalController} />
    
    <Button className="mt-4" onClick={handleSubmit}>Submit</Button>
  </div>
</div>
```

### Controller usage

```typescript
import { iVerGlobalController } from '@/toolkit/iver-sdk/iver-sdk.ts'

const data = iVerGlobalController.getIdentityData<VerificationResult>()
```

## Development

```bash
pnpm install
pnpm start
pnpm generate // to generate new SDK component
```