export const DELETE_PROPERTY = Symbol('DELETE_PROPERTY'); export class String_Keyed_Stack { constructor(target={}, stack=[]) { Object.assign(this, { target, stack }); } push(updates={}) { const frame = {} this.stack.push(frame); for (const [key, value] of Object.entries(updates)) { if (key in this.target) { frame[key] = this.target[key]; } else { frame[key] = DELETE_PROPERTY; } if (value === DELETE_PROPERTY) { delete this.target[key]; } else { this.target[key] = value; } } return frame; } push_defined(updates={}) { this.push(Object.fromEntries(Object.entries(updates).filter(([k ,v]) => v !== undefined ))); } pop(copy_previous_state=false) { const frame = this.stack.pop(); const { target } = this; const return_value = copy_previous_state ? { ...target } : null; for (const [key, value] of Object.entries(frame)) { if (value === DELETE_PROPERTY) { delete target[key]; } else { target[key] = value; } } return return_value; } get top_reverse_delta() { return this.stack.at(-1); } }