# React Native / TypeScript Client SDK — Gap Analysis & Extraction Plan

> **Scope:** `@bytelyst/*` client-side packages for React Native (Expo) and web PWAs
> **Location:** `learning_ai_common_plat/packages/`
> **Status:** ✅ Complete — All 6 packages extracted, NomGap fully migrated, 44 tests passing
>
> **Completion Date:** 2026-03-02

## 1. Executive Summary

NomGap (React Native / Expo) is the only non-native mobile app in the ByteLyst ecosystem. It now uses **8 shared packages** from common-plat (`auth-client`, `telemetry-client`, `feature-flag-client`, `kill-switch-client`, `offline-queue`, `platform-client`, `blob-client`). These same TypeScript client packages also benefit **ChronoMind web** (Next.js PWA), **MindLyst web**, and any future web or React Native products.

---

## 2. Current State

### 2.1 Packages That Exist

| Package                      | Location                     | Consumers                                      | LOC | Status        |
| ---------------------------- | ---------------------------- | ---------------------------------------------- | --- | ------------- |
| `@bytelyst/auth-client`      | `packages/auth-client/`      | NomGap, ChronoMind web, MindLyst web           | 266 | ✅ Production |
| `@bytelyst/telemetry-client` | `packages/telemetry-client/` | NomGap, ChronoMind web, LysnrAI user-dashboard | 237 | ✅ Production |

**Both packages are:**

- Pure TypeScript (no Node.js deps, no React Native deps)
- Browser + React Native safe (use `globalThis.fetch`)
- Factory pattern (`createAuthClient()`, `createTelemetryClient()`)
- Configurable storage adapters (localStorage, MMKV, or custom)

### 2.2 NomGap Integration (Reference Implementation)

NomGap demonstrates the correct pattern for consuming shared packages:

```json
// package.json
{
  "dependencies": {
    "@bytelyst/auth-client": "file:../learning_ai_common_plat/packages/auth-client",
    "@bytelyst/telemetry-client": "file:../learning_ai_common_plat/packages/telemetry-client"
  }
}
```

**Auth wiring** (`src/api/auth-api.ts` — 73 LOC):

- Creates `AuthClient` via factory with MMKV storage adapter
- Re-exports convenience wrappers (`loginUser`, `registerUser`, etc.)

**Telemetry wiring** (`src/lib/telemetry.ts` — 95 LOC):

- Creates `TelemetryClient` via factory with MMKV storage adapter
- Re-exports domain-specific helpers (`trackFastingEvent`, `trackScreenView`)

### 2.3 Hand-Rolled Modules in NomGap (Need Extraction)

| Module            | File                       | LOC | Should Be                                                                        |
| ----------------- | -------------------------- | --- | -------------------------------------------------------------------------------- |
| **Feature flags** | `src/lib/feature-flags.ts` | 72  | `@bytelyst/feature-flag-client`                                                  |
| **Offline queue** | `src/lib/offline-queue.ts` | 124 | `@bytelyst/offline-queue`                                                        |
| **API client**    | `src/api/client.ts`        | 116 | Already have `@bytelyst/api-client` (server-side); need RN-safe variant or merge |
| **API config**    | `src/api/config.ts`        | 18  | Stays app-specific (reads Expo env vars)                                         |
| **Kill switch**   | —                          | 0   | `@bytelyst/kill-switch-client` ✅ implemented                                    |
| **Blob upload**   | —                          | 0   | `@bytelyst/blob-client` ✅ implemented                                           |
| **License**       | —                          | 0   | `@bytelyst/license-client` (not yet implemented)                                 |
| **Biometric**     | —                          | 0   | Uses `expo-local-authentication` directly (no shared pkg needed)                 |

---

## 3. Proposed New Packages

### 3.1 Package Matrix

