@langchain/langgraph-sdk
Version:
Client library for interacting with the LangGraph API
106 lines (105 loc) • 3.73 kB
JavaScript
import { isRootNamespace } from "./namespace.js";
//#region src/stream/lifecycle-loading-tracker.ts
/**
* Drives root-snapshot `isLoading` from root lifecycle events.
*/
var LifecycleLoadingTracker = class {
/** Snapshot store whose `isLoading` slot we manage. */
#store;
/**
* Disposal probe. Consulted from the deferred `setTimeout` so a
* controller torn down between scheduling and firing doesn't end
* up writing to a defunct store.
*/
#isDisposed;
/**
* Highest sequence number of a terminal lifecycle we've observed.
* `running` events at or below this seq are stale replays and
* are dropped to avoid flipping the loading flag back on after the
* run has already ended.
*/
#lastTerminalLifecycleSeq = -1;
/**
* Highest sequence number of a `running` lifecycle we've observed.
* Deferred terminal resets compare against this so an
* `interrupted`/`completed`/`failed` whose macrotask fires after a
* newer `running` does not incorrectly clear `isLoading`.
*/
#lastRunningLifecycleSeq = -1;
/** Notified once `isLoading` actually settles to `false` on a terminal event. */
#onSettled;
#ignore;
/**
* @param params.store - Store whose `isLoading` slot we drive.
* @param params.isDisposed - Disposal probe consulted from deferred callbacks.
* @param params.onSettled - Called once `isLoading` flips to `false`,
* including for a run this client only observed and never
* dispatched itself.
* @param params.ignore - Lifecycle events to skip, e.g. another run's
* replayed terminal while this client's own run is in flight.
*/
constructor(params) {
this.#store = params.store;
this.#isDisposed = params.isDisposed;
this.#onSettled = params.onSettled;
this.#ignore = params.ignore;
}
/**
* Bound listener suitable for `EventBus.subscribe`. Re-exposed as a
* stable property so the controller can later remove the same
* function reference from the bus on teardown.
*/
listener = (event) => {
this.handle(event);
};
/**
* Reset internal state when rebinding to a new thread.
*
* The seq guards are per-thread: a new thread's lifecycle events
* are not stale relative to the old thread's.
*/
reset() {
this.#lastTerminalLifecycleSeq = -1;
this.#lastRunningLifecycleSeq = -1;
}
/**
* Process a single protocol event.
*
* Filters down to root-namespace lifecycle events, then mutates the
* store's `isLoading` slot. All other events are ignored.
*
* @param event - Any protocol event from the controller's root bus.
*/
handle(event) {
if (event.method !== "lifecycle") return;
if (!isRootNamespace(event.params.namespace)) return;
if (this.#ignore?.(event)) return;
const lifecycle = event.params.data;
const seq = typeof event.seq === "number" ? event.seq : void 0;
if (lifecycle?.event === "running") {
if (seq != null && seq <= this.#lastTerminalLifecycleSeq) return;
if (seq != null) this.#lastRunningLifecycleSeq = Math.max(this.#lastRunningLifecycleSeq, seq);
this.#store.setState((s) => s.isLoading ? s : {
...s,
isLoading: true
});
return;
}
if (lifecycle?.event === "completed" || lifecycle?.event === "failed" || lifecycle?.event === "interrupted") {
if (seq != null) this.#lastTerminalLifecycleSeq = Math.max(this.#lastTerminalLifecycleSeq, seq);
setTimeout(() => {
if (this.#isDisposed()) return;
if (seq != null && this.#lastRunningLifecycleSeq > seq) return;
const wasLoading = this.#store.getSnapshot().isLoading;
this.#store.setState((s) => s.isLoading ? {
...s,
isLoading: false
} : s);
if (wasLoading) this.#onSettled?.();
}, 0);
}
}
};
//#endregion
export { LifecycleLoadingTracker };
//# sourceMappingURL=lifecycle-loading-tracker.js.map