npm package: @lumarc-db/core
Description: TypeScript ORM for IndexedDB with SQL-like syntax
Latest version: 0.2.8

# @lumarc-db/core 🗄️

> SQL-like API for IndexedDB with full TypeScript support

[![npm version](https://img.shields.io/npm/v/@lumarc-db/core.svg)](https://www.npmjs.com/package/@lumarc-db/core)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**English** | [한국어](./README.ko.md)

The core library of LumarcDB that provides a powerful, SQL-like query builder for IndexedDB with complete TypeScript support.

---

## 📦 Installation

```bash
npm install @lumarc-db/core
# or
pnpm add @lumarc-db/core
# or
yarn add @lumarc-db/core
```

---

## 🚀 Quick Start

```typescript
import { LocalORM } from '@lumarc-db/core';

// Define schema
const schema = {
  users: {
    id: { type: 'number', primaryKey: true, autoIncrement: true },
    name: { type: 'string', required: true },
    email: { type: 'string', unique: true },
    age: { type: 'number' }
  }
};

// Initialize ORM
const orm = new LocalORM('myapp', schema);
await orm.initialize();

// Use it!
await orm.users.add({ name: 'John', email: 'john@example.com', age: 30 });
const users = await orm.users.where('age').greaterThan(25).toArray();
```

---

## 📚 Table of Contents

- [Schema Definition](#schema-definition)
- [Database Operations](#database-operations)
- [Table Access Methods](#table-access-methods)
- [CRUD Operations](#crud-operations)
- [Query Builder](#query-builder)
- [Aggregations](#aggregations)
- [Transactions](#transactions)
- [Backup & Restore](#backup--restore)
- [Data Seeding](#data-seeding)
- [TypeScript Support](#typescript-support)
- [Performance Tracking](#performance-tracking)

---

## 🔧 Schema Definition

### Basic Schema

```typescript
const schema = {
  tableName: {
    columnName: {
      type: 'string' | 'number' | 'boolean' | 'date' | 'object' | 'array',
      primaryKey?: boolean,
      autoIncrement?: boolean,
      unique?: boolean,
      indexed?: boolean,
      required?: boolean,
      default?: any
    }
  }
};
```

### Example Schema

```typescript
const schema = {
  users: {
    id: { 
      type: 'number', 
      primaryKey: true, 
      autoIncrement: true 
    },
    name: { 
      type: 'string', 
      required: true 
    },
    email: { 
      type: 'string', 
      unique: true,
      required: true
    },
    age: { 
      type: 'number',
      indexed: true
    },
    createdAt: { 
      type: 'date',
      default: () => new Date()
    },
    profile: {
      type: 'object'
    },
    tags: {
      type: 'array'
    }
  },
  
  posts: {
    id: { type: 'number', primaryKey: true, autoIncrement: true },
    userId: { type: 'number', indexed: true },
    title: { type: 'string', required: true },
    content: { type: 'string' },
    published: { type: 'boolean', default: false }
  }
};
```

### Schema Options

| Option | Type | Description |
|--------|------|-------------|
| `type` | string | Data type: string, number, boolean, date, object, array |
| `primaryKey` | boolean | Mark as primary key |
| `autoIncrement` | boolean | Auto-increment for primary key |
| `unique` | boolean | Create unique index |
| `indexed` | boolean | Create index for faster queries |
| `required` | boolean | Field is required |
| `default` | any/function | Default value or generator function |

---

## 🗄️ Database Operations

### Initialize Database

```typescript
const orm = new LocalORM('myapp', schema, version);
await orm.initialize();
```

### Check Database Status

```typescript
// Check if ready
const isReady = orm.isReady();

// Get schema
const currentSchema = orm.getSchema();
```

### Disconnect

```typescript
await orm.disconnect();
```

---

## 📋 Table Access Methods

### Method 1: Dynamic Property Access (Recommended)

```typescript
// Direct access - clean and simple
await orm.users.add({ name: 'John' });
await orm.posts.add({ title: 'Hello' });

// Full TypeScript support
interface User {
  id?: number;
  name: string;
  email: string;
}

const users = await orm.users.where('name').equals('John').toArray();
```

### Method 2: getTable() Method

```typescript
// Explicit table access
const users = orm.getTable('users');
await users.add({ name: 'John' });

// Dynamic table names
const tableName = getUserInput(); // Could be dynamic
const table = orm.getTable(tableName);
await table.add({ name: 'Jane' });

// With TypeScript
const users = orm.getTable<User>('users');
```

**When to use:**
- **Dynamic property** (`orm.users`) - Cleaner, better autocomplete
- **getTable()** - Dynamic table names, runtime flexibility

---

## ✏️ CRUD Operations

### Create (Add)

```typescript
// Add single record
const user = await orm.users.add({
  name: 'John Doe',
  email: 'john@example.com',
  age: 30
});

console.log(user.id); // Auto-generated ID

// Add with explicit ID
await orm.users.add({
  id: 100,
  name: 'Jane',
  email: 'jane@example.com'
});
```

### Read (Get)

```typescript
// Get by ID
const user = await orm.users.get(1);

// Get all
const allUsers = await orm.users.all().toArray();

// Count records
const count = await orm.users.count();
```

### Update

```typescript
// Update by ID
const updated = await orm.users.update(1, {
  age: 31,
  name: 'John Updated'
});

console.log(updated); // Updated record or null
```

### Delete

```typescript
// Delete by ID
const success = await orm.users.delete(1);

console.log(success); // true or false
```

---

## 🔍 Query Builder

### Where Conditions

#### Comparison Operators

```typescript
// equals
await orm.users.where('age').equals(30).toArray();

// greaterThan
await orm.users.where('age').greaterThan(25).toArray();

// greaterThanOrEqual
await orm.users.where('age').greaterThanOrEqual(30).toArray();

// lessThan
await orm.users.where('age').lessThan(40).toArray();

// lessThanOrEqual
await orm.users.where('age').lessThanOrEqual(35).toArray();

// notEquals
await orm.users.where('status').notEquals('inactive').toArray();

// between
await orm.users.where('age').between(25, 35).toArray();

// in
await orm.users.where('id').in([1, 2, 3, 4, 5]).toArray();
```

#### String Matching

```typescript
// startsWith
await orm.users.where('name').startsWith('John').toArray();

// endsWith
await orm.users.where('email').endsWith('@gmail.com').toArray();

// contains
await orm.users.where('name').contains('Doe').toArray();

// like (pattern matching)
await orm.users.where('name').like('John%').toArray(); // Starts with John
await orm.users.where('email').like('%@gmail.com').toArray(); // Ends with
await orm.users.where('text').like('%keyword%').toArray(); // Contains
```

### Logical Operators

#### AND Conditions

```typescript
// Multiple where() calls are AND by default
const users = await orm.users
  .where('age').greaterThan(25)
  .where('status').equals('active')
  .where('email').endsWith('@company.com')
  .toArray();

// Or use .and() explicitly
const users = await orm.users
  .where('age').greaterThan(25)
  .and('status').equals('active')
  .toArray();
```

#### OR Conditions

```typescript
// Use .or() for OR conditions
const users = await orm.users
  .where('age').lessThan(18)
  .or('age').greaterThan(65)
  .toArray();

// Complex conditions
const users = await orm.users
  .where('status').equals('premium')
  .or('credits').greaterThan(100)
  .and('verified').equals(true)
  .toArray();
```

### Sorting

```typescript
// Sort ascending
await orm.users.orderBy('age', 'asc').toArray();

// Sort descending
await orm.users.orderBy('createdAt', 'desc').toArray();
await orm.users.orderByDesc('age').toArray(); // Shorthand

// Multiple sorts (applied in order)
await orm.users
  .orderBy('status', 'asc')
  .orderBy('age', 'desc')
  .toArray();
```

### Pagination

```typescript
// limit()
await orm.users.limit(10).toArray(); // First 10 records

// offset()
await orm.users.offset(20).limit(10).toArray(); // Skip 20, take 10

// Aliases
await orm.users.take(10).toArray(); // Same as limit(10)
await orm.users.skip(20).take(10).toArray(); // Same as offset(20).limit(10)

// Pagination example
const page = 3;
const perPage = 20;
const users = await orm.users
  .offset((page - 1) * perPage)
  .limit(perPage)
  .toArray();
```

### Complex Queries

```typescript
// Combining everything
const results = await orm.users
  .where('age').between(25, 40)
  .and('status').equals('active')
  .or('role').equals('admin')
  .orderBy('createdAt', 'desc')
  .offset(0)
  .limit(20)
  .toArray();

// Query with multiple conditions
const premiumUsers = await orm.users
  .where('subscription').equals('premium')
  .and('credits').greaterThan(0)
  .and('verified').equals(true)
  .orderByDesc('lastLoginAt')
  .take(50)
  .toArray();
```

---

## 📊 Aggregations

### Basic Aggregations

```typescript
// Count
const total = await orm.users.count();
const activeCount = await orm.users
  .where('status').equals('active')
  .count();

// Sum
const totalAge = await orm.users.sum('age');
const orderTotal = await orm.orders.sum('amount');

// Average
const avgAge = await orm.users.avg('age');
const avgOrderAmount = await orm.orders.avg('amount');

// Min
const minAge = await orm.users.min('age');
const cheapestPrice = await orm.products.min('price');

// Max
const maxAge = await orm.users.max('age');
const mostExpensive = await orm.products.max('price');
```

### Group By & Having

```typescript
// Group by with aggregations (SQL-style)
const stats = await orm.orders
  .select(['userId', 'COUNT(*) as orderCount', 'SUM(amount) as totalSpent'])
  .groupBy('userId')
  .having('orderCount', 'greaterThan', 5)
  .toArray();

// Builder-style aggregation
const userStats = await orm.orders
  .aggregate({
    groupBy: 'userId',
    functions: [
      { type: 'count', alias: 'orderCount' },
      { type: 'sum', field: 'amount', alias: 'totalSpent' },
      { type: 'avg', field: 'amount', alias: 'avgOrder' }
    ],
    having: { 
      field: 'orderCount', 
      operator: 'greaterThan', 
      value: 5 
    }
  })
  .toArray();

console.log(userStats);
// [
//   { userId: 1, orderCount: 10, totalSpent: 5000, avgOrder: 500 },
//   { userId: 2, orderCount: 7, totalSpent: 3500, avgOrder: 500 }
// ]
```

### Advanced Aggregations

```typescript
// Multiple group by fields
const categoryStats = await orm.products
  .select([
    'category',
    'brand',
    'COUNT(*) as productCount',
    'AVG(price) as avgPrice',
    'MIN(price) as minPrice',
    'MAX(price) as maxPrice'
  ])
  .groupBy('category')
  .groupBy('brand')
  .having('productCount', 'greaterThan', 3)
  .toArray();

// With sorting
const topCategories = await orm.products
  .select(['category', 'COUNT(*) as count', 'SUM(sales) as totalSales'])
  .groupBy('category')
  .orderBy('totalSales', 'desc')
  .limit(10)
  .toArray();
```

---

## 🔄 Transactions

### Manual Transaction Control

```typescript
// Begin transaction
const tx = orm.beginTransaction(['users', 'orders']);

try {
  // Execute operations within transaction
  await tx.users.add({ name: 'Alice', email: 'alice@example.com' });
  await tx.orders.add({ userId: 1, amount: 1000 });
  
  // Commit transaction
  await tx.commit();
  console.log('Transaction committed successfully');
} catch (error) {
  // Rollback on error
  await tx.rollback();
  console.error('Transaction rolled back:', error);
}
```

### Auto-commit Transactions

```typescript
// Automatic commit/rollback
await orm.transaction(async (tx) => {
  await tx.users.add({ name: 'Alice', email: 'alice@example.com' });
  await tx.orders.add({ userId: 1, amount: 1000 });
  await tx.inventory.update(1, { stock: 99 });
  
  // Automatically commits if successful
  // Automatically rolls back if error occurs
});
```

### Read-only Transactions

```typescript
// Read-only transaction for consistent data reads
const data = await orm.readTransaction(async (tx) => {
  const user = await tx.users.get(1);
  const orders = await tx.orders.where('userId').equals(1).toArray();
  
  return { user, orders };
});
```

### Transaction Best Practices

```typescript
// ✅ Good: Specify tables explicitly
const tx = orm.beginTransaction(['users', 'orders', 'inventory']);

// ✅ Good: Keep transactions short
await orm.transaction(async (tx) => {
  // Quick operations only
  await tx.users.update(1, { lastLogin: Date.now() });
});

// ❌ Bad: Don't do heavy computation in transactions
await orm.transaction(async (tx) => {
  const users = await tx.users.all().toArray();
  // Heavy processing...
  await processMillionsOfRecords(users); // Bad!
});
```

### Practical Examples

#### Auto-backup Before Critical Operations

```typescript
async function dangerousOperation() {
  // Backup before risky operation
  const backup = await orm.backupToObject();
  
  try {
    await performRiskyDatabaseChanges();
  } catch (error) {
    // Restore on failure
    console.error('Operation failed, restoring backup...');
    await orm.restoreFromObject(backup);
  }
}
```

#### Scheduled Backups

```typescript
// Backup every hour
setInterval(async () => {
  const backup = await orm.backupToObject();
  
  // Save to cloud storage
  await fetch('https://api.myapp.com/backups', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(backup)
  });
  
  console.log('Backup saved to cloud');
}, 60 * 60 * 1000); // Every hour
```

#### Export/Import Feature

```typescript
// Export button handler
async function handleExport() {
  await orm.backup(); // Downloads JSON file
  alert('Database exported successfully!');
}

// Import button handler
async function handleImport(file: File) {
  if (!confirm('This will replace all existing data. Continue?')) {
    return;
  }
  
  try {
    await orm.restore(file, { clearExisting: true });
    alert('Database imported successfully!');
  } catch (error) {
    alert('Import failed: ' + error.message);
  }
}
```

---

## 🌱 Data Seeding

### Seed Multiple Records

```typescript
// Seed test data
const users = [
  { name: 'Alice', email: 'alice@test.com', age: 25 },
  { name: 'Bob', email: 'bob@test.com', age: 30 },
  { name: 'Charlie', email: 'charlie@test.com', age: 35 }
];

const results = await orm.users.seed(users);
console.log(`Seeded ${results.length} users`);
```

### Clear and Seed

```typescript
// Clear existing data and seed fresh data
await orm.users.seed(users, true); // clearFirst = true

// Or manually
await orm.users.clear();
await orm.users.seed(users);
```

### Clear Table

```typescript
// Remove all data from table
await orm.users.clear();

// Verify
const count = await orm.users.count();
console.log(count); // 0
```

### Seed with Validation

```typescript
const seedData = [
  { name: 'Alice', email: 'alice@test.com', age: 25 },
  { email: 'invalid@test.com', age: 30 }, // Missing required 'name'
  { name: 'Charlie', email: 'charlie@test.com', age: 35 }
];

// Invalid items are skipped automatically
const results = await orm.users.seed(seedData);
console.log(results.length); // 2 (invalid item skipped)
```

### Development Fixtures

```typescript
// Create fixtures for development
const fixtures = {
  users: [
    { name: 'Admin', email: 'admin@app.com', role: 'admin' },
    { name: 'User1', email: 'user1@app.com', role: 'user' },
    { name: 'User2', email: 'user2@app.com', role: 'user' }
  ],
  posts: [
    { userId: 1, title: 'Welcome Post', content: 'Hello!' },
    { userId: 2, title: 'First Post', content: 'My first post' }
  ]
};

// Seed all tables
async function seedDatabase() {
  for (const [tableName, data] of Object.entries(fixtures)) {
    await orm.getTable(tableName).seed(data, true);
  }
  console.log('Database seeded!');
}

// Run in development mode
if (import.meta.env.DEV) {
  await seedDatabase();
}
```

### Test Data Generation

```typescript
// Generate test data with faker
import { faker } from '@faker-js/faker';

async function seedTestUsers(count: number) {
  const users = Array.from({ length: count }, () => ({
    name: faker.person.fullName(),
    email: faker.internet.email(),
    age: faker.number.int({ min: 18, max: 80 }),
    city: faker.location.city()
  }));
  
  await orm.users.seed(users, true);
  console.log(`Seeded ${count} test users`);
}

await seedTestUsers(100);
```

### Seeding with Relationships

```typescript
// Seed related data
async function seedRelatedData() {
  // Clear all tables first
  await orm.users.clear();
  await orm.posts.clear();
  await orm.comments.clear();
  
  // Seed users
  const users = await orm.users.seed([
    { name: 'Alice', email: 'alice@test.com' },
    { name: 'Bob', email: 'bob@test.com' }
  ]);
  
  // Seed posts with user IDs
  const posts = await orm.posts.seed([
    { userId: users[0].id, title: 'Alice Post 1', content: 'Content 1' },
    { userId: users[0].id, title: 'Alice Post 2', content: 'Content 2' },
    { userId: users[1].id, title: 'Bob Post 1', content: 'Content 3' }
  ]);
  
  // Seed comments
  await orm.comments.seed([
    { postId: posts[0].id, userId: users[1].id, text: 'Great post!' },
    { postId: posts[0].id, userId: users[0].id, text: 'Thanks!' }
  ]);
  
  console.log('Related data seeded successfully');
}

await seedRelatedData();
```

### Reset Database

```typescript
// Complete database reset utility
async function resetDatabase() {
  const tableNames = Object.keys(orm.getSchema());
  
  // Clear all tables
  for (const tableName of tableNames) {
    await orm.getTable(tableName).clear();
  }
  
  // Reseed with fixtures
  await seedDatabase();
  
  console.log('Database reset complete');
}

// Use in tests or development
await resetDatabase();
```

---

## 💪 TypeScript Support

### Type-safe Schema

```typescript
interface User {
  id?: number;
  name: string;
  email: string;
  age: number;
  createdAt?: Date;
}

interface Post {
  id?: number;
  userId: number;
  title: string;
  content: string;
  published: boolean;
}

const schema = {
  users: {
    id: { type: 'number', primaryKey: true, autoIncrement: true },
    name: { type: 'string', required: true },
    email: { type: 'string', unique: true },
    age: { type: 'number' },
    createdAt: { type: 'date' }
  },
  posts: {
    id: { type: 'number', primaryKey: true, autoIncrement: true },
    userId: { type: 'number', indexed: true },
    title: { type: 'string', required: true },
    content: { type: 'string' },
    published: { type: 'boolean' }
  }
};

const orm = new LocalORM('myapp', schema);
await orm.initialize();

// Full type inference
const user: User = await orm.users.add({
  name: 'John',
  email: 'john@example.com',
  age: 30
});

// Type-safe queries
const adults: User[] = await orm.users
  .where('age').greaterThanOrEqual(18)
  .toArray();

// TypeScript will error on invalid fields
await orm.users.where('invalidField').equals(123); // ❌ Error
```

### Explicit Type Parameters

```typescript
// When using getTable()
const users = orm.getTable<User>('users');
const posts = orm.getTable<Post>('posts');

// Type-safe operations
const newUser: User = await users.add({
  name: 'Jane',
  email: 'jane@example.com',
  age: 25
});

const allPosts: Post[] = await posts
  .where('published').equals(true)
  .toArray();
```

---

## ⚡ Performance Tracking

### Enable Performance Tracking

```typescript
// Enable global performance tracking
orm.setPerformanceTracking(true);

// Execute queries - performance data is collected automatically
await orm.users.where('age').greaterThan(25).toArray();
await orm.orders.where('userId').equals(1).toArray();

// Check if enabled
const enabled = orm.isPerformanceTrackingEnabled();
```

### Get Index Suggestions

```typescript
// Get suggestions for missing indexes
const suggestions = orm.getIndexSuggestions();

console.log(suggestions);
// [
//   {
//     table: 'users',
//     field: 'age',
//     count: 150,
//     suggestion: 'Consider adding index on users.age (queried 150 times)'
//   }
// ]
```

### Field Usage Statistics

```typescript
// Get field usage statistics
const stats = orm.getFieldUsageStats();

console.log(stats);
// [
//   { table: 'users', field: 'age', count: 150, hasIndex: false },
//   { table: 'users', field: 'email', count: 80, hasIndex: true },
//   { table: 'orders', field: 'userId', count: 200, hasIndex: true }
// ]
```

### Reset Tracking

```typescript
// Reset all tracking statistics
orm.resetIndexTracking();
```

---

## 🎯 Complete Examples

### Todo App

```typescript
interface Todo {
  id?: number;
  title: string;
  completed: boolean;
  createdAt: Date;
}

const schema = {
  todos: {
    id: { type: 'number', primaryKey: true, autoIncrement: true },
    title: { type: 'string', required: true },
    completed: { type: 'boolean', default: false },
    createdAt: { type: 'date', default: () => new Date() }
  }
};

const orm = new LocalORM('todoapp', schema);
await orm.initialize();

// Add todo
await orm.todos.add({
  title: 'Learn LumarcDB',
  completed: false,
  createdAt: new Date()
});

// Get incomplete todos
const incomplete = await orm.todos
  .where('completed').equals(false)
  .orderByDesc('createdAt')
  .toArray();

// Mark as complete
await orm.todos.update(1, { completed: true });

// Delete completed
const completed = await orm.todos
  .where('completed').equals(true)
  .toArray();

for (const todo of completed) {
  await orm.todos.delete(todo.id!);
}

// Count todos
const total = await orm.todos.count();
const completedCount = await orm.todos
  .where('completed').equals(true)
  .count();
```

### E-commerce Shopping Cart

```typescript
interface CartItem {
  id?: number;
  productId: number;
  name: string;
  price: number;
  quantity: number;
  addedAt: Date;
}

const schema = {
  cart: {
    id: { type: 'number', primaryKey: true, autoIncrement: true },
    productId: { type: 'number', indexed: true },
    name: { type: 'string' },
    price: { type: 'number' },
    quantity: { type: 'number', default: 1 },
    addedAt: { type: 'date', default: () => new Date() }
  }
};

const orm = new LocalORM('shop', schema);
await orm.initialize();

// Add to cart
await orm.cart.add({
  productId: 123,
  name: 'Laptop',
  price: 999,
  quantity: 1,
  addedAt: new Date()
});

// Update quantity
const item = await orm.cart.get(1);
if (item) {
  await orm.cart.update(1, {
    quantity: item.quantity + 1
  });
}

// Get cart items
const items = await orm.cart.all().toArray();

// Calculate total
const total = items.reduce(
  (sum, item) => sum + item.price * item.quantity,
  0
);

// Clear cart
const allItems = await orm.cart.all().toArray();
for (const item of allItems) {
  await orm.cart.delete(item.id!);
}
```

---

## 📖 API Reference

### LocalORM Class

| Method | Description |
|--------|-------------|
| `initialize()` | Initialize database connection |
| `disconnect()` | Close database connection |
| `getTable<T>(name)` | Get table accessor with type |
| `getSchema()` | Get schema definition |
| `isReady()` | Check if database is initialized |
| `beginTransaction()` | Start manual transaction |
| `transaction()` | Execute auto-commit transaction |
| `readTransaction()` | Execute read-only transaction |
| `backup(table?, progress?)` | Backup database (download JSON) |
| `backupToObject(table?, progress?)` | Backup to object (no download) |
| `restore(file, options?, progress?)` | Restore from file |
| `restoreFromObject(data, options?, progress?)` | Restore from object |
| `setPerformanceTracking()` | Enable/disable tracking |
| `getIndexSuggestions()` | Get index suggestions |
| `getFieldUsageStats()` | Get field usage stats |
| `resetIndexTracking()` | Reset tracking data |

### TableAccessor Methods

| Method | Description |
|--------|-------------|
| `add(data)` | Insert record |
| `get(id)` | Get record by ID |
| `update(id, data)` | Update record |
| `delete(id)` | Delete record |
| `seed(data[], clearFirst?)` | Insert multiple records |
| `clear()` | Delete all records from table |
| `where(field)` | Start WHERE clause |
| `and(field)` | Add AND condition |
| `or(field)` | Add OR condition |
| `all()` | Get all records |
| `count()` | Count records |
| `sum(field)` | Sum field values |
| `avg(field)` | Average field values |
| `min(field)` | Minimum value |
| `max(field)` | Maximum value |
| `orderBy(field, dir)` | Sort results |
| `orderByDesc(field)` | Sort descending |
| `limit(n)` | Limit results |
| `offset(n)` | Skip records |
| `take(n)` | Alias for limit |
| `skip(n)` | Alias for offset |
| `groupBy(field)` | Group results |
| `having(field, op, val)` | Filter groups |
| `select(columns)` | Select columns |
| `aggregate(options)` | Advanced aggregations |
| `toArray()` | Execute and get results |

---

## 📄 License

MIT © [Your Name]

---

**Need help? Check out the [main documentation](../../README.md) or [open an issue](https://github.com/yourusername/lumarc-db/issues)!**