smartcontext/ts/logcontext.classes.asyncstore.ts

135 lines
4.0 KiB
TypeScript

import * as plugins from './logcontext.plugins.js';
export class AsyncStore {
private static idCounter = 0;
private id: number;
private parentStore?: AsyncStore;
private deletedKeys: string[] = [];
private dataObject: { [key: string]: any } = {};
constructor(parentStore?: AsyncStore) {
this.parentStore = parentStore;
this.id = AsyncStore.idCounter++;
}
/**
* Logs debug info if process.env.DEBUG is set.
*/
private logDebug(functionName: string, before: Record<string, any>, after: Record<string, any>) {
if (process.env.DEBUG) {
console.log(`Store ID: ${this.id}`);
console.log(`Function: ${functionName}`);
console.log('--- Before ---');
console.log(before);
console.log('--- After ---');
console.log(after);
console.log('-----------------------');
}
}
/**
* Cleans up the deleted keys if they no longer exist in any parent store.
*/
private cleanUp() {
for (const key of this.deletedKeys) {
if (this.parentStore && this.parentStore.get(key)) {
// Parent still has it, so keep in deletedKeys
} else {
const index = this.deletedKeys.indexOf(key);
if (index !== -1) {
this.deletedKeys.splice(index, 1);
}
}
}
}
/**
* Adds or updates a value under a specific key in this store.
*/
public add(keyArg: string, objectArg: any) {
// capture the before state
const before = { ...this.dataObject, deletedKeys: [...this.deletedKeys] };
this.cleanUp();
// If this key was previously deleted, remove it from deletedKeys.
if (this.deletedKeys.includes(keyArg)) {
this.deletedKeys = this.deletedKeys.filter((key) => key !== keyArg);
}
this.dataObject[keyArg] = objectArg;
// capture the after state
const after = { ...this.dataObject, deletedKeys: [...this.deletedKeys] };
this.logDebug('add', before, after);
}
/**
* Deletes a key from the current store.
* If a parent store has the key, we record it in `deletedKeys` so the child store "shadows" it.
*/
public delete(paramName: string) {
// capture the before state
const before = { ...this.dataObject, deletedKeys: [...this.deletedKeys] };
this.cleanUp();
if (this.parentStore?.get(paramName)) {
// The parent store has this key; let's mark it as deleted in the child
this.deletedKeys.push(paramName);
}
delete this.dataObject[paramName];
// capture the after state
const after = { ...this.dataObject, deletedKeys: [...this.deletedKeys] };
this.logDebug('delete', before, after);
}
/**
* Gets the value of a key, checking this store first, then the parent store if necessary.
* Will log the store state before/after for debugging.
*/
public get(paramName: string) {
// capture the before state
const before = { ...this.dataObject, deletedKeys: [...this.deletedKeys] };
this.cleanUp();
// figure out if paramName is deleted or present
let result: any;
if (this.deletedKeys.includes(paramName)) {
result = undefined;
} else {
result = this.dataObject[paramName] ?? this.parentStore?.get(paramName);
}
// capture the after state; we can also show the `result` in the log
const after = {
...this.dataObject,
deletedKeys: [...this.deletedKeys],
retrievedKey: paramName,
result
};
this.logDebug('get', before, after);
return result;
}
/**
* Returns all keys and values, merged with the parent store, but
* does NOT include keys that are "deleted" in the child.
* Child store should override parent if the same key exists in both.
*/
public getAll() {
this.cleanUp();
// first, get parent's data as a shallow copy
const parentData = { ...(this.parentStore?.getAll() || {}) };
// remove keys from parent data that this child has deleted
for (const key of this.deletedKeys) {
delete parentData[key];
}
// child's data overrides parent data for any matching keys
return {
...parentData,
...this.dataObject
};
}
}