Skip to content

feat: track session changes via deep proxification #3

@KnorpelSenf

Description

@KnorpelSenf

I suggest slightly reworking the session persistence condition. Currently, on any read, we write to persist the session, which produces unnecessary write operations. We can deeply proxy the session object to track mutations more precisely.

This is just a raw example of proxying; it can be adjusted only for objects and arrays (JSON serializable) and with if's for performance reasons.

const mutated = Symbol("mutated");

// for Set, Map, and Array
const mutation_methods = new Set([
  "set",
  "add",
  "delete",
  "clear",
  "push",
  "pop",
  "shift",
  "unshift",
  "splice",
  "sort",
  "reverse",
  "fill",
  "copyWithin",
]);

/**
 * Creates a proxy wrapper around an object to track mutations and handle cleanup
 * @param {any} object - The target object to proxify
 * @param {Function} [dispose] - Optional disposal function that runs during cleanup
 * @returns {Proxy} Proxied object that tracks mutations
 *
 * @example
 * const obj = { count: 0 };
 * const proxiedObj = proxify(obj, () => {
 *   console.log('cleanup');
 * });
 */
export function proxify<T>(
  object: any,
  dispose?: () => Promise<void> | void,
): T & { [Symbol.asyncDispose]: () => Promise<void>; [mutated]: boolean } {
  object[Symbol.asyncDispose] = dispose;
  object[mutated] = false;

  const handler: ProxyHandler<any> = {
    get(target, key: string) {
      if (typeof target[key] === "object" && target[key] !== null) {
        return new Proxy(target[key], handler);
      }
      if (typeof target[key] === "function") {
        return (...args: unknown[]) => {
          if (mutation_methods.has(key)) {
            object[mutated] = true;
          }
          return target[key](...args);
        };
      }
      return target[key];
    },
    set(target, prop: string, value) {
      object[mutated] = true;
      target[prop] = value;
      return true;
    },
  };

  return new Proxy(object, handler);
}

proxify.is_mutated = mutated;

usage with using:

async function app() {
  await using tracked_state = proxify(state, async () => {
    if (state[proxify.is_mutated]) {
      console.log("saving state");
      state[proxify.is_mutated] = false;
      await db.set(["state"], state);
    }
  });


  tracked_state.a = 1; // -> mutation

} // -> save

Originally posted by @shevernitskiy in #712

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions