190 lines
5.3 KiB
TypeScript
190 lines
5.3 KiB
TypeScript
import Debug from 'debug';
|
|
import { Delta, DeltaID, DeltaFilter } from '../core/delta';
|
|
import { DomainEntityID } from '../core/types';
|
|
import { DeltaQueryStorage, DeltaQuery, StorageStats } from './interface';
|
|
|
|
const debug = Debug('rz:storage:memory');
|
|
|
|
/**
|
|
* In-memory delta storage implementation
|
|
* Fast but non-persistent, suitable for development and testing
|
|
*/
|
|
export class MemoryDeltaStorage implements DeltaQueryStorage {
|
|
private deltas = new Map<DeltaID, Delta>();
|
|
private entityIndex = new Map<DomainEntityID, Set<DeltaID>>();
|
|
private contextIndex = new Map<string, Set<DeltaID>>(); // entityId:context -> deltaIds
|
|
|
|
async storeDelta(delta: Delta): Promise<void> {
|
|
debug(`Storing delta ${delta.id}`);
|
|
|
|
// Store the delta
|
|
this.deltas.set(delta.id, delta);
|
|
|
|
// Update entity index
|
|
for (const pointer of delta.pointers) {
|
|
if (typeof pointer.target === 'string' && pointer.targetContext) {
|
|
const entityId = pointer.target;
|
|
|
|
// Add to entity index
|
|
if (!this.entityIndex.has(entityId)) {
|
|
this.entityIndex.set(entityId, new Set());
|
|
}
|
|
this.entityIndex.get(entityId)!.add(delta.id);
|
|
|
|
// Add to context index
|
|
const contextKey = `${entityId}:${pointer.targetContext}`;
|
|
if (!this.contextIndex.has(contextKey)) {
|
|
this.contextIndex.set(contextKey, new Set());
|
|
}
|
|
this.contextIndex.get(contextKey)!.add(delta.id);
|
|
}
|
|
}
|
|
}
|
|
|
|
async getDelta(id: DeltaID): Promise<Delta | null> {
|
|
return this.deltas.get(id) || null;
|
|
}
|
|
|
|
async getAllDeltas(filter?: DeltaFilter): Promise<Delta[]> {
|
|
let results = Array.from(this.deltas.values());
|
|
|
|
if (filter) {
|
|
results = results.filter(filter);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
async getDeltasForEntity(entityId: DomainEntityID): Promise<Delta[]> {
|
|
const deltaIds = this.entityIndex.get(entityId);
|
|
if (!deltaIds) return [];
|
|
|
|
const results: Delta[] = [];
|
|
for (const deltaId of deltaIds) {
|
|
const delta = this.deltas.get(deltaId);
|
|
if (delta) {
|
|
results.push(delta);
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
async getDeltasByContext(entityId: DomainEntityID, context: string): Promise<Delta[]> {
|
|
const contextKey = `${entityId}:${context}`;
|
|
const deltaIds = this.contextIndex.get(contextKey);
|
|
if (!deltaIds) return [];
|
|
|
|
const results: Delta[] = [];
|
|
for (const deltaId of deltaIds) {
|
|
const delta = this.deltas.get(deltaId);
|
|
if (delta) {
|
|
results.push(delta);
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
async queryDeltas(query: DeltaQuery): Promise<Delta[]> {
|
|
let results = Array.from(this.deltas.values());
|
|
|
|
// Apply filters
|
|
if (query.creator) {
|
|
results = results.filter(d => d.creator === query.creator);
|
|
}
|
|
|
|
if (query.host) {
|
|
results = results.filter(d => d.host === query.host);
|
|
}
|
|
|
|
if (query.timeCreatedAfter) {
|
|
results = results.filter(d => d.timeCreated >= query.timeCreatedAfter!);
|
|
}
|
|
|
|
if (query.timeCreatedBefore) {
|
|
results = results.filter(d => d.timeCreated <= query.timeCreatedBefore!);
|
|
}
|
|
|
|
if (query.targetEntities && query.targetEntities.length > 0) {
|
|
const targetSet = new Set(query.targetEntities);
|
|
results = results.filter(d =>
|
|
d.pointers.some(p => typeof p.target === 'string' && targetSet.has(p.target))
|
|
);
|
|
}
|
|
|
|
if (query.contexts && query.contexts.length > 0) {
|
|
const contextSet = new Set(query.contexts);
|
|
results = results.filter(d =>
|
|
d.pointers.some(p => p.targetContext && contextSet.has(p.targetContext))
|
|
);
|
|
}
|
|
|
|
// Sort by creation time
|
|
results.sort((a, b) => a.timeCreated - b.timeCreated);
|
|
|
|
// Apply pagination
|
|
if (query.offset) {
|
|
results = results.slice(query.offset);
|
|
}
|
|
|
|
if (query.limit) {
|
|
results = results.slice(0, query.limit);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
async countDeltas(query: DeltaQuery): Promise<number> {
|
|
const results = await this.queryDeltas({ ...query, limit: undefined, offset: undefined });
|
|
return results.length;
|
|
}
|
|
|
|
async getStats(): Promise<StorageStats> {
|
|
const deltas = Array.from(this.deltas.values());
|
|
const entities = new Set<DomainEntityID>();
|
|
|
|
let oldestDelta: number | undefined;
|
|
let newestDelta: number | undefined;
|
|
|
|
for (const delta of deltas) {
|
|
// Track entities
|
|
for (const pointer of delta.pointers) {
|
|
if (typeof pointer.target === 'string' && pointer.targetContext) {
|
|
entities.add(pointer.target);
|
|
}
|
|
}
|
|
|
|
// Track time range
|
|
if (!oldestDelta || delta.timeCreated < oldestDelta) {
|
|
oldestDelta = delta.timeCreated;
|
|
}
|
|
if (!newestDelta || delta.timeCreated > newestDelta) {
|
|
newestDelta = delta.timeCreated;
|
|
}
|
|
}
|
|
|
|
return {
|
|
totalDeltas: this.deltas.size,
|
|
totalEntities: entities.size,
|
|
oldestDelta,
|
|
newestDelta
|
|
};
|
|
}
|
|
|
|
async close(): Promise<void> {
|
|
debug('Closing memory storage');
|
|
this.deltas.clear();
|
|
this.entityIndex.clear();
|
|
this.contextIndex.clear();
|
|
}
|
|
|
|
// Memory-specific methods for inspection
|
|
getInternalState() {
|
|
return {
|
|
deltasCount: this.deltas.size,
|
|
entitiesCount: this.entityIndex.size,
|
|
contextsCount: this.contextIndex.size
|
|
};
|
|
}
|
|
} |