Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugins: extract sessions to standalone plugin #712

Open
Tracked by #675
KnorpelSenf opened this issue Dec 15, 2024 · 1 comment
Open
Tracked by #675

plugins: extract sessions to standalone plugin #712

KnorpelSenf opened this issue Dec 15, 2024 · 1 comment

Comments

@KnorpelSenf
Copy link
Member

Its importance has faded. Mini apps do not work well with sessions due to race conditions. At the same time, storage adapters are used in more plugins, so the storage enhancements should not be tied to sessions, either.

@KnorpelSenf KnorpelSenf mentioned this issue Dec 15, 2024
51 tasks
@shevernitskiy
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants