# Migration Guide — Adopting `@bytelyst/*` Packages

Step-by-step guide for integrating `@bytelyst/*` shared packages into a new product or dashboard.

---

## Prerequisites

1. Clone both repos side-by-side:

   ```
   code/
   ├── learning_ai_common_plat/   # shared packages + services
   └── your-product/              # your product repo
   ```

2. Build all packages:
   ```bash
   cd learning_ai_common_plat
   pnpm install
   pnpm build
   ```

---

## Step 1: Add Package Dependencies

In your product's `package.json`, add `file:` references to the packages you need:

```json
{
  "dependencies": {
    "@bytelyst/errors": "file:../learning_ai_common_plat/packages/errors",
    "@bytelyst/cosmos": "file:../learning_ai_common_plat/packages/cosmos",
    "@bytelyst/config": "file:../learning_ai_common_plat/packages/config",
    "@bytelyst/auth": "file:../learning_ai_common_plat/packages/auth",
    "@bytelyst/api-client": "file:../learning_ai_common_plat/packages/api-client",
    "@bytelyst/logger": "file:../learning_ai_common_plat/packages/logger"
  }
}
```

Adjust the `../` path depth based on your directory structure. Then run `npm install`.

> **Important:** Run `pnpm build` in `learning_ai_common_plat` before `npm install` in your product. The `file:` refs need compiled `dist/` directories.

---

## Step 2: Wire Up Cosmos DB

Create `src/lib/cosmos.ts`:

```typescript
import {
  getCosmosClient,
  getDatabase,
  registerContainers,
  getRegisteredContainer,
} from '@bytelyst/cosmos';

// Define your containers
const CONTAINERS = [
  'users',
  'settings',
  'audit_log',
  // ... your containers
];

let initialized = false;

export async function initializeAllContainers() {
  if (initialized) return;
  await registerContainers(CONTAINERS);
  initialized = true;
}

export function getContainer(name: string) {
  return getRegisteredContainer(name);
}

export { getCosmosClient, getDatabase, initializeAllContainers };
```

Required env vars: `COSMOS_ENDPOINT`, `COSMOS_KEY`, `COSMOS_DATABASE`

---

## Step 3: Wire Up Auth (Server-Side)

Create `src/lib/auth-server.ts`:

```typescript
import { createJwtUtils, hashPassword, verifyPassword } from '@bytelyst/auth';

const jwt = createJwtUtils({
  issuer: 'your-product-name',
  accessTokenExpiry: '1h',
  refreshTokenExpiry: '30d',
});

export { jwt, hashPassword, verifyPassword };
```

Required env var: `JWT_SECRET` (min 32 chars)

---

## Step 4: Wire Up Product Identity

Create `src/lib/product-config.ts`:

```typescript
import { loadProductIdentity } from '@bytelyst/config';

const identity = loadProductIdentity();
export const PRODUCT_ID = identity.productId;
export const PRODUCT_NAME = identity.productName;
```

Required env vars: `DEFAULT_PRODUCT_ID` (or set in `shared/product.json`)

> **Rule:** Every Cosmos document MUST include a `productId` field using this value.

---

## Step 5: Wire Up Service Clients (If Using Microservices)

Create `src/lib/billing-client.ts` (example):

```typescript
import { createApiClient } from '@bytelyst/api-client';

const billingApi = createApiClient({
  baseUrl: process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003',
  getToken: () => {
    // Return the current user's JWT token
    return localStorage.getItem('access_token');
  },
});

export async function getSubscription(userId: string) {
  return billingApi.fetch(`/api/subscriptions/${userId}`);
}
```

---

## Step 6: Wire Up Error Handling

Import typed errors for consistent HTTP error responses:

```typescript
import {
  BadRequestError,
  UnauthorizedError,
  ForbiddenError,
  NotFoundError,
  ConflictError,
} from '@bytelyst/errors';

// In your API routes:
if (!user) throw new NotFoundError('User not found');
if (!isAdmin) throw new ForbiddenError('Admin access required');
```

---

## Step 7: Wire Up Auth Context (React Dashboards)

For admin-style dashboards, use the factory:

```typescript
import { createAuthProvider } from '@bytelyst/react-auth';

interface MyUser {
  id: string;
  email: string;
  role: string;
}

const { AuthProvider, useAuth } = createAuthProvider<MyUser>({
  storagePrefix: 'myapp',
  onLoginFallback: async (email, password) => {
    const res = await fetch('/api/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password }),
    });
    if (!res.ok) return null;
    const data = await res.json();
    return { user: data.user, token: data.accessToken };
  },
});

export { AuthProvider, useAuth };
```

> **Note:** If your auth flow needs SSO cookies, registration, or session restore via API calls, keep a custom auth context instead.

---

## Docker Builds

### Fastify Services (in `learning_ai_common_plat`)

Services use pnpm monorepo builds with repo root as context. See any service's `Dockerfile` for the pattern:

- Multi-stage: `pnpm install` → build packages + service → `pnpm deploy`
- `docker-compose.yml` sets `context: .` with `dockerfile: services/<name>/Dockerfile`

### Next.js Dashboards (in your product repo)

Dashboards need pre-built packages copied into the build context:

1. Run `./scripts/docker-prep-dashboards.sh` to copy `@bytelyst/*` into `.docker-deps/`
2. Dockerfile copies `.docker-deps/@bytelyst/` to `/learning_ai_common_plat/packages/` so `file:` refs resolve

---

## Checklist

- [ ] `pnpm build` in `learning_ai_common_plat` runs clean
- [ ] `npm install` in your product resolves all `@bytelyst/*` deps
- [ ] `tsc --noEmit` passes in your product
- [ ] Every Cosmos document includes `productId` field
- [ ] `JWT_SECRET` is set and shared across all services
- [ ] No `console.log` in production code (use `req.log` or `@bytelyst/logger`)
- [ ] Commit: `feat(integration): wire @bytelyst/* shared packages`
