# Adding a New Library

This guide walks through the process of adding a new shared library to the Oshun
monorepo.

## Overview

Libraries in Oshun are reusable code packages that can be shared across
applications and other libraries. They live in the `libs/` directory and follow
a structured organization pattern.

## Library Categories

| Directory         | Purpose                      | Tags                              |
| ----------------- | ---------------------------- | --------------------------------- |
| `libs/shared/`    | Cross-domain utilities       | `scope:shared`                    |
| `libs/contracts/` | API contracts, events, types | `scope:shared`, `layer:contracts` |
| `libs/{domain}/`  | Domain-specific libraries    | `scope:{domain}`                  |

**Domains with apps and libraries**: iris, lilith, yemaya, isis, sophia, hathor,
bellona, tara, veritas, psyche, nyx, aja, aphrodite.

**Library-only domains**: aje, themis, galatea, shakti, nous, uzume, demeter,
euterpe, hestia, arete, kuanyin, meditation.

## Step-by-Step Guide

### Step 1: Plan Your Library

Before creating a library, consider:

1. **Purpose**: What problem does this library solve?
2. **Scope**: Which domains will use it?
3. **Dependencies**: What does it depend on?
4. **Consumers**: Who will import this library?

### Step 2: Choose the Right Location

```
libs/
├── shared/              # Cross-domain utilities (any domain can use)
│   ├── utils/           # Common utilities
│   ├── types/           # Shared TypeScript types
│   └── {your-lib}/      # ← Add here for shared utilities
│
├── contracts/           # Cross-domain contracts
│   ├── events/          # Event schemas
│   └── types/           # Shared types
│
└── {domain}/            # Domain-specific libraries
    ├── database/        # Domain database client
    ├── client/          # Domain API client
    └── {your-lib}/      # ← Add here for domain-specific code
```

### Step 3: Create the Library

#### Using Nx Generator (Recommended)

```bash
# Shared utility library
nx g @nx/js:library my-lib --directory=libs/shared/my-lib --importPath=@oshun/my-lib

# Domain-specific library
nx g @nx/js:library my-lib --directory=libs/isis/my-lib --importPath=@isis/my-lib
```

#### Manual Creation

Create the directory structure:

```
libs/shared/my-lib/
├── src/
│   ├── index.ts           # Public API exports
│   ├── my-lib.ts          # Main implementation
│   └── my-lib.spec.ts     # Tests
├── project.json           # Nx project configuration
├── package.json           # Package metadata
├── tsconfig.json          # TypeScript configuration
├── tsconfig.lib.json      # Build configuration
├── tsconfig.spec.json     # Test configuration
└── README.md              # Documentation
```

### Step 4: Configure project.json

Create `libs/shared/my-lib/project.json`:

```json
{
  "name": "@oshun/my-lib",
  "$schema": "../../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "libs/shared/my-lib/src",
  "projectType": "library",
  "tags": ["scope:shared", "type:lib", "layer:domain"],
  "targets": {
    "build": {
      "executor": "@nx/js:tsc",
      "outputs": ["{options.outputPath}"],
      "options": {
        "outputPath": "dist/libs/shared/my-lib",
        "main": "libs/shared/my-lib/src/index.ts",
        "tsConfig": "libs/shared/my-lib/tsconfig.lib.json",
        "assets": ["libs/shared/my-lib/*.md"]
      }
    },
    "test": {
      "executor": "@nx/vite:test",
      "options": {
        "config": "libs/shared/my-lib/vitest.config.ts"
      }
    },
    "lint": {
      "executor": "@nx/eslint:lint",
      "outputs": ["{options.outputFile}"]
    },
    "typecheck": {
      "executor": "nx:run-commands",
      "options": {
        "cwd": "libs/shared/my-lib",
        "command": "tsc --noEmit"
      }
    }
  }
}
```

### Step 5: Configure package.json

Create `libs/shared/my-lib/package.json`:

```json
{
  "name": "@oshun/my-lib",
  "version": "0.1.0",
  "description": "Description of your library",
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  },
  "files": ["dist", "README.md"],
  "scripts": {
    "build": "tsc -p tsconfig.lib.json",
    "test": "vitest run",
    "lint": "eslint src --ext ts",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    // Add your dependencies here
  },
  "devDependencies": {
    "typescript": "catalog:",
    "vitest": "catalog:"
  },
  "peerDependencies": {
    // Add peer dependencies if needed
  }
}
```

### Step 6: Configure TypeScript

Create `libs/shared/my-lib/tsconfig.json`:

```json
{
  "extends": "../../../tsconfig.base.json",
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "bundler",
    "declaration": true,
    "declarationMap": true,
    "types": ["vitest/globals"]
  },
  "files": [],
  "include": [],
  "references": [
    { "path": "./tsconfig.lib.json" },
    { "path": "./tsconfig.spec.json" }
  ]
}
```

Create `libs/shared/my-lib/tsconfig.lib.json`:

```json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./dist",
    "declaration": true,
    "declarationMap": true,
    "rootDir": "./src"
  },
  "include": ["src/**/*.ts"],
  "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]
}
```

Create `libs/shared/my-lib/tsconfig.spec.json`:

```json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./dist/test",
    "types": ["vitest/globals"]
  },
  "include": ["src/**/*.spec.ts", "src/**/*.test.ts"]
}
```

### Step 7: Configure Vitest

Create `libs/shared/my-lib/vitest.config.ts`:

```typescript
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    include: ['src/**/*.{test,spec}.ts'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
    },
  },
});
```

### Step 8: Implement Your Library

Create `libs/shared/my-lib/src/index.ts`:

```typescript
/**
 * @oshun/my-lib - Description of your library
 *
 * @packageDocumentation
 */

export { MyClass, MyFunction, MyType } from './my-lib';
```

Create `libs/shared/my-lib/src/my-lib.ts`:

```typescript
/**
 * Example interface
 */
export interface MyType {
  id: string;
  name: string;
  value: number;
}

/**
 * Example class
 */
export class MyClass {
  private data: MyType[];

  constructor() {
    this.data = [];
  }

  add(item: MyType): void {
    this.data.push(item);
  }

  get(id: string): MyType | undefined {
    return this.data.find((item) => item.id === id);
  }

  getAll(): MyType[] {
    return [...this.data];
  }
}

/**
 * Example function
 *
 * @param input - The input value
 * @returns The processed output
 */
export function MyFunction(input: string): string {
  return input.toUpperCase();
}
```

### Step 9: Write Tests

Create `libs/shared/my-lib/src/my-lib.spec.ts`:

```typescript
import { describe, it, expect, beforeEach } from 'vitest';
import { MyClass, MyFunction, MyType } from './my-lib';

describe('MyClass', () => {
  let instance: MyClass;

  beforeEach(() => {
    instance = new MyClass();
  });

  describe('add', () => {
    it('should add items', () => {
      const item: MyType = { id: '1', name: 'Test', value: 42 };
      instance.add(item);
      expect(instance.getAll()).toHaveLength(1);
    });
  });

  describe('get', () => {
    it('should retrieve items by id', () => {
      const item: MyType = { id: '1', name: 'Test', value: 42 };
      instance.add(item);
      expect(instance.get('1')).toEqual(item);
    });

    it('should return undefined for non-existent ids', () => {
      expect(instance.get('non-existent')).toBeUndefined();
    });
  });
});

describe('MyFunction', () => {
  it('should convert input to uppercase', () => {
    expect(MyFunction('hello')).toBe('HELLO');
  });

  it('should handle empty strings', () => {
    expect(MyFunction('')).toBe('');
  });
});
```

### Step 10: Add to Path Mappings

Update `tsconfig.base.json` at the repository root:

```json
{
  "compilerOptions": {
    "paths": {
      "@oshun/my-lib": ["libs/shared/my-lib/src/index.ts"]
      // ... other paths
    }
  }
}
```

### Step 11: Create README

Create `libs/shared/my-lib/README.md`:

```markdown
# @oshun/my-lib

Description of your library.

## Installation

This library is part of the Oshun monorepo and is available as a workspace
dependency.

\`\`\`bash pnpm add @oshun/my-lib \`\`\`

## Usage

\`\`\`typescript import { MyClass, MyFunction } from '@oshun/my-lib';

const instance = new MyClass(); instance.add({ id: '1', name: 'Example', value:
100 });

const result = MyFunction('hello'); \`\`\`

## API

### MyClass

- `add(item: MyType): void` - Add an item
- `get(id: string): MyType | undefined` - Get item by ID
- `getAll(): MyType[]` - Get all items

### MyFunction

- `MyFunction(input: string): string` - Convert input to uppercase
```

### Step 12: Verify the Library

```bash
# Build the library
nx build @oshun/my-lib

# Run tests
nx test @oshun/my-lib

# Run linting
nx lint @oshun/my-lib

# Type check
nx typecheck @oshun/my-lib

# View in dependency graph
nx graph
```

## Best Practices

### 1. Single Responsibility

Each library should have a clear, focused purpose:

```
✅ @oshun/retry      # Just retry logic
✅ @oshun/cache      # Just caching
✅ @oshun/validation # Just validation

❌ @oshun/utils      # Too broad - split into specific utilities
```

### 2. Explicit Exports

Only export what consumers need:

```typescript
// index.ts

// ✅ Explicit exports
export { PublicClass } from './public-class';
export type { PublicType } from './types';

// ❌ Don't re-export internal utilities
// export * from './internal-helpers';
```

### 3. Proper Dependency Management

Distinguish between dependency types:

```json
{
  "dependencies": {
    // Runtime dependencies required by consumers
    "zod": "catalog:"
  },
  "devDependencies": {
    // Only needed during development/testing
    "vitest": "catalog:"
  },
  "peerDependencies": {
    // Must be provided by consumer
    "typescript": "catalog:"
  }
}
```

### 4. Use Appropriate Tags

Apply the correct tags for module boundaries:

| Situation                      | Tags                                          |
| ------------------------------ | --------------------------------------------- |
| Shared utility for all domains | `scope:shared`, `type:lib`                    |
| Domain-specific business logic | `scope:{domain}`, `type:lib`, `layer:domain`  |
| Database access layer          | `scope:{domain}`, `type:lib`, `layer:data`    |
| API contracts                  | `scope:shared`, `type:lib`, `layer:contracts` |

### 5. Write Comprehensive Tests

Aim for high test coverage:

```typescript
// Test public API thoroughly
describe('PublicFunction', () => {
  // Happy path
  it('should handle valid input', () => {});

  // Edge cases
  it('should handle empty input', () => {});
  it('should handle null/undefined', () => {});

  // Error cases
  it('should throw on invalid input', () => {});
});
```

### 6. Document Your API

Use JSDoc comments for documentation:

````typescript
/**
 * Performs an operation with retry logic.
 *
 * @param operation - The async operation to retry
 * @param options - Retry configuration options
 * @returns The result of the operation
 * @throws {RetryError} When all retry attempts fail
 *
 * @example
 * ```typescript
 * const result = await retry(
 *   () => fetchData(),
 *   { maxRetries: 3, delay: 1000 }
 * );
 * ```
 */
export async function retry<T>(
  operation: () => Promise<T>,
  options: RetryOptions
): Promise<T> {
  // ...
}
````

## Troubleshooting

### Library not found when importing

1. Check path mappings in `tsconfig.base.json`
2. Run `nx reset` to clear cache
3. Restart your IDE's TypeScript server

### Circular dependency detected

1. Use `nx graph` to visualize dependencies
2. Extract shared code to a new library
3. Use dependency injection to break cycles

### Build fails with type errors

1. Ensure `tsconfig.lib.json` excludes test files
2. Check that all types are properly exported
3. Verify peer dependencies are installed

## Checklist

Before submitting your PR:

- [ ] Library compiles without errors
- [ ] All tests pass
- [ ] Linting passes
- [ ] TypeScript types are correct
- [ ] README is complete
- [ ] Public API is documented
- [ ] Added to path mappings in `tsconfig.base.json`
- [ ] Tags are set correctly in `project.json`
