@zenithdb/core
v0.3.0
Published
Core state management and database engine for Zenith
Downloads
8
Maintainers
Readme
ZenithDB Core
Core state management and database engine that provides a powerful wrapper around IndexedDB with type-safe schemas, fluent queries, and reactive subscriptions.
Description
@zenithdb/core is the heart of the ZenithDB ecosystem, providing an enhanced wrapper around IndexedDB with features like type-safe schema definition, fluent query APIs, reactive subscriptions, and automatic validation. It transforms the complex IndexedDB API into an intuitive, developer-friendly interface while maintaining full TypeScript support.
Key Features
- 🗄️ Enhanced IndexedDB: Clean, intuitive API wrapper around IndexedDB
- 🎯 Type-Safe Schemas: Full TypeScript inference with field helpers
- 🔄 Fluent Queries: Chainable query API with natural syntax
- 📊 Reactive Subscriptions: Observable pattern for real-time UI updates
- 🛡️ Validation: Runtime schema validation with detailed error messages
- 🔧 Transactions: Built-in transaction management with rollback support
- 🔗 Sync Engine: Built-in bidirectional synchronization with retry logic and exponential backoff
- 📈 Performance: Optimized bulk operations and in-memory caching
Installation
NPM
# Install core package
npm install @zenithdb/core @zenithdb/storage @zenithdb/types
# Or install everything at once
npm install @zenithdb/kitNote: For complete functionality, consider using
@zenithdb/kitwhich includes core, storage adapters, and sync engine.
Quick Start
Basic Database Setup
import { createDatabase, createSchema, fields } from "@zenithdb/core";
// Define your schema with type safety
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 }),
age: fields.number({ min: 0 }),
createdAt: fields.timestamp({ defaultValue: () => Date.now() }),
preferences: fields.object({ defaultValue: {} }),
},
},
posts: {
primaryKey: "id",
schema: {
id: fields.string({ required: true }),
title: fields.string({ required: true }),
content: fields.string(),
authorId: fields.string({ required: true }),
published: fields.boolean({ defaultValue: false }),
tags: fields.array({ defaultValue: [] }),
},
},
},
adapter: "indexeddb",
});
// Create database instance
const db = await createDatabase(schema);CRUD Operations
// Create records
const user = await db.users.add({
id: "user-1",
name: "John Doe",
email: "[email protected]",
age: 30,
});
// Read with fluent queries
const users = await db.users
.where("age")
.above(18)
.where("name")
.contains("John")
.sortBy("createdAt", "desc")
.limit(10)
.toArray();
// Update records
await db.users.update("user-1", { age: 31 });
// Delete records
await db.users.delete("user-1");Reactive Subscriptions
// Subscribe to all users
const unsubscribe = db.users.subscribe((users) => {
console.log("Users updated:", users);
updateUI(users);
});
// Subscribe to filtered results
db.users
.where("age")
.above(21)
.subscribe((adults) => {
console.log("Adult users:", adults);
});
// Subscribe to single record
db.users
.where("id")
.equals("user-1")
.subscribeOne((user) => {
if (user) {
console.log("User updated:", user);
}
});
// Cleanup when done
unsubscribe();Advanced Queries
// Complex AND/OR logic
const results = await db.users
.where("age")
.between(18, 65)
.and("name")
.contains("John")
.or("email")
.contains("example.com")
.sortBy("createdAt", "desc")
.toArray();
// Bulk operations
await db.users.bulkAdd([
{ id: "1", name: "Alice", email: "[email protected]" },
{ id: "2", name: "Bob", email: "[email protected]" },
{ id: "3", name: "Charlie", email: "[email protected]" },
]);Transactions
// Manual transaction management
const adapter = db.getAdapter();
const transaction = await adapter.beginTransaction(
["users", "posts"],
"readwrite",
);
try {
const user = await transaction.insert("users", {
id: "user-2",
name: "Jane Doe",
email: "[email protected]",
});
await transaction.insert("posts", {
id: "post-1",
title: "Hello World",
authorId: user.id,
content: "This is my first post!",
});
await transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}Sync Integration
Basic Sync Integration
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: "[email protected]",
});
// 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);Table-Level Sync Control
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: "[email protected]",
});
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 settingsFramework Integration Examples
React Integration
import { createDatabase, createSchema, fields } from "@zenithdb/kit";
import { useState, useEffect } from "react";
// Custom hook
function useZenithTable(db: any, tableName: string) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadData = async () => {
const items = await db[tableName].toArray();
setData(items);
setLoading(false);
};
loadData();
// Subscribe to changes
const unsubscribe = db[tableName].subscribe(setData);
return unsubscribe;
}, [db, tableName]);
return { data, loading };
}
// Component
function UsersList({ db }: { db: any }) {
const { data: users, loading } = useZenithTable(db, "users");
if (loading) return <div>Loading...</div>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name} - {user.email}
</li>
))}
</ul>
);
}Performance Tips
- 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
Contributing
Development
# Install dependencies
pnpm install
# Instll types
pnpm build --filter=@zenithdb/types
# Build the package
pnpm build
# Development mode
pnpm devLicense
MIT © Smith Gajjar
