fix(core): Updated dependencies and improved AsyncStore debugging and cleanup

This commit is contained in:
2025-01-19 20:06:18 +01:00
parent 8548ad9684
commit 98f6afec7e
8 changed files with 164 additions and 80 deletions

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartcontext',
version: '2.1.5',
version: '2.1.6',
description: 'A module providing advanced asynchronous context management to enrich logs with context and manage scope effectively in Node.js applications.'
}

View File

@ -11,8 +11,7 @@ export class AsyncContext {
this._store = value;
}
public async runScoped(functionArg: () => Promise<void>) {
const childStore = new AsyncStore(this.store);
await this._context.run(childStore, async () => {
await this._context.run(new AsyncStore(this.store), async () => {
await functionArg()
});
}

View File

@ -1,50 +1,135 @@
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} = {};
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)) {
// ok still valid
// Parent still has it, so keep in deletedKeys
} else {
delete this.deletedKeys[key];
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)) {
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)) {
return undefined;
result = undefined;
} else {
result = this.dataObject[paramName] ?? this.parentStore?.get(paramName);
}
return 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();
return {...this.dataObject, ...(this.parentStore?.getAll() || {})};
// 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
};
}
}
}

View File

@ -1,9 +1,3 @@
// pushrocks scope
import * as lik from '@push.rocks/lik';
import * as smartunique from '@push.rocks/smartunique';
export { lik, smartunique };
// third party scope
import simpleAsyncContext from 'simple-async-context';