npm package: @indexeddb-orm/idb-orm
Description: TypeScript ORM wrapper for indexeddb with Zod runtime validation, build on top of dexie.js
Author: radommaciej
Latest version: 0.0.3

# Dexie ORM

TypeScript ORM wrapper for indexed-db with Zod runtime validation, built on the top of dexie.js.

**[View Homepage](https://idb-orm.com)** - Interactive documentation and examples

## Features

- **TypeScript Support** - Full type safety with generics
- **Zod Validation** - Runtime validation with Zod schemas
- **Config-based entities** - Define entities with a simple `defineEntity(...)` API
 
- **Dexie Compatibility** - All Dexie.js operations supported
- **Clean API** - Simple and intuitive API design

## Installation

```bash
npm install @indexeddb-orm/idb-orm dexie zod
```

## Important Notes

### Dexie Dependencies
This library includes Dexie.js internally. To ensure that `liveQuery` works properly, you need to configure Vite to dedupe Dexie.

```typescript
// In your vite.config.ts, add:
import { defineConfig } from 'vite';

export default defineConfig({
  resolve: {
    dedupe: ['dexie'],
  },
  // ... other config
});

// This ensures liveQuery works correctly
// Without dedupe, liveQuery may not function properly
```

### Built on Dexie.js
This library is built on top of [Dexie.js](https://dexie.org) - a powerful wrapper for IndexedDB. All Dexie.js methods and features are available through the `db` object.

```typescript
// All Dexie.js methods work with our library
const db = await Database.createDatabase({...});

// Direct Dexie.js operations
await db.transaction('rw', db.users, async () => {
    await db.users.add({ name: 'John', email: 'john@example.com' });
});

// Dexie.js query methods
const users = await db.users
    .where('age')
    .above(18)
    .and(user => user.name.startsWith('J'))
    .toArray();

// Dexie.js liveQuery (reactive queries)
import { liveQuery } from 'dexie';
const observableUsers = liveQuery(() => 
    db.users.where('active').equals(true).toArray()
);
```

**Benefits:** You get all the power of Dexie.js plus our ORM features like entity validation, relations, and cloud sync.

### Index Requirements for Queries
**Critical:** When searching by any field, you MUST create an index on that field. Queries without proper indexes will fail.

```typescript
const UserSchema = z.object({
    id: z.number(),
    name: z.string(),
    email: z.string(),
    age: z.number()
});

defineEntity(User, {
    schema: UserSchema,
    indexes: [
        { key: 'email' },        // Required for email queries
        { key: 'age' },          // Required for age queries
        { key: 'name' }          // Required for name queries
    ]
});
```

### Compound Indexes for Complex Queries
For queries involving multiple fields, you need compound indexes. Single field indexes won't work for multi-field queries.

```typescript
defineEntity(Post, {
    schema: PostSchema,
    compoundIndexes: [
        { key: ['category', 'status'] },
        { key: ['authorId', 'createdAt'] }
    ]
});
```

## Usage

### 1. Define Entities 

```typescript
import { BaseEntity, defineEntity } from '@indexeddb-orm/idb-orm';
import { z } from 'zod';

// Zod schema for validation
const UserSchema = z.object({
  id: z.number().optional(),
  name: z.string().min(2, 'Name must be at least 2 characters'),
  email: z.string().email('Invalid email format'),
  age: z.number().min(18, 'Must be at least 18 years old'),
  createdAt: z.number(),
  updatedAt: z.number(),
  isActive: z.boolean().default(true),
  tags: z.array(z.string()).default([]),
  metadata: z.record(z.string(), z.unknown()).default({}),
});

export class UserEntity extends BaseEntity<number> {
  name!: string;
  email!: string;
  age!: number;
  createdAt!: number;
  updatedAt!: number;
  isActive!: boolean;
  tags!: string[];
  metadata!: Record<string, unknown>;
}

defineEntity(UserEntity, {
  tableName: 'users',
  schema: UserSchema,
  columns: {
    name: { required: true, indexed: true },
    email: { required: true, unique: true },
    age: { indexed: true },
    createdAt: { indexed: true },
    updatedAt: { indexed: true },
    isActive: { indexed: true },
    tags: {},
    metadata: {},
  },
  // Example relations (optional)
  // relations: {
  //   posts: { type: 'one-to-many', target: PostEntity, foreignKey: 'authorId' },
  // },
});
```

### 2. Create Database

```typescript
import { Database } from '@indexeddb-orm/idb-orm';
import { UserEntity } from './entities/User';
import { PostEntity } from './entities/Post';

export const db = Database.createDatabase({
  name: 'MyApp',
  version: 1,
  entities: [UserEntity, PostEntity],
  config: { 
    onSchemaChangeStrategy: 'all',
    cloudSync: {
      databaseUrl: 'https://your-sync-server.com',
      enableOfflineQueue: true,
      syncInterval: 30000
    }
  },
});
```

### Database Configuration Options

The `createDatabase()` method accepts the following configuration options:

#### Basic Configuration
```typescript
const db = Database.createDatabase({
  name: string,           // Required: Database name
  version: number,        // Required: Schema version
  entities: EntityConstructor[], // Required: Array of entity classes
  config?: {             // Optional: Additional configuration
    // Schema change handling
    onSchemaChangeStrategy?: 'selective' | 'all',
    
    // Migrations list
    migrations?: Migration[],
    
    // Cloud synchronization
    cloudSync?: CloudSyncConfig
  }
});
```

#### Schema Change Strategy Options

**`onSchemaChangeStrategy`** - Defines how to handle database schema changes:

- **`'selective'`** (recommended for production): Only resets tables that have schema changes, preserving data in unchanged tables
- **`'all'`**: Resets the entire database when any schema change is detected, useful for development

#### Cloud Sync Configuration Options

**`CloudSyncConfig`** - Configuration for cloud synchronization:

```typescript
interface CloudSyncConfig {
  databaseUrl: string;                    // Required: Base URL of the sync server
  enableOfflineSupport?: boolean;         // Optional: Queue changes when offline (default: false)
  syncInterval?: number;                  // Optional: Auto-sync interval in milliseconds (default: 30000)
  // Note: Authentication is handled by Dexie Cloud addon configuration
}
```

**Cloud Sync Options:**
- **`databaseUrl`** (required): The base URL of your synchronization server
- **`enableOfflineSupport`** (optional): If `true`, changes are queued when offline and synced when connection is restored
- **`syncInterval`** (optional): Automatic synchronization interval in milliseconds (default: 30 seconds)

**Authentication Note:**
Authentication for cloud sync is handled by the Dexie Cloud addon configuration, not through this interface. You need to configure authentication separately when setting up the Dexie Cloud addon.

#### Schema Change Strategy Examples
```typescript
// Option 1: Reset only changed tables (recommended for production)
const db = Database.createDatabase({
  name: 'MyApp',
  version: 1,
  entities: [UserEntity, PostEntity],
  config: {
    onSchemaChangeStrategy: 'selective' // Only reset tables that changed
  }
});

// Option 2: Reset entire database on schema changes
const db = Database.createDatabase({
  name: 'MyApp',
  version: 1,
  entities: [UserEntity, PostEntity],
  config: {
    onSchemaChangeStrategy: 'all' // Reset all data when schema changes
  }
});
```

#### Migration Configuration
```typescript
const db = Database.createDatabase({
  name: 'MyApp',
  version: 2,
  entities: [UserEntity, PostEntity],
  config: {
    migrations: [
      {
        version: 2,
        name: 'Add fullName field',
        up: async (db) => {
          await db.getRepository(UserEntity).toCollection().modify(user => {
            user.fullName = `${user.firstName} ${user.lastName}`;
          });
        },
        down: async (db) => {
          await db.getRepository(UserEntity).toCollection().modify(user => {
            delete user.fullName;
          });
        }
      }
    ]
  }
});
```

#### Cloud Synchronization Configuration
```typescript
const db = Database.createDatabase({
  name: 'MyApp',
  version: 1,
  entities: [UserEntity, PostEntity],
  config: {
    cloudSync: {
      databaseUrl: 'https://your-sync-server.com', // Required: Sync server URL
      enableOfflineSupport: true,                  // Optional: Queue changes when offline
      syncInterval: 30000                          // Optional: Auto-sync interval (30s)
    }
  }
});
```

#### Complete Configuration Example
```typescript
const db = Database.createDatabase({
  name: 'MyApp',
  version: 3,
  entities: [UserEntity, PostEntity, CommentEntity],
  config: {
    // Handle schema changes by resetting only affected tables
    onSchemaChangeStrategy: 'selective',
    
    // Define migrations for version upgrades
    migrations: [
      {
        version: 2,
        name: 'Add user profiles',
        up: async (db) => {
          // Migration logic for version 2
        }
      },
      {
        version: 3,
        name: 'Add comment system',
        up: async (db) => {
          // Migration logic for version 3
        }
      }
    ],
    
    // Enable cloud synchronization
    cloudSync: {
      databaseUrl: 'https://api.myapp.com/sync',
      enableOfflineSupport: true,
      syncInterval: 60000 // Sync every minute
    }
  }
});
```

### 3. Basic CRUD Operations

```typescript
// Prefer explicit repositories for strict typing
const users = await db.getRepository(UserEntity).toArray(); // Type: UserEntity[]
const posts = await db.getRepository(PostEntity).toArray(); // Type: PostEntity[]

// Create new entity with validation in one call
import { newEntity } from '@indexeddb-orm/idb-orm';

const newUser = newEntity(UserEntity, {
  name: 'John Doe',
  email: 'john@example.com',
  age: 25,
  createdAt: Date.now(),
  updatedAt: Date.now(),
  tags: ['developer'],
  metadata: { source: 'manual' },
});

// Save to database
await db.getRepository(UserEntity).add(newUser);

// Query with full Dexie.js API
const activeUsers = await db.getRepository(UserEntity)
  .where('isActive')
  .equals(true)
  .toArray();
```

### 4. Entity Relations

```typescript
// Define entities with relations
defineEntity(UserEntity, {
  tableName: 'users',
  schema: UserSchema,
  columns: { /* ... */ },
  relations: {
    posts: { 
      type: 'one-to-many', 
      target: PostEntity, 
      foreignKey: 'authorId' 
    },
    profile: { 
      type: 'one-to-one', 
      target: ProfileEntity, 
      foreignKey: 'userId' 
    }
  }
});

defineEntity(PostEntity, {
  tableName: 'posts',
  schema: PostSchema,
  columns: { /* ... */ },
  relations: {
    author: { 
      type: 'many-to-one', 
      target: UserEntity, 
      foreignKey: 'authorId' 
    },
    tags: { 
      type: 'many-to-many', 
      target: TagEntity, 
      joinTable: 'post_tags' 
    }
  }
});

// Load relations
const userWithPosts = await db.loadRelations({
  entity: user,
  entityClass: UserEntity,
  relationNames: ['posts', 'profile']
});

// Load specific relation
const userPosts = await db.loadRelationByName({
  entity: user,
  entityClass: UserEntity,
  relationName: 'posts'
});

// Save entity with all its relations
const savedUser = await db.saveWithRelations({
  entity: user,
  entityClass: UserEntity
});

// Delete entity with cascade handling for relations
await db.deleteWithRelations({
  entity: user,
  entityClass: UserEntity
});
```

### 5. Reactive Queries with liveQuery

Use Dexie's `liveQuery` for reactive data that automatically updates when the database changes. Works with all major frameworks:

#### React Example
```typescript
import { liveQuery } from 'dexie';
import { useObservable } from 'dexie-react-hooks';
import { Database } from 'indexed-db-orm';
import { User } from './entities/User';

function UserList() {
  const users = useObservable(
    liveQuery(() => {
      const userRepo = db.getRepository(User);
      return userRepo.where('active').equals(true).toArray();
    })
  );
  
  return (
    <div>
      {users?.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  );
}
```

#### Vue Example
```typescript
import { liveQuery } from 'dexie';
import { ref, onMounted, onUnmounted } from 'vue';
import { Database } from 'indexed-db-orm';
import { User } from './entities/User';

export default {
  setup() {
    const users = ref<User[]>([]);
    let subscription;
    
    onMounted(() => {
      subscription = liveQuery(() => {
        const userRepo = db.getRepository(User);
        return userRepo.where('active').equals(true).toArray();
      }).subscribe(result => {
        users.value = result;
      });
    });
    
    onUnmounted(() => {
      subscription?.unsubscribe();
    });
    
    return { users };
  }
};
```

#### Svelte Example
```typescript
import { liveQuery } from 'dexie';
import { onMount, onDestroy } from 'svelte';
import { Database } from 'indexed-db-orm';
import { User } from './entities/User';

let users: User[] = [];
let subscription;

onMount(() => {
  subscription = liveQuery(() => {
    const userRepo = db.getRepository(User);
    return userRepo.where('active').equals(true).toArray();
  }).subscribe(result => {
    users = result;
  });
});

onDestroy(() => {
  subscription?.unsubscribe();
});
```

#### Angular Example
```typescript
import { liveQuery } from 'dexie';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { Database } from 'indexed-db-orm';
import { User } from './entities/User';

@Component({
  selector: 'app-user-list',
  template: '<div *ngFor="let user of users">{{user.name}}</div>'
})
export class UserListComponent implements OnInit, OnDestroy {
  users: User[] = [];
  private subscription?: Subscription;
  
  ngOnInit() {
    this.subscription = liveQuery(() => {
      const userRepo = db.getRepository(User);
      return userRepo.where('active').equals(true).toArray();
    }).subscribe(result => {
      this.users = result;
    });
  }
  
  ngOnDestroy() {
    this.subscription?.unsubscribe();
  }
}
```

### 6. Advanced Queries and Aggregation

```typescript
// Aggregation operations
const result = await db.aggregate({
  entityClass: PostEntity,
  options: {
    where: { category: 'tech' },
    sort: { field: 'views', direction: 'desc' },
    limit: 10,
    count: true,
    sum: ['views'],
    avg: ['likes'],
    groupBy: ['category']
  }
});

// Complex filtering and sorting
const topPosts = await db.getRepository(PostEntity)
  .where('published')
  .equals(true)
  .and(post => post.views > 100)
  .orderBy('views')
  .reverse()
  .limit(5)
  .toArray();
```

### 6. Entity Validation

```typescript
// Manual validation
const user = new UserEntity();
user.name = 'A'; // Too short
user.email = 'invalid-email'; // Invalid format

const result = user.validate();
if (!result.isValid) {
  console.log(result.errors);
  // ['name: Name must be at least 2 characters', 'email: Invalid email format']
}

// Validation with error throwing
try {
  user.validateOrThrow();
} catch (error) {
  console.log('Validation failed:', error.message);
}

// Initialize with validation
const validUser = new UserEntity().init({
  name: 'John Doe',
  email: 'john@example.com',
  age: 25
});
```

### 7. Database Management

```typescript
// Check for schema changes
const hasChanges = await db.checkSchemaChanges();
if (hasChanges) {
  await db.performSelectiveReset(); // Reset only changed tables
  // or
  await db.resetDatabase(); // Reset entire database
}

// Clear all data
await db.clearAllData();

// Run migrations
await db.runMigrations([
  {
    version: 2,
    up: async (db) => {
      await db.getRepository(UserEntity).toCollection().modify(user => {
        user.fullName = `${user.firstName} ${user.lastName}`;
      });
    }
  }
]);
```

### 8. Cloud Synchronization

#### Installation
```bash
npm install dexie-cloud-addon
```

#### Configuration
```typescript
// Basic cloud sync configuration
const db = Database.createDatabase({
  name: 'MyApp',
  version: 1,
  entities: [UserEntity, PostEntity],
  config: {
    cloudSync: {
      databaseUrl: 'https://your-database-url.dexie.cloud',
      enableOfflineSupport: true,
      syncInterval: 30000 // 30 seconds
    }
  }
});

// Enable cloud sync manually
await db.enableCloudSync({
  databaseUrl: 'https://your-sync-server.com',
  enableOfflineSupport: true,
  syncInterval: 30000
});
```

#### Cloud Sync API
```typescript
// Check sync status
const status = db.getSyncStatus();
console.log('Sync enabled:', status.enabled);
console.log('Last sync:', status.lastSync);
console.log('Online:', status.isOnline);

// Manual sync
await db.sync();

// Sync specific tables
await db.syncTables(['users', 'posts']);

// Check if cloud sync is enabled
const isEnabled = db.isCloudSyncEnabled();

// Get cloud sync configuration
const config = db.getCloudSyncConfig();

// Disable cloud sync
db.disableCloudSync();
```

#### Automatic Synchronization
```typescript
// Configure automatic sync with interval
const db = Database.createDatabase({
  name: 'MyApp',
  version: 1,
  entities: [UserEntity, PostEntity],
  config: {
    cloudSync: {
      databaseUrl: 'https://your-database-url.dexie.cloud',
      syncInterval: 60000 // Sync every minute
    }
  }
});
```

#### React Integration Example
```typescript
import { useState, useEffect } from 'react';
import { useLiveQuery } from 'dexie-react-hooks';

function MyComponent() {
  const [syncStatus, setSyncStatus] = useState({ enabled: false });
  
  // Data with automatic synchronization
  const users = useLiveQuery(() => db.getRepository(UserEntity).toArray());
  
  useEffect(() => {
    const status = db.getSyncStatus();
    setSyncStatus(status);
  }, []);
  
  const handleSync = async () => {
    try {
      await db.sync();
      alert('Synchronization completed!');
    } catch (error) {
      alert(`Sync error: ${error.message}`);
    }
  };
  
  return (
    <div>
      <h2>Status: {syncStatus.enabled ? 'Enabled' : 'Disabled'}</h2>
      <button onClick={handleSync}>Sync Now</button>
      
      {users?.map(user => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  );
}
```

#### Troubleshooting
```typescript
// Check if cloud sync is configured
const config = db.getCloudSyncConfig();

if (!config) {
  await db.enableCloudSync({
    databaseUrl: 'https://your-database-url.dexie.cloud'
  });
}

// Check sync status for debugging
const status = db.getSyncStatus();
console.log('Online:', status.isOnline);
console.log('Last sync:', status.lastSync);

// Try manual sync if automatic sync fails
await db.sync();

```

### 9. Compound Indexes

Compound indexes allow you to create indexes on multiple columns for better query performance.

#### Defining Compound Indexes

```typescript
import { BaseEntity, defineEntity } from '@indexeddb-orm/idb-orm';
import { z } from 'zod';

// Zod schema for validation
const UserSchema = z.object({
  id: z.number().optional(),
  name: z.string(),
  email: z.string().email(),
  age: z.number(),
  isActive: z.boolean(),
});

export class UserEntity extends BaseEntity {
  name!: string;
  email!: string;
  age!: number;
  isActive!: boolean;
}

// Define entity with compound indexes
defineEntity(UserEntity, {
  tableName: 'users',
  schema: UserSchema,
  columns: {
    name: { indexed: true },
    email: { indexed: true, unique: true },
    age: { indexed: true },
    isActive: { indexed: true },
  },
  compoundIndexes: [
    {
      columns: ['name', 'email'],
      unique: false,
      name: 'name_email_index'
    },
    {
      columns: ['age', 'isActive'],
      unique: false,
      name: 'age_active_index'
    },
    {
      columns: ['email'],
      unique: true,
      name: 'unique_email_index'
    }
  ]
});
```

#### Querying with Compound Indexes

```typescript
// Query using compound index (name + email)
const users = await db.getRepository(UserEntity)
  .where(['name', 'email'])
  .equals(['John Doe', 'john@example.com'])
  .toArray();

// Query using compound index (age + isActive)
const activeAdults = await db.getRepository(UserEntity)
  .where(['age', 'isActive'])
  .above([18, 1]) // age > 18, isActive = true
  .toArray();

// Query using unique compound index
const user = await db.getRepository(UserEntity)
  .where('email')
  .equals('john@example.com')
  .first();
```

#### Different Query Operators

```typescript
// equals - exact match
const users = await db.getRepository(UserEntity)
  .where(['name', 'email'])
  .equals(['John', 'john@example.com'])
  .toArray();

// above - greater than
const adults = await db.getRepository(UserEntity)
  .where(['age', 'isActive'])
  .above([18, 1])
  .toArray();

// below - less than
const youngUsers = await db.getRepository(UserEntity)
  .where(['age'])
  .below([25])
  .toArray();

// between - between values
const middleAged = await db.getRepository(UserEntity)
  .where(['age'])
  .between([25, 50])
  .toArray();
```

#### Real-world Examples

```typescript
// 1. User search by name and email
defineEntity(UserEntity, {
  tableName: 'users',
  schema: UserSchema,
  columns: {
    name: { indexed: true },
    email: { indexed: true },
  },
  compoundIndexes: [
    { columns: ['name', 'email'], name: 'name_email_index' }
  ]
});
// Usage: Find user by exact name and email
const user = await db.getRepository(UserEntity)
  .where(['name', 'email'])
  .equals(['John Doe', 'john@example.com'])
  .first();

// 2. Active adults filter
defineEntity(UserEntity, {
  tableName: 'users',
  schema: UserSchema,
  columns: {
    age: { indexed: true },
    isActive: { indexed: true },
  },
  compoundIndexes: [
    { columns: ['age', 'isActive'], name: 'age_active_index' }
  ]
});
// Usage: Find active adults
const activeAdults = await db.getRepository(UserEntity)
  .where(['age', 'isActive'])
  .above([18, 1])
  .toArray();

// 3. Posts by date and category
defineEntity(PostEntity, {
  tableName: 'posts',
  schema: PostSchema,
  columns: {
    createdAt: { indexed: true },
    category: { indexed: true },
  },
  compoundIndexes: [
    { columns: ['createdAt', 'category'], name: 'date_category_index' }
  ]
});
// Usage: Find recent posts in specific category
const recentTechPosts = await db.getRepository(PostEntity)
  .where(['createdAt', 'category'])
  .above([Date.now() - 7 * 24 * 60 * 60 * 1000, 'tech'])
  .toArray();
```

#### Compound Index Benefits

- **Query Performance**: Faster searches on multiple columns
- **Uniqueness**: Guarantee unique combinations of values
- **Flexibility**: Create indexes on any column combinations
- **Optimization**: Better performance for complex queries

### Vite note: ensure a single Dexie instance for live queries

If `useLiveQuery` doesn't re-render with this library's `Database`, make sure Vite bundles a single Dexie copy by deduping Dexie:

```ts
// vite.config.ts
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [react()],
  resolve: { dedupe: ['dexie'] },
});
```

This ensures the app and the library share the same Dexie singleton so reactivity works correctly.

### Indexing Note

When querying with `where('field')` or sorting with `orderBy('field')`, ensure the field is indexed in your entity definition:

```ts
defineEntity(PostEntity, {
  tableName: 'posts',
  columns: {
    createdAt: { indexed: true },
    published: { indexed: true },
    likes: { indexed: true },
  },
});
```

If you change indexes, bump your database `version` or run a migration so Dexie can apply the schema update.

## Example Applications

The library includes comprehensive demo applications showcasing all features across different frameworks:

### Available Demo Apps

#### **React Demo App** 
- **Framework**: React 19 + TypeScript
- **UI Library**: Material-UI (MUI)
- **Features**: Live queries, TypeScript demo, Cloud sync, Aggregations
- **Run**: `cd react-demo-app && npm install && npm run dev`
- **URL**: `http://localhost:5173`

#### **Vue Demo App**
- **Framework**: Vue 3 + TypeScript  
- **UI Library**: Vuetify 3
- **Features**: Reactive components, Cloud sync, Entity management
- **Run**: `cd vue-demo-app && npm install && npm run dev`
- **URL**: `http://localhost:5173`

#### **Svelte Demo App**
- **Framework**: SvelteKit + TypeScript
- **UI Library**: Material-UI (MUI)
- **Features**: Server-side rendering, Live queries, TypeScript integration
- **Run**: `cd svelte-demo-app && npm install && npm run dev`
- **URL**: `http://localhost:5173`

#### **Angular Demo App**
- **Framework**: Angular 17 + TypeScript
- **UI Library**: Angular Material
- **Features**: Services, Dependency injection, Reactive forms
- **Run**: `cd angular-demo-app && npm install && npm start`
- **URL**: `http://localhost:4200`

### Demo App Features

All demo applications showcase:

#### **Core Features**
- **Entity Definition** - Using `defineEntity()` API
- **Zod Validation** - Runtime validation with error handling
- **Relations** - One-to-one, one-to-many, many-to-many relationships
- **Aggregations** - Count, sum, average, min, max operations
- **Live Queries** - Reactive data with `useLiveQuery()`
- **TypeScript** - Full type safety and IntelliSense

#### **Advanced Features**
- **Cloud Sync** - Dexie Cloud synchronization
- **Migrations** - Database schema migrations
- **Compound Indexes** - Multi-column indexing
- **Entity Management** - CRUD operations with relations
- **Error Handling** - Validation errors and user feedback

### Demo App Structure

Each demo app follows similar structure:

```
demo-app/
├── src/
│   ├── entities/         # Entity definitions
│   ├── database/         # Database configuration
│   └── migrations/       # Database migrations
├── package.json
└── README.md
```

Each demo is fully functional and can serve as a starting point for your own applications!

## API Reference

### Core Classes

#### `Database`
Main database class extending Dexie with ORM capabilities.

**Static Methods:**
- `Database.createDatabase(params)` - Create database with entity registration

**Instance Methods:**
- `getRepository<T>(entityClass)` - Get typed repository for entity
- `aggregate<T>(params)` - Perform aggregation operations
- `clearAllData()` - Clear all data from database
- `resetDatabase()` - Reset database when schema changes
- `checkSchemaChanges()` - Check if schema has changed
- `performSelectiveReset()` - Reset only changed tables
- `runMigrations(migrations)` - Run database migrations
- `getTypedTable<T>(entityClass)` - Get typed table for entity
- `getTableForEntity<T>(entityClass)` - Get table with proper typing
- `getEntities()` - Get all registered entities
- `getEntity(tableName)` - Get entity by table name
- `loadRelations<T>(params)` - Load relations for entity
- `loadRelationByName<T, K>(params)` - Load specific relation
- `saveWithRelations<T>(params)` - Save entity with relations
- `deleteWithRelations<T>(params)` - Delete entity with cascade
- `sync()` - Manual cloud sync
- `getSyncStatus()` - Get cloud sync status
- `enableCloudSync(config)` - Enable cloud synchronization
- `disableCloudSync()` - Disable cloud sync
- `isCloudSyncEnabled()` - Check if cloud sync is enabled
- `getCloudSyncConfig()` - Get cloud sync configuration
- `syncTables(tableNames)` - Sync specific tables

#### `BaseEntity<TKey>`
Base class for all entities with validation capabilities.

**Methods:**
- `validate(): ValidationResult` - Validate entity against schema
- `validateOrThrow(): void` - Validate and throw error if invalid
- `init(data): this` - Initialize entity with data and validate

#### `EntitySchema<T>`
Schema management for entities.

**Methods:**
- `getTableName(): string` - Get table name for entity
- `getSchema(): ZodSchema | undefined` - Get Zod schema
- `getColumns(): Record<string, ColumnOptions>` - Get column metadata
- `validate(data): ValidationResult` - Validate data against schema
- `create(data?): T` - Create new entity instance

### Entity Definition

#### `defineEntity(EntityClass, options)`
Define entity with metadata and configuration.

**Parameters:**
- `EntityClass` - Entity constructor class
- `options` - Entity configuration object

**Options:**
```typescript
interface EntityOptions {
  tableName?: string;           // Custom table name
  schema?: ZodSchema;           // Zod validation schema
  timestamps?: boolean;         // Enable timestamps
  columns?: Record<string, ColumnOptions>;
  relations?: Record<string, RelationOptions>;
  compoundIndexes?: CompoundIndexOptions[];
}
```

#### `newEntity(EntityClass, data)`
Create new entity instance with validation.

**Parameters:**
- `EntityClass` - Entity constructor
- `data` - Partial entity data

**Returns:** Fully initialized and validated entity instance

### Decorators

#### `@Entity(options?)`
Class decorator for entity definition.

#### `@Column(options?)`
Property decorator for column configuration.

#### `@Relation(options)`
Property decorator for relation definition.

**Relation Types:**
- `@OneToOne(target, options?)` - One-to-one relation
- `@OneToMany(target, foreignKey)` - One-to-many relation
- `@ManyToMany(target, joinTable)` - Many-to-many relation

### Type Definitions

#### `EntityConstructor<T>`
```typescript
interface EntityConstructor<T extends BaseEntity = BaseEntity> {
  new (): T;
  schema?: ZodSchema;
  tableName?: string;
}
```

#### `ColumnOptions`
```typescript
interface ColumnOptions {
  kind?: 'string' | 'number' | 'boolean' | 'array' | 'object';
  unique?: boolean;
  indexed?: boolean;
  required?: boolean;
  default?: unknown;
  primaryKey?: boolean;
  autoIncrement?: boolean;
}
```

#### `RelationOptions`
```typescript
interface RelationOptions {
  type: 'one-to-one' | 'one-to-many' | 'many-to-many';
  target: EntityConstructor | string;
  foreignKey?: string;
  joinTable?: string;
  cascade?: boolean;
  eager?: boolean;
}
```

#### `ValidationResult`
```typescript
interface ValidationResult {
  isValid: boolean;
  errors: string[];
}
```

#### `DatabaseConfig`
```typescript
interface DatabaseConfig {
  name: string;
  version: number;
  entities: EntityConstructor[];
  onSchemaChangeStrategy?: 'selective' | 'all';
  migrations?: Migration[];
  cloudSync?: CloudSyncConfig;
}
```

#### `CloudSyncConfig`
```typescript
interface CloudSyncConfig {
  databaseUrl: string;
  enableOfflineSupport?: boolean;
  syncInterval?: number;
}
```

#### `Migration`
```typescript
interface Migration {
  version: number;
  name: string;
  up: (db: Dexie) => Promise<void>;
  down?: (db: Dexie) => Promise<void>;
}
```

#### `AggregationOptions<T>`
```typescript
interface AggregationOptions<T extends BaseEntity> {
  where?: Partial<T>;
  count?: boolean;
  sum?: (keyof T)[];
  avg?: (keyof T)[];
  min?: (keyof T)[];
  max?: (keyof T)[];
  groupBy?: keyof T;
  include?: string[];
  limit?: number;
  sort?: {
    field: keyof T;
    direction: 'asc' | 'desc';
  };
}
```

#### `AggregationResult`
```typescript
interface AggregationResult {
  count?: number;
  sum?: Record<string, number>;
  avg?: Record<string, number>;
  min?: Record<string, number>;
  max?: Record<string, number>;
  groups?: Array<{
    key: unknown;
    count: number;
    sum?: Record<string, number>;
    avg?: Record<string, number>;
    min?: Record<string, number>;
    max?: Record<string, number>;
  }>;
  data?: unknown[];
}
```

### Error Classes

#### `ValidationError`
Error thrown when entity validation fails.

**Properties:**
- `message: string` - Error message
- `errors: string[]` - Array of validation errors

### Utility Functions

#### `newEntity(EntityClass, data)`
Factory function for creating validated entities.

#### `defineEntity(EntityClass, options)`
Function for defining entity metadata.

### Schema Change Strategies

#### `'selective'`
Only resets tables that have schema changes, preserving data in unchanged tables.

#### `'all'`
Resets the entire database when any schema change is detected.

### Cloud Sync Status

```typescript
interface SyncStatus {
  enabled: boolean;
  lastSync?: Date;
  isOnline?: boolean;
}
```

## License
MIT