Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
3.6 kB · 121 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122/** * Synchronous state machine for the query lifecycle, compatible with * React's `useSyncExternalStore`. * * Three states: * idle → no query, safe to dequeue and process * dispatching → an item was dequeued, async chain hasn't reached onQuery yet * running → onQuery called tryStart(), query is executing * * Transitions: * idle → dispatching (reserve) * dispatching → running (tryStart) * idle → running (tryStart, for direct user submissions) * running → idle (end / forceEnd) * dispatching → idle (cancelReservation, when processQueueIfReady fails) * * `isActive` returns true for both dispatching and running, preventing * re-entry from the queue processor during the async gap. * * Usage with React: * const queryGuard = useRef(new QueryGuard()).current * const isQueryActive = useSyncExternalStore( * queryGuard.subscribe, * queryGuard.getSnapshot, * ) */import { createSignal } from './signal.js'
export class QueryGuard { private _status: 'idle' | 'dispatching' | 'running' = 'idle' private _generation = 0 private _changed = createSignal()
/** * Reserve the guard for queue processing. Transitions idle → dispatching. * Returns false if not idle (another query or dispatch in progress). */ reserve(): boolean { if (this._status !== 'idle') return false this._status = 'dispatching' this._notify() return true }
/** * Cancel a reservation when processQueueIfReady had nothing to process. * Transitions dispatching → idle. */ cancelReservation(): void { if (this._status !== 'dispatching') return this._status = 'idle' this._notify() }
/** * Start a query. Returns the generation number on success, * or null if a query is already running (concurrent guard). * Accepts transitions from both idle (direct user submit) * and dispatching (queue processor path). */ tryStart(): number | null { if (this._status === 'running') return null this._status = 'running' ++this._generation this._notify() return this._generation }
/** * End a query. Returns true if this generation is still current * (meaning the caller should perform cleanup). Returns false if a * newer query has started (stale finally block from a cancelled query). */ end(generation: number): boolean { if (this._generation !== generation) return false if (this._status !== 'running') return false this._status = 'idle' this._notify() return true }
/** * Force-end the current query regardless of generation. * Used by onCancel where any running query should be terminated. * Increments generation so stale finally blocks from the cancelled * query's promise rejection will see a mismatch and skip cleanup. */ forceEnd(): void { if (this._status === 'idle') return this._status = 'idle' ++this._generation this._notify() }
/** * Is the guard active (dispatching or running)? * Always synchronous — not subject to React state batching delays. */ get isActive(): boolean { return this._status !== 'idle' }
get generation(): number { return this._generation }
// -- // useSyncExternalStore interface
/** Subscribe to state changes. Stable reference — safe as useEffect dep. */ subscribe = this._changed.subscribe
/** Snapshot for useSyncExternalStore. Returns `isActive`. */ getSnapshot = (): boolean => { return this._status !== 'idle' }
private _notify(): void { this._changed.emit() }}