|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class SyncableMap<K, V> extends Map<K, V> { |
|
private subscriptions = new Set<() => void>(); |
|
private lastSnapshot: Map<K, V> | null = null; |
|
|
|
constructor(entries?: readonly (readonly [K, V])[] | null) { |
|
super(entries); |
|
} |
|
|
|
set = (key: K, value: V): this => { |
|
super.set(key, value); |
|
this.notifySubscribers(); |
|
return this; |
|
}; |
|
|
|
delete = (key: K): boolean => { |
|
const result = super.delete(key); |
|
this.notifySubscribers(); |
|
return result; |
|
}; |
|
|
|
clear = (): void => { |
|
super.clear(); |
|
this.notifySubscribers(); |
|
}; |
|
|
|
|
|
|
|
|
|
private notifySubscribers = () => { |
|
for (const callback of this.subscriptions) { |
|
callback(); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
subscribe = (callback: () => void): (() => void) => { |
|
this.subscriptions.add(callback); |
|
return () => { |
|
this.subscriptions.delete(callback); |
|
}; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
getSnapshot = (): Map<K, V> => { |
|
const currentSnapshot = new Map(this); |
|
if (!this.lastSnapshot || !this.areSnapshotsEqual(this.lastSnapshot, currentSnapshot)) { |
|
this.lastSnapshot = currentSnapshot; |
|
} |
|
|
|
return this.lastSnapshot; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private areSnapshotsEqual = (snapshotA: Map<K, V>, snapshotB: Map<K, V>): boolean => { |
|
if (snapshotA.size !== snapshotB.size) { |
|
return false; |
|
} |
|
|
|
for (const [key, value] of snapshotA) { |
|
if (!Object.is(value, snapshotB.get(key))) { |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
}; |
|
} |
|
|