authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-20 11:08:32-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-20 11:08:32-07:00
logfccecd8749056d4f811962a1523229500e7a4e72
treeb4280023f149d091d854c0b2b54b9912237755f3
parent30a084c463acb7b8b75550a117b9bd82e98c5ac4
signature Signed by PGP key B5690EEEBB952194

[codex] Preserve detached desktop action causes (#3371)

Co-authored-by: codex <codex@users.noreply.github.com>

3 files changed, 71 insertions(+), 13 deletions(-)

apps/desktop/src/app/DesktopDetachedActionErrors.test.ts created+37
......@@ -0,0 +1,37 @@
1import { assert, describe, it } from "@effect/vitest";
2import * as Cause from "effect/Cause";
3
4import { DesktopLifecycleRelaunchError } from "./DesktopLifecycle.ts";
5import { DesktopApplicationMenuActionError } from "../window/DesktopApplicationMenu.ts";
6
7describe("desktop detached action errors", () => {
8 it("preserves the complete relaunch failure cause and reason", () => {
9 const cause = Cause.combine(
10 Cause.fail(new Error("shutdown failed")),
11 Cause.die(new Error("relaunch defect")),
12 );
13 const error = new DesktopLifecycleRelaunchError({
14 reason: "apply update",
15 cause,
16 });
17
18 assert.strictEqual(error.cause, cause);
19 assert.equal(error.reason, "apply update");
20 assert.equal(error.message, 'Desktop relaunch failed for reason "apply update".');
21 });
22
23 it("preserves the complete menu action failure cause and action", () => {
24 const cause = Cause.combine(
25 Cause.fail(new Error("window unavailable")),
26 Cause.die(new Error("dispatch defect")),
27 );
28 const error = new DesktopApplicationMenuActionError({
29 action: "open-settings",
30 cause,
31 });
32
33 assert.strictEqual(error.cause, cause);
34 assert.equal(error.action, "open-settings");
35 assert.equal(error.message, 'Desktop menu action "open-settings" failed.');
36 });
37});
apps/desktop/src/app/DesktopLifecycle.ts+17-6
......@@ -1,8 +1,8 @@
1import * as Cause from "effect/Cause";
21import * as Context from "effect/Context";
32import * as Effect from "effect/Effect";
43import * as Layer from "effect/Layer";
54import * as Ref from "effect/Ref";
5import * as Schema from "effect/Schema";
66import * as Scope from "effect/Scope";
77
88import type * as Electron from "electron";
......@@ -15,6 +15,18 @@ import * as ElectronTheme from "../electron/ElectronTheme.ts";
1515import * as DesktopState from "./DesktopState.ts";
1616import * as DesktopWindow from "../window/DesktopWindow.ts";
1717
18export class DesktopLifecycleRelaunchError extends Schema.TaggedErrorClass<DesktopLifecycleRelaunchError>()(
19 "DesktopLifecycleRelaunchError",
20 {
21 reason: Schema.String,
22 cause: Schema.Defect(),
23 },
24) {
25 override get message(): string {
26 return `Desktop relaunch failed for reason "${this.reason}".`;
27 }
28}
29
1830export type DesktopLifecycleRuntimeServices =
1931 | DesktopEnvironment.DesktopEnvironment
2032 | DesktopShutdown.DesktopShutdown
......@@ -142,11 +154,10 @@ export const make = DesktopLifecycle.of({
142154 });
143155 yield* electronApp.exit(0);
144156 }).pipe(
145 Effect.catchCause((cause) =>
146 logLifecycleError("desktop relaunch failed", {
147 cause: Cause.pretty(cause),
148 }),
149 ),
157 Effect.catchCause((cause) => {
158 const error = new DesktopLifecycleRelaunchError({ reason, cause });
159 return logLifecycleError(error.message, { error });
160 }),
150161 Effect.forkDetach,
151162 Effect.asVoid,
152163 );
apps/desktop/src/window/DesktopApplicationMenu.ts+17-7
......@@ -1,8 +1,8 @@
1import * as Cause from "effect/Cause";
21import * as Context from "effect/Context";
32import * as Effect from "effect/Effect";
43import * as Layer from "effect/Layer";
54import * as Option from "effect/Option";
5import * as Schema from "effect/Schema";
66
77import type * as Electron from "electron";
88
......@@ -14,6 +14,18 @@ import * as DesktopEnvironment from "../app/DesktopEnvironment.ts";
1414import * as DesktopUpdates from "../updates/DesktopUpdates.ts";
1515import * as DesktopWindow from "./DesktopWindow.ts";
1616
17export class DesktopApplicationMenuActionError extends Schema.TaggedErrorClass<DesktopApplicationMenuActionError>()(
18 "DesktopApplicationMenuActionError",
19 {
20 action: Schema.String,
21 cause: Schema.Defect(),
22 },
23) {
24 override get message(): string {
25 return `Desktop menu action "${this.action}" failed.`;
26 }
27}
28
1729export class DesktopApplicationMenu extends Context.Service<
1830 DesktopApplicationMenu,
1931 {
......@@ -100,12 +112,10 @@ export const make = Effect.gen(function* () {
100112 effect.pipe(
101113 Effect.annotateLogs({ action }),
102114 Effect.withSpan("desktop.menu.action"),
103 Effect.catchCause((cause) =>
104 logMenuError("desktop menu action failed", {
105 action,
106 cause: Cause.pretty(cause),
107 }),
108 ),
115 Effect.catchCause((cause) => {
116 const error = new DesktopApplicationMenuActionError({ action, cause });
117 return logMenuError(error.message, { error });
118 }),
109119 ),
110120 );
111121 };