mirror of https://github.com/sveltejs/svelte
parent
a2bff0c0b5
commit
2e292b1a6d
@ -1,56 +1,112 @@
|
||||
/** @implements {ReadonlyMap<string, any>} */
|
||||
/** @import { ObservableCache } from './observable-cache.js' */
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @implements {ReadonlyMap<string, T>} */
|
||||
export class BaseCacheObserver {
|
||||
/** @type {ReadonlyMap<string, any>} */
|
||||
#cache;
|
||||
/**
|
||||
* This is a function so that you can create an ObservableCache instance globally and as long as you don't actually
|
||||
* use it until you're inside the server render lifecycle you'll be okay
|
||||
* @type {() => ObservableCache}
|
||||
*/
|
||||
#get_cache;
|
||||
|
||||
/** @type {string} */
|
||||
#prefix;
|
||||
|
||||
/**
|
||||
* @param {() => ObservableCache} get_cache
|
||||
* @param {string} [prefix]
|
||||
*/
|
||||
constructor(get_cache, prefix = '') {
|
||||
this.#get_cache = get_cache;
|
||||
this.#prefix = prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be called when a new key is inserted
|
||||
* @param {(key: string, value: T) => void} callback
|
||||
* @returns {() => void} Function to unregister the callback
|
||||
*/
|
||||
onInsert(callback) {
|
||||
return this.#get_cache().on_insert((key, value) => {
|
||||
if (!key.startsWith(this.#prefix)) return;
|
||||
callback(key, value.item);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be called when an existing key is updated
|
||||
* @param {(key: string, value: T, old_value: T) => void} callback
|
||||
* @returns {() => void} Function to unregister the callback
|
||||
*/
|
||||
onUpdate(callback) {
|
||||
return this.#get_cache().on_update((key, value, old_value) => {
|
||||
if (!key.startsWith(this.#prefix)) return;
|
||||
callback(key, value.item, old_value.item);
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {Map<string, any>} cache */
|
||||
constructor(cache) {
|
||||
this.#cache = cache;
|
||||
/**
|
||||
* Register a callback to be called when a key is deleted
|
||||
* @param {(key: string, old_value: T) => void} callback
|
||||
* @returns {() => void} Function to unregister the callback
|
||||
*/
|
||||
onDelete(callback) {
|
||||
return this.#get_cache().on_delete((key, old_value) => {
|
||||
if (!key.startsWith(this.#prefix)) return;
|
||||
callback(key, old_value.item);
|
||||
});
|
||||
}
|
||||
|
||||
/** @type {ReadonlyMap<string, any>['get']} */
|
||||
/** @param {string} key */
|
||||
get(key) {
|
||||
const entry = this.#cache.get(key);
|
||||
const entry = this.#get_cache().get(this.#key(key));
|
||||
return entry?.item;
|
||||
}
|
||||
|
||||
/** @type {ReadonlyMap<string, any>['has']} */
|
||||
/** @param {string} key */
|
||||
has(key) {
|
||||
return this.#cache.has(key);
|
||||
return this.#get_cache().has(this.#key(key));
|
||||
}
|
||||
|
||||
/** @type {ReadonlyMap<string, any>['size']} */
|
||||
get size() {
|
||||
return this.#cache.size;
|
||||
return [...this.keys()].length;
|
||||
}
|
||||
|
||||
/** @type {ReadonlyMap<string, any>['forEach']} */
|
||||
/** @param {(item: T, key: string, map: ReadonlyMap<string, T>) => void} cb */
|
||||
forEach(cb) {
|
||||
this.#cache.forEach((entry, key) => cb(entry.item, key, this));
|
||||
this.entries().forEach(([key, entry]) => cb(entry, key, this));
|
||||
}
|
||||
|
||||
/** @type {ReadonlyMap<string, any>['entries']} */
|
||||
*entries() {
|
||||
for (const [key, entry] of this.#cache.entries()) {
|
||||
yield [key, entry.item];
|
||||
for (const [key, entry] of this.#get_cache().entries()) {
|
||||
if (!key.startsWith(this.#prefix)) continue;
|
||||
yield /** @type {[string, T]} */ ([key, entry.item]);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** @type {ReadonlyMap<string, any>['keys']} */
|
||||
*keys() {
|
||||
for (const key of this.#cache.keys()) {
|
||||
for (const [key] of this.entries()) {
|
||||
yield key;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** @type {ReadonlyMap<string, any>['values']} */
|
||||
*values() {
|
||||
for (const entry of this.#cache.values()) {
|
||||
yield entry.item;
|
||||
for (const [, entry] of this.entries()) {
|
||||
yield entry;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this.entries();
|
||||
}
|
||||
|
||||
/** @param {string} key */
|
||||
#key(key) {
|
||||
return this.#prefix + key;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
/** @import { CacheEntry } from '#shared' */
|
||||
|
||||
/**
|
||||
* @extends {Map<string, CacheEntry>}
|
||||
*/
|
||||
export class ObservableCache extends Map {
|
||||
/** @type {Set<(key: string, value: CacheEntry) => void>} */
|
||||
#insert_callbacks = new Set();
|
||||
|
||||
/** @type {Set<(key: string, value: CacheEntry, old_value: CacheEntry) => void>} */
|
||||
#update_callbacks = new Set();
|
||||
|
||||
/** @type {Set<(key: string, old_value: CacheEntry) => void>} */
|
||||
#delete_callbacks = new Set();
|
||||
|
||||
/**
|
||||
* @param {(key: string, value: CacheEntry) => void} callback
|
||||
* @returns {() => void} Function to unregister the callback
|
||||
*/
|
||||
on_insert(callback) {
|
||||
this.#insert_callbacks.add(callback);
|
||||
return () => this.#insert_callbacks.delete(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(key: string, value: CacheEntry, old_value: CacheEntry) => void} callback
|
||||
* @returns {() => void} Function to unregister the callback
|
||||
*/
|
||||
on_update(callback) {
|
||||
this.#update_callbacks.add(callback);
|
||||
return () => this.#update_callbacks.delete(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(key: string, old_value: CacheEntry) => void} callback
|
||||
* @returns {() => void} Function to unregister the callback
|
||||
*/
|
||||
on_delete(callback) {
|
||||
this.#delete_callbacks.add(callback);
|
||||
return () => this.#delete_callbacks.delete(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {CacheEntry} value
|
||||
* @returns {this}
|
||||
*/
|
||||
set(key, value) {
|
||||
const had = this.has(key);
|
||||
if (had) {
|
||||
const old_value = /** @type {CacheEntry} */ (super.get(key));
|
||||
super.set(key, value);
|
||||
for (const callback of this.#update_callbacks) {
|
||||
callback(key, value, old_value);
|
||||
}
|
||||
} else {
|
||||
super.set(key, value);
|
||||
for (const callback of this.#insert_callbacks) {
|
||||
callback(key, value);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @returns {boolean}
|
||||
*/
|
||||
delete(key) {
|
||||
const old_value = super.get(key);
|
||||
const deleted = super.delete(key);
|
||||
if (deleted) {
|
||||
for (const callback of this.#delete_callbacks) {
|
||||
callback(key, /** @type {CacheEntry} */ (old_value));
|
||||
}
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
clear() {
|
||||
for (const [key, value] of this) {
|
||||
for (const callback of this.#delete_callbacks) {
|
||||
callback(key, value);
|
||||
}
|
||||
}
|
||||
super.clear();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue