{"id":24807646,"url":"https://github.com/maifeeulasad/idb-ts","last_synced_at":"2026-05-31T08:01:24.933Z","repository":{"id":272948717,"uuid":"917822389","full_name":"maifeeulasad/idb-ts","owner":"maifeeulasad","description":"Use IndexedDB with TypeScript in a declarative style","archived":false,"fork":false,"pushed_at":"2025-10-09T06:34:26.000Z","size":5223,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-19T15:42:56.996Z","etag":null,"topics":["crud","database","database-management","db","idb","indexed-db","indexed-db-orm","indexeddb","indexeddb-api","indexeddb-examples","indexeddb-tools","indexeddb-wrapper","reflect","reflect-metadata","storage","ts","typescript","typescript-library"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/idb-ts","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maifeeulasad.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-01-16T17:56:47.000Z","updated_at":"2025-10-11T21:10:42.000Z","dependencies_parsed_at":null,"dependency_job_id":"b5226f75-a321-4031-a9e8-b3e122385008","html_url":"https://github.com/maifeeulasad/idb-ts","commit_stats":null,"previous_names":["maifeeulasad/idb-ts","maifeeulasad/idb-crud"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/maifeeulasad/idb-ts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maifeeulasad%2Fidb-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maifeeulasad%2Fidb-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maifeeulasad%2Fidb-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maifeeulasad%2Fidb-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maifeeulasad","download_url":"https://codeload.github.com/maifeeulasad/idb-ts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maifeeulasad%2Fidb-ts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29809086,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-24T22:43:48.403Z","status":"online","status_checked_at":"2026-02-25T02:00:07.329Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crud","database","database-management","db","idb","indexed-db","indexed-db-orm","indexeddb","indexeddb-api","indexeddb-examples","indexeddb-tools","indexeddb-wrapper","reflect","reflect-metadata","storage","ts","typescript","typescript-library"],"created_at":"2025-01-30T09:27:16.963Z","updated_at":"2026-05-31T08:01:24.925Z","avatar_url":"https://github.com/maifeeulasad.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🚀 idb-ts\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/idb-ts\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/idb-ts.svg\" alt=\"npm version\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://badgen.net/bundlephobia/min/idb-ts\"\u003e\n    \u003cimg src=\"https://badgen.net/bundlephobia/min/idb-ts\u0026cache-control=no-cache\" alt=\"minified\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://badgen.net/bundlephobia/minzip/idb-ts\"\u003e\n    \u003cimg src=\"https://badgen.net/bundlephobia/minzip/idb-ts\u0026cache-control=no-cache\" alt=\"minified + gzipped\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/maifeeulasad/idb-ts/stargazers\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/stars/maifeeulasad/idb-ts\" alt=\"GitHub stars\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/maifeeulasad/idb-ts/watchers\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/watchers/maifeeulasad/idb-ts\" alt=\"GitHub watchers\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://img.shields.io/github/commits-since/maifeeulasad/idb-ts/latest/main?include_prereleases\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/commits-since/maifeeulasad/idb-ts/latest/main?include_prereleases\" alt=\"Commits after release\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\n## 📌 Introduction\n**idb-ts** is a lightweight, declarative, and type-safe way to work with IndexedDB using TypeScript. Effortlessly perform CRUD operations on your database with clean, structured code! 🔥\n\n## 📦 Installation\nInstall via npm and start using IndexedDB like a pro! ⚡\n```sh\nnpm i idb-ts     # for pure npm users\npnpm add idb-ts  # for pnpm users\nyarn add idb-ts  # for yarn users\n```\n\n## ✨ Features\n- ✅ **Declarative \u0026 Type-Safe** - Define your data models with decorators.\n- ⚡ **Easy CRUD Operations** - Perform create, read, update, and delete seamlessly.\n- 🚀 **Fully Typed API** - Benefit from TypeScript’s powerful type system.\n- 🏎️ **Performance Optimized** - Minimal overhead with IndexedDB's native capabilities.\n- 🔄 **Schema Versioning** - Manage database schema evolution with automatic migration support.\n- 🔑 **Advanced Key Management** - Auto-increment, UUID, timestamp, custom generators, and composite keys.\n\n---\n\n## 📖 Example Usage\n\n### 🏗️ Declaring Entities\nUse decorators to define your data models. Each class must have exactly one `@KeyPath()` and be decorated with `@DataClass()`.\n\n```typescript\nimport { Database, DataClass, KeyPath, Index } from \"idb-ts\";\n\n@DataClass()\nclass User {\n  @KeyPath()\n  id!: string;\n\n  @Index()\n  email!: string;\n\n  name!: string;\n  age!: number;\n\n  constructor(id: string, name: string, age: number, email?: string) {\n    this.id = id;\n    this.name = name;\n    this.age = age;\n    this.email = email || `${name.toLowerCase()}@example.com`;\n  }\n}\n\n@DataClass()\nclass Location {\n  @KeyPath()\n  id!: string;\n\n  @Index()\n  city!: string;\n\n  country!: string;\n\n  constructor(id: string, city: string, country: string) {\n    this.id = id;\n    this.city = city;\n    this.country = country;\n  }\n}\n```\n\n### 🔄 CRUD Operations\nPerform database operations using the repository API:\n\n```typescript\nconst db = await Database.build(\"idb-crud\", [User, Location]);\n\nconst alice = new User(\"u1\", \"Alice\", 25);\nconst bob = new User(\"u2\", \"Bob\", 30);\nconst nyc = new Location(\"1\", \"New York\", \"USA\");\nconst sf = new Location(\"2\", \"San Francisco\", \"USA\");\n\nawait db.User.create(alice);\nawait db.User.create(bob);\nawait db.Location.create(nyc);\nawait db.Location.create(sf);\n\nconst readAlice = await db.User.read(\"u1\");\nconsole.log(\"👤 Read user:\", readAlice);\n\nalice.age = 26;\nawait db.User.update(alice);\n\nconst users = await db.User.list();\nconsole.log(\"📋 All users:\", users);\n\n// Pagination\nconst page1 = await db.User.listPaginated(1, 2); // page 1, 2 users per page\nconsole.log(\"📄 Page 1:\", page1);\n\nawait db.User.delete(\"u1\");\nconsole.log(\"❌ User Alice deleted.\");\n\nconst remainingUsers = await db.User.list();\nconsole.log(\"🔍 Remaining users:\", remainingUsers);\n\nconst locations = await db.Location.list();\nconsole.log(\"🌍 All locations:\", locations);\n```\n\n### 🔍 Indexing Support\nCreate indexes on fields for fast querying. Query indexes using the repository API:\n\n```typescript\n@DataClass()\nclass Product {\n  @KeyPath()\n  id!: string;\n\n  @Index()\n  category!: string;\n\n  @Index()\n  price!: number;\n\n  name!: string;\n  description!: string;\n\n  constructor(id: string, category: string, price: number, name: string, description: string) {\n    this.id = id;\n    this.category = category;\n    this.price = price;\n    this.name = name;\n    this.description = description;\n  }\n}\n\nconst db = await Database.build(\"products-db\", [Product]);\n\nconst electronics = await db.Product.findByIndex('category', 'Electronics');\nconst expensiveItems = await db.Product.findByIndex('price', 999.99);\nconst firstElectronic = await db.Product.findOneByIndex('category', 'Electronics');\n```\n\n#### Index Methods:\n- `findByIndex(indexName, value): Promise\u003cT[]\u003e` - Find all records matching the index value\n- `findOneByIndex(indexName, value): Promise\u003cT | undefined\u003e` - Find the first record matching the index value\n\n### Creation \u0026 Update Timestamps\n\nEach entity managed by `idb-ts` automatically gets two internal timestamp fields:\n\n- `__idb_createdAt`: numeric epoch milliseconds set when the record is first created.\n- `__idb_updatedAt`: numeric epoch milliseconds updated on each successful update.\n\nThese fields are applied automatically during `create` and `update` operations and can be used for auditing, sorting, or retention policies. They are stored as numbers (milliseconds since Unix epoch).\n\nExample usage (reading timestamps):\n\n```ts\nconst item = await db.MyEntity.read('key');\nconsole.log(item.__idb_createdAt, item.__idb_updatedAt);\n```\n\n### Retention Policy \u0026 Cleanup Job\n\n`idb-ts` supports per-entity data retention via the `@RetentionPolicy()` class decorator. It accepts the following options:\n\n- `seconds` (required): number of seconds after which records are considered expired.\n- `enabled` (optional, default `true`): whether cleanup is active for this entity.\n- `field` (optional, default `__idb_createdAt`): the numeric field to use for age calculation (usually creation timestamp).\n\nWhen one or more entities register retention policies, the library computes a single cleanup interval equal to the greatest common divisor (GCD) of all configured `seconds` values and runs a background cleanup job at that interval. On each tick the job scans the configured entity stores and deletes records whose `field` value is older than the configured retention window.\n\nExample:\n\n```ts\n@RetentionPolicy({ seconds: 60 * 60 * 24 * 30 }) // 30 days\n@DataClass()\nclass Session { /* ... */ }\n\n// Database will run a periodic cleanup that removes sessions older than 30 days\n```\n\nNotes:\n\n- Cleanup runs with readwrite transactions and deletes records one-by-one via cursors. It runs at startup and then periodically. Logs are emitted for inspection when debug logging is enabled.\n- To temporarily disable cleanup for an entity, set `enabled: false` on the decorator.\n\n### Field Validation\n\nYou can declare validation rules for individual properties using the `@Validate(predicate, message)` property decorator. Each rule must provide a predicate function that receives the property value and the full item and returns `true` when valid.\n\nValidation is enforced on `create` and `update` operations. If any rule fails, the repository operation throws an error with a concise message describing the failing fields.\n\nExample:\n\n```ts\n@DataClass()\nclass User {\n  @KeyPath()\n  id!: string;\n\n  @Validate((v) =\u003e typeof v === 'string' \u0026\u0026 v.includes('@'), 'must be a valid email')\n  email!: string;\n\n  @Validate((v) =\u003e typeof v === 'number' \u0026\u0026 v \u003e= 0, 'age must be \u003e= 0')\n  age!: number;\n}\n\nawait db.User.create(new User('u1', 'alice@example.com', 30));\n```\n\nThe thrown error contains all failing rules in the format `field: message` joined by `; `.\n\n### Bulk Operations\n\nRepositories include convenience bulk helpers for common batch operations:\n\n- `createMany(items: T[])`: creates multiple items (runs validators and generators for each item).\n- `updateMany(items: T[])`: updates multiple items.\n- `deleteMany(keys: Array\u003cstring | string[] | number\u003e)`: deletes multiple keys.\n\nThese helpers are implemented by iterating the corresponding single-item operations. They are convenient for simple bulk workloads but are not currently implemented as a single atomic transaction across all items. For high-throughput or atomic requirements, consider batching items into a single transaction or performing multiple operations inside a custom `performOperation` call.\n\nExample:\n\n```ts\nawait db.User.createMany([alice, bob, charlie]);\nawait db.User.deleteMany(['u1', 'u2']);\n```\n\nPerformance note: `createMany` will trigger validation and key generation per item. If you need large batch inserts frequently, batching these into a single transaction or adding a dedicated bulk API may improve throughput.\n\n#### Error Handling\n- If you query a non-existent index, an error is thrown:\n  ```typescript\n  await db.Product.findByIndex('nonexistent', 'value'); // throws\n  ```\n\n---\n\n## 🔑 Multi-Field \u0026 Composite Key Support\n\nidb-ts provides flexible key management options including auto-increment keys, key generators, and composite keys for complex data relationships.\n\n### Auto-Increment Keys\nPerfect for entities where you want the database to automatically generate sequential IDs:\n\n```typescript\n@DataClass()\nclass Task {\n  @KeyPath({ autoIncrement: true })\n  id!: number;\n\n  title!: string;\n  completed!: boolean;\n\n  constructor(title: string, completed = false) {\n    this.title = title;\n    this.completed = completed;\n  }\n}\n\nconst db = await Database.build(\"tasks-db\", [Task]);\n\n// IDs are automatically generated: 1, 2, 3, etc.\nconst task1 = await db.Task.create(new Task(\"Learn TypeScript\"));\nconst task2 = await db.Task.create(new Task(\"Build amazing apps\"));\nconsole.log(task1.id); // 1\nconsole.log(task2.id); // 2\n```\n\n### Key Generators\nGenerate keys automatically using built-in generators:\n\n#### UUID Keys\n```typescript\n@DataClass()\nclass Document {\n  @KeyPath({ generator: 'uuid' })\n  uuid!: string;\n\n  @Index()\n  category!: string;\n\n  title!: string;\n  content!: string;\n\n  constructor(category: string, title: string, content: string) {\n    this.category = category;\n    this.title = title;\n    this.content = content;\n  }\n}\n\nconst db = await Database.build(\"docs-db\", [Document]);\n\nconst doc = await db.Document.create(new Document(\"tutorial\", \"Getting Started\", \"Welcome...\"));\nconsole.log(doc.uuid); // e.g., \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\"\n```\n\n#### Timestamp Keys\n```typescript\n@DataClass()\nclass Event {\n  @KeyPath({ generator: 'timestamp' })\n  timestamp!: number;\n\n  @Index()\n  type!: string;\n\n  data!: any;\n\n  constructor(type: string, data: any) {\n    this.type = type;\n    this.data = data;\n  }\n}\n\nconst event = await db.Event.create(new Event(\"user_login\", { userId: \"123\" }));\nconsole.log(event.timestamp); // e.g., 1696118400000\n```\n\n#### Random Keys\n```typescript\n@DataClass()\nclass Session {\n  @KeyPath({ generator: 'random' })\n  sessionId!: string;\n\n  userId!: string;\n  expiresAt!: Date;\n\n  constructor(userId: string, expiresAt: Date) {\n    this.userId = userId;\n    this.expiresAt = expiresAt;\n  }\n}\n\nconst session = await db.Session.create(new Session(\"user123\", new Date()));\nconsole.log(session.sessionId); // e.g., \"xyz789abc123\"\n```\n\n### Custom Key Generators\nCreate your own key generation logic:\n\n```typescript\n@DataClass()\nclass Invoice {\n  @KeyPath({ generator: (entity: any) =\u003e `INV-${entity.year}-${String(entity.number).padStart(4, '0')}` })\n  invoiceId!: string;\n\n  year!: number;\n  number!: number;\n  amount!: number;\n\n  constructor(year: number, number: number, amount: number) {\n    this.year = year;\n    this.number = number;\n    this.amount = amount;\n  }\n}\n\nconst invoice = await db.Invoice.create(new Invoice(2024, 1, 1500.00));\nconsole.log(invoice.invoiceId); // \"INV-2024-0001\"\n```\n\n### Composite Keys\nHandle many-to-many relationships with composite keys using the `@CompositeKeyPath` decorator:\n\n```typescript\nimport { CompositeKeyPath } from \"idb-ts\";\n\n@CompositeKeyPath(['userId', 'projectId'])\n@DataClass()\nclass UserProject {\n  userId!: string;\n  projectId!: string;\n\n  @Index()\n  role!: string;\n\n  joinedAt!: Date;\n\n  constructor(userId: string, projectId: string, role: string) {\n    this.userId = userId;\n    this.projectId = projectId;\n    this.role = role;\n    this.joinedAt = new Date();\n  }\n}\n\nconst db = await Database.build(\"collaboration-db\", [UserProject]);\n\n// Create relationships\nawait db.UserProject.create(new UserProject(\"user123\", \"project456\", \"developer\"));\nawait db.UserProject.create(new UserProject(\"user123\", \"project789\", \"admin\"));\nawait db.UserProject.create(new UserProject(\"user456\", \"project456\", \"viewer\"));\n\n// Read with composite key\nconst relationship = await db.UserProject.read(['user123', 'project456']);\nconsole.log(relationship?.role); // \"developer\"\n\n// Update relationship\nif (relationship) {\n  relationship.role = \"maintainer\";\n  await db.UserProject.update(relationship);\n}\n\n// Delete with composite key\nawait db.UserProject.delete(['user123', 'project789']);\n\n// Query by role index\nconst developers = await db.UserProject.findByIndex('role', 'developer');\n```\n\n### Key Generation Utilities\nAccess key generators directly for your custom logic:\n\n```typescript\nimport { KeyGenerators } from \"idb-ts\";\n\nconst uuid = KeyGenerators.uuid();        // Generate UUID\nconst timestamp = KeyGenerators.timestamp(); // Current timestamp\nconst random = KeyGenerators.random();    // Random string\n```\n\n---\n\n## 🔄 Schema Versioning\n\nidb-ts supports schema versioning to manage database evolution over time. Version your entities and let the library handle automatic migration!\n\n### Basic Usage\n\n```typescript\n@DataClass({ version: 1 })\nclass User {\n  @KeyPath() id!: string;\n  @Index() email!: string;\n  name!: string;\n}\n\n@DataClass({ version: 2 })\nclass Post {\n  @KeyPath() id!: string;\n  @Index() authorId!: string;\n  title!: string;\n  content!: string;\n}\n\n@DataClass({ version: 3 })\nclass Comment {\n  @KeyPath() id!: string;\n  @Index() postId!: string;\n  @Index() authorId!: string;\n  text!: string;\n}\n\n// Database version will be 3 (highest entity version)\nconst db = await Database.build(\"blog\", [User, Post, Comment]);\n\nconsole.log(db.getDatabaseVersion()); // 3\nconsole.log(db.getEntityVersions()); // Map with entity versions\n```\n\n### Key Features\n\n- **Automatic Version Calculation**: Database version = highest entity version\n- **Seamless Migration**: Only new/updated entities are processed during upgrades\n- **Backward Compatibility**: Entities without version default to version 1\n- **Index Evolution**: New indexes are automatically created during migration\n\n### Version Management\n\n```typescript\n// Check versions\nconst dbVersion = db.getDatabaseVersion();\nconst entityVersions = db.getEntityVersions();\nconst userVersion = db.getEntityVersion('User');\n\n// Version upgrade flow:\n// v1.0: User(v1) -\u003e Database v1\n// v1.1: User(v1), Post(v2) -\u003e Database v2  \n// v1.2: User(v1), Post(v2), Comment(v3) -\u003e Database v3\n```\n\n---\n\n## 🔗 Useful Links\n- 📂 **GitHub**: [maifeeulasad/idb-ts](https://github.com/maifeeulasad/idb-ts)\n- 📦 **NPM**: [idb-ts](https://www.npmjs.com/package/idb-ts)\n- Demo: https://maifeeulasad.github.io/idb-ts/\n- Code Coverage report: https://maifeeulasad.github.io/idb-ts/coverage/lcov-report/\n\n🎉 **Enjoy seamless IndexedDB integration with TypeScript! Happy coding!** 🚀\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaifeeulasad%2Fidb-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaifeeulasad%2Fidb-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaifeeulasad%2Fidb-ts/lists"}