| #   | Package                         | Description                                     | Est. LOC | Priority | Consumers                                            |
| --- | ------------------------------- | ----------------------------------------------- | -------- | -------- | ---------------------------------------------------- |
| 1   | `@bytelyst/feature-flag-client` | Flag polling + caching (browser + RN safe)      | ~120     | P0       | NomGap, ChronoMind web, MindLyst web                 |
| 2   | `@bytelyst/kill-switch-client`  | Kill switch check (fail-open)                   | ~60      | P0       | NomGap, all web apps                                 |
| 3   | `@bytelyst/offline-queue`       | Persistent retry queue (configurable storage)   | ~150     | P1       | NomGap, any offline-capable app                      |
| 4   | `@bytelyst/blob-client`         | SAS token + upload to Azure Blob                | ~120     | P1       | NomGap (audio), future web uploads                   |
| 5   | `@bytelyst/license-client`      | License activation + status                     | ~100     | P2       | LysnrAI web (if needed)                              |
| 6   | `@bytelyst/platform-client`     | Generic typed fetch wrapper (browser + RN safe) | ~130     | P0       | All consumer apps (replaces hand-rolled API clients) |

### 3.2 Existing Package — Needs Clarification

`@bytelyst/api-client` already exists in `packages/api-client/` but is designed for **server-to-server** (Node.js) communication (dashboard → platform-service). It uses Node.js-specific patterns.

**Decision needed:**

- **Option A:** Extend `@bytelyst/api-client` to be browser + RN safe (add `globalThis.fetch` path)
- **Option B:** Create separate `@bytelyst/platform-client` for client-side use ← **Recommended** (different auth model: bearer token from storage vs. httpOnly cookies)

---

## 4. Package Designs

### 4.1 `@bytelyst/feature-flag-client`

Extract from NomGap's `src/lib/feature-flags.ts`:

```typescript
// packages/feature-flag-client/src/index.ts
export interface FeatureFlagClientConfig {
  baseUrl: string;
  productId: string;
  platform: string;
  pollIntervalMs?: number; // default 5 min
  storage?: { getItem(k: string): string | null; setItem(k: string, v: string): void };
}

export interface FeatureFlagClient {
  init(params?: { userId?: string }): Promise<void>;
  isEnabled(key: string): boolean;
  getAllFlags(): Readonly<Record<string, boolean>>;
  refresh(): Promise<void>;
  stop(): void;
}

export function createFeatureFlagClient(config: FeatureFlagClientConfig): FeatureFlagClient;
```

**Key improvements over NomGap's hand-rolled version:**

- Optional persistent cache via storage adapter (survives app restart)
- Configurable poll interval
- Product ID from config (not hardcoded)

### 4.2 `@bytelyst/kill-switch-client`

```typescript
// packages/kill-switch-client/src/index.ts
export interface KillSwitchClientConfig {
  baseUrl: string;
  productId: string;
}

export interface KillSwitchResult {
  disabled: boolean;
  message: string | null;
}

export function createKillSwitchClient(config: KillSwitchClientConfig): {
  check(): Promise<KillSwitchResult>;
};
```

**Behavior:** Fail-open (returns `{ disabled: false }` on network error).

### 4.3 `@bytelyst/offline-queue`

Extract from NomGap's `src/lib/offline-queue.ts`:

```typescript
// packages/offline-queue/src/index.ts
export interface QueueStorage {
  getItem(key: string): string | null;
  setItem(key: string, value: string): void;
}

export interface OfflineQueueConfig {
  storageKey: string;
  storage: QueueStorage;
  maxRetries?: number; // default 5
  maxQueueSize?: number; // default 50
}

export interface OfflineQueue {
  enqueue(item: {
    id: string;
    action: string;
    path: string;
    payload: Record<string, unknown>;
  }): void;
  flush(
    executor: (action: string, path: string, payload: Record<string, unknown>) => Promise<void>
  ): Promise<{ flushed: number; failed: number }>;
  length(): number;
  clear(): void;
}

export function createOfflineQueue(config: OfflineQueueConfig): OfflineQueue;
```

**Key improvements:**

- Storage adapter (not hardcoded to MMKV)
- Configurable max retries and queue size
- Product-agnostic (storageKey includes product prefix)

### 4.4 `@bytelyst/blob-client`

```typescript
// packages/blob-client/src/index.ts
export interface BlobClientConfig {
  baseUrl: string;
  productId: string;
  getAccessToken: () => string | null;
}

export interface BlobClient {
  upload(
    data: Blob | ArrayBuffer,
    container: string,
    fileName: string,
    contentType: string
  ): Promise<string | null>;
  uploadAudio(data: Blob | ArrayBuffer, fileName: string): Promise<string | null>;
}

export function createBlobClient(config: BlobClientConfig): BlobClient;
```

