A framework-agnostic, local-first state and sync engine that provides a powerful wrapper around IndexedDB with advanced features like reactive subscriptions, offline-first sync, and type-safe queries.
- 🗄️ Enhanced IndexedDB: A powerful wrapper around IndexedDB with a clean, intuitive API
- 🔄 Offline-First: Built-in sync engine with conflict resolution and offline queue management
- ⚡ Type-Safe: Full TypeScript support with schema definition and query inference
- 🔌 Framework-Agnostic: Works with React, Vue, Svelte, or vanilla JavaScript
- 📱 Reactive: Observable subscriptions for real-time UI updates
- 🎯 Fluent Queries: Chainable query API with support for complex operations
- 🚀 Sync Engine: Built-in bidirectional synchronization with retry logic and exponential backoff
- 🔧 Pluggable: Adapter-based architecture supporting indexedDB and more in the future (WebSQL/SQLlite)
- 🛡️ Validation: Runtime schema validation with custom error messages
- @zenithdb/core - Core database engine with schema definition and queries
- @zenithdb/storage - Storage adapters (IndexedDB, and future SQL/SQLite)
- @zenithdb/sync - Backend-agnostic sync engine with offline support
- @zenithdb/types - TypeScript type definitions
- @zenithdb/utils - Utility functions and helpers
- @zenithdb/kit - Meta-package that installs everything you need
- Core Package - Database engine documentation
- Storage Package - Storage adapters documentation
- Sync Package - Sync engine documentation
# Complete package (recommended)
npm install @zenithdb/kit
# Or install individual packages
npm install @zenithdb/core @zenithdb/storage @zenithdb/sync @zenithdb/types
Note: The
@zenithdb/kit
package installs everything you need to get started with Zenith, including core functionality, storage adapters, and sync engine.
import { createDatabase, createSchema, fields } from "@zenithdb/core";
import { IndexedDBAdapter } from "@zenithdb/storage";
import { ZenithSyncEngine } from "@zenithdb/sync";
// Define your schema
const schema = createSchema({
name: "MyApp",
version: 1,
tables: {
users: {
primaryKey: "id",
schema: {
id: fields.string({ required: true }),
name: fields.string({ required: true }),
email: fields.string({ unique: true }),
createdAt: fields.timestamp({ defaultValue: () => Date.now() }),
},
},
},
adapter: "indexeddb",
});
// Create database
const db = await createDatabase(schema);
// Create a user
const user = await db.users.add({
id: "user-1",
name: "John Doe",
email: "john@example.com",
});
// Query with fluent API
const users = await db.users
.where("name")
.contains("John")
.sortBy("createdAt", "desc")
.limit(10)
.toArray();
// Subscribe to changes
db.users.subscribe((users) => {
console.log("Users updated:", users);
});
// Set up sync engine (optional)
const syncEngine = new ZenithSyncEngine(transport, adapter, {
conflictResolution: "server-wins",
autoSyncInterval: 30000,
});
await syncEngine.start();
import { createDatabase, createSchema, fields } from "@zenithdb/core";
import { createRestTransport } from "@zenithdb/sync";
const transport = createRestTransport({
baseUrl: "https://api.yourapp.com",
endpoints: {
push: "/sync/push",
pull: "/sync/pull",
},
headers: {
Authorization: "Bearer your-token",
},
});
// Create sync-enabled database schema
const schema = createSchema({
name: "SyncIntegrationTest",
version: 1,
tables: {
users: {
primaryKey: "id",
schema: {
id: fields.string({ required: true }),
name: fields.string({ required: true }),
email: fields.string({ required: true }),
createdAt: fields.timestamp({ defaultValue: () => Date.now() }),
updatedAt: fields.timestamp({ defaultValue: () => Date.now() }),
},
indexes: [{ name: "email", field: "email", unique: true }],
},
},
sync: {
transport,
config: {
conflictResolution: "server-wins",
autoSyncInterval: 5000,
batchSize: 10,
immediateSync: true,
immediateSyncDebounce: 100,
},
autoStart: false,
},
});
// Create and use sync-enabled database
const db = await createDatabase(schema);
// Start sync engine
await db.startSync();
// Perform operations that will be automatically synced
const user = await db.users.add({
id: "user-1",
name: "John Doe",
email: "john@example.com",
});
// Update operation
await db.users.update("user-1", { name: "John Smith" });
// Manual sync trigger
await db.syncNow();
// Check sync status
const status = db.getSyncStatus();
console.log("Sync status:", status);
import { createDatabase, createSchema, fields } from "@zenithdb/core";
import { createGraphQLTransport } from "@zenithdb/sync";
const graphqlTransport = createGraphQLTransport({
endpoint: "https://api.example.com/graphql",
headers: {
Authorization: "Bearer token",
},
mutations: {
pushData: `
mutation SyncPush($operations: [SyncOperationInput!]!) {
syncPush(operations: $operations) {
success
conflicts { id table localValue remoteValue }
}
}
`,
},
queries: {
pullData: `
query SyncPull($lastSync: DateTime) {
syncPull(since: $lastSync) {
operations { id table data timestamp }
cursor
}
}
`,
},
});
// Create schema with table-specific sync settings
const schema = createSchema({
name: "TableSyncControlTest",
version: 1,
tables: {
users: {
primaryKey: "id",
schema: {
id: fields.string({ required: true }),
name: fields.string({ required: true }),
email: fields.string({ required: true }),
createdAt: fields.timestamp({ defaultValue: () => Date.now() }),
},
},
posts: {
primaryKey: "id",
schema: {
id: fields.string({ required: true }),
title: fields.string({ required: true }),
content: fields.string({ required: true }),
authorId: fields.string({ required: true }),
published: fields.boolean({ defaultValue: false }),
createdAt: fields.timestamp({ defaultValue: () => Date.now() }),
},
},
},
sync: {
transport: graphqlTransport,
config: {
conflictResolution: "server-wins",
autoSyncInterval: 5000,
},
tableSettings: {
users: {
enabled: true,
autoSync: true,
syncDirection: "bidirectional", // Can sync both ways
},
posts: {
enabled: true,
autoSync: true,
syncDirection: "push-only", // Only push to server
},
},
autoStart: true,
},
});
const db = await createDatabase(schema);
// Operations on different tables with different sync behaviors
await db.users.add({
id: "user-1",
name: "Bob",
email: "bob@example.com",
});
await db.posts.add({
id: "post-1",
title: "Test Post",
content: "This is a test post",
authorId: "user-1",
});
// Both operations will be synced according to their table settings
- Use Indexes: Define indexes on frequently queried fields
- Batch Operations: Use
bulkAdd()
for multiple records - Subscribe Wisely: Unsubscribe from unused subscriptions
- Transaction Scope: Keep transactions as small as possible
- Query Optimization: Use specific queries instead of filtering large datasets
We welcome contributions! Please see our contributing guidelines for details.
# Clone the repository
git clone https://github.com/smithg09/zenithdb.git
cd zenithdb
# Install dependencies
pnpm install
# Instll types
pnpm build --filter=@zenithdb/types
# Build all packages
pnpm build
# Start development mode
pnpm dev
This project uses a monorepo structure with multiple packages:
- Each package has its own
package.json
and build configuration - Shared TypeScript configuration and build tools
- Turborepo for efficient task running
- Changesets for version management and publishing
MIT © Smith Gajjar