Download packages/api/session-controller/src/client/sessions/notifier.ts from SaylorTwift/deepseek-harness: direct link, hf CLI and curl.
- Browser
- Download file 2.85 kB
-
https://huggingface.co/SaylorTwift/deepseek-harness/resolve/main/packages/api/session-controller/src/client/sessions/notifier.ts
- Command line
-
hf download hf://SaylorTwift/deepseek-harness/packages/api/session-controller/src/client/sessions/notifier.ts
-
curl -L -o notifier.ts https://huggingface.co/SaylorTwift/deepseek-harness/resolve/main/packages/api/session-controller/src/client/sessions/notifier.ts
2.85 kB
| import { notifySubscribers } from '@deepseek-ai/dsh-client-store' | |
| /** | |
| * Batches structural updates in microtasks and stream updates by animation | |
| * frame. Reads may rebuild a dirty snapshot without consuming the pending | |
| * subscriber notification. | |
| */ | |
| export class Notifier { | |
| private listeners = new Set<() => void>() | |
| private dirty = false | |
| private notifyPending = false | |
| private scheduled: 'none' | 'microtask' | 'frame' = 'none' | |
| private scheduleGeneration = 0 | |
| /** @param rebuild - snapshot rebuild function injected by the owner (writes the owner's snapshotCache). */ | |
| constructor(private readonly rebuild: () => void) {} | |
| /** | |
| * uSES subscription entry. | |
| * @param listener - change callback. | |
| * @returns the unsubscribe function. | |
| */ | |
| subscribe(listener: () => void): () => void { | |
| this.listeners.add(listener) | |
| return () => { | |
| this.listeners.delete(listener) | |
| } | |
| } | |
| /** Mark the snapshot dirty and notify in a microtask. */ | |
| markDirty(): void { | |
| this.dirty = true | |
| this.notifyPending = true | |
| if (this.scheduled === 'microtask') return | |
| this.schedule('microtask') | |
| } | |
| /** Mark the snapshot dirty and publish cumulative state at most once per frame. */ | |
| markFrameDirty(): void { | |
| this.dirty = true | |
| this.notifyPending = true | |
| if (this.scheduled !== 'none') return | |
| this.schedule(typeof globalThis.requestAnimationFrame === 'function' ? 'frame' : 'microtask') | |
| } | |
| /** | |
| * Synchronous flush: controlled-input writes must notify in the same tick as | |
| * onChange, or React rolls the DOM back to the stale value and the caret jumps to the end. | |
| */ | |
| notifyNow(): void { | |
| this.dirty = true | |
| this.notifyPending = true | |
| this.invalidateSchedule() | |
| this.flush() | |
| } | |
| /** | |
| * Pre-getSnapshot check: rebuild synchronously when dirty (read path | |
| * before first subscribe / while unobserved). Notification stays pending. | |
| */ | |
| ensureFresh(): void { | |
| if (!this.dirty) return | |
| this.dirty = false | |
| this.rebuild() | |
| } | |
| private schedule(kind: 'microtask' | 'frame'): void { | |
| const generation = ++this.scheduleGeneration | |
| this.scheduled = kind | |
| const publish = () => { | |
| if (generation !== this.scheduleGeneration) return | |
| this.scheduled = 'none' | |
| this.flush() | |
| } | |
| if (kind === 'frame') { | |
| globalThis.requestAnimationFrame(publish) | |
| } else { | |
| queueMicrotask(publish) | |
| } | |
| } | |
| private invalidateSchedule(): void { | |
| this.scheduleGeneration++ | |
| this.scheduled = 'none' | |
| } | |
| private flush(): void { | |
| if (!this.notifyPending) return | |
| if (this.listeners.size === 0) return // lazy: dirty (if still set) rebuilds on next getSnapshot | |
| this.notifyPending = false | |
| if (this.dirty) { | |
| this.dirty = false | |
| this.rebuild() | |
| } | |
| notifySubscribers(this.listeners, '[session-controller]') | |
| } | |
| } | |