Repository: goauthentik/authentik
File: web/src/flow/FlowExecutor.ts (TypeScript)

Code before:

        const { tag, variant } = mapping;

        const props = spread(
            match(variant)
                .with("challenge", () => challengeProps)
                .with("standard", () => ({ ...challengeProps, ...litParts }))
                .with("inspect", () => ({ ...challengeProps, "?promptUser": inspectorOpen }))
                .exhaustive(),
        );

        return staticHTML`<${unsafeStatic(tag)} ${props}></${unsafeStatic(tag)}>`;
    }

    protected renderChallengeError(error: unknown): SlottedTemplateResult {
        const detail = pluckErrorDetail(error);

        // eslint-disable-next-line no-console
        console.trace(error);

        const errorChallenge: FlowErrorChallenge = {
            component: "ak-stage-flow-error",
            error: detail,
            requestId: "",
        };

        return html`<ak-stage-flow-error .challenge=${errorChallenge}></ak-stage-flow-error>`;
    }

    //#endregion

    //#region Render Inspector

    @listen(AKFlowInspectorChangeEvent)
    protected toggleInspector = () => {
        this.inspectorOpen = !this.inspectorOpen;

        const drawer = document.getElementById("flow-drawer");

        if (!drawer) {
            return;
        }

        drawer.classList.toggle("pf-m-expanded", this.inspectorOpen);
        drawer.classList.toggle("pf-m-collapsed", !this.inspectorOpen);
    };

    protected renderInspectorButton() {
        return guard([this.inspectorAvailable, this.inspectorOpen], () => {
            if (!this.inspectorAvailable || this.inspectorOpen) {
                return null;

Review comment (suggestion):
This was inspired from #20261, splitting off a rendering state into a method. This also let's us handle arbitrary errors encountered while rendering.

Code after:
            part: "challenge",
            exportparts: exportParts(["additional-actions", "footer-band"], "challenge"),
        };

        let mapping: StageMapping;

        try {
            mapping = await StageMapping.from(stageEntry);
        } catch (error: unknown) {
            return this.renderChallengeError(error);
        }

        const { tag, variant } = mapping;

        const props = spread(
            match(variant)
                .with("challenge", () => challengeProps)
                .with("standard", () => ({ ...challengeProps, ...litParts }))
                .with("inspect", () => ({ ...challengeProps, "?promptUser": inspectorOpen }))
                .exhaustive(),
        );

        return staticHTML`<${unsafeStatic(tag)} ${props}></${unsafeStatic(tag)}>`;
    }

    protected renderChallengeError(error: unknown): SlottedTemplateResult {
        const detail = pluckErrorDetail(error);

        // eslint-disable-next-line no-console
        console.trace(error);

        const errorChallenge: FlowErrorChallenge = {
            component: "ak-stage-flow-error",
            error: detail,
            requestId: "",
        };

        return html`<ak-stage-flow-error .challenge=${errorChallenge}></ak-stage-flow-error>`;
    }

    //#endregion

    //#region Render Inspector

    @listen(AKFlowInspectorChangeEvent)
    protected toggleInspector = () => {
        this.inspectorOpen = !this.inspectorOpen;

        const drawer = document.getElementById("flow-drawer");

        if (!drawer) {