auth-yes/tasks/complete/2026-0824.02.jul.feat.sdk.realtime-invalidation-bus-1200.md
google-labs-jules[bot] 3d66535889 feat: implement zero-dependency real-time event bus in AuthSdk
Added a lightweight, zero-dependency event bus to the `AuthSdk` to listen for Valkey RESP3 push invalidation events and emit them to registered listeners.
The implementation properly captures errors from synchronous and asynchronous listeners, ensuring they do not crash the primary SDK listener loop. Extracted the Valkey event processor to improve hermetic testability.

Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
2026-08-24 04:19:40 +00:00

4.1 KiB

TASK METADATA

  • Target Files: sdk/mod.ts, sdk/mod.test.ts
  • Core Objective: Extend @auth-yes/sdk with a zero-dependency real-time event bus to listen for Valkey 8 RESP3 push invalidation events and emit them to registered listeners.
  • Dependencies: None.
  • Additional Important Notes: Handlers must support async execution. Errors thrown by handlers must be caught and logged so they do not crash the Valkey push listener or stop other registered handlers. No external EventEmitter libraries are allowed.

Architectural Considerations & Risks

Risks

  • Memory Leaks: Downstream consumers registering listeners for "invalidate" without subsequently calling off could result in an unbounded map of listeners. We must clearly document the off method and emphasize its usage during cleanup (e.g., when a consumer class is destroyed or a connection pool closes).
  • Callback Crash Propagation: If a downstream consumer's callback throws an exception (e.g., failing to terminate a WebSocket), and it is not caught, it could crash the internal Valkey push event handler. The implementation must guarantee isolated execution via a try/catch wrapper and gracefully handle Promise rejections.
  • Payload Alignment: Any discrepancy between the raw key emitted by Valkey and the expected token by downstream listeners could result in missed invalidations. As confirmed, Valkey stores and emits the raw token without prefixes, so we must emit the token strictly as received.

Alternatives

  • Using the web standard EventTarget natively supported in Deno. While native, standard EventTarget limits the payload to custom Event objects (CustomEvent), which adds boilerplate (event.detail.token) and type-casting complexity for downstream users. A custom lightweight callback Set (Set<InvalidationHandler>) allows us to pass the token: string payload directly and cleanly.
  • Using an external EventEmitter package. This was rejected per the zero-dependency directive.

Proposed Implementation

1. Define the Handler Type

In sdk/mod.ts, define the TypeScript type for the invalidation handler:

export type InvalidationHandler = (token: string) => void | Promise<void>;

2. Implement the Internal Event Bus

In the AuthSdk class (sdk/mod.ts):

  • Add a private property to store listeners:
    private listeners: Map<string, Set<InvalidationHandler>> = new Map();
    
  • Implement the on method:
    /**
     * Registers a callback to be invoked when a specific event occurs.
     * Currently, only the "invalidate" event is supported.
     *
     * @param event The event name (e.g., "invalidate").
     * @param handler The callback function.
     */
    on(event: "invalidate", handler: InvalidationHandler): void;
    
  • Implement the off method:
    /**
     * Unregisters a previously registered callback.
     *
     * @param event The event name (e.g., "invalidate").
     * @param handler The callback function to remove.
     */
    off(event: "invalidate", handler: InvalidationHandler): void;
    

3. Integrate with Valkey push Events

In the initValkeyClient method of AuthSdk (sdk/mod.ts):

  • Locate the existing "push" event listener for the Valkey client.
  • Inside the loop that iterates over keysToInvalidate (which represents the raw tokens), add the emission logic.
  • Retrieve the Set of listeners for the "invalidate" event.
  • Iterate over the handlers and invoke them safely using a try/catch block and Promise.resolve(...).catch(...) to prevent any single failing handler from impacting the others or crashing the SDK.

4. Write Unit Tests

In sdk/mod.test.ts:

  • Write tests to verify that on and off correctly add and remove handlers.
  • Simulate the Valkey "push" event (e.g., by creating an instance with a mocked valkeyClient or directly testing an internal emit method, or sending mock payloads if the Valkey client is mocked).
  • Verify that registered listeners are invoked with the correct token.
  • Verify that a throwing or rejecting handler does not disrupt the execution of subsequent handlers.