import { clonePlain } from './clone-plain'; export class MutationProxy { private readonly getOriginal: () => T; private readonly getMutated: () => Partial; constructor( getOriginal: () => T, getMutated: () => Partial, ) { this.getOriginal = getOriginal; this.getMutated = getMutated; } create(): T { return new Proxy({} as T, { get: (_target, prop: string | symbol) => { if (typeof prop !== 'string') { return undefined; } const key = prop as keyof T; const mutated = this.getMutated(); const original = this.getOriginal(); return key in mutated ? mutated[key] : original[key]; }, set: (_target, prop: string | symbol, value: unknown) => { if (typeof prop === 'string') { const key = prop as keyof T; (this.getMutated() as Record)[key] = clonePlain(value); } return true; }, has: (_target, prop: string | symbol) => { if (typeof prop !== 'string') { return false; } const mutated = this.getMutated(); const original = this.getOriginal(); return prop in mutated || prop in original; }, ownKeys: () => { const mutated = this.getMutated(); const original = this.getOriginal(); return Array.from(new Set([ ...Reflect.ownKeys(original), ...Reflect.ownKeys(mutated), ])); }, getOwnPropertyDescriptor: () => ({ enumerable: true, configurable: true, }), }); } }