Doc commit tag conventions
This commit is contained in:
@@ -66,6 +66,51 @@ export type PolicyFunction<T = any> = (
|
||||
newActivity: Activity<string, T>,
|
||||
) => Activity<string, T>;
|
||||
|
||||
/**
|
||||
* Implements an event sourcing pattern for managing application state.
|
||||
*
|
||||
* Event sourcing is an architectural pattern where:
|
||||
* - All changes to application state are stored as a sequence of immutable events
|
||||
* - The current state is derived by replaying these events in chronological order
|
||||
* - Events serve as both the source of truth and a complete audit log
|
||||
*
|
||||
* This implementation provides:
|
||||
* - Eventually consistent state across distributed systems
|
||||
* - Conflict resolution through configurable policies
|
||||
* - Point-in-time state reconstruction capability
|
||||
* - Separation between event storage and derived state (snapshot)
|
||||
*
|
||||
* Events (Activities) in this system:
|
||||
* - Have a unique identifier
|
||||
* - Include a timestamp for ordering
|
||||
* - Contain a type and associated value
|
||||
* - Are processed according to registered type-specific policies
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Create Activities manager with a store implementation
|
||||
* const activities = new Activities(new MyBaseStore());
|
||||
*
|
||||
* // Register a custom policy for wallet name conflicts
|
||||
* activities.registerPolicy('WalletName', (existing, newActivity) => {
|
||||
* // Keep the most recent wallet name
|
||||
* return existing?.timestamp! > newActivity.timestamp! ? existing : newActivity;
|
||||
* });
|
||||
*
|
||||
* // Initialize by compiling snapshot from stored events
|
||||
* await activities.compileSnapshot();
|
||||
*
|
||||
* // Add a new activity
|
||||
* await activities.addActivity({
|
||||
* type: 'WalletName',
|
||||
* id: 'wallet123',
|
||||
* value: 'My Primary Wallet'
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @template SnapshotByType - Structured representation of the current system state
|
||||
* @template PolicyFunction - Function type for conflict resolution strategies
|
||||
*/
|
||||
export class Activities {
|
||||
// TODO: Consider splitting into its own mutable class: ActivitySnapshot.
|
||||
// This might be cleaner as we'll likely want to be able to view a raw timeline of activities.
|
||||
@@ -110,6 +155,10 @@ export class Activities {
|
||||
storeKey: string,
|
||||
activity: Activity<string, any>,
|
||||
) {
|
||||
if (!activity) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!activity.timestamp) {
|
||||
console.warn('Activity without timestamp encountered:', storeKey);
|
||||
return;
|
||||
@@ -132,12 +181,12 @@ export class Activities {
|
||||
const oldStoreKey = this.storeKeyMap.get(snapshotKey);
|
||||
if (oldStoreKey) {
|
||||
// Delete the old activity from the store
|
||||
await this.store.delete(oldStoreKey);
|
||||
await this.store.set(oldStoreKey, null);
|
||||
}
|
||||
this.storeKeyMap.set(snapshotKey, storeKey);
|
||||
} else {
|
||||
// If we kept the existing activity, delete the new activity from the store
|
||||
await this.store.delete(storeKey);
|
||||
await this.store.set(storeKey, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,8 +216,11 @@ export class Activities {
|
||||
const activities = await this.store.all<Activity<string, any>>();
|
||||
const entries = Object.entries(activities);
|
||||
|
||||
// Filter for null payloads
|
||||
const filteredEntries = entries.filter(([_, payload]) => payload);
|
||||
|
||||
// Sort activities by timestamp using UUID v7's embedded timestamp
|
||||
const activitiesSorted = entries.sort(([keyA], [keyB]) => {
|
||||
const activitiesSorted = filteredEntries.sort(([keyA], [keyB]) => {
|
||||
const timestampA = getTimestampFromUUID(keyA);
|
||||
const timestampB = getTimestampFromUUID(keyB);
|
||||
return Number(timestampA - timestampB);
|
||||
|
||||
Reference in New Issue
Block a user