Plan Checker (neues Studenten-Werkzeug, Seitenleiste + Admin → Plan-Checker- Merkmale): lädt einen Bauplan als PDF hoch und lässt ChatGPT oder Claude ihn gegen eine gepflegte Merkmalsliste prüfen. PDF-Rendering (pdf.js + @napi-rs/canvas) berücksichtigt jetzt Ebenen (Optional Content Groups) und WASM-Decoder korrekt, sendet eine Übersicht plus mehrere hochaufgelöste Kachel-Ausschnitte statt eines einzelnen, zu groben Bilds, und beide Anbieter bekommen dasselbe Bildmaterial. Jedes Prüfergebnis kann auf eines der gesendeten Bilder als Beleg verweisen (bildIndex) — als Screenshot im Ergebnis anklickbar, öffnet vergrößert in einem Modal statt eines von Chrome blockierten target=_blank-Links auf eine data:-URL. KI-Modellname ist jetzt pro Konto überschreibbar (Einstellungen → Mein Konto) für Drittanbieter mit abweichendem Modell-Katalog. Wochenbericht: dichte Kartenansicht bekommt einen Fokus-Modus — nur der Kommentar liegt offen, Leistungsphasen und Beteiligte stecken in aufklappbaren Abschnitten statt dauerhaft nebeneinander zu stehen. Projektbereich: Terminplan-Auslastung rechnet jetzt exakt wie der Auslastungs-Tab/die Wandtafel (statt einer unabhängigen, tageweisen Schätzung); die LPH-Fortschrittsliste im Dashboard zeigt nur noch die beauftragten Leistungsphasen. Wandtafel: Dashboard-Kopf mit Kennzahlen-Reihe und Panel-Reihe neu strukturiert, in den Blautönen des Firmenlogos; dieselbe Palette wurde anschließend auf den gesamten App-weiten Dark Mode übernommen. Dateibereinigung: nicht mehr benötigte Test-Uploads/Backups entfernt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
6.7 KiB
Client Lifecycle
A {Client} can be understood as a state machine. As requests are dispatched, processed, and drained, the client moves through a well-defined set of states until it is eventually destroyed. This document describes those states and the transitions between them.
The {Client} class is not literally implemented as a state machine, so the actual execution may deviate slightly from what is described here. Treat this guide as a conceptual model for reasoning about a client's behavior rather than as a formal specification.
State transition overview
- A {Client} begins in the idle state with no socket connection and no
queued requests.
- The connect transition moves the {Client} to the pending state, where requests can be queued before they are processed.
- Calling
client.close()orclient.destroy()moves the {Client} to the destroyed state. Because there are no queued requests in this state, close transitions straight to destroyed.
- The pending state indicates that the underlying socket connection has been
established and that requests are queueing.
- The process transition moves the {Client} to the processing state, where requests are processed.
- If requests are queued, close transitions to processing; otherwise it transitions to destroyed.
- The destroy transition moves the {Client} to destroyed.
- The processing state initializes to the processing.running sub-state.
- If the current request body requires draining, needDrain moves the {Client} into the processing.busy sub-state, which returns to processing.running once drainComplete fires.
- After all queued requests complete, keepalive moves the {Client} back to the pending state. If no requests are queued before the socket times out, the {Client} transitions to idle.
- If close is fired while the {Client} still has queued requests, the {Client} transitions to processing.closing, where it completes all outstanding requests before firing done.
- The done transition moves the {Client} gracefully to destroyed.
- At any time, destroy moves the {Client} from processing to destroyed, aborting any queued requests.
- The destroyed state is final; the {Client} is no longer functional.
A state diagram representing a {Client} instance:
stateDiagram-v2
[*] --> idle
idle --> pending : connect
idle --> destroyed : destroy/close
pending --> idle : timeout
pending --> destroyed : destroy
state close_fork <<fork>>
pending --> close_fork : close
close_fork --> processing
close_fork --> destroyed
pending --> processing : process
processing --> pending : keepalive
processing --> destroyed : done
processing --> destroyed : destroy
destroyed --> [*]
state processing {
[*] --> running
running --> closing : close
running --> busy : needDrain
busy --> running : drainComplete
running --> [*] : keepalive
closing --> [*] : done
}
State details
idle
idle is the initial state of a {Client} instance. Although an origin is
required to construct a {Client}, the underlying socket connection is not
established until a request is queued through client.dispatch(). Calling
client.dispatch() directly, or through one of its higher-level wrappers
(client.connect(), client.pipeline(), client.request(),
client.stream(), or client.upgrade()), moves the {Client} from
idle to pending, and then usually directly on to processing.
Calling client.close() or client.destroy() in this state moves the
{Client} straight to destroyed, since there are no queued requests.
pending
pending signifies a connected but non-processing {Client}. On entering this
state, the {Client} establishes a socket connection and emits the
'connect' event, signalling that a connection to the origin supplied at
construction time has been established. The internal queue starts empty, and
requests can begin queueing.
Calling client.close() with queued requests moves the {Client} to the
processing state; without queued requests it moves to destroyed.
Calling client.destroy() moves the {Client} directly to destroyed,
regardless of any queued requests.
processing
processing is itself a sub-state machine that initializes to the
processing.running sub-state. client.dispatch(), client.close(),
and client.destroy() can all be called while the {Client} is in this
state. client.dispatch() queues additional requests while existing ones
continue to be processed, client.close() moves the {Client} to the
processing.closing sub-state, and client.destroy() moves it to
destroyed.
running
In processing.running, queued requests are processed in FIFO order. If a
request body requires draining, needDrain moves the {Client} to the
processing.busy sub-state. The close transition moves the {Client} to the
processing.closing sub-state. When all queued requests have completed and
neither client.close() nor client.destroy() has been called, the
keepalive transition moves the {Client} back to the pending state. There
the {Client} waits for the socket connection to time out; once it does, the
{Client} returns to the idle state.
busy
This sub-state is entered only when a request body is a {stream.Readable} that requires draining. The {Client} cannot process additional requests while in this state and waits until the current request body has been fully drained before returning to processing.running.
closing
This sub-state is entered only when a {Client} has queued requests and
client.close() is called. The {Client} continues to process requests as
usual, except that no additional requests may be queued. Once all queued
requests have been processed, done fires and the {Client} moves gracefully to
destroyed without an error.
destroyed
destroyed is the final state of a {Client} instance. Once in this state, the {Client} is no longer functional, and calling any further {Client} method rejects or fails with a {ClientDestroyedError}.