newOS for Developers
Cache System
IndexedDB-based caching layer using Dexie for persistent client-side storage.
packages/newgraph-signals/src/cache.ts
Overview
The cache system provides a graph-based storage layer built on IndexedDB via Dexie. It stores edges between entities (users, folders, posts) enabling efficient traversal and relationship queries.
import { Store } from "newgraph-signals";
// Create a store with custom tables
const cache = Store<MyTables>("my-cache", {
users: "id,username",
folders: "id,ownerId,updated"
});Store Class
Extended Dexie class with transaction queue management and edge storage capabilities.
Store<T>(
name: string = "iosdk-cache",
stores: Record<string, any>
): T & _StoreParameters
name- Database name (default: "iosdk-cache")stores- Table definitions with index schemas
Edge Storage
The cache uses a graph edge model to represent relationships between entities. All edges are stored in a special __EDGES table.
IGraphEdge Interface
type IGraphEdge = {
id?: string; // Edge identifier
fromLabel?: string; // Source entity type (e.g., "user")
toLabel?: string; // Target entity type (e.g., "folder")
from?: string; // Source entity ID
to?: string; // Target entity ID
label: string; // Relationship type (e.g., "owns", "contains")
props?: any; // Additional edge properties
};Edge Entry (Stored Format)
type IGraphEdgeEntry = IGraphEdge & {
cached: string; // ISO timestamp when cached
__outE: string; // Outbound edge key: "fromLabel+from+label+toLabel+to"
__inE: string; // Inbound edge key: "toLabel+to+label+fromLabel+from"
};The __outE and __inE composite keys enable efficient bidirectional traversal.
Edge Methods
| Method | Description |
|---|---|
| storeEdge(edge) | Store a single edge relationship |
| storeEdges(edges) | Bulk store multiple edges |
| storeEdgesDelayed(edges) | Bulk store with transaction queuing |
| deleteEdges(ids) | Remove edges by ID |
Usage Example
// Store a relationship
await cache.storeEdge({
fromLabel: "user",
from: "user123",
label: "owns",
toLabel: "folder",
to: "folder456"
});
// Bulk store multiple relationships
await cache.storeEdges([
{ fromLabel: "folder", from: "f1", label: "contains", toLabel: "post", to: "p1" },
{ fromLabel: "folder", from: "f1", label: "contains", toLabel: "post", to: "p2" }
]);Transaction Queue
The store manages concurrent IndexedDB transactions with a queue system to prevent overwhelming the database with too many parallel operations.
| Method | Description |
|---|---|
| block() | Request a transaction slot (waits if at max) |
| unblock() | Release a transaction slot |
| flushQueue(minItems?) | Wait for queue to drain |
DelayedTable Interface
Extended Dexie.Table with delayed bulk operations that respect the transaction queue.
interface DelayedTable<T, U> extends Dexie.Table<T, U> {
bulkAddDelayed: Dexie.Table["bulkAdd"];
bulkPutDelayed: Dexie.Table["bulkPut"];
}Use bulkAddDelayed and bulkPutDelayed for large batch operations to prevent transaction overflow.
Edges Table Schema
// Index definition
"++id,__outE,__inE,updated"
// Enables queries like:
// - Find all edges FROM a specific entity
cache.__EDGES.where("__outE").startsWith("user+user123+");
// - Find all edges TO a specific entity
cache.__EDGES.where("__inE").startsWith("folder+folder456+");Common Patterns
Cache with Action
// Actions automatically cache results
export function readFolder(params: { id: string }) {
const cache = db.folders.where("id").equals(params.id);
return progressiveHandler(
params,
cache,
async (progress, cache, params) => {
const result = await api.folder.read(params);
await db.folders.put(result); // Cache the result
return { done: true };
}
);
}Relationship Caching
// Cache folder-post relationships
async function cacheFolderPosts(folderId: string, posts: Post[]) {
await cache.storeEdges(
posts.map(post => ({
fromLabel: "folder",
from: folderId,
label: "contains",
toLabel: "post",
to: post.id
}))
);
}Imported Utilities
CountersUtilityTransaction counter for tracking concurrent operations.
XPromiseUtilityExternally resolvable promise for queue management.