Files
cloudbeaver/webapp/packages/core-utils/src/MetadataMap.ts
T
sergeyteleshevandmr-anton-t 900be41734 [CB] Bump NPM dependencies (#3628)
* dbeaver/pro#5764 updates current dependencies

* migrates to zod 4

* updates deps

* lint fix

* pr fixes

* reverts connection changes

* reverts connection changes 2

* fix types for screen service

* reverts changes for screen service

* reverts string boolean refactor

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
2025-08-07 18:03:31 +08:00

127 lines
3.3 KiB
TypeScript

/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { action, makeAutoObservable, observable } from 'mobx';
import type { schema } from './schema.js';
import { TempMap } from './TempMap.js';
export type MetadataValueGetter<TKey, TValue> = (key: TKey, metadata: MetadataMap<TKey, any>) => TValue;
export type DefaultValueGetter<TKey, TValue> = (key: TKey, metadata: MetadataMap<TKey, TValue>) => TValue;
export class MetadataMap<TKey, TValue> implements Map<TKey, TValue> {
get size(): number {
return this.temp.size;
}
private syncData: Array<[TKey, TValue]> | null;
private readonly temp: TempMap<TKey, TValue>;
private readonly data: Map<TKey, TValue>;
constructor(private readonly defaultValueGetter?: DefaultValueGetter<TKey, TValue>) {
this.syncData = null;
this.data = observable(new Map());
this.temp = new TempMap<TKey, TValue>(this.data, () => {
this.syncData?.splice(0, this.syncData.length, ...this.data.entries());
});
makeAutoObservable(this, {
sync: action,
unSync: action,
});
}
[Symbol.iterator](): ArrayIterator<[TKey, TValue]> {
return this.temp[Symbol.iterator]();
}
get [Symbol.toStringTag](): string {
return 'MetadataMap';
}
sync(entities: Array<[TKey, TValue]>): void {
if (this.syncData === entities) {
return;
}
this.temp.clear();
this.data.clear();
for (const [key, value] of entities) {
this.data.set(key, value);
}
this.syncData = entities;
}
unSync(): void {
this.syncData = null;
}
forEach(callbackfn: (value: TValue, key: TKey, map: Map<TKey, TValue>) => void, thisArg?: any): void {
this.temp.forEach(callbackfn, thisArg);
}
entries(): ArrayIterator<[TKey, TValue]> {
return this.temp.entries();
}
keys(): ArrayIterator<TKey> {
return this.temp.keys();
}
values(): ArrayIterator<TValue> {
return this.temp.values();
}
has(key: TKey): boolean {
return this.temp.has(key);
}
set(key: TKey, value: TValue): this {
this.temp.set(key, value);
return this;
}
// TODO replace zod schema with just validation callback returning true/false.
// In case we use something else than zod
get(key: TKey, defaultValue?: DefaultValueGetter<TKey, TValue>, schema?: schema.ZodObject): TValue {
const value = this.temp.get(key);
let invalidate = !this.temp.has(key);
if (!invalidate && schema) {
const parsed = schema.safeParse(value);
if (!parsed.success) {
invalidate = true;
}
}
if (invalidate) {
const provider = defaultValue || this.defaultValueGetter;
if (!provider) {
throw new Error('MetadataMap: defaultValue should be provided if defaultValueGetter not set');
}
const value = provider(key, this);
const isNotPrimitiveValue = typeof value === 'object' && value !== null;
this.temp.set(key, isNotPrimitiveValue ? observable(value) : value);
}
return this.temp.get(key)!;
}
delete(key: TKey): boolean {
return this.temp.delete(key);
}
clear(): void {
this.data.clear();
this.temp.clear();
this.syncData?.splice(0, this.syncData.length);
}
}