authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-20 11:19:11-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-20 11:19:11-07:00
logbfe61741b83dd7f2f66af7e7570ae0f67a16d631
tree0227a45efce004ccc733fced021fd4f8e552de03
parent4d3fcacd840d7b710f8415b4bebf3826df358c91
signature Signed by PGP key B5690EEEBB952194

[codex] Simplify desktop client settings errors (#3265)

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

2 files changed, 52 insertions(+), 24 deletions(-)

apps/desktop/src/settings/DesktopClientSettings.test.ts+4-1
...@@ -5,6 +5,7 @@ import * as Effect from "effect/Effect";...@@ -5,6 +5,7 @@ import * as Effect from "effect/Effect";
5import * as FileSystem from "effect/FileSystem";5import * as FileSystem from "effect/FileSystem";
6import * as Layer from "effect/Layer";6import * as Layer from "effect/Layer";
7import * as Option from "effect/Option";7import * as Option from "effect/Option";
8import * as PlatformError from "effect/PlatformError";
8import * as Schema from "effect/Schema";9import * as Schema from "effect/Schema";
910
10import * as DesktopConfig from "../app/DesktopConfig.ts";11import * as DesktopConfig from "../app/DesktopConfig.ts";
...@@ -117,11 +118,13 @@ describe("DesktopClientSettings", () => {...@@ -117,11 +118,13 @@ describe("DesktopClientSettings", () => {
117 assert.instanceOf(error, DesktopClientSettings.DesktopClientSettingsWriteError);118 assert.instanceOf(error, DesktopClientSettings.DesktopClientSettingsWriteError);
118 assert.equal(error.operation, "replace-settings-file");119 assert.equal(error.operation, "replace-settings-file");
119 assert.equal(error.path, environment.clientSettingsPath);120 assert.equal(error.path, environment.clientSettingsPath);
120 assert.exists(error.cause);121 assert.instanceOf(error.cause, PlatformError.PlatformError);
122 assert.isString(error.cause.stack);
121 assert.equal(123 assert.equal(
122 error.message,124 error.message,
123 `Desktop client settings write failed during replace-settings-file at ${environment.clientSettingsPath}.`,125 `Desktop client settings write failed during replace-settings-file at ${environment.clientSettingsPath}.`,
124 );126 );
127 assert.notInclude(error.message, error.cause.message);
125 }),128 }),
126 ),129 ),
127 );130 );
apps/desktop/src/settings/DesktopClientSettings.ts+48-23
...@@ -25,7 +25,9 @@ const decodeClientSettingsJsonValue = Schema.decodeEffect(ClientSettingsJson);...@@ -25,7 +25,9 @@ const decodeClientSettingsJsonValue = Schema.decodeEffect(ClientSettingsJson);
25const decodeClientSettingsJson = (raw: string): Effect.Effect<ClientSettings, Schema.SchemaError> =>25const decodeClientSettingsJson = (raw: string): Effect.Effect<ClientSettings, Schema.SchemaError> =>
26 decodeLegacyClientSettingsDocumentJson(raw).pipe(26 decodeLegacyClientSettingsDocumentJson(raw).pipe(
27 Effect.map((document) => document.settings),27 Effect.map((document) => document.settings),
28 Effect.catch(() => decodeClientSettingsJsonValue(raw)),28 Effect.catchTags({
29 SchemaError: () => decodeClientSettingsJsonValue(raw),
30 }),
29 );31 );
30const encodeClientSettingsJson = Schema.encodeEffect(ClientSettingsJson);32const encodeClientSettingsJson = Schema.encodeEffect(ClientSettingsJson);
3133
...@@ -36,7 +38,6 @@ const DesktopClientSettingsWriteOperation = Schema.Literals([...@@ -36,7 +38,6 @@ const DesktopClientSettingsWriteOperation = Schema.Literals([
36 "write-temporary-file",38 "write-temporary-file",
37 "replace-settings-file",39 "replace-settings-file",
38]);40]);
39type DesktopClientSettingsWriteOperation = typeof DesktopClientSettingsWriteOperation.Type;
4041
41export class DesktopClientSettingsWriteError extends Schema.TaggedErrorClass<DesktopClientSettingsWriteError>()(42export class DesktopClientSettingsWriteError extends Schema.TaggedErrorClass<DesktopClientSettingsWriteError>()(
42 "DesktopClientSettingsWriteError",43 "DesktopClientSettingsWriteError",
...@@ -51,13 +52,6 @@ export class DesktopClientSettingsWriteError extends Schema.TaggedErrorClass<Des...@@ -51,13 +52,6 @@ export class DesktopClientSettingsWriteError extends Schema.TaggedErrorClass<Des
51 }52 }
52}53}
5354
54const writeError = (
55 operation: DesktopClientSettingsWriteOperation,
56 path: string,
57 cause: unknown,
58): DesktopClientSettingsWriteError =>
59 new DesktopClientSettingsWriteError({ operation, path, cause });
60
61export class DesktopClientSettings extends Context.Service<55export class DesktopClientSettings extends Context.Service<
62 DesktopClientSettings,56 DesktopClientSettings,
63 {57 {
...@@ -96,19 +90,45 @@ const writeClientSettings = Effect.fnUntraced(function* (input: {...@@ -96,19 +90,45 @@ const writeClientSettings = Effect.fnUntraced(function* (input: {
96 const directory = input.path.dirname(input.settingsPath);90 const directory = input.path.dirname(input.settingsPath);
97 const tempPath = `${input.settingsPath}.${process.pid}.${input.suffix}.tmp`;91 const tempPath = `${input.settingsPath}.${process.pid}.${input.suffix}.tmp`;
98 const encoded = yield* encodeClientSettingsJson(input.settings).pipe(92 const encoded = yield* encodeClientSettingsJson(input.settings).pipe(
99 Effect.mapError((cause) => writeError("encode-document", input.settingsPath, cause)),93 Effect.mapError(
94 (cause) =>
95 new DesktopClientSettingsWriteError({
96 operation: "encode-document",
97 path: input.settingsPath,
98 cause,
99 }),
100 ),
101 );
102 yield* input.fileSystem.makeDirectory(directory, { recursive: true }).pipe(
103 Effect.mapError(
104 (cause) =>
105 new DesktopClientSettingsWriteError({
106 operation: "create-directory",
107 path: directory,
108 cause,
109 }),
110 ),
111 );
112 yield* input.fileSystem.writeFileString(tempPath, `${encoded}\n`).pipe(
113 Effect.mapError(
114 (cause) =>
115 new DesktopClientSettingsWriteError({
116 operation: "write-temporary-file",
117 path: tempPath,
118 cause,
119 }),
120 ),
121 );
122 yield* input.fileSystem.rename(tempPath, input.settingsPath).pipe(
123 Effect.mapError(
124 (cause) =>
125 new DesktopClientSettingsWriteError({
126 operation: "replace-settings-file",
127 path: input.settingsPath,
128 cause,
129 }),
130 ),
100 );131 );
101 yield* input.fileSystem
102 .makeDirectory(directory, { recursive: true })
103 .pipe(Effect.mapError((cause) => writeError("create-directory", directory, cause)));
104 yield* input.fileSystem
105 .writeFileString(tempPath, `${encoded}\n`)
106 .pipe(Effect.mapError((cause) => writeError("write-temporary-file", tempPath, cause)));
107 yield* input.fileSystem
108 .rename(tempPath, input.settingsPath)
109 .pipe(
110 Effect.mapError((cause) => writeError("replace-settings-file", input.settingsPath, cause)),
111 );
112});132});
113133
114export const make = Effect.gen(function* () {134export const make = Effect.gen(function* () {
...@@ -124,8 +144,13 @@ export const make = Effect.gen(function* () {...@@ -124,8 +144,13 @@ export const make = Effect.gen(function* () {
124 set: (settings) =>144 set: (settings) =>
125 crypto.randomUUIDv4.pipe(145 crypto.randomUUIDv4.pipe(
126 Effect.map((uuid) => uuid.replace(/-/g, "")),146 Effect.map((uuid) => uuid.replace(/-/g, "")),
127 Effect.mapError((cause) =>147 Effect.mapError(
128 writeError("create-temporary-file-name", environment.clientSettingsPath, cause),148 (cause) =>
149 new DesktopClientSettingsWriteError({
150 operation: "create-temporary-file-name",
151 path: environment.clientSettingsPath,
152 cause,
153 }),
129 ),154 ),
130 Effect.flatMap((suffix) =>155 Effect.flatMap((suffix) =>
131 writeClientSettings({156 writeClientSettings({