@decentrl/event-store
v0.5.4
Published
Event-driven storage and communication library for decentrl
Readme
@decentrl/event-store
A library for event-driven storage and communication in the decentrl ecosystem. This library abstracts away the complexity of event handling, encryption, and mediator communication, allowing you to focus on business logic.
Features
- Event Publishing: Store events locally and optionally send to recipients
- Event Querying: Retrieve stored events with filtering and pagination
- Pending Event Processing: Handle incoming events from other participants
- Automatic Encryption/Decryption: Transparent handling of cryptographic operations
- Type Safety: Full TypeScript support with generic event types
- Error Handling: Consistent error handling with detailed error information
Installation
pnpm add @decentrl/event-storeBasic Usage
1. Setup Event Store
import { DecentrlEventStore, EventStoreConfig } from '@decentrl/event-store';
import { useIdentityStore } from './stores/identityStore';
import { useSignedCommunicationContractsStore } from './stores/contractsStore';
const eventStoreConfig: EventStoreConfig = {
identity: {
did: identity.identityDid,
keys: identity.getIdentityKeys(),
mediatorEndpoint: identity.identityMediatorEndpoint,
mediatorDid: identity.mediatorDid,
},
communicationContracts: () => contractsStore.getActiveContracts(),
};
const eventStore = new DecentrlEventStore(eventStoreConfig);2. Define Event Types
interface ChatCreateEvent {
type: "chat.create";
data: {
id: string;
participants: [string, string];
createdBy: string;
createdAt: Date;
};
}
interface MessageSendEvent {
type: "message.send";
data: {
id: string;
chatId: string;
content: string;
sender: string;
timestamp: Date;
};
}
type ChatEvent = ChatCreateEvent | MessageSendEvent;3. Publish Events
// Publish event locally and send to recipient
await eventStore.publishEvent(chatCreateEvent, {
recipient: recipientDid,
tags: ["chat", `chat.${chatId}`, `participant.${recipientDid}`]
});
// Publish event locally only
await eventStore.publishEvent(messageEvent, {
tags: [`chat.${chatId}`]
});4. Query Events
// Query all chat creation events
const chatEvents = await eventStore.queryEvents<ChatCreateEvent>({
tags: ["chat"]
});
// Query messages for specific chat
const messageEvents = await eventStore.queryEvents<MessageSendEvent>({
tags: [`chat.${chatId}`]
});
// Query with pagination and filters
const events = await eventStore.queryEvents<ChatEvent>({
tags: ["chat"],
participantDid: "did:decentrl:participant123",
afterTimestamp: Date.now() - 86400000, // Last 24 hours
pagination: { page: 0, pageSize: 50 }
});5. Process Pending Events
// Process all pending events from known contacts
const newEvents = await eventStore.processPendingEvents<ChatEvent>();
newEvents.forEach(event => {
if (event.type === "chat.create") {
// Handle new chat
} else if (event.type === "message.send") {
// Handle new message
}
});API Reference
DecentrlEventStore
publishEvent<T>(event: T, options: PublishOptions): Promise<void>
Publishes an event, storing it locally and optionally sending to a recipient.
Parameters:
event: The event object to publishoptions.recipient?: DID of the recipient (if sending to someone)options.tags: Array of tags for querying the event later
queryEvents<T>(options?: QueryOptions): Promise<T[]>
Queries stored events from the mediator.
Parameters:
options.tags?: Filter by encrypted tagsoptions.participantDid?: Filter by participant DIDoptions.afterTimestamp?: Filter events after timestampoptions.beforeTimestamp?: Filter events before timestampoptions.pagination?: Pagination options
processPendingEvents<T>(): Promise<T[]>
Processes pending events from other participants and stores them locally.
Returns: Array of new events that were processed
Error Handling
The library provides structured error handling with the EventStoreError class:
import { EventStoreError } from '@decentrl/event-store';
try {
await eventStore.publishEvent(event, options);
} catch (error) {
if (error instanceof EventStoreError) {
console.error(`Event store error [${error.code}]:`, error.message);
console.error('Details:', error.details);
}
}Benefits Over Manual Implementation
- 90% Less Boilerplate: Eliminates repetitive encryption, command generation, and HTTP handling
- Consistent Error Handling: Centralized error handling with detailed error information
- Type Safety: Full TypeScript support with generic event types
- Testing: Much easier to unit test business logic without infrastructure concerns
- Maintainability: Clear separation between business logic and infrastructure
Example: Before vs After
Before (with manual implementation):
// 140+ lines of infrastructure code for sending a message
sendMessage: async (chatId: string, content: string) => {
// Identity validation...
// Contract lookup...
// Encryption key derivation...
// Event encryption for recipient...
// Command generation...
// HTTP request...
// Self-encryption...
// Tag generation...
// Local storage command...
// Another HTTP request...
// Error handling...
}After (with event store):
// ~15 lines focused on business logic
sendMessage: async (chatId: string, content: string) => {
const message = { id: uuid(), chatId, content, sender: userDid, timestamp: new Date() };
const event = { type: "message.send", data: message };
await eventStore.publishEvent(event, {
recipient: recipientDid,
tags: [`chat.${chatId}`]
});
addMessageToState(message);
}Integration with Zustand Stores
See example-integration.ts for a complete example of how to refactor an existing Zustand store to use the event store library.