**Flow:** Get SAS token from platform-service → Upload directly to Azure Blob → Return blob URL.

---

## 5. Migration Plan

### Phase 1: Core Extraction (Sprint 1 — 2 days)

| Task                                                                     | Effort |
| ------------------------------------------------------------------------ | ------ |
| Create `packages/feature-flag-client/` with factory pattern              | 3h     |
| Create `packages/kill-switch-client/` with fail-open behavior            | 1.5h   |
| Create `packages/platform-client/` (generic typed fetch for client-side) | 3h     |
| Write tests for all 3 packages (Vitest)                                  | 3h     |
| `pnpm build` — verify all packages compile                               | 0.5h   |

### Phase 2: NomGap Migration (Sprint 1 — 1 day)

| Task                                                                 | Effort |
| -------------------------------------------------------------------- | ------ |
| Add `@bytelyst/feature-flag-client` to NomGap `package.json`         | 0.5h   |
| Replace `src/lib/feature-flags.ts` with thin wrapper (~20 LOC)       | 1h     |
| Add `@bytelyst/kill-switch-client` + implement kill switch check     | 1h     |
| Replace `src/api/client.ts` with `@bytelyst/platform-client` wrapper | 1.5h   |
| Run NomGap tests (`npm test`) — verify 283 tests pass                | 0.5h   |

### Phase 3: Web App Migration (Sprint 2 — 1 day)

| Task                                                               | Effort |
| ------------------------------------------------------------------ | ------ |
| Replace ChronoMind web `src/lib/feature-flags.ts` (if hand-rolled) | 1h     |
| Add kill switch check to ChronoMind web, MindLyst web              | 1.5h   |
| Verify all web builds pass (`npm run typecheck && npm run build`)  | 1h     |

### Phase 4: Extended Packages (Sprint 2 — 2 days)

| Task                                                     | Effort |
| -------------------------------------------------------- | ------ |
| Create `packages/offline-queue/` (extract from NomGap)   | 3h     |
| Create `packages/blob-client/` (client-side blob upload) | 3h     |
| Migrate NomGap `src/lib/offline-queue.ts` to package     | 1h     |
| Write tests for both packages                            | 3h     |

---

## 6. Updated Package Inventory (After Completion)

### Client-Side Packages (Browser + React Native Safe)

| Package                         | Status    | Used By                                        |
| ------------------------------- | --------- | ---------------------------------------------- |
| `@bytelyst/auth-client`         | ✅ Exists | NomGap, ChronoMind web, MindLyst web           |
| `@bytelyst/telemetry-client`    | ✅ Exists | NomGap, ChronoMind web, LysnrAI user-dashboard |
| `@bytelyst/feature-flag-client` | ✅ Done   | NomGap, all web apps                           |
| `@bytelyst/kill-switch-client`  | ✅ Done   | NomGap, all web apps                           |
| `@bytelyst/platform-client`     | ✅ Done   | NomGap, ChronoMind web                         |
| `@bytelyst/offline-queue`       | ✅ Done   | NomGap                                         |
| `@bytelyst/blob-client`         | ✅ Done   | NomGap, future web uploads                     |
| `@bytelyst/license-client`      | 🔲 Future | LysnrAI web (if needed)                        |

### Server-Side Packages (Node.js / Fastify)

These already exist and are not affected:

| Package                   | Purpose                                                       |
| ------------------------- | ------------------------------------------------------------- |
| `@bytelyst/api-client`    | Server-to-server fetch wrapper (dashboard → platform-service) |
| `@bytelyst/auth`          | JWT issuance + password hashing (server-side)                 |
| `@bytelyst/cosmos`        | Azure Cosmos DB client singleton                              |
| `@bytelyst/config`        | Env loader + product identity                                 |
| `@bytelyst/errors`        | Typed HTTP errors                                             |
| `@bytelyst/fastify-core`  | Fastify app factory                                           |
| `@bytelyst/logger`        | Pino-based structured logging                                 |
| `@bytelyst/events`        | In-memory event bus                                           |
| `@bytelyst/blob`          | Server-side Azure Blob client                                 |
| `@bytelyst/extraction`    | Extraction service client                                     |
| `@bytelyst/testing`       | Test mocks + helpers                                          |
| `@bytelyst/monitoring`    | Health check utilities                                        |
| `@bytelyst/react-auth`    | React auth context factory                                    |
| `@bytelyst/design-tokens` | Cross-platform token generator                                |

