| author | |
| committer | |
| log | fccecd8749056d4f811962a1523229500e7a4e72 |
| tree | b4280023f149d091d854c0b2b54b9912237755f3 |
| parent | 30a084c463acb7b8b75550a117b9bd82e98c5ac4 |
| signature | Signed by PGP key B5690EEEBB952194 |
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 @@ | ||
| 1 | import { assert, describe, it } from "@effect/vitest"; | ||
| 2 | import * as Cause from "effect/Cause"; | ||
| 3 | |||
| 4 | import { DesktopLifecycleRelaunchError } from "./DesktopLifecycle.ts"; | ||
| 5 | import { DesktopApplicationMenuActionError } from "../window/DesktopApplicationMenu.ts"; | ||
| 6 | |||
| 7 | describe("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 @@ | ... | @@ -1,8 +1,8 @@ |
| 1 | import * as Cause from "effect/Cause"; | ||
| 2 | import * as Context from "effect/Context"; | 1 | import * as Context from "effect/Context"; |
| 3 | import * as Effect from "effect/Effect"; | 2 | import * as Effect from "effect/Effect"; |
| 4 | import * as Layer from "effect/Layer"; | 3 | import * as Layer from "effect/Layer"; |
| 5 | import * as Ref from "effect/Ref"; | 4 | import * as Ref from "effect/Ref"; |
| 5 | import * as Schema from "effect/Schema"; | ||
| 6 | import * as Scope from "effect/Scope"; | 6 | import * as Scope from "effect/Scope"; |
| 7 | 7 | ||
| 8 | import type * as Electron from "electron"; | 8 | import type * as Electron from "electron"; |
| ... | @@ -15,6 +15,18 @@ import * as ElectronTheme from "../electron/ElectronTheme.ts"; | ... | @@ -15,6 +15,18 @@ import * as ElectronTheme from "../electron/ElectronTheme.ts"; |
| 15 | import * as DesktopState from "./DesktopState.ts"; | 15 | import * as DesktopState from "./DesktopState.ts"; |
| 16 | import * as DesktopWindow from "../window/DesktopWindow.ts"; | 16 | import * as DesktopWindow from "../window/DesktopWindow.ts"; |
| 17 | 17 | ||
| 18 | export 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 | |||
| 18 | export type DesktopLifecycleRuntimeServices = | 30 | export type DesktopLifecycleRuntimeServices = |
| 19 | | DesktopEnvironment.DesktopEnvironment | 31 | | DesktopEnvironment.DesktopEnvironment |
| 20 | | DesktopShutdown.DesktopShutdown | 32 | | DesktopShutdown.DesktopShutdown |
| ... | @@ -142,11 +154,10 @@ export const make = DesktopLifecycle.of({ | ... | @@ -142,11 +154,10 @@ export const make = DesktopLifecycle.of({ |
| 142 | }); | 154 | }); |
| 143 | yield* electronApp.exit(0); | 155 | yield* electronApp.exit(0); |
| 144 | }).pipe( | 156 | }).pipe( |
| 145 | Effect.catchCause((cause) => | 157 | Effect.catchCause((cause) => { |
| 146 | logLifecycleError("desktop relaunch failed", { | 158 | const error = new DesktopLifecycleRelaunchError({ reason, cause }); |
| 147 | cause: Cause.pretty(cause), | 159 | return logLifecycleError(error.message, { error }); |
| 148 | }), | 160 | }), |
| 149 | ), | ||
| 150 | Effect.forkDetach, | 161 | Effect.forkDetach, |
| 151 | Effect.asVoid, | 162 | Effect.asVoid, |
| 152 | ); | 163 | ); |
apps/desktop/src/window/DesktopApplicationMenu.ts+17-7| ... | @@ -1,8 +1,8 @@ | ... | @@ -1,8 +1,8 @@ |
| 1 | import * as Cause from "effect/Cause"; | ||
| 2 | import * as Context from "effect/Context"; | 1 | import * as Context from "effect/Context"; |
| 3 | import * as Effect from "effect/Effect"; | 2 | import * as Effect from "effect/Effect"; |
| 4 | import * as Layer from "effect/Layer"; | 3 | import * as Layer from "effect/Layer"; |
| 5 | import * as Option from "effect/Option"; | 4 | import * as Option from "effect/Option"; |
| 5 | import * as Schema from "effect/Schema"; | ||
| 6 | 6 | ||
| 7 | import type * as Electron from "electron"; | 7 | import type * as Electron from "electron"; |
| 8 | 8 | ||
| ... | @@ -14,6 +14,18 @@ import * as DesktopEnvironment from "../app/DesktopEnvironment.ts"; | ... | @@ -14,6 +14,18 @@ import * as DesktopEnvironment from "../app/DesktopEnvironment.ts"; |
| 14 | import * as DesktopUpdates from "../updates/DesktopUpdates.ts"; | 14 | import * as DesktopUpdates from "../updates/DesktopUpdates.ts"; |
| 15 | import * as DesktopWindow from "./DesktopWindow.ts"; | 15 | import * as DesktopWindow from "./DesktopWindow.ts"; |
| 16 | 16 | ||
| 17 | export 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 | |||
| 17 | export class DesktopApplicationMenu extends Context.Service< | 29 | export class DesktopApplicationMenu extends Context.Service< |
| 18 | DesktopApplicationMenu, | 30 | DesktopApplicationMenu, |
| 19 | { | 31 | { |
| ... | @@ -100,12 +112,10 @@ export const make = Effect.gen(function* () { | ... | @@ -100,12 +112,10 @@ export const make = Effect.gen(function* () { |
| 100 | effect.pipe( | 112 | effect.pipe( |
| 101 | Effect.annotateLogs({ action }), | 113 | Effect.annotateLogs({ action }), |
| 102 | Effect.withSpan("desktop.menu.action"), | 114 | Effect.withSpan("desktop.menu.action"), |
| 103 | Effect.catchCause((cause) => | 115 | Effect.catchCause((cause) => { |
| 104 | logMenuError("desktop menu action failed", { | 116 | const error = new DesktopApplicationMenuActionError({ action, cause }); |
| 105 | action, | 117 | return logMenuError(error.message, { error }); |
| 106 | cause: Cause.pretty(cause), | 118 | }), |
| 107 | }), | ||
| 108 | ), | ||
| 109 | ), | 119 | ), |
| 110 | ); | 120 | ); |
| 111 | }; | 121 | }; |