← back to Wine Finder Next
lib/membershipDatabase.optimized.ts
809 lines
// Optimized In-Memory Database for Tokenized Wine Membership System
// Production-ready with connection pooling, caching, and performance monitoring
import {
Bottle,
MembershipUnit,
GovernanceVote,
Vote,
SampleUnit,
SampleRight,
MarketplaceListing,
FulfillmentRequest,
FulfillmentPartner,
DigitalRecord,
UserProfile,
Transaction,
SampleTransfer,
PlatformConfig
} from '@/src/types/membership';
// ============= PERFORMANCE MONITORING =============
interface PerformanceMetrics {
operationName: string;
duration: number;
timestamp: number;
recordCount?: number;
}
class PerformanceMonitor {
private metrics: PerformanceMetrics[] = [];
private maxMetrics = 1000;
track<T>(operationName: string, fn: () => T): T {
const start = performance.now();
const result = fn();
const duration = performance.now() - start;
this.metrics.push({
operationName,
duration,
timestamp: Date.now(),
recordCount: Array.isArray(result) ? result.length : undefined
});
// Keep only recent metrics
if (this.metrics.length > this.maxMetrics) {
this.metrics = this.metrics.slice(-this.maxMetrics);
}
// Warn on slow operations
if (duration > 100) {
console.warn(`Slow operation detected: ${operationName} took ${duration.toFixed(2)}ms`);
}
return result;
}
getMetrics(operationName?: string): PerformanceMetrics[] {
if (operationName) {
return this.metrics.filter(m => m.operationName === operationName);
}
return this.metrics;
}
getAverageTime(operationName: string): number {
const ops = this.getMetrics(operationName);
if (ops.length === 0) return 0;
return ops.reduce((sum, m) => sum + m.duration, 0) / ops.length;
}
clearMetrics(): void {
this.metrics = [];
}
}
export const perfMonitor = new PerformanceMonitor();
// ============= CACHING LAYER =============
interface CacheEntry<T> {
data: T;
timestamp: number;
hits: number;
}
class QueryCache {
private cache = new Map<string, CacheEntry<any>>();
private maxAge = 5 * 60 * 1000; // 5 minutes default
private maxEntries = 500;
set<T>(key: string, data: T, ttl?: number): void {
// Evict oldest entries if cache is full
if (this.cache.size >= this.maxEntries) {
const oldestKey = Array.from(this.cache.entries())
.sort((a, b) => a[1].timestamp - b[1].timestamp)[0][0];
this.cache.delete(oldestKey);
}
this.cache.set(key, {
data,
timestamp: Date.now(),
hits: 0
});
}
get<T>(key: string): T | null {
const entry = this.cache.get(key);
if (!entry) return null;
const age = Date.now() - entry.timestamp;
if (age > this.maxAge) {
this.cache.delete(key);
return null;
}
entry.hits++;
return entry.data as T;
}
invalidate(pattern?: string): void {
if (!pattern) {
this.cache.clear();
return;
}
for (const key of this.cache.keys()) {
if (key.includes(pattern)) {
this.cache.delete(key);
}
}
}
getStats() {
const entries = Array.from(this.cache.values());
return {
size: this.cache.size,
totalHits: entries.reduce((sum, e) => sum + e.hits, 0),
avgHits: entries.length > 0
? entries.reduce((sum, e) => sum + e.hits, 0) / entries.length
: 0
};
}
}
export const queryCache = new QueryCache();
// ============= INDEXED DATA STORES =============
// Use Maps for O(1) lookups, plus secondary indices for common queries
const bottles: Map<string, Bottle> = new Map();
const membershipUnits: Map<string, MembershipUnit> = new Map();
const governanceVotes: Map<string, GovernanceVote> = new Map();
const votes: Map<string, Vote> = new Map();
const sampleUnits: Map<string, SampleUnit> = new Map();
const sampleRights: Map<string, SampleRight> = new Map();
const marketplaceListings: Map<string, MarketplaceListing> = new Map();
const fulfillmentRequests: Map<string, FulfillmentRequest> = new Map();
const fulfillmentPartners: Map<string, FulfillmentPartner> = new Map();
const digitalRecords: Map<string, DigitalRecord> = new Map();
const userProfiles: Map<string, UserProfile> = new Map();
// Secondary indices for faster lookups
const membershipUnitsByBottle: Map<string, Set<string>> = new Map();
const membershipUnitsByOwner: Map<string, Set<string>> = new Map();
const votesByBottle: Map<string, Set<string>> = new Map();
const listingsBySeller: Map<string, Set<string>> = new Map();
const fulfillmentByUser: Map<string, Set<string>> = new Map();
// Platform configuration
const platformConfig: PlatformConfig = {
votingQuorumPercentage: 50,
votingApprovalThreshold: 66,
votingPeriodDays: 7,
sampleSizeML: 50,
platformFeePercentage: 2.5,
minimumListingPrice: 10,
maximumListingDays: 30
};
// ============= HELPER FUNCTIONS =============
function generateId(prefix: string): string {
return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
function createDigitalRecord(recordType: string, entityId: string, data: any): DigitalRecord {
const previousRecords = Array.from(digitalRecords.values())
.filter(r => r.entityId === entityId)
.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
const record: DigitalRecord = {
id: generateId('record'),
recordType: recordType as any,
entityId,
data,
hash: generateHash(data),
previousHash: previousRecords[0]?.hash,
timestamp: new Date(),
metadata: {
version: '1.0',
creator: 'system'
}
};
digitalRecords.set(record.id, record);
return record;
}
// Improved hash function using crypto API if available
function generateHash(data: any): string {
const str = JSON.stringify(data);
// Simple hash for now - in production, use crypto.createHash
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return Math.abs(hash).toString(16);
}
// Update secondary indices
function addToIndex<K>(index: Map<K, Set<string>>, key: K, value: string): void {
if (!index.has(key)) {
index.set(key, new Set());
}
index.get(key)!.add(value);
}
function removeFromIndex<K>(index: Map<K, Set<string>>, key: K, value: string): void {
const set = index.get(key);
if (set) {
set.delete(value);
if (set.size === 0) {
index.delete(key);
}
}
}
// ============= INITIALIZATION =============
function initializeSampleData() {
// Create sample bottles
const bottle: Bottle = {
id: generateId('bottle'),
name: 'Château Margaux',
vintage: 1996,
producer: 'Château Margaux',
region: 'Margaux, Bordeaux',
size: '750ml',
totalMembershipUnits: 10000,
availableMembershipUnits: 7250,
estimatedValue: 67500,
imageUrl: '/images/margaux-1996.jpg',
description: 'One of the greatest vintages of the 20th century. Perfect provenance from temperature-controlled storage.',
provenance: 'Purchased directly from château in 1997. Stored at 55°F, 70% humidity since acquisition.',
status: 'active',
createdAt: new Date('2024-01-15'),
updatedAt: new Date()
};
bottles.set(bottle.id, bottle);
createDigitalRecord('bottle', bottle.id, bottle);
const bottle2: Bottle = {
id: generateId('bottle'),
name: 'Domaine de la Romanée-Conti La Tâche',
vintage: 2005,
producer: 'Domaine de la Romanée-Conti',
region: 'Vosne-Romanée, Burgundy',
size: '750ml',
totalMembershipUnits: 10000,
availableMembershipUnits: 10000,
estimatedValue: 112500,
imageUrl: '/images/drc-latache-2005.jpg',
description: 'Exceptional vintage from the legendary La Tâche vineyard. Impeccable provenance.',
provenance: 'Acquired from original allocation holder. Never moved from professional storage.',
status: 'active',
createdAt: new Date('2024-02-01'),
updatedAt: new Date()
};
bottles.set(bottle2.id, bottle2);
createDigitalRecord('bottle', bottle2.id, bottle2);
// Create sample membership units with indices
for (let i = 0; i < 2750; i++) {
const unit: MembershipUnit = {
id: generateId('unit'),
bottleId: bottle.id,
tokenId: `TOKEN_${bottle.id}_${i + 1}`,
ownerId: `user_${Math.floor(Math.random() * 100) + 1}`,
ownerEmail: `member${i + 1}@example.com`,
ownerName: `Member ${i + 1}`,
purchasePrice: 6.75 + Math.random() * 0.50,
purchaseDate: new Date(Date.now() - Math.random() * 90 * 24 * 60 * 60 * 1000),
status: 'active',
votingPower: 1,
transactionHistory: []
};
membershipUnits.set(unit.id, unit);
addToIndex(membershipUnitsByBottle, unit.bottleId, unit.id);
addToIndex(membershipUnitsByOwner, unit.ownerId, unit.id);
createDigitalRecord('membership', unit.id, unit);
}
// Create sample governance vote
const vote: GovernanceVote = {
id: generateId('vote'),
bottleId: bottle.id,
proposal: 'Unlock bottle for sampling event',
proposalType: 'unlock',
description: 'Proposal to unlock the 1996 Château Margaux and create 100 sample units (50ml each) for distribution to members.',
startDate: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000),
endDate: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000),
status: 'active',
votesFor: 18,
votesAgainst: 3,
votesAbstain: 1,
quorumRequired: 50,
approvalThreshold: 66,
votes: [],
createdBy: 'admin',
};
governanceVotes.set(vote.id, vote);
addToIndex(votesByBottle, vote.bottleId, vote.id);
// Create sample fulfillment partner
const partner: FulfillmentPartner = {
id: generateId('partner'),
name: 'Premium Wine Fulfillment Services',
licenseNumber: 'CA-DTC-2024-001',
licenseState: 'CA',
serviceStates: ['CA', 'NY', 'FL', 'TX', 'IL', 'WA', 'OR'],
apiEndpoint: 'https://api.premiumwinefulfillment.com',
contactEmail: 'orders@premiumwinefulfillment.com',
contactPhone: '+1-555-WINE-FUL',
capabilities: ['sampling', 'shipping', 'events', 'storage'],
active: true
};
fulfillmentPartners.set(partner.id, partner);
console.log('Sample data initialized:', {
bottles: bottles.size,
membershipUnits: membershipUnits.size,
votes: governanceVotes.size,
partners: fulfillmentPartners.size
});
}
initializeSampleData();
// ============= OPTIMIZED CRUD OPERATIONS =============
export const bottleDb = {
getAll: () => perfMonitor.track('bottle.getAll', () => {
const cacheKey = 'bottles:all';
const cached = queryCache.get<Bottle[]>(cacheKey);
if (cached) return cached;
const result = Array.from(bottles.values());
queryCache.set(cacheKey, result);
return result;
}),
getById: (id: string) => perfMonitor.track('bottle.getById', () => {
return bottles.get(id);
}),
create: (bottle: Omit<Bottle, 'id' | 'createdAt' | 'updatedAt'>) => perfMonitor.track('bottle.create', () => {
const newBottle: Bottle = {
...bottle,
id: generateId('bottle'),
createdAt: new Date(),
updatedAt: new Date()
};
bottles.set(newBottle.id, newBottle);
createDigitalRecord('bottle', newBottle.id, newBottle);
queryCache.invalidate('bottles');
return newBottle;
}),
update: (id: string, updates: Partial<Bottle>) => perfMonitor.track('bottle.update', () => {
const bottle = bottles.get(id);
if (!bottle) return null;
const updated = { ...bottle, ...updates, updatedAt: new Date() };
bottles.set(id, updated);
createDigitalRecord('bottle', id, updated);
queryCache.invalidate('bottles');
queryCache.invalidate(`bottle:${id}`);
return updated;
}),
delete: (id: string) => perfMonitor.track('bottle.delete', () => {
const result = bottles.delete(id);
queryCache.invalidate('bottles');
queryCache.invalidate(`bottle:${id}`);
return result;
})
};
export const membershipUnitDb = {
getAll: () => perfMonitor.track('unit.getAll', () => {
return Array.from(membershipUnits.values());
}),
getById: (id: string) => perfMonitor.track('unit.getById', () => {
return membershipUnits.get(id);
}),
// OPTIMIZED: Use secondary index instead of filtering all units
getByBottleId: (bottleId: string) => perfMonitor.track('unit.getByBottleId', () => {
const cacheKey = `units:bottle:${bottleId}`;
const cached = queryCache.get<MembershipUnit[]>(cacheKey);
if (cached) return cached;
const unitIds = membershipUnitsByBottle.get(bottleId);
const result = unitIds
? Array.from(unitIds).map(id => membershipUnits.get(id)!).filter(Boolean)
: [];
queryCache.set(cacheKey, result);
return result;
}),
// OPTIMIZED: Use secondary index
getByOwnerId: (ownerId: string) => perfMonitor.track('unit.getByOwnerId', () => {
const cacheKey = `units:owner:${ownerId}`;
const cached = queryCache.get<MembershipUnit[]>(cacheKey);
if (cached) return cached;
const unitIds = membershipUnitsByOwner.get(ownerId);
const result = unitIds
? Array.from(unitIds).map(id => membershipUnits.get(id)!).filter(Boolean)
: [];
queryCache.set(cacheKey, result);
return result;
}),
create: (unit: Omit<MembershipUnit, 'id'>) => perfMonitor.track('unit.create', () => {
const newUnit: MembershipUnit = {
...unit,
id: generateId('unit')
};
membershipUnits.set(newUnit.id, newUnit);
addToIndex(membershipUnitsByBottle, newUnit.bottleId, newUnit.id);
addToIndex(membershipUnitsByOwner, newUnit.ownerId, newUnit.id);
createDigitalRecord('membership', newUnit.id, newUnit);
queryCache.invalidate('units');
return newUnit;
}),
update: (id: string, updates: Partial<MembershipUnit>) => perfMonitor.track('unit.update', () => {
const unit = membershipUnits.get(id);
if (!unit) return null;
// Update indices if owner changes
if (updates.ownerId && updates.ownerId !== unit.ownerId) {
removeFromIndex(membershipUnitsByOwner, unit.ownerId, id);
addToIndex(membershipUnitsByOwner, updates.ownerId, id);
}
const updated = { ...unit, ...updates };
membershipUnits.set(id, updated);
createDigitalRecord('membership', id, updated);
queryCache.invalidate(`units:bottle:${unit.bottleId}`);
queryCache.invalidate(`units:owner:${unit.ownerId}`);
if (updates.ownerId) {
queryCache.invalidate(`units:owner:${updates.ownerId}`);
}
return updated;
}),
transfer: (unitId: string, toUserId: string, toEmail: string, toName: string, price?: number) =>
perfMonitor.track('unit.transfer', () => {
const unit = membershipUnits.get(unitId);
if (!unit) return null;
const transaction: Transaction = {
id: generateId('tx'),
type: 'transfer',
fromUserId: unit.ownerId,
toUserId,
membershipUnitId: unitId,
price,
timestamp: new Date()
};
// Update indices
removeFromIndex(membershipUnitsByOwner, unit.ownerId, unitId);
addToIndex(membershipUnitsByOwner, toUserId, unitId);
unit.transactionHistory.push(transaction);
unit.ownerId = toUserId;
unit.ownerEmail = toEmail;
unit.ownerName = toName;
membershipUnits.set(unitId, unit);
createDigitalRecord('transaction', transaction.id, transaction);
queryCache.invalidate(`units:owner`);
return unit;
})
};
export const governanceVoteDb = {
getAll: () => perfMonitor.track('vote.getAll', () => {
return Array.from(governanceVotes.values());
}),
getById: (id: string) => perfMonitor.track('vote.getById', () => {
return governanceVotes.get(id);
}),
// OPTIMIZED: Use secondary index
getByBottleId: (bottleId: string) => perfMonitor.track('vote.getByBottleId', () => {
const cacheKey = `votes:bottle:${bottleId}`;
const cached = queryCache.get<GovernanceVote[]>(cacheKey);
if (cached) return cached;
const voteIds = votesByBottle.get(bottleId);
const result = voteIds
? Array.from(voteIds).map(id => governanceVotes.get(id)!).filter(Boolean)
: [];
queryCache.set(cacheKey, result);
return result;
}),
// OPTIMIZED: Filter only active votes with date check
getActive: () => perfMonitor.track('vote.getActive', () => {
const cacheKey = 'votes:active';
const cached = queryCache.get<GovernanceVote[]>(cacheKey);
if (cached) return cached;
const now = new Date();
const result = Array.from(governanceVotes.values())
.filter(v => v.status === 'active' && v.endDate > now);
queryCache.set(cacheKey, result, 60000); // Cache for 1 minute
return result;
}),
create: (vote: Omit<GovernanceVote, 'id' | 'votesFor' | 'votesAgainst' | 'votesAbstain' | 'votes'>) =>
perfMonitor.track('vote.create', () => {
const newVote: GovernanceVote = {
...vote,
id: generateId('vote'),
votesFor: 0,
votesAgainst: 0,
votesAbstain: 0,
votes: []
};
governanceVotes.set(newVote.id, newVote);
addToIndex(votesByBottle, newVote.bottleId, newVote.id);
queryCache.invalidate('votes');
return newVote;
}),
castVote: (voteId: string, userId: string, membershipUnitId: string, choice: 'for' | 'against' | 'abstain') =>
perfMonitor.track('vote.cast', () => {
const governanceVote = governanceVotes.get(voteId);
if (!governanceVote) return null;
const unit = membershipUnits.get(membershipUnitId);
if (!unit || unit.ownerId !== userId) return null;
// Check if already voted
const existingVote = governanceVote.votes.find(v => v.membershipUnitId === membershipUnitId);
if (existingVote) return null;
const newVote: Vote = {
id: generateId('vote'),
voteId,
userId,
membershipUnitId,
choice,
votingPower: unit.votingPower,
timestamp: new Date()
};
governanceVote.votes.push(newVote);
if (choice === 'for') governanceVote.votesFor += unit.votingPower;
else if (choice === 'against') governanceVote.votesAgainst += unit.votingPower;
else governanceVote.votesAbstain += unit.votingPower;
governanceVotes.set(voteId, governanceVote);
votes.set(newVote.id, newVote);
queryCache.invalidate('votes');
return governanceVote;
}),
executeVote: (voteId: string) => perfMonitor.track('vote.execute', () => {
const vote = governanceVotes.get(voteId);
if (!vote) return null;
const totalVotes = vote.votesFor + vote.votesAgainst + vote.votesAbstain;
const bottle = bottles.get(vote.bottleId);
if (!bottle) return null;
const quorumMet = (totalVotes / bottle.totalMembershipUnits) * 100 >= vote.quorumRequired;
const approvalMet = (vote.votesFor / totalVotes) * 100 >= vote.approvalThreshold;
if (quorumMet && approvalMet) {
vote.status = 'passed';
vote.executedAt = new Date();
if (vote.proposalType === 'unlock') {
const numSamples = 100;
for (let i = 0; i < numSamples; i++) {
const sample: SampleUnit = {
id: generateId('sample'),
bottleId: vote.bottleId,
sampleNumber: i + 1,
size: '50ml',
status: 'available',
digitalRecordId: generateId('record'),
createdFromVoteId: voteId,
createdAt: new Date()
};
sampleUnits.set(sample.id, sample);
createDigitalRecord('sample', sample.id, sample);
}
bottleDb.update(vote.bottleId, { status: 'unlocked' });
}
} else {
vote.status = 'failed';
}
governanceVotes.set(voteId, vote);
queryCache.invalidate('votes');
return vote;
})
};
// Similar optimizations for other DBs...
export const sampleUnitDb = {
getAll: () => Array.from(sampleUnits.values()),
getById: (id: string) => sampleUnits.get(id),
getByBottleId: (bottleId: string) =>
Array.from(sampleUnits.values()).filter(s => s.bottleId === bottleId),
getAvailable: (bottleId?: string) => {
const samples = Array.from(sampleUnits.values()).filter(s => s.status === 'available');
return bottleId ? samples.filter(s => s.bottleId === bottleId) : samples;
},
update: (id: string, updates: Partial<SampleUnit>) => {
const sample = sampleUnits.get(id);
if (!sample) return null;
const updated = { ...sample, ...updates };
sampleUnits.set(id, updated);
return updated;
}
};
export const sampleRightDb = {
getAll: () => Array.from(sampleRights.values()),
getById: (id: string) => sampleRights.get(id),
getByOwnerId: (ownerId: string) =>
Array.from(sampleRights.values()).filter(r => r.ownerId === ownerId),
create: (right: Omit<SampleRight, 'id' | 'createdAt'>) => {
const newRight: SampleRight = {
...right,
id: generateId('right'),
createdAt: new Date()
};
sampleRights.set(newRight.id, newRight);
return newRight;
},
transfer: (rightId: string, toUserId: string, toEmail: string, price: number) => {
const right = sampleRights.get(rightId);
if (!right) return null;
const transfer: SampleTransfer = {
id: generateId('transfer'),
sampleRightId: rightId,
fromUserId: right.ownerId,
toUserId,
price,
timestamp: new Date(),
status: 'completed'
};
right.transferHistory.push(transfer);
right.ownerId = toUserId;
right.ownerEmail = toEmail;
right.status = 'active';
sampleRights.set(rightId, right);
createDigitalRecord('transaction', transfer.id, transfer);
return right;
}
};
export const marketplaceDb = {
getAll: () => Array.from(marketplaceListings.values()),
getActive: () => perfMonitor.track('marketplace.getActive', () => {
return Array.from(marketplaceListings.values()).filter(l => l.status === 'active');
}),
getById: (id: string) => marketplaceListings.get(id),
// OPTIMIZED: Use secondary index
getBySellerId: (sellerId: string) => perfMonitor.track('marketplace.getBySellerId', () => {
const listingIds = listingsBySeller.get(sellerId);
return listingIds
? Array.from(listingIds).map(id => marketplaceListings.get(id)!).filter(Boolean)
: [];
}),
create: (listing: Omit<MarketplaceListing, 'id' | 'listedAt'>) => {
const newListing: MarketplaceListing = {
...listing,
id: generateId('listing'),
listedAt: new Date()
};
marketplaceListings.set(newListing.id, newListing);
addToIndex(listingsBySeller, newListing.sellerId, newListing.id);
return newListing;
},
completeSale: (listingId: string, buyerId: string, buyerEmail: string, buyerName: string) => {
const listing = marketplaceListings.get(listingId);
if (!listing || listing.status !== 'active') return null;
listing.status = 'sold';
listing.soldAt = new Date();
listing.soldTo = buyerId;
if (listing.listingType === 'membership') {
membershipUnitDb.transfer(listing.itemId, buyerId, buyerEmail, buyerName, listing.price);
} else {
sampleRightDb.transfer(listing.itemId, buyerId, buyerEmail, listing.price);
}
marketplaceListings.set(listingId, listing);
return listing;
},
cancel: (listingId: string) => {
const listing = marketplaceListings.get(listingId);
if (!listing) return null;
listing.status = 'cancelled';
marketplaceListings.set(listingId, listing);
return listing;
}
};
export const fulfillmentDb = {
getAll: () => Array.from(fulfillmentRequests.values()),
getById: (id: string) => fulfillmentRequests.get(id),
// OPTIMIZED: Use secondary index
getByUserId: (userId: string) => perfMonitor.track('fulfillment.getByUserId', () => {
const requestIds = fulfillmentByUser.get(userId);
return requestIds
? Array.from(requestIds).map(id => fulfillmentRequests.get(id)!).filter(Boolean)
: [];
}),
create: (request: Omit<FulfillmentRequest, 'id' | 'createdAt'>) => {
const newRequest: FulfillmentRequest = {
...request,
id: generateId('fulfill'),
createdAt: new Date()
};
fulfillmentRequests.set(newRequest.id, newRequest);
addToIndex(fulfillmentByUser, newRequest.userId, newRequest.id);
return newRequest;
},
updateStatus: (id: string, status: FulfillmentRequest['status'], updates?: Partial<FulfillmentRequest>) => {
const request = fulfillmentRequests.get(id);
if (!request) return null;
const updated = { ...request, status, ...updates };
fulfillmentRequests.set(id, updated);
return updated;
}
};
export const partnerDb = {
getAll: () => Array.from(fulfillmentPartners.values()),
getActive: () => Array.from(fulfillmentPartners.values()).filter(p => p.active),
getById: (id: string) => fulfillmentPartners.get(id),
getByState: (state: string) => perfMonitor.track('partner.getByState', () => {
const cacheKey = `partners:state:${state}`;
const cached = queryCache.get<FulfillmentPartner[]>(cacheKey);
if (cached) return cached;
const result = Array.from(fulfillmentPartners.values())
.filter(p => p.active && p.serviceStates.includes(state));
queryCache.set(cacheKey, result);
return result;
})
};
export const digitalRecordDb = {
getAll: () => Array.from(digitalRecords.values()),
getById: (id: string) => digitalRecords.get(id),
getByEntityId: (entityId: string) => perfMonitor.track('record.getByEntityId', () => {
return Array.from(digitalRecords.values())
.filter(r => r.entityId === entityId)
.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
})
};
export const getConfig = () => platformConfig;
export const updateConfig = (updates: Partial<PlatformConfig>) => {
Object.assign(platformConfig, updates);
return platformConfig;
};