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 {
|
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) {
|
* Register a callback to be called when a key is deleted
|
||||||
this.#cache = cache;
|
* @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) {
|
get(key) {
|
||||||
const entry = this.#cache.get(key);
|
const entry = this.#get_cache().get(this.#key(key));
|
||||||
return entry?.item;
|
return entry?.item;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {ReadonlyMap<string, any>['has']} */
|
/** @param {string} key */
|
||||||
has(key) {
|
has(key) {
|
||||||
return this.#cache.has(key);
|
return this.#get_cache().has(this.#key(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {ReadonlyMap<string, any>['size']} */
|
|
||||||
get 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) {
|
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() {
|
*entries() {
|
||||||
for (const [key, entry] of this.#cache.entries()) {
|
for (const [key, entry] of this.#get_cache().entries()) {
|
||||||
yield [key, entry.item];
|
if (!key.startsWith(this.#prefix)) continue;
|
||||||
|
yield /** @type {[string, T]} */ ([key, entry.item]);
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {ReadonlyMap<string, any>['keys']} */
|
|
||||||
*keys() {
|
*keys() {
|
||||||
for (const key of this.#cache.keys()) {
|
for (const [key] of this.entries()) {
|
||||||
yield key;
|
yield key;
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {ReadonlyMap<string, any>['values']} */
|
|
||||||
*values() {
|
*values() {
|
||||||
for (const entry of this.#cache.values()) {
|
for (const [, entry] of this.entries()) {
|
||||||
yield entry.item;
|
yield entry;
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Symbol.iterator]() {
|
[Symbol.iterator]() {
|
||||||
return this.entries();
|
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