auth-yes/tasks/new/2026-0824.02.jul.feat.sdk.realtime-invalidation-bus-1200.md

108 lines
4.1 KiB
Markdown

# 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:
```typescript
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:
```typescript
private listeners: Map<string, Set<InvalidationHandler>> = new Map();
```
- Implement the `on` method:
```typescript
/**
* 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:
```typescript
/**
* 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.