# Column Types

Locality IDB supports a wide range of column types. These are defined using the chainable `column` builder.

| Type | Description | Example |
| :--- | :--- | :--- |
| `number()` / `float()` | Numeric values (integer or float) | `column.number()` |
| `int()` | Numeric values (only integer is allowed) | `column.int()` |
| `numeric()` | Number or numeric string | `column.numeric()` |
| `bigint()` | Large integers | `column.bigint()` |
| `text()` / `string()` | Text strings | `column.text()` |
| `char(length?)` | Fixed-length string | `column.char(10)` |
| `varchar(length?)` | Variable-length string | `column.varchar(255)` |
| `email()` | Email strings | `column.email()` |
| `url()` | URL strings | `column.url()` |
| `bool()` / `boolean()` | Boolean values | `column.bool()` |
| `date()` | Date objects | `column.date()` |
| `timestamp()` | ISO 8601 timestamps (auto-generated) | `column.timestamp()` |
| `uuid()` | UUID strings (auto-generated v4) | `column.uuid()` |
| `object<T>()` | Generic objects | `column.object<UserData>()` |
| `array<T>()` | Arrays | `column.array<number>()` |
| `list<T>()` | Read-only arrays | `column.list<string>()` |
| `tuple<T>()` | Fixed-size tuples | `column.tuple<string, number>()` |
| `set<T>()` | Sets | `column.set<string>()` |
| `map<K,V>()` | Maps | `column.map<string, number>()` |
| `custom<T>()` | Custom types | `column.custom<MyType>()` |

## Type Extensions

Most column types support **generic type parameters** to allow branding, literal unions, or domain-specific types.

:::note
Type extensions are compile-time only and do not affect runtime validation. Use custom validators for runtime type enforcement.
:::

### Numeric Types (`int`, `float`, `number`)

You can brand numeric types for stronger compile-time guarantees (e.g. preventing mixing up different types of IDs):

```typescript
import { Branded, defineSchema, column } from 'locality-idb';

type UserId = Branded<number, 'UserId'>;
type ProductId = Branded<number, 'ProductId'>;

const schema = defineSchema({
  users: {
    id: column.int<UserId>().pk().auto(),
    age: column.int(),
  },
  products: {
    id: column.int<ProductId>().pk().auto(),
    userId: column.int<UserId>(), // Type-safe foreign key referencing users
    price: column.float(),
  },
});

// Prevention of mixing IDs
const userId: UserId = 1 as UserId;
const productId: ProductId = 2 as ProductId;
// userId = productId; // ❌ Type error!
```

### String Types (`text`, `string`, `char`, `varchar`, `email`, `url`)

Literal unions can represent enum-like options in columns:

```typescript
type Role = 'admin' | 'user' | 'guest';
type Status = 'draft' | 'published' | 'archived';

const schema = defineSchema({
  users: {
    id: column.int().pk().auto(),
    role: column.text<Role>().default('user'),
    status: column.string<Status>().default('draft'),
  },
});
```

You can also brand strings for specific types, or use the specialized `email` & `url` types that include built-in runtime format validation:

```typescript
type Email = Branded<string, 'Email'>;
type URL = Branded<string, 'URL'>;

const profileSchema = defineSchema({
  profiles: {
    id: column.int().pk().auto(),
    email: column.varchar<Email>(255).unique(),
    website: column.varchar<URL>(500).optional(),
  },
});

// Or use built-in email and url validations
const advancedProfileSchema = defineSchema({
  profiles: {
    id: column.int().pk().auto(),
    email: column.email().unique(), // Validates email formats at runtime
    website: column.url().optional(), // Validates URL formats at runtime
  },
});
```

## Auto-generated Types (`uuid`, `timestamp`)

Auto-generated columns automatically compute a value during insertion if one is not explicitly provided.

```typescript
import { uuid } from 'toolbox-x';
import { getTimestamp } from 'locality-idb';

const schema = defineSchema({
  sessions: {
    id: column.uuid().pk(), // Auto-generated UUID v4
    idWithDefault: column.uuid().pk().default(uuid({ version: 'v6' })), // Custom UUID generator override
    createdAt: column.timestamp(), // Auto-generated timestamp (ISO 8601)
    defaultTs: column.timestamp().default(getTimestamp()), // Auto-generated timestamp with custom default helper
  },
});
```

:::tip
* Auto-generated values can be overridden by providing explicit values during insertion.
* Auto-generated values are computed at runtime during insertion.
* The `onUpdate()` modifier can be used to automatically update columns like `updatedAt` during update operations.
:::

## Boolean Types (`bool`, `boolean`)

You can brand boolean values for clearer domain boundaries:

```typescript
type EmailVerified = Branded<boolean, 'EmailVerified'>;
type TwoFactorEnabled = Branded<boolean, 'TwoFactorEnabled'>;

const schema = defineSchema({
  users: {
    id: column.int().pk().auto(),
    emailVerified: column.bool<EmailVerified>().default(false as EmailVerified),
    twoFactorEnabled: column.boolean<TwoFactorEnabled>().default(false as TwoFactorEnabled),
  },
});
```

## Complex Types (`object`, `array`, `list`, `tuple`, `set`, `map`)

You can define nested and complex data structures using TypeScript interfaces:

```typescript
interface UserProfile {
  avatar: string;
  bio: string;
  socials: {
    twitter?: string;
    github?: string;
  };
}

interface Comment {
  author: string;
  text: string;
  date: string;
}

interface CacheEntry {
  value: any;
  expires: number;
}

const schema = defineSchema({
  users: {
    id: column.int().pk().auto(),
    profile: column.object<UserProfile>(),
    tags: column.array<string>(),
    comments: column.array<Comment>(),
    permissions: column.set<'read' | 'write' | 'delete'>(),
    cache: column.map<string, CacheEntry>(),
  },
  
  // Tuples for fixed layouts
  locations: {
    id: column.int().pk().auto(),
    coordinates: column.tuple<number, number>(), // [latitude, longitude]
    rgbColor: column.tuple<number, number, number>(), // [r, g, b]
  },
  
  // Immutable list (readonly array)
  config: {
    id: column.int().pk().auto(),
    allowedOrigins: column.list<string>(), // Read-only at type level
  },
});
```

## Numeric & Bigint types

```typescript
import { Branded, Numeric } from 'locality-idb';

type SerialNumber = Branded<Numeric, 'SerialNumber'>;
type SnowflakeId = Branded<bigint, 'SnowflakeId'>;

const schema = defineSchema({
  products: {
    id: column.int().pk().auto(),
    serialNumber: column.numeric<SerialNumber>(), // Accepts both numbers and numeric strings
    largeId: column.bigint<SnowflakeId>(), // For very large integers
  },
});
```