---

## 7. Conventions for Client-Side Packages

### MUST follow

1. **Zero Node.js dependencies** — use `globalThis.fetch`, not `node-fetch`
2. **Zero React dependencies** — pure TypeScript (consumable by any framework)
3. **Zero React Native dependencies** — no `react-native-*` imports
4. **Factory pattern** — `createXxxClient(config)` returns an interface
5. **Configurable storage** — accept `{ getItem, setItem }` adapter (works with localStorage, MMKV, AsyncStorage)
6. **ESM + CJS dual export** — `"type": "module"` with `exports` field
7. **Vitest for tests** — `passWithNoTests: true` at root
8. **`pnpm build` must pass** before consumer `npm install`
9. **No `console.log`** — silent by default; errors returned, not thrown (for telemetry/flags)

### MUST NOT do

1. Never import from `react-native` or `expo-*`
2. Never assume `window` exists (use `globalThis`)
3. Never assume `navigator.sendBeacon` exists (feature-detect)
4. Never throw on network errors in fire-and-forget clients (telemetry, flags, kill switch)
5. Never hardcode product IDs — always from config

---

## 8. EAS / Docker Build Considerations

NomGap uses `file:` refs to `@bytelyst/*` packages. These break in EAS Build and Docker where the sibling repo isn't available.

**Existing workaround:** `scripts/docker-prep.sh` in each app repo:

1. `cd ../learning_ai_common_plat && pnpm build`
2. `pnpm pack` each consumed package into `.tgz`
3. Rewrite `package.json` `file:` refs to `file:./tarballs/xxx.tgz`
4. Build proceeds self-contained
5. `./scripts/docker-prep.sh --restore` undoes changes

**Action:** When adding new `@bytelyst/*` packages to NomGap, update `scripts/docker-prep.sh` to include them.

---

## 9. Timeline Summary

| Sprint    | Duration   | Deliverable                                                                       |
| --------- | ---------- | --------------------------------------------------------------------------------- |
| Sprint 1  | 3 days     | 3 new packages + NomGap migrated                                                  |
| Sprint 2  | 3 days     | 2 more packages + web apps migrated                                               |
| **Total** | **6 days** | **8 client-side packages (2 existing + 6 new), NomGap + web apps on shared SDKs** |

---

## 10. Cross-Platform SDK Comparison

| Component       | Swift SDK             | Kotlin SDK (Proposed) | TS Client Packages                  |
| --------------- | --------------------- | --------------------- | ----------------------------------- |
| Platform config | `BLPlatformConfig`    | `BLPlatformConfig`    | Config per app (`api/config.ts`)    |
| HTTP client     | `BLPlatformClient`    | `BLPlatformClient`    | `@bytelyst/platform-client`         |
| Secure storage  | `BLKeychain`          | `BLSecureStore`       | Storage adapter pattern             |
| Auth            | `BLAuthClient`        | `BLAuthClient`        | `@bytelyst/auth-client` ✅          |
| Telemetry       | `BLTelemetryClient`   | `BLTelemetryClient`   | `@bytelyst/telemetry-client` ✅     |
| Feature flags   | `BLFeatureFlagClient` | `BLFeatureFlagClient` | `@bytelyst/feature-flag-client` ✅  |
| Sync engine     | `BLSyncEngine`        | `BLSyncEngine`        | `@bytelyst/offline-queue` ✅        |
| Blob upload     | `BLBlobClient`        | `BLBlobClient`        | `@bytelyst/blob-client` ✅          |
| Kill switch     | `BLKillSwitchClient`  | `BLKillSwitchClient`  | `@bytelyst/kill-switch-client` ✅   |
| License         | `BLLicenseClient`     | `BLLicenseClient`     | `@bytelyst/license-client` (future) |
| Biometric       | `BLBiometricAuth`     | `BLBiometricAuth`     | expo-local-authentication (native)  |
| Crash report    | `BLCrashReporter`     | `BLCrashReporter`     | Sentry/Expo Updates (native)        |
| Audit log       | `BLAuditLogger`       | `BLAuditLogger`       | Not needed (telemetry covers it)    |
