diff --git a/apps/desktop/src/app/DesktopAppIdentity.test.ts b/apps/desktop/src/app/DesktopAppIdentity.test.ts index ce0c7d013a9916048373b2beaac0401e3b8eb7c5..295a07355acf5b785b460ea8081c9a706a25fac8 100644 --- a/apps/desktop/src/app/DesktopAppIdentity.test.ts +++ b/apps/desktop/src/app/DesktopAppIdentity.test.ts @@ -76,7 +76,7 @@ const makeAssetsLayer = (png: Option.Option) => icns: Option.none(), png, }), - resolveResourcePath: () => Effect.succeed(Option.none()), + resolveResourcePath: () => Effect.succeedNone, } satisfies DesktopAssets.DesktopAssets["Service"]); const makeEnvironmentLayer = (overrides: TestEnvironmentInput = {}) => { diff --git a/apps/desktop/src/app/DesktopConnectionCatalogStore.test.ts b/apps/desktop/src/app/DesktopConnectionCatalogStore.test.ts index fdc69841343cbaef094a6b87ded3107e1527fa83..5cb7aafbbd2006d73bfd6740f401dc246d8bcdd5 100644 --- a/apps/desktop/src/app/DesktopConnectionCatalogStore.test.ts +++ b/apps/desktop/src/app/DesktopConnectionCatalogStore.test.ts @@ -46,7 +46,7 @@ function makeSafeStorageLayer(available: boolean, failDecrypt: Ref.Ref return decoded.slice("encrypted:".length); }); }, - selectedStorageBackend: Effect.succeed(Option.none()), + selectedStorageBackend: Effect.succeedNone, } satisfies ElectronSafeStorage.ElectronSafeStorage["Service"]); } diff --git a/apps/desktop/src/app/DesktopConnectionCatalogStore.ts b/apps/desktop/src/app/DesktopConnectionCatalogStore.ts index e2e0cd413bd98285fd681e2cd8b1261645f0f2c5..46b2546e681ca3fccbfc5430ae24574da3c3b459 100644 --- a/apps/desktop/src/app/DesktopConnectionCatalogStore.ts +++ b/apps/desktop/src/app/DesktopConnectionCatalogStore.ts @@ -205,7 +205,7 @@ const readDocument = ( raw === null ? Effect.succeed(Option.none()) : decodeEncryptedConnectionCatalogDocumentJson(raw).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.mapError( (cause) => new DesktopConnectionCatalogStoreDocumentDecodeError({ diff --git a/apps/desktop/src/backend/DesktopBackendConfiguration.ts b/apps/desktop/src/backend/DesktopBackendConfiguration.ts index ff693d1ac9e30197f3bbc04bbbff6828a033f445..13b6eef442a06e9036cca303d528727c05cffc85 100644 --- a/apps/desktop/src/backend/DesktopBackendConfiguration.ts +++ b/apps/desktop/src/backend/DesktopBackendConfiguration.ts @@ -199,11 +199,11 @@ const readPersistedBackendObservabilitySettings = Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const environment = yield* DesktopEnvironment.DesktopEnvironment; const raw = yield* fileSystem.readFileString(environment.serverSettingsPath).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ PlatformError: (cause) => cause.reason._tag === "NotFound" - ? Effect.succeed(Option.none()) + ? Effect.succeedNone : logBackendObservabilitySettingsReadFailure(environment.serverSettingsPath, cause).pipe( Effect.as(Option.none()), ), diff --git a/apps/desktop/src/backend/DesktopBackendManager.test.ts b/apps/desktop/src/backend/DesktopBackendManager.test.ts index df2001f1ac204836548792a06abe5e61ab7bf59e..901d9f4a270865287a86140f92f5178a21d1b0da 100644 --- a/apps/desktop/src/backend/DesktopBackendManager.test.ts +++ b/apps/desktop/src/backend/DesktopBackendManager.test.ts @@ -162,7 +162,7 @@ function makeTestInstance(input: MakeInstanceInput) { forInstance: () => Effect.succeed(stubLog), } satisfies DesktopObservability.DesktopBackendOutputLogFactory["Service"]), Layer.succeed(DesktopTelemetryPublisher.DesktopTelemetryPublisher, { - latest: Effect.succeed(Option.none()), + latest: Effect.succeedNone, changes: Stream.empty, encoded: input.desktopTelemetryStream ?? Stream.empty, handleControlForSource: () => Effect.void, @@ -1545,7 +1545,7 @@ describe("DesktopBackendManager", () => { const mockPool = Layer.succeed(DesktopBackendPool.DesktopBackendPool, { list: Effect.succeed([instance1, instance2]), - get: () => Effect.succeed(Option.none()), + get: () => Effect.succeedNone, primary: Effect.die(new Error("primary not implemented")), register: () => Effect.die(new Error("register not implemented")), unregister: () => Effect.die(new Error("unregister not implemented")), diff --git a/apps/desktop/src/backend/DesktopBackendPool.test.ts b/apps/desktop/src/backend/DesktopBackendPool.test.ts index 7859223161b734547469b1e6d03a1f8c51e5a3a4..8373437ae3124718fb56a9a557ef2342efa81444 100644 --- a/apps/desktop/src/backend/DesktopBackendPool.test.ts +++ b/apps/desktop/src/backend/DesktopBackendPool.test.ts @@ -67,7 +67,7 @@ function makePoolLayer( } satisfies DesktopObservability.DesktopBackendOutputLogShape), } satisfies DesktopObservability.DesktopBackendOutputLogFactory["Service"]), Layer.succeed(DesktopTelemetryPublisher.DesktopTelemetryPublisher, { - latest: Effect.succeed(Option.none()), + latest: Effect.succeedNone, changes: Stream.empty, encoded: Stream.empty, handleControlForSource: () => Effect.void, @@ -135,9 +135,7 @@ describe("DesktopBackendPool", () => { it.effect("layerTest dies when no instances are supplied", () => Effect.exit( - Effect.gen(function* () { - yield* DesktopBackendPool.DesktopBackendPool; - }).pipe(Effect.provide(DesktopBackendPool.layerTest([]))), + DesktopBackendPool.DesktopBackendPool.pipe(Effect.provide(DesktopBackendPool.layerTest([]))), ).pipe(Effect.map((exit) => assert.equal(exit._tag, "Failure"))), ); diff --git a/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.test.ts b/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.test.ts index e7a58baef140c7984b342533cdbfacf3a1f5ee67..ed71bd332d24ee8e6f4317933d148b1a895172e0 100644 --- a/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.test.ts +++ b/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.test.ts @@ -1,7 +1,6 @@ import { assert, describe, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; -import * as Option from "effect/Option"; import * as Ref from "effect/Ref"; import * as HttpClient from "effect/unstable/http/HttpClient"; import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"; @@ -60,7 +59,7 @@ describe("DesktopLocalEnvironmentAuth", () => { { id: PRIMARY_LOCAL_ENVIRONMENT_ID, label: Effect.succeed("Windows"), - currentConfig: Effect.succeed(Option.some(config)), + currentConfig: Effect.succeedSome(config), }, ]), } as unknown as DesktopBackendPool.DesktopBackendPool["Service"]); diff --git a/apps/desktop/src/electron/ElectronMenu.ts b/apps/desktop/src/electron/ElectronMenu.ts index 1360fb545bd02bc9dde54998cea0a18e6e73b12d..401e0c27ccd88e91c539448b32a2bfb7258a2169 100644 --- a/apps/desktop/src/electron/ElectronMenu.ts +++ b/apps/desktop/src/electron/ElectronMenu.ts @@ -226,7 +226,7 @@ export const make = Effect.gen(function* () { Effect.callback>((resume) => { const normalizedItems = normalizeContextMenuItems(input.items); if (normalizedItems.length === 0) { - resume(Effect.succeed(Option.none())); + resume(Effect.succeedNone); return; } diff --git a/apps/desktop/src/ipc/methods/snapShot.test.ts b/apps/desktop/src/ipc/methods/snapShot.test.ts index 7e20890ef55484622a2f64b0338d99570c9bf412..138bcbc36d3022a16586f6c0f72fdb3c0b1b8e59 100644 --- a/apps/desktop/src/ipc/methods/snapShot.test.ts +++ b/apps/desktop/src/ipc/methods/snapShot.test.ts @@ -51,7 +51,7 @@ describe("window capture IPC", () => { Effect.provide( Layer.mergeAll( Layer.succeed(ElectronWindow.ElectronWindow, { - main: Effect.succeed(Option.some({ webContents: { id: 7 } })), + main: Effect.succeedSome({ webContents: { id: 7 } }), } as ElectronWindow.ElectronWindow["Service"]), Layer.succeed(DesktopSnapShot.DesktopSnapShot, { previewConfig: () => @@ -87,7 +87,7 @@ describe("window capture IPC", () => { Effect.provide( Layer.mergeAll( Layer.succeed(ElectronWindow.ElectronWindow, { - main: Effect.succeed(Option.some({ webContents: { id: 7 } })), + main: Effect.succeedSome({ webContents: { id: 7 } }), } as ElectronWindow.ElectronWindow["Service"]), Layer.succeed(DesktopSnapShot.DesktopSnapShot, { state: Effect.succeed({ @@ -120,7 +120,7 @@ describe("window capture IPC", () => { Effect.provide( Layer.mergeAll( Layer.succeed(ElectronWindow.ElectronWindow, { - main: Effect.succeed(Option.some({ webContents: { id: 7 } })), + main: Effect.succeedSome({ webContents: { id: 7 } }), } as ElectronWindow.ElectronWindow["Service"]), Layer.succeed(DesktopSnapShot.DesktopSnapShot, { state: Effect.succeed({ linuxBackend: "niri" }), @@ -156,13 +156,11 @@ describe("window capture IPC", () => { Layer.succeed( ElectronWindow.ElectronWindow, ElectronWindow.ElectronWindow.of({ - main: Effect.succeed( - Option.some({ - getBounds: () => ({ x: 100, y: 80, width: 1_000, height: 700 }), - getContentBounds: () => ({ x: 100, y: 118, width: 1_000, height: 662 }), - webContents, - }), - ), + main: Effect.succeedSome({ + getBounds: () => ({ x: 100, y: 80, width: 1_000, height: 700 }), + getContentBounds: () => ({ x: 100, y: 118, width: 1_000, height: 662 }), + webContents, + }), } as ElectronWindow.ElectronWindow["Service"]), ), Layer.succeed( @@ -220,7 +218,7 @@ describe("window capture IPC", () => { Layer.succeed( ElectronWindow.ElectronWindow, ElectronWindow.ElectronWindow.of({ - main: Effect.succeed(Option.some({ webContents })), + main: Effect.succeedSome({ webContents }), } as ElectronWindow.ElectronWindow["Service"]), ), Layer.succeed( @@ -256,7 +254,7 @@ describe("window capture IPC", () => { Effect.provideService( ElectronWindow.ElectronWindow, ElectronWindow.ElectronWindow.of({ - main: Effect.succeed(Option.some({ webContents: { id: 7 } })), + main: Effect.succeedSome({ webContents: { id: 7 } }), } as ElectronWindow.ElectronWindow["Service"]), ), Effect.provideService(DesktopSnapShot.DesktopSnapShot, null as never), @@ -277,7 +275,7 @@ describe("window capture IPC", () => { Effect.provide( Layer.mergeAll( Layer.succeed(ElectronWindow.ElectronWindow, { - main: Effect.succeed(Option.some({ webContents: { id: 7 } })), + main: Effect.succeedSome({ webContents: { id: 7 } }), } as ElectronWindow.ElectronWindow["Service"]), Layer.succeed(DesktopSnapShot.DesktopSnapShot, { setup: (action: string) => @@ -295,7 +293,7 @@ describe("window capture IPC", () => { Layer.succeed( ElectronWindow.ElectronWindow, ElectronWindow.ElectronWindow.of({ - main: Effect.succeed(Option.some({ webContents: { id: 7 } })), + main: Effect.succeedSome({ webContents: { id: 7 } }), } as ElectronWindow.ElectronWindow["Service"]), ), Layer.succeed( @@ -320,7 +318,7 @@ describe("window capture IPC", () => { Layer.succeed( ElectronWindow.ElectronWindow, ElectronWindow.ElectronWindow.of({ - main: Effect.succeed(Option.some({ webContents: { id: 7 } })), + main: Effect.succeedSome({ webContents: { id: 7 } }), } as ElectronWindow.ElectronWindow["Service"]), ), Layer.succeed( diff --git a/apps/desktop/src/ipc/methods/sshEnvironment.ts b/apps/desktop/src/ipc/methods/sshEnvironment.ts index cfb993d35cfa5e25da55c2c9e85f894cff7c6045..e9535300104db62a55ae900f2d42f76338883bbe 100644 --- a/apps/desktop/src/ipc/methods/sshEnvironment.ts +++ b/apps/desktop/src/ipc/methods/sshEnvironment.ts @@ -137,13 +137,11 @@ export const ensureSshEnvironment = DesktopIpc.makeIpcMethod({ }) { const sshEnvironment = yield* DesktopSshEnvironment.DesktopSshEnvironment; return yield* sshEnvironment.ensureEnvironment(target, options).pipe( - Effect.catch((error) => - DesktopSshEnvironment.isDesktopSshPasswordPromptCancellation(error) - ? Effect.succeed({ - type: DesktopSshPasswordPromptCancelledType, - message: error.message, - }) - : Effect.fail(error), + Effect.catchIf(DesktopSshEnvironment.isDesktopSshPasswordPromptCancellation, (error) => + Effect.succeed({ + type: DesktopSshPasswordPromptCancelledType, + message: error.message, + }), ), ); }), diff --git a/apps/desktop/src/ipc/methods/window.test.ts b/apps/desktop/src/ipc/methods/window.test.ts index 764056742372944758ef54cc337e0c87a5495f51..0b43fa9e6ef9a25bdd3d0d09f6ebc43a8637fa63 100644 --- a/apps/desktop/src/ipc/methods/window.test.ts +++ b/apps/desktop/src/ipc/methods/window.test.ts @@ -61,7 +61,7 @@ const defaultWslInstance: DesktopBackendManager.DesktopBackendInstance = { label: Effect.succeed("WSL (default distro)"), start: Effect.void, stop: () => Effect.void, - currentConfig: Effect.succeed(Option.some(readyWslConfig)), + currentConfig: Effect.succeedSome(readyWslConfig), snapshot: Effect.succeed({ desiredRunning: true, ready: true, @@ -101,7 +101,7 @@ describe("getLocalEnvironmentBootstraps", () => { }; const retryingInstance: DesktopBackendManager.DesktopBackendInstance = { ...defaultWslInstance, - currentConfig: Effect.succeed(Option.some(retryingConfig)), + currentConfig: Effect.succeedSome(retryingConfig), snapshot: Effect.succeed({ desiredRunning: true, ready: false, @@ -128,16 +128,14 @@ describe("getLocalEnvironmentBootstraps", () => { it.effect("omits a bounded transient bootstrap after retries stop", () => { const stoppedInstance: DesktopBackendManager.DesktopBackendInstance = { ...defaultWslInstance, - currentConfig: Effect.succeed( - Option.some({ - ...readyWslConfig, - preflightFailure: Option.some({ - reason: "WSL probe timed out", - fatal: false, - retryLimit: 12, - }), + currentConfig: Effect.succeedSome({ + ...readyWslConfig, + preflightFailure: Option.some({ + reason: "WSL probe timed out", + fatal: false, + retryLimit: 12, }), - ), + }), snapshot: Effect.succeed({ desiredRunning: false, ready: false, @@ -163,7 +161,7 @@ describe("getWindowFullscreenState", () => { }).pipe( Effect.provide( Layer.mock(ElectronWindow.ElectronWindow)({ - currentMainOrFirst: Effect.succeed(Option.some(window)), + currentMainOrFirst: Effect.succeedSome(window), }), ), ); @@ -206,7 +204,7 @@ describe("pasteAsText", () => { }).pipe( Effect.provide( Layer.mock(ElectronWindow.ElectronWindow)({ - main: Effect.succeed(Option.some(window)), + main: Effect.succeedSome(window), }), ), ); @@ -219,7 +217,7 @@ describe("pickProjectFavicon", () => { Layer.mergeAll( Layer.mock(ElectronDialog.ElectronDialog)({ pickFiles }), Layer.mock(ElectronWindow.ElectronWindow)({ - focusedMainOrFirst: Effect.succeed(Option.none()), + focusedMainOrFirst: Effect.succeedNone, }), DesktopAppSettings.layerTest(settings), ); diff --git a/apps/desktop/src/ipc/methods/wsl.test.ts b/apps/desktop/src/ipc/methods/wsl.test.ts index bfd1a6e679d942fcefe1a3c24f3ef2c65789eaf1..65137cbbd895201690703b978ab36131fa73d97d 100644 --- a/apps/desktop/src/ipc/methods/wsl.test.ts +++ b/apps/desktop/src/ipc/methods/wsl.test.ts @@ -2,7 +2,6 @@ import { DesktopWslStateSchema } from "@t3tools/contracts"; import { assert, describe, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; -import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; import * as DesktopEnvironment from "../../app/DesktopEnvironment.ts"; @@ -34,7 +33,7 @@ function makeWslBackendLayer(input: { readonly onReconcile?: Effect.Effect DesktopWslBackend.DesktopWslBackend, DesktopWslBackend.DesktopWslBackend.of({ reconcile: input.onReconcile ?? Effect.void, - lastPreflightError: Effect.succeed(Option.none()), + lastPreflightError: Effect.succeedNone, }), ); } diff --git a/apps/desktop/src/preview/BrowserImport/ChromiumKeys.ts b/apps/desktop/src/preview/BrowserImport/ChromiumKeys.ts index 9310fd1c92f731737d618a51df745d6693ce1561..9c03523191a3ad006c34eedf6c81706f434099ab 100644 --- a/apps/desktop/src/preview/BrowserImport/ChromiumKeys.ts +++ b/apps/desktop/src/preview/BrowserImport/ChromiumKeys.ts @@ -315,8 +315,9 @@ export const resolveChromiumKeys = Effect.fn("ChromiumKeys.resolveChromiumKeys") // v10 remains importable when Secret Service is absent or does not // contain a key. An explicit denial/lock/cancel remains a consent // failure rather than being silently downgraded. - Effect.catch((error) => - error.reason === "needsKeychainApproval" ? Effect.fail(error) : Effect.succeed(error), + Effect.catchIf( + (error) => error.reason !== "needsKeychainApproval", + (error) => Effect.succeed(error), ), ) : undefined; diff --git a/apps/desktop/src/preview/BrowserImport/FirefoxCookies.ts b/apps/desktop/src/preview/BrowserImport/FirefoxCookies.ts index 96d27129b78a9840e12b91d218aad18912f45bbe..0db4fc9096ec753a4900f9990740526d5f4381f4 100644 --- a/apps/desktop/src/preview/BrowserImport/FirefoxCookies.ts +++ b/apps/desktop/src/preview/BrowserImport/FirefoxCookies.ts @@ -114,56 +114,55 @@ const expiryToSeconds = (expiry: number, schemaVersion: number): number | undefi return schemaVersion >= FIREFOX_EXPIRY_MILLISECONDS_SCHEMA ? Math.floor(expiry / 1000) : expiry; }; -export const readFirefoxCookies = Effect.fn("FirefoxCookies.readFirefoxCookies")(function* ( - cookieDatabasePath: string, -) { - const snapshotPath = yield* snapshotCookieDatabase(cookieDatabasePath).pipe( - Effect.mapError((cause) => new FirefoxCookieReadError({ cookieDatabasePath, cause })), - ); +export const readFirefoxCookies = Effect.fn("FirefoxCookies.readFirefoxCookies")( + function* (cookieDatabasePath: string) { + const snapshotPath = yield* snapshotCookieDatabase(cookieDatabasePath); - const { rows, schemaVersion } = yield* Effect.gen(function* () { - const sql = yield* SqlClient.SqlClient; - const [versionRow] = yield* decodeUserVersion(yield* sql`pragma user_version`); - const schemaVersion = versionRow?.user_version ?? 0; - const hasRawSameSite = - schemaVersion >= FIREFOX_RAW_SAMESITE_FIRST_SCHEMA && - schemaVersion <= FIREFOX_RAW_SAMESITE_LAST_SCHEMA; - // Only the default container. Firefox isolates cookies per container and - // per private window via `originAttributes` (`^userContextId=2`, - // `^privateBrowsingId=1`); Electron has no equivalent, so importing them - // all would collapse several identities onto one host/name/path and hand - // the profile an arbitrary container's session. - const raw = hasRawSameSite - ? yield* sql` + const { rows, schemaVersion } = yield* Effect.gen(function* () { + const sql = yield* SqlClient.SqlClient; + const [versionRow] = yield* decodeUserVersion(yield* sql`pragma user_version`); + const schemaVersion = versionRow?.user_version ?? 0; + const hasRawSameSite = + schemaVersion >= FIREFOX_RAW_SAMESITE_FIRST_SCHEMA && + schemaVersion <= FIREFOX_RAW_SAMESITE_LAST_SCHEMA; + // Only the default container. Firefox isolates cookies per container and + // per private window via `originAttributes` (`^userContextId=2`, + // `^privateBrowsingId=1`); Electron has no equivalent, so importing them + // all would collapse several identities onto one host/name/path and hand + // the profile an arbitrary container's session. + const raw = hasRawSameSite + ? yield* sql` select host, name, value, path, expiry, isSecure, isHttpOnly, sameSite, rawSameSite from moz_cookies where originAttributes = '' ` - : yield* sql` + : yield* sql` select host, name, value, path, expiry, isSecure, isHttpOnly, sameSite, null as rawSameSite from moz_cookies where originAttributes = '' `; - return { rows: yield* decodeCookieRows(raw), schemaVersion }; - }).pipe( - Effect.provide(NodeSqliteClient.layer({ filename: snapshotPath, readonly: true })), - Effect.mapError((cause) => new FirefoxCookieReadError({ cookieDatabasePath, cause })), - ); + return { rows: yield* decodeCookieRows(raw), schemaVersion }; + }).pipe(Effect.provide(NodeSqliteClient.layer({ filename: snapshotPath, readonly: true }))); - return rows.map((row) => { - const secure = row.isSecure === 1; - const scope = cookieScope(row.host, row.path, secure); - return { - url: scope.url, - name: row.name, - value: row.value, - domain: scope.domain, - path: row.path, - secure, - httpOnly: row.isHttpOnly === 1, - expirationDate: expiryToSeconds(row.expiry, schemaVersion), - sameSite: sameSiteFromColumn(row.sameSite, row.rawSameSite), - } satisfies ImportedCookie; - }); -}); + return rows.map((row) => { + const secure = row.isSecure === 1; + const scope = cookieScope(row.host, row.path, secure); + return { + url: scope.url, + name: row.name, + value: row.value, + domain: scope.domain, + path: row.path, + secure, + httpOnly: row.isHttpOnly === 1, + expirationDate: expiryToSeconds(row.expiry, schemaVersion), + sameSite: sameSiteFromColumn(row.sameSite, row.rawSameSite), + } satisfies ImportedCookie; + }); + }, + (effect, cookieDatabasePath) => + effect.pipe( + Effect.mapError((cause) => new FirefoxCookieReadError({ cookieDatabasePath, cause })), + ), +); diff --git a/apps/desktop/src/preview/BrowserSession.ts b/apps/desktop/src/preview/BrowserSession.ts index 930c13990d51a45b4f6850cc6752bd5947f82cc9..664302d7f83b7274e1c06ad8f5473d64d1c30186 100644 --- a/apps/desktop/src/preview/BrowserSession.ts +++ b/apps/desktop/src/preview/BrowserSession.ts @@ -231,8 +231,9 @@ export const make = Effect.gen(function* BrowserSessionMake() { getSession, clearCookies: Effect.fn("BrowserSession.clearCookies")(function* (partitions?) { const sessions = yield* SynchronizedRef.get(sessionsRef); - yield* Effect.all( - selectSessions(sessions, partitions).map(([partition, browserSession]) => + yield* Effect.forEach( + selectSessions(sessions, partitions), + ([partition, browserSession]) => Effect.tryPromise({ try: () => browserSession.clearStorageData({ @@ -244,14 +245,14 @@ export const make = Effect.gen(function* BrowserSessionMake() { cause, }), }), - ), { concurrency: "unbounded", discard: true }, ); }), clearCache: Effect.fn("BrowserSession.clearCache")(function* (partitions?) { const sessions = yield* SynchronizedRef.get(sessionsRef); - yield* Effect.all( - selectSessions(sessions, partitions).map(([partition, browserSession]) => + yield* Effect.forEach( + selectSessions(sessions, partitions), + ([partition, browserSession]) => Effect.tryPromise({ try: () => browserSession.clearCache(), catch: (cause) => @@ -260,7 +261,6 @@ export const make = Effect.gen(function* BrowserSessionMake() { cause, }), }), - ), { concurrency: "unbounded", discard: true }, ); }), diff --git a/apps/desktop/src/preview/Manager.ts b/apps/desktop/src/preview/Manager.ts index 468c470653154dd354b8951a3479e37a356249df..b97af6f052046a16b238f8459f9c00e7041c278c 100644 --- a/apps/desktop/src/preview/Manager.ts +++ b/apps/desktop/src/preview/Manager.ts @@ -942,14 +942,14 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function delivery: () => Effect.Effect, ) => Effect.suspend(delivery).pipe( - Effect.catchCause((cause) => - Cause.hasInterrupts(cause) - ? Effect.failCause(cause) - : Effect.logWarning("Desktop preview event listener failed.", { - eventKind, - tabId, - cause, - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterrupts(cause), + (cause) => + Effect.logWarning("Desktop preview event listener failed.", { + eventKind, + tabId, + cause, + }), ), ); @@ -1115,15 +1115,13 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function } return resolvedPath; }).pipe( - Effect.flatMap((resolvedPath) => - resolvedPath === null - ? Effect.fail( - new PreviewArtifactPathOutsideDirectoryError({ - artifactPath, - artifactDirectory: resolvedArtifactDirectory, - }), - ) - : Effect.succeed(resolvedPath), + Effect.filterOrFail( + (resolvedPath) => resolvedPath !== null, + () => + new PreviewArtifactPathOutsideDirectoryError({ + artifactPath, + artifactDirectory: resolvedArtifactDirectory, + }), ), ); @@ -1404,14 +1402,13 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function wcDebugger.on("message", onMessage); wcDebugger.attach("1.3"); }); - yield* Effect.all( - ["Runtime.enable", "Accessibility.enable", "Network.enable", "Log.enable"].map( - (method) => - attemptPromise( - { operation: `initializeDebugger.${method}`, webContentsId: wc.id }, - () => wcDebugger.sendCommand(method), - ), - ), + yield* Effect.forEach( + ["Runtime.enable", "Accessibility.enable", "Network.enable", "Log.enable"], + (method) => + attemptPromise( + { operation: `initializeDebugger.${method}`, webContentsId: wc.id }, + () => wcDebugger.sendCommand(method), + ), { concurrency: "unbounded", discard: true }, ); return [ @@ -4056,13 +4053,13 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function receiptKey: JSON.stringify(`__t3NativeKey_${NodeCrypto.randomUUID()}`), })), ({ frames, receiptKey }) => - Effect.all( - frames.map((frame) => + Effect.forEach( + frames, + (frame) => evaluate(frame, `globalThis[${receiptKey}]?.dispose()`).pipe( Effect.timeoutOption(1_000), Effect.ignore, ), - ), { concurrency: "unbounded", discard: true }, ), ); diff --git a/apps/desktop/src/settings/DesktopClientSettings.ts b/apps/desktop/src/settings/DesktopClientSettings.ts index e199f2b2f32e3e37d7a4725bf0d1771b0a678fb6..cd7fea6ca116f4ce8f051dfcbe638ec1701c20f1 100644 --- a/apps/desktop/src/settings/DesktopClientSettings.ts +++ b/apps/desktop/src/settings/DesktopClientSettings.ts @@ -75,7 +75,7 @@ const readClientSettings = ( settingsPath: string, ): Effect.Effect, DesktopClientSettingsReadError> => fileSystem.readFileString(settingsPath).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ PlatformError: (cause) => cause.reason._tag === "NotFound" @@ -98,7 +98,7 @@ const readClientSettings = ( onNone: () => Effect.succeed(Option.none()), onSome: (raw) => decodeClientSettingsJson(raw).pipe( - Effect.map((settings) => Option.some(settings)), + Effect.asSome, Effect.catchTags({ SchemaError: (cause) => Effect.logWarning("Could not decode desktop client settings.", cause).pipe( diff --git a/apps/desktop/src/settings/DesktopSavedEnvironments.test.ts b/apps/desktop/src/settings/DesktopSavedEnvironments.test.ts index dad53815f3a65c7b7d02327fd8c2b4e17525ca2d..b1e467869e8e8a542449767f04ade4ad72785966 100644 --- a/apps/desktop/src/settings/DesktopSavedEnvironments.test.ts +++ b/apps/desktop/src/settings/DesktopSavedEnvironments.test.ts @@ -94,7 +94,7 @@ function makeSafeStorageLayer(input: { } return Effect.succeed(decoded.slice("enc:".length)); }, - selectedStorageBackend: Effect.succeed(Option.none()), + selectedStorageBackend: Effect.succeedNone, } satisfies ElectronSafeStorage.ElectronSafeStorage["Service"]); } diff --git a/apps/desktop/src/snapShot/DesktopSnapShot.ts b/apps/desktop/src/snapShot/DesktopSnapShot.ts index d7634f980e25a180442bdcee2bf92a8eb1c0b41c..7ee4557569300532e40b93c8fbc763e42677b0a7 100644 --- a/apps/desktop/src/snapShot/DesktopSnapShot.ts +++ b/apps/desktop/src/snapShot/DesktopSnapShot.ts @@ -820,7 +820,7 @@ export const make = Effect.gen(function* () { }; const emit = (event: DesktopSnapShotEvent) => - desktopWindow.dispatchSnapShotEvent(event).pipe(Effect.catchCause(() => Effect.void)); + desktopWindow.dispatchSnapShotEvent(event).pipe(Effect.ignoreCause); const setFailure = (message: string, captureId?: string) => Ref.update(stateRef, (state) => ({ ...state, message })).pipe( Effect.andThen( @@ -846,10 +846,9 @@ export const make = Effect.gen(function* () { const discardCapture = Effect.fn("desktop.snapShot.discardCapture")(function* (id: string) { closeLinuxFeedback(id); transition.dismiss(id); - yield* Effect.all( - [`${id}.png`, `${id}.tmp.png`, `${id}.json`, `${id}.json.tmp`].map((name) => - fileSystem.remove(path.join(captureDirectory, name), { force: true }), - ), + yield* Effect.forEach( + [`${id}.png`, `${id}.tmp.png`, `${id}.json`, `${id}.json.tmp`], + (name) => fileSystem.remove(path.join(captureDirectory, name), { force: true }), { concurrency: "unbounded", discard: true }, ).pipe(Effect.ignore); }); @@ -904,7 +903,7 @@ export const make = Effect.gen(function* () { if (snapshot.animationStarted) { yield* emit({ type: "started", id: id as DesktopSnapShotId }); } else { - yield* desktopWindow.activate.pipe(Effect.catchCause(() => Effect.void)); + yield* desktopWindow.activate.pipe(Effect.ignoreCause); } return { id, capturedAt, ...snapshot }; }).pipe(Effect.mapError((cause) => captureFailure(cause, id))); @@ -1511,7 +1510,7 @@ export const make = Effect.gen(function* () { null, ), ), - Effect.catch(() => Effect.void), + Effect.ignore, ), ), configure, diff --git a/apps/desktop/src/ssh/DesktopSshPasswordPrompts.test.ts b/apps/desktop/src/ssh/DesktopSshPasswordPrompts.test.ts index aa3b7c0b4d992f7b77d6247b62f94feb8a983b3f..27ab73d2e5e26fa0bcd0164a101d4c4cf0d1f605 100644 --- a/apps/desktop/src/ssh/DesktopSshPasswordPrompts.test.ts +++ b/apps/desktop/src/ssh/DesktopSshPasswordPrompts.test.ts @@ -4,7 +4,6 @@ import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; -import * as Option from "effect/Option"; import * as TestClock from "effect/testing/TestClock"; import type * as Electron from "electron"; @@ -92,9 +91,9 @@ function makeElectronWindowLayer(window: ReturnType["wind ElectronWindow.ElectronWindow, ElectronWindow.ElectronWindow.of({ create: () => Effect.die("unexpected BrowserWindow creation"), - main: Effect.succeed(Option.some(window as Electron.BrowserWindow)), - currentMainOrFirst: Effect.succeed(Option.some(window as Electron.BrowserWindow)), - focusedMainOrFirst: Effect.succeed(Option.some(window as Electron.BrowserWindow)), + main: Effect.succeedSome(window as Electron.BrowserWindow), + currentMainOrFirst: Effect.succeedSome(window as Electron.BrowserWindow), + focusedMainOrFirst: Effect.succeedSome(window as Electron.BrowserWindow), setMain: () => Effect.void, clearMain: () => Effect.void, prepareReveal: () => Effect.succeed(false), diff --git a/apps/desktop/src/telemetry/DesktopTelemetryPublisher.ts b/apps/desktop/src/telemetry/DesktopTelemetryPublisher.ts index 18ea0a8380a327e376d0ac1d2fbe3eac93c448d7..a191a0d586ae0fd66b357d9c2026183969521b4e 100644 --- a/apps/desktop/src/telemetry/DesktopTelemetryPublisher.ts +++ b/apps/desktop/src/telemetry/DesktopTelemetryPublisher.ts @@ -289,12 +289,12 @@ export const make = Effect.fn("desktop.telemetryPublisher.make")(function* () { yield* Ref.set(latest, Option.some(snapshot)); yield* PubSub.publish(changes, snapshot); }).pipe( - Effect.catchCause((cause) => - Cause.hasInterrupts(cause) - ? Effect.failCause(cause) - : Effect.logWarning("Failed to sample Electron telemetry", { - cause: String(cause), - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterrupts(cause), + (cause) => + Effect.logWarning("Failed to sample Electron telemetry", { + cause: String(cause), + }), ), ); @@ -306,6 +306,7 @@ export const make = Effect.fn("desktop.telemetryPublisher.make")(function* () { Ref.get(diagnosticsDemandSources).pipe(Effect.map((sources) => sources.size > 0)), Ref.get(hostPowerIntervals), ]); + // @effect-diagnostics-next-line raceFirstWithSleepToTimeout:off - races a trigger queue against the interval; both arms are real outcomes, not a timeout const allowSuspendRecovery = yield* Effect.raceFirst( Queue.take(sampleTriggers).pipe(Effect.as(false)), Effect.sleep(sampleInterval(currentPower, demand, intervals)).pipe(Effect.as(true)), diff --git a/apps/desktop/src/updates/DesktopUpdates.ts b/apps/desktop/src/updates/DesktopUpdates.ts index 5336c8ac1d22dde5eaec0e165b10eec2765217ef..c42ef564918f1d83169903b4ac8ae1c30cf7f878 100644 --- a/apps/desktop/src/updates/DesktopUpdates.ts +++ b/apps/desktop/src/updates/DesktopUpdates.ts @@ -668,24 +668,25 @@ export const make = Effect.gen(function* () { }), ).pipe(Effect.withSpan("desktop.updates.installDownloadedUpdate")); - const installWithExpectedVersion = (expectedVersion?: string) => - Effect.gen(function* () { - if (yield* Ref.get(desktopState.quitting)) { - return { - accepted: false, - completed: false, - failed: false, - state: yield* Ref.get(updateStateRef), - }; - } - const result = yield* installDownloadedUpdate(expectedVersion); + const installWithExpectedVersion = Effect.fn("desktop.updates.install")(function* ( + expectedVersion?: string, + ) { + if (yield* Ref.get(desktopState.quitting)) { return { - accepted: result.accepted, - completed: result.completed, - failed: result.failed, + accepted: false, + completed: false, + failed: false, state: yield* Ref.get(updateStateRef), }; - }).pipe(Effect.withSpan("desktop.updates.install")); + } + const result = yield* installDownloadedUpdate(expectedVersion); + return { + accepted: result.accepted, + completed: result.completed, + failed: result.failed, + state: yield* Ref.get(updateStateRef), + }; + }); const startUpdatePollers: Effect.Effect = Effect.gen(function* () { yield* Effect.sleep(AUTO_UPDATE_STARTUP_DELAY).pipe( diff --git a/apps/desktop/src/updates/updatesTestHarness.ts b/apps/desktop/src/updates/updatesTestHarness.ts index cc50349e1fe8761d07e8a321d6fb3f68d51e048a..a0b9a36399d21affa4ddf71ea62ff49abc1b7bbe 100644 --- a/apps/desktop/src/updates/updatesTestHarness.ts +++ b/apps/desktop/src/updates/updatesTestHarness.ts @@ -107,9 +107,9 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { const windowLayer = Layer.succeed(ElectronWindow.ElectronWindow, { create: () => Effect.die("unexpected BrowserWindow creation"), - main: Effect.succeed(Option.none()), - currentMainOrFirst: Effect.succeed(Option.none()), - focusedMainOrFirst: Effect.succeed(Option.none()), + main: Effect.succeedNone, + currentMainOrFirst: Effect.succeedNone, + focusedMainOrFirst: Effect.succeedNone, setMain: () => Effect.void, clearMain: () => Effect.void, prepareReveal: () => Effect.succeed(false), @@ -131,7 +131,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { installSteps.push("startBackend"); }).pipe(Effect.andThen(options.startBackend ?? Effect.void)), stop: () => options.stopBackend ?? Effect.void, - currentConfig: Effect.succeed(Option.none()), + currentConfig: Effect.succeedNone, snapshot: Effect.succeed({ desiredRunning: false, ready: false, @@ -245,7 +245,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { installSteps, updateRestartMarkers, downloadCount: () => downloadCount, - feedUrls: () => feedUrls, + feedUrls: (): ElectronUpdater.ElectronUpdaterFeedUrl[] => feedUrls, fullChangelog: () => fullChangelog, listenerCount: () => Array.from(listeners.values()).reduce( diff --git a/apps/desktop/src/window/DesktopApplicationMenu.test.ts b/apps/desktop/src/window/DesktopApplicationMenu.test.ts index 5f5cbaa1d7fc454cfa06b80bb477c1581ec356b5..d24ffdcba48f0ccd9f2c27a4394c54bdf67cf428 100644 --- a/apps/desktop/src/window/DesktopApplicationMenu.test.ts +++ b/apps/desktop/src/window/DesktopApplicationMenu.test.ts @@ -3,7 +3,6 @@ import { assert, describe, it } from "@effect/vitest"; import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; -import * as Option from "effect/Option"; import type * as Electron from "electron"; @@ -51,7 +50,7 @@ const electronAppLayer = Layer.succeed(ElectronApp.ElectronApp, { } satisfies ElectronApp.ElectronApp["Service"]); const electronDialogLayer = Layer.succeed(ElectronDialog.ElectronDialog, { - pickFolder: () => Effect.succeed(Option.none()), + pickFolder: () => Effect.succeedNone, pickFiles: () => Effect.succeed([]), showMessageBox: () => Effect.succeed({ response: 0, checkboxChecked: false }), showErrorBox: () => Effect.void, @@ -63,7 +62,7 @@ const desktopUpdatesLayer = Layer.succeed(DesktopUpdates.DesktopUpdates, { isInstallActive: Effect.succeed(false), subscribe: Effect.die("unexpected subscribe"), emitState: Effect.void, - disabledReason: Effect.succeed(Option.none()), + disabledReason: Effect.succeedNone, configure: Effect.void, setChannel: () => Effect.die("unexpected setChannel"), check: () => Effect.die("unexpected check"), @@ -98,7 +97,7 @@ const makeElectronMenuLayer = ( setApplicationMenu: (template) => Deferred.succeed(applicationMenuTemplate, template).pipe(Effect.asVoid), popupTemplate: () => Effect.void, - showContextMenu: () => Effect.succeed(Option.none()), + showContextMenu: () => Effect.succeedNone, } satisfies ElectronMenu.ElectronMenu["Service"]); const configureMenu = ( diff --git a/apps/desktop/src/window/DesktopWindow.test.ts b/apps/desktop/src/window/DesktopWindow.test.ts index ab3405b34aa8929645798903e5df4cf0f04a650b..5df400b90c44d5b5defe43f08c3ffd678ff9a208 100644 --- a/apps/desktop/src/window/DesktopWindow.test.ts +++ b/apps/desktop/src/window/DesktopWindow.test.ts @@ -151,7 +151,7 @@ function makeFakeBrowserWindow() { } const desktopClientSettingsLayer = Layer.mock(DesktopClientSettings.DesktopClientSettings)({ - get: Effect.succeed(Option.none()), + get: Effect.succeedNone, }); const electronAppLayer = Layer.mock(ElectronApp.ElectronApp)({ @@ -185,7 +185,7 @@ const desktopServerExposureLayer = Layer.succeed(DesktopServerExposure.DesktopSe const electronMenuLayer = Layer.succeed(ElectronMenu.ElectronMenu, { setApplicationMenu: () => Effect.void, popupTemplate: () => Effect.void, - showContextMenu: () => Effect.succeed(Option.none()), + showContextMenu: () => Effect.succeedNone, } satisfies ElectronMenu.ElectronMenu["Service"]); const electronThemeLayer = Layer.succeed(ElectronTheme.ElectronTheme, { @@ -294,7 +294,7 @@ function makeTestLayer(input: { electronAppLayer, Layer.succeed(ElectronMenu.ElectronMenu, { setApplicationMenu: () => Effect.void, - showContextMenu: () => Effect.succeed(Option.none()), + showContextMenu: () => Effect.succeedNone, popupTemplate: input.onPopupTemplate ?? (() => Effect.void), }), Layer.succeed(ElectronShell.ElectronShell, { diff --git a/apps/desktop/src/wsl/DesktopWslBackend.ts b/apps/desktop/src/wsl/DesktopWslBackend.ts index 3f20e58aa6809caf7750c72ca80b526b440af003..72bad30c06cc9a3e216a976ae4bea7a7439bdf8d 100644 --- a/apps/desktop/src/wsl/DesktopWslBackend.ts +++ b/apps/desktop/src/wsl/DesktopWslBackend.ts @@ -138,7 +138,7 @@ export const layer = Layer.effect( const primaryConfig = yield* serverExposure.backendConfig; const port = yield* scanForWslPort(primaryConfig.port + 1).pipe( Effect.provideService(NetService.NetService, net), - Effect.map((value) => Option.some(value)), + Effect.asSome, Effect.catch((error) => logWslBackendWarning("could not allocate port for WSL backend", { error: error.message, @@ -171,7 +171,7 @@ export const layer = Layer.effect( onReady: () => Ref.set(preflightErrorRef, Option.none()), }) .pipe( - Effect.map((registered) => Option.some(registered)), + Effect.asSome, Effect.catch((error) => logWslBackendWarning("WSL backend already registered, skipping start", { id: targetId, diff --git a/apps/desktop/src/wsl/DesktopWslEnvironment.ts b/apps/desktop/src/wsl/DesktopWslEnvironment.ts index 95b217c622b1c3e9373f649d73be3329c81db2b8..2f922a48f4d841bb6dea4eb9c487ffd4dadfa240 100644 --- a/apps/desktop/src/wsl/DesktopWslEnvironment.ts +++ b/apps/desktop/src/wsl/DesktopWslEnvironment.ts @@ -1040,11 +1040,7 @@ const preWarmImpl = ( const handle = yield* spawner.spawn(command); yield* handle.exitCode; }), - ).pipe( - Effect.timeoutOption(PRE_WARM_TIMEOUT), - Effect.asVoid, - Effect.catch(() => Effect.void), - ); + ).pipe(Effect.timeoutOption(PRE_WARM_TIMEOUT), Effect.ignore); const windowsToWslPathImpl = ( distro: string | null, @@ -1272,15 +1268,14 @@ export const layer = Layer.effect( // distro. Negative results aren't cached so a transient wsl.exe failure // doesn't permanently disable tilde expansion. const userHomeCache = new Map(); - const getUserHome = (distro: string | null) => - Effect.gen(function* () { - const key = distro ?? "__default__"; - const cached = userHomeCache.get(key); - if (cached !== undefined) return Option.some(cached); - const resolved = yield* provideSpawner(getUserHomeImpl(distro)); - if (Option.isSome(resolved)) userHomeCache.set(key, resolved.value); - return resolved; - }).pipe(Effect.withSpan("desktop.wsl.getUserHome")); + const getUserHome = Effect.fn("desktop.wsl.getUserHome")(function* (distro: string | null) { + const key = distro ?? "__default__"; + const cached = userHomeCache.get(key); + if (cached !== undefined) return Option.some(cached); + const resolved = yield* provideSpawner(getUserHomeImpl(distro)); + if (Option.isSome(resolved)) userHomeCache.set(key, resolved.value); + return resolved; + }); const getDistroIp = (distro: string | null) => provideSpawner(getDistroIpImpl(distro)).pipe(Effect.withSpan("desktop.wsl.getDistroIp")); diff --git a/apps/server/integration/orphanedProviderSessionStartup.integration.test.ts b/apps/server/integration/orphanedProviderSessionStartup.integration.test.ts index 77173af1b377b8aa69cfdf0d10ed5ef93e8cf780..b483a3f99f8ebc9498f68d021fd79495ba0c22fb 100644 --- a/apps/server/integration/orphanedProviderSessionStartup.integration.test.ts +++ b/apps/server/integration/orphanedProviderSessionStartup.integration.test.ts @@ -100,7 +100,7 @@ const startupDependencies = Layer.mergeAll( Layer.succeed(ServiceLauncherClient.ServiceLauncherClient, { managed: false, requestUpdate: () => Effect.die("unused"), - prepareTrial: Effect.sync(() => undefined), + prepareTrial: Effect.undefined, }), Layer.succeed( HttpServer.HttpServer, diff --git a/apps/server/src/assets/AssetAccess.ts b/apps/server/src/assets/AssetAccess.ts index e1f6633fbecae1e29bb36772479b014d75cb2f4f..8dbd1c7559667c018ebc846a6fa6d2614561208e 100644 --- a/apps/server/src/assets/AssetAccess.ts +++ b/apps/server/src/assets/AssetAccess.ts @@ -190,7 +190,7 @@ const optionOnNotFound = ( effect: Effect.Effect, ): Effect.Effect, PlatformError.PlatformError, R> => effect.pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ PlatformError: (error) => error.reason._tag === "NotFound" ? Effect.succeed(Option.none()) : Effect.fail(error), @@ -213,9 +213,9 @@ const resolveCanonicalWorkspaceFile = Effect.fn("AssetAccess.resolveCanonicalWor const fileSystem = yield* FileSystem.FileSystem; const workspacePaths = yield* WorkspacePaths.WorkspacePaths; const resolved = yield* workspacePaths.resolveRelativePathWithinRoot(input).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ - WorkspacePathOutsideRootError: () => Effect.succeed(Option.none()), + WorkspacePathOutsideRootError: () => Effect.succeedNone, }), ); if (Option.isNone(resolved)) return null; diff --git a/apps/server/src/assets/NativeAppIconResolver.ts b/apps/server/src/assets/NativeAppIconResolver.ts index 89a6d063601231367259ae888ddd5df888369807..34299f95142fea0414baf66acd68d2399724de68 100644 --- a/apps/server/src/assets/NativeAppIconResolver.ts +++ b/apps/server/src/assets/NativeAppIconResolver.ts @@ -43,10 +43,10 @@ function appFromCacheKey(key: string): ToolActivityNativeAppReference { const existingFile = Effect.fn("NativeAppIconResolver.existingFile")(function* (filePath: string) { const fileSystem = yield* FileSystem.FileSystem; const info = yield* fileSystem.stat(filePath).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ PlatformError: (error) => - error.reason._tag === "NotFound" ? Effect.succeed(Option.none()) : Effect.fail(error), + error.reason._tag === "NotFound" ? Effect.succeedNone : Effect.fail(error), }), ); return Option.isSome(info) && info.value.type === "File" ? filePath : null; diff --git a/apps/server/src/auth/PairingGrantStore.test.ts b/apps/server/src/auth/PairingGrantStore.test.ts index 9a093be41ca4077b66f3ecb4f0ece6fe3677a486..beb07627c66a473d2938e53d1119079e2c3188ae 100644 --- a/apps/server/src/auth/PairingGrantStore.test.ts +++ b/apps/server/src/auth/PairingGrantStore.test.ts @@ -3,7 +3,6 @@ import { expect, it } from "@effect/vitest"; import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; -import * as Option from "effect/Option"; import * as Stream from "effect/Stream"; import * as Queue from "effect/Queue"; import * as TestClock from "effect/testing/TestClock"; @@ -47,10 +46,10 @@ const makePairingGrantStoreTestLayer = ( AuthPairingLinks.AuthPairingLinkRepository, AuthPairingLinks.AuthPairingLinkRepository.of({ create: () => Effect.void, - consumeAvailable: () => Effect.succeed(Option.none()), + consumeAvailable: () => Effect.succeedNone, listActive: () => Effect.succeed([]), revoke: () => Effect.succeed(false), - getByCredential: () => Effect.succeed(Option.none()), + getByCredential: () => Effect.succeedNone, ...overrides, }), ), diff --git a/apps/server/src/auth/ServerSecretStore.ts b/apps/server/src/auth/ServerSecretStore.ts index e936a1f85c996a8e2bbc95eb6a38e5edc7065dc2..c386f7e51d3c91f058cd52a287a779fc1b0994c1 100644 --- a/apps/server/src/auth/ServerSecretStore.ts +++ b/apps/server/src/auth/ServerSecretStore.ts @@ -174,7 +174,7 @@ export const make = Effect.gen(function* () { Effect.map((bytes) => Option.some(Uint8Array.from(bytes))), Effect.catch((cause) => cause.reason._tag === "NotFound" - ? Effect.succeed(Option.none()) + ? Effect.succeedNone : Effect.fail( new SecretStoreReadError({ resource: `secret ${name}`, diff --git a/apps/server/src/auth/dpop.ts b/apps/server/src/auth/dpop.ts index 43f90e440915ddd397baf9c0d8c8f8ac1423f35e..a841f4fd444c7630180f3a9e40d45587c4462e6f 100644 --- a/apps/server/src/auth/dpop.ts +++ b/apps/server/src/auth/dpop.ts @@ -115,7 +115,7 @@ export const verifyRequestDpopProof = (input: { "environment.dpop.failure_code": mapped.dpopFailureReason, }); } - return yield* Effect.fail(mapped); + return yield* mapped; }), ), ); diff --git a/apps/server/src/auth/http.test.ts b/apps/server/src/auth/http.test.ts index 3d53ee088376cd2f8cb3abf7da27c69f7e66dc54..793f86349650d9f51b0777d4fb6dec775f2f443e 100644 --- a/apps/server/src/auth/http.test.ts +++ b/apps/server/src/auth/http.test.ts @@ -5,7 +5,6 @@ import * as Context from "effect/Context"; import * as Crypto from "effect/Crypto"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; -import * as Option from "effect/Option"; import * as Redacted from "effect/Redacted"; import * as Schema from "effect/Schema"; import * as Etag from "effect/unstable/http/Etag"; @@ -69,7 +68,7 @@ it.effect("sets the selected browser session cookies through the HTTP route", () Effect.gen(function* () { const crypto = yield* Crypto.Crypto; const unusedSecretStore = ServerSecretStore.ServerSecretStore.of({ - get: () => Effect.succeed(Option.none()), + get: () => Effect.succeedNone, set: () => Effect.void, create: () => Effect.void, getOrCreateRandom: () => Effect.die("Not used by these routes."), diff --git a/apps/server/src/checkpointing/CheckpointDiffQuery.test.ts b/apps/server/src/checkpointing/CheckpointDiffQuery.test.ts index 7bfd313a2103ebcec4c7ef7db826755b9f2769c2..7fa6065109c37ade25e4259444a00883fe9246fe 100644 --- a/apps/server/src/checkpointing/CheckpointDiffQuery.test.ts +++ b/apps/server/src/checkpointing/CheckpointDiffQuery.test.ts @@ -89,10 +89,10 @@ describe("CheckpointDiffQuery.layer", () => { getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), getEventReplayStats: () => Effect.die("unused"), - getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), + getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, getProjectShells: () => Effect.die("unused"), - getProjectShellById: () => Effect.succeed(Option.none()), - getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), + getProjectShellById: () => Effect.succeedNone, + getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, getImportedAgentSessionSources: () => Effect.die("unused"), getThreadCheckpointContext: () => Effect.sync(() => { @@ -113,9 +113,9 @@ describe("CheckpointDiffQuery.layer", () => { }), getThreadRuntimeContext: () => Effect.die("unused"), getTurnStartMessage: () => Effect.die("unused"), - getThreadShellById: () => Effect.succeed(Option.none()), - getThreadDetailById: () => Effect.succeed(Option.none()), - getThreadDetailSnapshot: () => Effect.succeed(Option.none()), + getThreadShellById: () => Effect.succeedNone, + getThreadDetailById: () => Effect.succeedNone, + getThreadDetailSnapshot: () => Effect.succeedNone, searchThreads: () => Effect.succeed({ matches: [] }), }), ), @@ -206,18 +206,18 @@ describe("CheckpointDiffQuery.layer", () => { getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), getEventReplayStats: () => Effect.die("unused"), - getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), + getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, getProjectShells: () => Effect.die("unused"), - getProjectShellById: () => Effect.succeed(Option.none()), - getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), + getProjectShellById: () => Effect.succeedNone, + getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, getImportedAgentSessionSources: () => Effect.die("unused"), - getThreadCheckpointContext: () => Effect.succeed(Option.some(threadCheckpointContext)), + getThreadCheckpointContext: () => Effect.succeedSome(threadCheckpointContext), getFullThreadDiffContext: () => Effect.die("unused"), getThreadRuntimeContext: () => Effect.die("unused"), getTurnStartMessage: () => Effect.die("unused"), - getThreadShellById: () => Effect.succeed(Option.none()), - getThreadDetailById: () => Effect.succeed(Option.none()), - getThreadDetailSnapshot: () => Effect.succeed(Option.none()), + getThreadShellById: () => Effect.succeedNone, + getThreadDetailById: () => Effect.succeedNone, + getThreadDetailSnapshot: () => Effect.succeedNone, searchThreads: () => Effect.succeed({ matches: [] }), }), ), @@ -298,18 +298,18 @@ describe("CheckpointDiffQuery.layer", () => { getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), getEventReplayStats: () => Effect.die("unused"), - getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), + getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, getProjectShells: () => Effect.die("unused"), - getProjectShellById: () => Effect.succeed(Option.none()), - getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), + getProjectShellById: () => Effect.succeedNone, + getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, getImportedAgentSessionSources: () => Effect.die("unused"), - getThreadCheckpointContext: () => Effect.succeed(Option.some(threadCheckpointContext)), + getThreadCheckpointContext: () => Effect.succeedSome(threadCheckpointContext), getFullThreadDiffContext: () => Effect.die("unused"), getThreadRuntimeContext: () => Effect.die("unused"), getTurnStartMessage: () => Effect.die("unused"), - getThreadShellById: () => Effect.succeed(Option.none()), - getThreadDetailById: () => Effect.succeed(Option.none()), - getThreadDetailSnapshot: () => Effect.succeed(Option.none()), + getThreadShellById: () => Effect.succeedNone, + getThreadDetailById: () => Effect.succeedNone, + getThreadDetailSnapshot: () => Effect.succeedNone, searchThreads: () => Effect.succeed({ matches: [] }), }), ), @@ -375,18 +375,18 @@ describe("CheckpointDiffQuery.layer", () => { getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), getEventReplayStats: () => Effect.die("unused"), - getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), + getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, getProjectShells: () => Effect.die("unused"), - getProjectShellById: () => Effect.succeed(Option.none()), - getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), + getProjectShellById: () => Effect.succeedNone, + getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, getImportedAgentSessionSources: () => Effect.die("unused"), - getThreadCheckpointContext: () => Effect.succeed(Option.some(threadCheckpointContext)), + getThreadCheckpointContext: () => Effect.succeedSome(threadCheckpointContext), getFullThreadDiffContext: () => Effect.die("unused"), getThreadRuntimeContext: () => Effect.die("unused"), getTurnStartMessage: () => Effect.die("unused"), - getThreadShellById: () => Effect.succeed(Option.none()), - getThreadDetailById: () => Effect.succeed(Option.none()), - getThreadDetailSnapshot: () => Effect.succeed(Option.none()), + getThreadShellById: () => Effect.succeedNone, + getThreadDetailById: () => Effect.succeedNone, + getThreadDetailSnapshot: () => Effect.succeedNone, searchThreads: () => Effect.succeed({ matches: [] }), }), ), @@ -437,18 +437,18 @@ describe("CheckpointDiffQuery.layer", () => { getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), getEventReplayStats: () => Effect.die("unused"), - getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), + getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, getProjectShells: () => Effect.die("unused"), - getProjectShellById: () => Effect.succeed(Option.none()), - getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), + getProjectShellById: () => Effect.succeedNone, + getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, getImportedAgentSessionSources: () => Effect.die("unused"), - getThreadCheckpointContext: () => Effect.succeed(Option.none()), - getFullThreadDiffContext: () => Effect.succeed(Option.none()), + getThreadCheckpointContext: () => Effect.succeedNone, + getFullThreadDiffContext: () => Effect.succeedNone, getThreadRuntimeContext: () => Effect.die("unused"), getTurnStartMessage: () => Effect.die("unused"), - getThreadShellById: () => Effect.succeed(Option.none()), - getThreadDetailById: () => Effect.succeed(Option.none()), - getThreadDetailSnapshot: () => Effect.succeed(Option.none()), + getThreadShellById: () => Effect.succeedNone, + getThreadDetailById: () => Effect.succeedNone, + getThreadDetailSnapshot: () => Effect.succeedNone, searchThreads: () => Effect.succeed({ matches: [] }), }), ), diff --git a/apps/server/src/cli/theme.ts b/apps/server/src/cli/theme.ts index 26a0c337fb27ba248ef3478bede69c0f1d4d95f8..ab662596267a713db1f0e5b7a8c27d2f3769e58d 100644 --- a/apps/server/src/cli/theme.ts +++ b/apps/server/src/cli/theme.ts @@ -260,12 +260,10 @@ const writeDefaultTheme = Effect.fn(function* (input: { // Falling through here would overwrite whatever landed in between, which // is exactly the loss this loop exists to prevent. if (attempt >= CONCURRENT_WRITE_ATTEMPTS) { - return yield* Effect.fail( - new ThemeSettingsBusyError({ - settingsPath: input.settingsPath, - attempts: CONCURRENT_WRITE_ATTEMPTS, - }), - ); + return yield* new ThemeSettingsBusyError({ + settingsPath: input.settingsPath, + attempts: CONCURRENT_WRITE_ATTEMPTS, + }); } continue; } @@ -299,12 +297,13 @@ const publishThemeFile = Effect.fn(function* (input: { Effect.mapError((cause) => new ThemeFileUnreadableError({ filePath: input.filePath, cause })), ); if (info.type !== "File") { - return yield* Effect.fail(new ThemeFileUnreadableError({ filePath: input.filePath })); + return yield* new ThemeFileUnreadableError({ filePath: input.filePath }); } if (Number(info.size) > MAX_THEME_FILE_BYTES) { - return yield* Effect.fail( - new ThemeFileTooLargeError({ filePath: input.filePath, limit: MAX_THEME_FILE_BYTES }), - ); + return yield* new ThemeFileTooLargeError({ + filePath: input.filePath, + limit: MAX_THEME_FILE_BYTES, + }); } // An explicit source path is the user's own input, and a symlink there is a @@ -320,17 +319,15 @@ const publishThemeFile = Effect.fn(function* (input: { ); const raw = readThemeFileGuarded(resolvedSource, MAX_THEME_FILE_BYTES); if (raw === null) { - return yield* Effect.fail(new ThemeFileUnreadableError({ filePath: input.filePath })); + return yield* new ThemeFileUnreadableError({ filePath: input.filePath }); } const decoded = decodeThemeFileJsonExit(raw); if (decoded._tag === "Failure") { - return yield* Effect.fail( - new ThemeFileInvalidError({ filePath: input.filePath, cause: decoded.cause }), - ); + return yield* new ThemeFileInvalidError({ filePath: input.filePath, cause: decoded.cause }); } if (!environmentThemeFileHasColors(decoded.value)) { - return yield* Effect.fail(new ThemeFileColorlessError({ filePath: input.filePath })); + return yield* new ThemeFileColorlessError({ filePath: input.filePath }); } const fileBasename = path.basename(input.filePath, ".json"); @@ -338,7 +335,7 @@ const publishThemeFile = Effect.fn(function* (input: { // The same rules the watcher applies when it reads the directory back, so a // publish cannot report success for a file that will then be skipped. if (!isEnvironmentThemeId(themeId) || UNPUBLISHABLE_THEME_IDS.has(themeId)) { - return yield* Effect.fail(new ThemeFileIdInvalidError({ themeId, filePath: input.filePath })); + return yield* new ThemeFileIdInvalidError({ themeId, filePath: input.filePath }); } const destinationPath = path.join(input.themesDir, `${themeId}.json`); @@ -479,7 +476,7 @@ const themeSetCommand = Command.make("set", { const fs = yield* FileSystem.FileSystem; const target = yield* expandHomePath(flags.theme.trim()); if (target.length === 0) { - return yield* Effect.fail(new ThemeTargetMissingError()); + return yield* new ThemeTargetMissingError(); } const paths = yield* resolveThemePaths(flags.baseDir); @@ -514,15 +511,15 @@ const themeSetCommand = Command.make("set", { revertPublish = published.revert; cleanupPublish = published.cleanup; } else if (looksLikePath) { - return yield* Effect.fail(new ThemeFileUnreadableError({ filePath: target })); + return yield* new ThemeFileUnreadableError({ filePath: target }); } else if (isEnvironmentThemeId(target)) { const known = yield* resolvableThemeIds(paths.themesDir); if (!known.includes(target)) { - return yield* Effect.fail(new ThemeIdUnknownError({ themeId: target, known })); + return yield* new ThemeIdUnknownError({ themeId: target, known }); } themeId = target; } else { - return yield* Effect.fail(new ThemeIdInvalidError({ themeId: target })); + return yield* new ThemeIdInvalidError({ themeId: target }); } // set means set: if the default cannot be written, the publish that diff --git a/apps/server/src/cloud/CliTokenManager.ts b/apps/server/src/cloud/CliTokenManager.ts index 4172578d45f335d1a54a817c405396b29df83abb..2a666651a9a5b9533163d50c548ef8a2f960df35 100644 --- a/apps/server/src/cloud/CliTokenManager.ts +++ b/apps/server/src/cloud/CliTokenManager.ts @@ -341,7 +341,7 @@ const pollDeviceToken = Effect.fn("cloud.cli_token.poll_device_token")(function* const response = yield* HttpClientRequest.post(metadata.tokenEndpoint).pipe( HttpClientRequest.bodyUrlParams(params), httpClient.execute, - Effect.map(Option.some), + Effect.asSome, Effect.catchIf(isTransportError, () => Effect.succeedNone), ); // Transport failures and upstream 5xx are transient while the device code diff --git a/apps/server/src/cloud/ManagedEndpointRuntime.test.ts b/apps/server/src/cloud/ManagedEndpointRuntime.test.ts index a38989a4733d336caa192d5149a9ce25707df7e7..71677a43d6b8411ae13aac832c004b055d3c65a2 100644 --- a/apps/server/src/cloud/ManagedEndpointRuntime.test.ts +++ b/apps/server/src/cloud/ManagedEndpointRuntime.test.ts @@ -40,7 +40,7 @@ const runtimeDependencies = ( Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner), relayClientLayer, Layer.mock(ServerSecretStore.ServerSecretStore)({ - get: () => Effect.succeed(Option.none()), + get: () => Effect.succeedNone, }), ); diff --git a/apps/server/src/cloud/http.test.ts b/apps/server/src/cloud/http.test.ts index ab4cdcc7d25d23adefe3d27cde2f25aa729718b4..64a512a33abaec933fd48177c7af2dd3249e38b8 100644 --- a/apps/server/src/cloud/http.test.ts +++ b/apps/server/src/cloud/http.test.ts @@ -252,7 +252,7 @@ describe("reconcileDesiredCloudLink", () => { CliTokenManager.CloudCliTokenManager, CliTokenManager.CloudCliTokenManager.of({ get: unusedSecretStoreOperation(), - getExisting: Effect.succeed(Option.none()), + getExisting: Effect.succeedNone, hasCredential: unusedSecretStoreOperation(), store: () => unusedSecretStoreOperation(), clear: unusedSecretStoreOperation(), @@ -411,7 +411,7 @@ describe("releaseManagedTunnelOnShutdown", () => { CliTokenManager.CloudCliTokenManager, CliTokenManager.CloudCliTokenManager.of({ get: unusedSecretStoreOperation(), - getExisting: Effect.succeed(Option.some(cliToken)), + getExisting: Effect.succeedSome(cliToken), hasCredential: unusedSecretStoreOperation(), store: () => unusedSecretStoreOperation(), clear: unusedSecretStoreOperation(), diff --git a/apps/server/src/cloud/http.ts b/apps/server/src/cloud/http.ts index 4943923764aa5bbe243ca6cfb112e0a06198cf21..b4c366a500c1865658c7a328da7d0836c3715f91 100644 --- a/apps/server/src/cloud/http.ts +++ b/apps/server/src/cloud/http.ts @@ -152,8 +152,9 @@ export function consumeCloudReplayGuards(input: { readonly names: ReadonlyArray; readonly value: Uint8Array; }) { - return Effect.all( - input.names.map((name) => + return Effect.forEach( + input.names, + (name) => input.secrets.create(name, input.value).pipe( Effect.as(true), Effect.catchIf(ServerSecretStore.isSecretStoreError, (error) => @@ -162,7 +163,6 @@ export function consumeCloudReplayGuards(input: { : Effect.fail(error), ), ), - ), { concurrency: input.names.length }, ).pipe(Effect.map((created) => created.every(Boolean))); } diff --git a/apps/server/src/cloud/managedTunnelStartup.ts b/apps/server/src/cloud/managedTunnelStartup.ts index fe666cd0f24b17bf367268fbc9c54cc468d11bbb..511d5545a0ac94d3434baa76babae08f5f2df76d 100644 --- a/apps/server/src/cloud/managedTunnelStartup.ts +++ b/apps/server/src/cloud/managedTunnelStartup.ts @@ -49,19 +49,20 @@ export const retryManagedTunnelRegistration = ( ), Schedule.jittered, ); - return registration.pipe( + const withinWindow = registration.pipe( Effect.retry({ while: isRetryable, schedule: schedule.pipe( Schedule.upTo({ duration: MANAGED_TUNNEL_REGISTRATION_RETRY_WINDOW }), ), }), - Effect.catch((error) => - onRetryWindowExhausted !== undefined && isRetryable(error) - ? onRetryWindowExhausted.pipe( - Effect.andThen(registration.pipe(Effect.retry({ while: isRetryable, schedule }))), - ) - : Effect.fail(error), + ); + if (onRetryWindowExhausted === undefined) return withinWindow; + return withinWindow.pipe( + Effect.catchIf(isRetryable, () => + onRetryWindowExhausted.pipe( + Effect.andThen(registration.pipe(Effect.retry({ while: isRetryable, schedule }))), + ), ), ); }; diff --git a/apps/server/src/cloud/selfUpdate.test.ts b/apps/server/src/cloud/selfUpdate.test.ts index 8ca0baa64a323fc7ca031bc955223e2bed13d2a3..05c96c95c2df4760caf5660e3dc1786407506306 100644 --- a/apps/server/src/cloud/selfUpdate.test.ts +++ b/apps/server/src/cloud/selfUpdate.test.ts @@ -105,7 +105,7 @@ const makeHarness = Effect.fn("test.make_self_update_harness")(function* ( order.push("accept"); return "launcher-id"; })), - prepareTrial: Effect.sync((): undefined => undefined), + prepareTrial: Effect.undefined, }); const config = yield* ServerConfig.ServerConfig.pipe( Effect.provide(ServerConfig.layerTest(process.cwd(), baseDir)), diff --git a/apps/server/src/desktopUpdate/DesktopAppUpdate.ts b/apps/server/src/desktopUpdate/DesktopAppUpdate.ts index 9c5d88bd7910b48ee587cc9317e742036c9ec8ef..dc29375f0a470ab46ea637e71fd1213d8a5d23d0 100644 --- a/apps/server/src/desktopUpdate/DesktopAppUpdate.ts +++ b/apps/server/src/desktopUpdate/DesktopAppUpdate.ts @@ -106,7 +106,7 @@ export const make = Effect.fn("desktopUpdate.desktopAppUpdate.make")(function* ( ? emitStage(desktopUpdateProgressStage(report.state)).pipe( Effect.as(Option.none()), ) - : Effect.succeed(Option.some(report)), + : Effect.succeedSome(report), ), Stream.filterMap( Option.match({ diff --git a/apps/server/src/device/DeviceHubProxy.ts b/apps/server/src/device/DeviceHubProxy.ts index 0fb49c68e281f33c1aec9a94d237d9f2cc082d32..4db5d32c845ba2a788cad33b40465d4f57231d44 100644 --- a/apps/server/src/device/DeviceHubProxy.ts +++ b/apps/server/src/device/DeviceHubProxy.ts @@ -142,7 +142,7 @@ const proxyWebSocket = Effect.fn("DeviceHubProxy.proxyWebSocket")(function* ( pumpFrames(client, writeToUpstream), ); }), - ).pipe(Effect.catchCause(() => Effect.void)); + ).pipe(Effect.ignoreCause); return HttpServerResponse.empty(); }); diff --git a/apps/server/src/device/DeviceService.ts b/apps/server/src/device/DeviceService.ts index 2435fbca34ab3935c18a8209880c05896d568265..dcb81d95eb99eb3044d3baf9a6c3077dbe7bc23b 100644 --- a/apps/server/src/device/DeviceService.ts +++ b/apps/server/src/device/DeviceService.ts @@ -906,13 +906,11 @@ export const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* lifecycleLock.withPermit( Effect.gen(function* () { if (!installTool) - return yield* Effect.fail( - new DeviceOperationError({ - operation: "update device tool", - reason: "request_failed", - cause: new Error("Tool installation is unavailable in this device service."), - }), - ); + return yield* new DeviceOperationError({ + operation: "update device tool", + reason: "request_failed", + cause: new Error("Tool installation is unavailable in this device service."), + }); yield* installTool(tool); return yield* inspect; }), diff --git a/apps/server/src/device/LocalDeviceHost.ts b/apps/server/src/device/LocalDeviceHost.ts index e704d28bbc34edafa9504dc4a5d0e60530bcfa79..5d0ba5176a1e94a9d6639ba048a6b051be44975e 100644 --- a/apps/server/src/device/LocalDeviceHost.ts +++ b/apps/server/src/device/LocalDeviceHost.ts @@ -326,7 +326,7 @@ export const make = Effect.fn("LocalDeviceHost.make")(function* () { } } yield* fs.remove(hubStatePath(), { force: true }).pipe(Effect.ignore); - }).pipe(Effect.catchCause(() => Effect.void)); + }).pipe(Effect.ignoreCause); const recordHub = (hub: HubProcess, hubTool: DeviceToolPaths) => encodeHubStateFile({ @@ -422,7 +422,7 @@ export const make = Effect.fn("LocalDeviceHost.make")(function* () { Stream.runForEach((line) => Effect.logDebug("Device hub output", { pid: Number(hub.child.pid), output: line }), ), - Effect.catchCause(() => Effect.void), + Effect.ignoreCause, ); /** diff --git a/apps/server/src/device/deviceToolMaintenance.ts b/apps/server/src/device/deviceToolMaintenance.ts index 43cc64e8facac6d353e5989f378de08bab41b82d..0d7d61a18d59fc331d96d400d174a027c2781bdb 100644 --- a/apps/server/src/device/deviceToolMaintenance.ts +++ b/apps/server/src/device/deviceToolMaintenance.ts @@ -128,9 +128,12 @@ const runMaintenance = Effect.fn("DeviceToolchain.maintenance")(function* ( ], }); if (result.code !== 0) - return yield* Effect.fail( - new DeviceToolMaintenanceError({ operation, tool, exitCode: result.code, cause: result }), - ); + return yield* new DeviceToolMaintenanceError({ + operation, + tool, + exitCode: result.code, + cause: result, + }); }); export const pruneLocalDeviceTools = Effect.fn("DeviceToolchain.prune")(function* ( diff --git a/apps/server/src/diagnostics/TraceDiagnostics.ts b/apps/server/src/diagnostics/TraceDiagnostics.ts index 58b08ea8b5721f954ca84f62d5c1c919bc2cbc3a..ca5552058f3fe2b4e15d903d0bdcb85ba25ced00 100644 --- a/apps/server/src/diagnostics/TraceDiagnostics.ts +++ b/apps/server/src/diagnostics/TraceDiagnostics.ts @@ -420,8 +420,9 @@ export const make = Effect.gen(function* () { const readAt = options.readAt ?? (yield* DateTime.now); const slowSpanThresholdMs = options.slowSpanThresholdMs ?? DEFAULT_SLOW_SPAN_THRESHOLD_MS; const paths = toRotatedTracePaths(options.traceFilePath, options.maxFiles); - const results = yield* Effect.all( - paths.map((path) => + const results = yield* Effect.forEach( + paths, + (path) => readTraceFile(fileSystem, path).pipe( Effect.tapError((cause) => Effect.logWarning("Failed to read local trace file.").pipe( @@ -434,7 +435,6 @@ export const make = Effect.gen(function* () { ), Effect.result, ), - ), { concurrency: 1, }, diff --git a/apps/server/src/environment/ServerEnvironment.test.ts b/apps/server/src/environment/ServerEnvironment.test.ts index 6181b9702bb35bd61c861e5850ed4b316fb44c78..12ae321c1cf36f1c300bea6348f584daea509efa 100644 --- a/apps/server/src/environment/ServerEnvironment.test.ts +++ b/apps/server/src/environment/ServerEnvironment.test.ts @@ -6,7 +6,6 @@ import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; -import * as Option from "effect/Option"; import * as PlatformError from "effect/PlatformError"; import * as Schema from "effect/Schema"; @@ -35,7 +34,7 @@ const makeServerEnvironmentLayer = (baseDir: string) => const emptySecretStoreLayer = Layer.succeed( ServerSecretStore.ServerSecretStore, ServerSecretStore.ServerSecretStore.of({ - get: () => Effect.succeed(Option.none()), + get: () => Effect.succeedNone, set: () => Effect.void, create: () => Effect.void, getOrCreateRandom: () => Effect.succeed(new Uint8Array()), diff --git a/apps/server/src/environment/ServerEnvironment.ts b/apps/server/src/environment/ServerEnvironment.ts index 17fe73cc326f405219ec04a47c3788349cc4819a..d58c014d86e85a21c28e6e50093caf21c707049f 100644 --- a/apps/server/src/environment/ServerEnvironment.ts +++ b/apps/server/src/environment/ServerEnvironment.ts @@ -127,13 +127,12 @@ const makeIdentity = Effect.gen(function* () { }); yield* fileSystem.writeFileString(tempPath, `${value}\n`); // Publish the completed file without replacing an ID created by another process. - yield* fileSystem - .link(tempPath, destinationPath) - .pipe( - Effect.catch((cause) => - cause.reason._tag === "AlreadyExists" ? Effect.void : Effect.fail(cause), - ), - ); + yield* fileSystem.link(tempPath, destinationPath).pipe( + Effect.catchIf( + (cause) => cause.reason._tag === "AlreadyExists", + () => Effect.void, + ), + ); if (mode === "recover") { // Keep the recovery ID so delayed initializers also publish the same winner. yield* fileSystem.remove(tempPath); diff --git a/apps/server/src/environment/ServerEnvironmentLabel.ts b/apps/server/src/environment/ServerEnvironmentLabel.ts index 1d944492331af9485065a6ad4dd455dc6a5aaf78..4a5d0a009b9a2a4ff5903a402f67adc2c390fc45 100644 --- a/apps/server/src/environment/ServerEnvironmentLabel.ts +++ b/apps/server/src/environment/ServerEnvironmentLabel.ts @@ -128,7 +128,7 @@ const runFriendlyLabelCommand = Effect.fn("runFriendlyLabelCommand")(function* ( cause, }), ), - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ ServerEnvironmentLabelCommandError: (error) => Effect.logDebug(error.message).pipe( diff --git a/apps/server/src/environment/ServerEnvironmentMachine.ts b/apps/server/src/environment/ServerEnvironmentMachine.ts index 9d11a1ef59fdd4a17a5d84e88dbf122c94be559b..e9f5e51af6005615b6741db8fb19c1ab4757daaa 100644 --- a/apps/server/src/environment/ServerEnvironmentMachine.ts +++ b/apps/server/src/environment/ServerEnvironmentMachine.ts @@ -107,7 +107,7 @@ const readOptionalFile = Effect.fn("readOptionalFile")(function* (path: string) const fileSystem = yield* FileSystem.FileSystem; return yield* fileSystem.readFileString(path).pipe( Effect.map(normalize), - Effect.catch(() => Effect.succeed(null)), + Effect.orElseSucceed(() => null), ); }); @@ -125,7 +125,7 @@ const runProbe = Effect.fn("runMachineProbe")(function* (input: { }) .pipe( Effect.map((result) => (result.code === 0 ? normalize(result.stdout) : null)), - Effect.catch(() => Effect.succeed(null)), + Effect.orElseSucceed(() => null), ); }); diff --git a/apps/server/src/git/GitManager.ts b/apps/server/src/git/GitManager.ts index 43d674662d6161b2109a0ea4576db0bf01d575d2..f57afeb2a4191c16d130d905df99091465c0732f 100644 --- a/apps/server/src/git/GitManager.ts +++ b/apps/server/src/git/GitManager.ts @@ -2097,7 +2097,7 @@ export const make = Effect.gen(function* () { title: generated.title, bodyFile, }) - .pipe(Effect.ensuring(fileSystem.remove(bodyFile).pipe(Effect.catch(() => Effect.void)))); + .pipe(Effect.ensuring(fileSystem.remove(bodyFile).pipe(Effect.ignore))); const created = yield* findOpenPr(cwd, headContext); if (!created) { diff --git a/apps/server/src/git/linkCreatedPullRequest.test.ts b/apps/server/src/git/linkCreatedPullRequest.test.ts index 2c33d82792bd66b88718ca80bc8dfb77224ac920..2e8be05febe740c0af4f2354527dd2e3716c55df 100644 --- a/apps/server/src/git/linkCreatedPullRequest.test.ts +++ b/apps/server/src/git/linkCreatedPullRequest.test.ts @@ -83,7 +83,7 @@ const makeDependencies = ( Layer.mergeAll( Layer.mock(ProjectionSnapshotQuery)({ getThreadShellById: () => Effect.succeed(Option.fromNullishOr(threadShell)), - getProjectShellById: () => Effect.succeed(Option.some(project)), + getProjectShellById: () => Effect.succeedSome(project), }), Layer.mock(OrchestrationEngineService)({ readEvents: () => Stream.empty, diff --git a/apps/server/src/keybindings.test.ts b/apps/server/src/keybindings.test.ts index ec7070809435af792f50e2463577e0c29b0252c3..fb256f25500a21db0f650b357a887d2ab4143d25 100644 --- a/apps/server/src/keybindings.test.ts +++ b/apps/server/src/keybindings.test.ts @@ -567,13 +567,13 @@ it.layer(NodeServices.layer)("keybindings", (it) => { ); yield* Effect.gen(function* () { const keybindings = yield* Keybindings.Keybindings; - yield* Effect.all( - commands.map((command, index) => + yield* Effect.forEach( + commands, + (command, index) => keybindings.upsertKeybindingRule({ key: `mod+${String.fromCharCode(97 + index)}`, command, }), - ), { concurrency: "unbounded", discard: true }, ); }); diff --git a/apps/server/src/mcp/McpHttpServer.test.ts b/apps/server/src/mcp/McpHttpServer.test.ts index 15518e47a4974461333e50e58e1ecb4c71d230cb..16bf696e935cc2f7803c312b04cad448e476b811 100644 --- a/apps/server/src/mcp/McpHttpServer.test.ts +++ b/apps/server/src/mcp/McpHttpServer.test.ts @@ -6,7 +6,6 @@ import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; -import * as Option from "effect/Option"; import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; @@ -56,7 +55,7 @@ const PullRequestsTestLayer = McpHttpServer.PullRequestsToolkitRegistrationLive. Layer.provide( Layer.mergeAll( Layer.mock(ProjectionSnapshotQuery)({ - getThreadShellById: () => Effect.succeed(Option.none()), + getThreadShellById: () => Effect.succeedNone, }), Layer.mock(OrchestrationEngineService)({}), NodeServices.layer, diff --git a/apps/server/src/mcp/McpInvocationContext.ts b/apps/server/src/mcp/McpInvocationContext.ts index eddfa7270a77777806d4a63c319f37d4f7f3472e..96ddd8f7bfc168f8f08a200da1e09b8562e35715 100644 --- a/apps/server/src/mcp/McpInvocationContext.ts +++ b/apps/server/src/mcp/McpInvocationContext.ts @@ -47,9 +47,11 @@ const missingCapability = ( export const requireMcpCapability = ( capability: C, ): Effect.Effect, McpInvocationContext> => - Effect.flatMap(McpInvocationContext, (invocation) => - invocation.capabilities.has(capability) - ? Effect.succeed(invocation) - : // The conditional type narrows what the literal argument decided at runtime. - Effect.fail(missingCapability(invocation, capability) as McpCapabilityError), - ).pipe(Effect.withSpan("mcp.requireCapability")); + McpInvocationContext.pipe( + Effect.filterOrFail( + (invocation) => invocation.capabilities.has(capability), + // The conditional type narrows what the literal argument decided at runtime. + (invocation) => missingCapability(invocation, capability) as McpCapabilityError, + ), + Effect.withSpan("mcp.requireCapability"), + ); diff --git a/apps/server/src/mcp/McpSessionRegistry.ts b/apps/server/src/mcp/McpSessionRegistry.ts index fa0a17d826f91fb310998a7d23fe8c9804ffd971..50441df96a635f77540702f173ba3c4a20e6677b 100644 --- a/apps/server/src/mcp/McpSessionRegistry.ts +++ b/apps/server/src/mcp/McpSessionRegistry.ts @@ -230,7 +230,7 @@ export const issueActiveMcpCredential = ( ? activeMcpSessionRegistry .revokeThread(request.threadId) .pipe(Effect.andThen(activeMcpSessionRegistry.issue(request))) - : Effect.sync((): McpIssuedCredential | undefined => undefined); + : Effect.undefined; /** * Refreshes the liveness of a thread's MCP credential. Called on every provider diff --git a/apps/server/src/mcp/toolkits/preview/handlers.ts b/apps/server/src/mcp/toolkits/preview/handlers.ts index caa4cbd157cf9bf3a431ae07c8f3fca441e41c89..b70e688626839ceccacbc353c1d9962a009cd3da 100644 --- a/apps/server/src/mcp/toolkits/preview/handlers.ts +++ b/apps/server/src/mcp/toolkits/preview/handlers.ts @@ -87,7 +87,7 @@ const invoke = Effect.fn("PreviewToolkit.invoke")(function* ( updateCurrentTab: false, ...(statusTabId === undefined ? {} : { tabId: statusTabId }), }) - .pipe(Effect.catch(() => Effect.succeed(null))); + .pipe(Effect.orElseSucceed(() => null)); return { result, ...(page?.url && /^https?:\/\//i.test(page.url) && page.url.length <= 4096 @@ -175,10 +175,11 @@ export const claimPreviewRecording = Effect.fn("PreviewToolkit.claimRecording")( yield* fileSystem.rename(currentPath, finalPath); }).pipe( // Another stop may already have claimed this exact upload for this thread. - Effect.catch((cause) => - cause._tag !== "PreviewAutomationRecordingTransferError" && cause.reason._tag === "NotFound" - ? validateFile(finalPath) - : Effect.fail(cause), + Effect.catchIf( + (cause) => + cause._tag !== "PreviewAutomationRecordingTransferError" && + cause.reason._tag === "NotFound", + () => validateFile(finalPath), ), Effect.mapError((cause) => new PreviewAutomationRecordingTransferError({ threadId, cause })), ); diff --git a/apps/server/src/orchestration/Layers/CheckpointReactor.ts b/apps/server/src/orchestration/Layers/CheckpointReactor.ts index 4cfdccfd8f7425afc13fde87c857c93db5c47bb6..7814a9eec9e6a6b8c758520ed948e4d8ecf9f2ba 100644 --- a/apps/server/src/orchestration/Layers/CheckpointReactor.ts +++ b/apps/server/src/orchestration/Layers/CheckpointReactor.ts @@ -97,12 +97,12 @@ const make = Effect.gen(function* () { const entryRefreshWorker = yield* makeDrainableWorker((cwd: string) => Effect.sync(() => queuedEntryRefreshes.delete(cwd)).pipe( Effect.andThen(workspaceEntries.refresh(cwd)), - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("failed to refresh checkpoint workspace entries", { - cwd, - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + () => + Effect.logWarning("failed to refresh checkpoint workspace entries", { + cwd, + }), ), ), ); @@ -625,15 +625,14 @@ const make = Effect.gen(function* () { branch: checkedOutBranch, }); }).pipe( - Effect.catchCause((cause) => { - if (Cause.hasInterruptsOnly(cause)) { - return Effect.failCause(cause); - } - return Effect.logWarning("failed to follow worktree branch drift", { - threadId: input.threadId, - cause: Cause.pretty(cause), - }); - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("failed to follow worktree branch drift", { + threadId: input.threadId, + cause: Cause.pretty(cause), + }), + ), ); }); @@ -643,12 +642,12 @@ const make = Effect.gen(function* () { const statusRefreshWorker = yield* makeDrainableWorker( (event: Extract) => refreshLocalGitStatusFromTurnCompletion(event).pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("failed to refresh git status after turn completion", { - threadId: event.threadId, - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + () => + Effect.logWarning("failed to refresh git status after turn completion", { + threadId: event.threadId, + }), ), ), ); @@ -749,11 +748,7 @@ const make = Effect.gen(function* () { for (const candidate of paths) { const otherCwd = yield* fileSystem .realPath(candidate) - .pipe( - Effect.catch((error) => - error.reason._tag === "NotFound" ? Effect.succeed(null) : Effect.fail(error), - ), - ); + .pipe(Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(null))); if (otherCwd === null) continue; const isWithin = (parent: string, child: string) => { const relative = path.relative(parent, child); @@ -791,7 +786,7 @@ const make = Effect.gen(function* () { preferSessionRuntime: true, }).pipe( Effect.catch((error) => - event.payload.restoreFiles === false ? Effect.succeed(undefined) : Effect.fail(error), + event.payload.restoreFiles === false ? Effect.undefined : Effect.fail(error), ), ); @@ -1014,16 +1009,15 @@ const make = Effect.gen(function* () { const processInputSafely = (input: ReactorInput) => processInput(input).pipe( - Effect.catchCause((cause) => { - if (Cause.hasInterruptsOnly(cause)) { - return Effect.failCause(cause); - } - return Effect.logWarning("checkpoint reactor failed to process input", { - source: input.source, - eventType: input.event.type, - cause: Cause.pretty(cause), - }); - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("checkpoint reactor failed to process input", { + source: input.source, + eventType: input.event.type, + cause: Cause.pretty(cause), + }), + ), ); const worker = yield* makeDrainableWorker(processInputSafely); diff --git a/apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts b/apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts index 078750967471468fee759c8a46137effe461afd4..f855771b8f01a6cac697f1307c498a5f5dd02f54 100644 --- a/apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts +++ b/apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts @@ -446,18 +446,18 @@ describe("OrchestrationEngine", () => { Effect.succeed({ snapshotSequence: projectionSnapshot.snapshotSequence }), getCounts: () => Effect.succeed({ projectCount: 1, threadCount: 1 }), getEventReplayStats: () => Effect.die("unused"), - getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), - getProjectShellById: () => Effect.succeed(Option.none()), + getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, + getProjectShellById: () => Effect.succeedNone, getProjectShells: () => Effect.succeed([]), - getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), + getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, getImportedAgentSessionSources: () => Effect.die("unused"), - getThreadCheckpointContext: () => Effect.succeed(Option.none()), - getFullThreadDiffContext: () => Effect.succeed(Option.none()), + getThreadCheckpointContext: () => Effect.succeedNone, + getFullThreadDiffContext: () => Effect.succeedNone, getThreadRuntimeContext: () => Effect.die("unused"), getTurnStartMessage: () => Effect.die("unused"), - getThreadShellById: () => Effect.succeed(Option.none()), - getThreadDetailById: () => Effect.succeed(Option.none()), - getThreadDetailSnapshot: () => Effect.succeed(Option.none()), + getThreadShellById: () => Effect.succeedNone, + getThreadDetailById: () => Effect.succeedNone, + getThreadDetailSnapshot: () => Effect.succeedNone, searchThreads: () => Effect.succeed({ matches: [] }), }), ), diff --git a/apps/server/src/orchestration/Layers/OrchestrationEngine.ts b/apps/server/src/orchestration/Layers/OrchestrationEngine.ts index fb2fadde5e63e61fb8e389b772304979580db4b3..9136d080c1c3757ceac7e38ff15d316f3811a3ac 100644 --- a/apps/server/src/orchestration/Layers/OrchestrationEngine.ts +++ b/apps/server/src/orchestration/Layers/OrchestrationEngine.ts @@ -400,7 +400,7 @@ const makeOrchestrationEngine = Effect.gen(function* () { status: "rejected", error: error.message, }) - .pipe(Effect.catch(() => Effect.void)); + .pipe(Effect.ignore); } } diff --git a/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts b/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts index bdf4fe8e69d990c73cd883e6e687180a30f1fff5..bff5a60964212032bfcb93afafe23b8cc6032983 100644 --- a/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts +++ b/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts @@ -511,14 +511,14 @@ const make = Effect.gen(function* () { Effect.andThen( gitWorkflow.createWorktree({ cwd, refName: branch, path: worktreePath }, { submodules }), ), - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("provider command reactor failed to recreate worktree", { - threadId: thread.id, - worktreePath, - cause: Cause.pretty(cause), - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("provider command reactor failed to recreate worktree", { + threadId: thread.id, + worktreePath, + cause: Cause.pretty(cause), + }), ), ); }); @@ -1154,15 +1154,14 @@ const make = Effect.gen(function* () { return; } const result = yield* regenerateThreadTitle(event, requestId).pipe( - Effect.catchCause((cause) => { - if (Cause.hasInterruptsOnly(cause)) { - return Effect.failCause(cause); - } - return Effect.logWarning("provider command reactor failed to regenerate thread title", { - threadId: event.payload.threadId, - cause: Cause.pretty(cause), - }).pipe(Effect.as({ _tag: "Completed", title: undefined } as const)); - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("provider command reactor failed to regenerate thread title", { + threadId: event.payload.threadId, + cause: Cause.pretty(cause), + }).pipe(Effect.as({ _tag: "Completed", title: undefined } as const)), + ), ); if (result._tag === "Superseded") { return; @@ -1174,34 +1173,26 @@ const make = Effect.gen(function* () { ...(result.title !== undefined ? { title: result.title } : {}), }; yield* dispatchThreadTitleRegenerationCompletion(completion).pipe( - Effect.catchCause((cause) => { - if (Cause.hasInterruptsOnly(cause)) { - return Effect.failCause(cause); - } - return Effect.logWarning( - "provider command reactor retrying title regeneration completion", - { + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("provider command reactor retrying title regeneration completion", { threadId: event.payload.threadId, cause: Cause.pretty(cause), - }, - ).pipe(Effect.andThen(dispatchThreadTitleRegenerationCompletion(completion))); - }), + }).pipe(Effect.andThen(dispatchThreadTitleRegenerationCompletion(completion))), + ), ); }, (effect, event) => effect.pipe( - Effect.catchCause((cause) => { - if (Cause.hasInterruptsOnly(cause)) { - return Effect.failCause(cause); - } - return Effect.logWarning( - "provider command reactor failed to complete title regeneration", - { + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("provider command reactor failed to complete title regeneration", { threadId: event.payload.threadId, cause: Cause.pretty(cause), - }, - ); - }), + }), + ), ), ); const threadTitleRegenerationWorker = yield* makeDrainableWorker( @@ -1496,7 +1487,7 @@ const make = Effect.gen(function* () { interactionMode: event.payload.interactionMode, createdAt: event.payload.createdAt, }).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchCause((cause) => handleTurnStartFailure(cause).pipe(Effect.as(Option.none()))), ); diff --git a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts index d26930637db1479b1f61fd46405e9c6e1c5895ef..072ac5110b9d0a91dd35be8be57bc0a8a719ad0f 100644 --- a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts +++ b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts @@ -2671,17 +2671,16 @@ const make = Effect.gen(function* () { (source: string, event: { readonly eventId: string; readonly type: string }) => (effect: Effect.Effect) => effect.pipe( - Effect.catchCause((cause) => { - if (Cause.hasInterruptsOnly(cause)) { - return Effect.failCause(cause); - } - return Effect.logWarning("provider runtime ingestion failed to process event", { - source, - eventId: event.eventId, - eventType: event.type, - cause: Cause.pretty(cause), - }); - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("provider runtime ingestion failed to process event", { + source, + eventId: event.eventId, + eventType: event.type, + cause: Cause.pretty(cause), + }), + ), ); const worker = yield* makeDrainableWorker((input: RuntimeIngestionInput) => diff --git a/apps/server/src/orchestration/Layers/ThreadDeletionReactor.ts b/apps/server/src/orchestration/Layers/ThreadDeletionReactor.ts index 14a92a5eaef58ee6ef26970bef8b90f7412ef084..092ca1b1d471363ef255b11daec3c3e87e73e920 100644 --- a/apps/server/src/orchestration/Layers/ThreadDeletionReactor.ts +++ b/apps/server/src/orchestration/Layers/ThreadDeletionReactor.ts @@ -27,15 +27,14 @@ export const logCleanupCauseUnlessInterrupted = ({ readonly threadId: ThreadDeletedEvent["payload"]["threadId"]; }): Effect.Effect => effect.pipe( - Effect.catchCause((cause) => { - if (Cause.hasInterruptsOnly(cause)) { - return Effect.failCause(cause); - } - return Effect.logDebug(message, { - threadId, - cause: Cause.pretty(cause), - }); - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logDebug(message, { + threadId, + cause: Cause.pretty(cause), + }), + ), ); const make = Effect.gen(function* () { @@ -67,16 +66,15 @@ const make = Effect.gen(function* () { const processThreadDeletedSafely = (event: ThreadDeletedEvent) => processThreadDeleted(event).pipe( - Effect.catchCause((cause) => { - if (Cause.hasInterruptsOnly(cause)) { - return Effect.failCause(cause); - } - return Effect.logWarning("thread deletion reactor failed to process event", { - eventType: event.type, - threadId: event.payload.threadId, - cause: Cause.pretty(cause), - }); - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("thread deletion reactor failed to process event", { + eventType: event.type, + threadId: event.payload.threadId, + cause: Cause.pretty(cause), + }), + ), ); const worker = yield* makeDrainableWorker(processThreadDeletedSafely); diff --git a/apps/server/src/orchestration/PullRequestSyncReactor.ts b/apps/server/src/orchestration/PullRequestSyncReactor.ts index 319ea46ad64b34f0d27b5b9fc912c7baff70c1dd..2bdf3ff9e71dcb5de99a1e265e96a9a84c3351e0 100644 --- a/apps/server/src/orchestration/PullRequestSyncReactor.ts +++ b/apps/server/src/orchestration/PullRequestSyncReactor.ts @@ -259,12 +259,12 @@ export const make = Effect.gen(function* () { Effect.map((stack) => ({ stack: stack === null ? null : ({ kind: "native", ...stack } as const), })), - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("pull request stack lookup failed", { - key, - }).pipe(Effect.as(null)), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + () => + Effect.logWarning("pull request stack lookup failed", { + key, + }).pipe(Effect.as(null)), ), ) : null; diff --git a/apps/server/src/orchestration/ThreadPullRequestReactor.ts b/apps/server/src/orchestration/ThreadPullRequestReactor.ts index 86026632a6afb7dcf0bb13a75a3b9a32a947f947..17efc74e4f40fe1575c7a511fba3c3dc8a5d63f1 100644 --- a/apps/server/src/orchestration/ThreadPullRequestReactor.ts +++ b/apps/server/src/orchestration/ThreadPullRequestReactor.ts @@ -207,16 +207,16 @@ export const make = Effect.gen(function* () { } return { thread, branchPullRequest, replacement }; }).pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("thread pull request discovery failed", { - threadId: thread.id, - cause: Cause.pretty(cause), - }).pipe( - Effect.tap(() => Effect.sync(() => failBackfill([thread]))), - Effect.as(null), - ), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("thread pull request discovery failed", { + threadId: thread.id, + cause: Cause.pretty(cause), + }).pipe( + Effect.tap(() => Effect.sync(() => failBackfill([thread]))), + Effect.as(null), + ), ), ), ); @@ -273,25 +273,25 @@ export const make = Effect.gen(function* () { OrchestrationCommandInvariantError: () => Effect.sync(() => finishBackfill([thread])), }), - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("thread pull request update failed", { - threadId: thread.id, - cause: Cause.pretty(cause), - }).pipe(Effect.tap(() => Effect.sync(() => failBackfill([thread])))), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("thread pull request update failed", { + threadId: thread.id, + cause: Cause.pretty(cause), + }).pipe(Effect.tap(() => Effect.sync(() => failBackfill([thread])))), ), ), { discard: true }, ); }).pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("thread branch pull request lookup failed", { - threadIds: group.map((thread) => thread.id), - cause: Cause.pretty(cause), - }).pipe(Effect.tap(() => Effect.sync(() => failBackfill(group)))), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("thread branch pull request lookup failed", { + threadIds: group.map((thread) => thread.id), + cause: Cause.pretty(cause), + }).pipe(Effect.tap(() => Effect.sync(() => failBackfill(group)))), ), ), { concurrency: 8, discard: true }, @@ -300,12 +300,12 @@ export const make = Effect.gen(function* () { const worker = yield* makeDrainableWorker((request: RefreshRequest) => synchronize(request).pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("thread pull request refresh failed", { - cause: Cause.pretty(cause), - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("thread pull request refresh failed", { + cause: Cause.pretty(cause), + }), ), ), ); diff --git a/apps/server/src/orchestration/ThreadSettlementReactor.ts b/apps/server/src/orchestration/ThreadSettlementReactor.ts index ff6f995df46f1637a41a17c53e729d9a7453eee7..4192896efed523a74ae2dd6e8d66b1c036c06476 100644 --- a/apps/server/src/orchestration/ThreadSettlementReactor.ts +++ b/apps/server/src/orchestration/ThreadSettlementReactor.ts @@ -137,13 +137,13 @@ export const make = Effect.gen(function* () { }, (effect, thread) => effect.pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("automatic thread settlement skipped", { - threadId: thread.id, - cause: Cause.pretty(cause), - }).pipe(Effect.as(null)), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("automatic thread settlement skipped", { + threadId: thread.id, + cause: Cause.pretty(cause), + }).pipe(Effect.as(null)), ), ), ); @@ -305,13 +305,13 @@ export const make = Effect.gen(function* () { discard: true, }); }).pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("automatic thread settlement skipped", { - threadIds: group.map((thread) => thread.id), - cause: Cause.pretty(cause), - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("automatic thread settlement skipped", { + threadIds: group.map((thread) => thread.id), + cause: Cause.pretty(cause), + }), ), ), { concurrency: 8, discard: true }, @@ -323,12 +323,12 @@ export const make = Effect.gen(function* () { threadId?: ThreadId, ) => sweep(mergedPullRequest, threadId).pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("automatic thread settlement sweep failed", { - cause: Cause.pretty(cause), - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("automatic thread settlement sweep failed", { + cause: Cause.pretty(cause), + }), ), ); const worker = yield* makeDrainableWorker((threadId: ThreadId | undefined) => diff --git a/apps/server/src/orchestration/commandInvariants.ts b/apps/server/src/orchestration/commandInvariants.ts index 873eab007beaabd76c20b776aea0692ed799276a..244a6a9e9cabf7bbd194c132530b2422833c474d 100644 --- a/apps/server/src/orchestration/commandInvariants.ts +++ b/apps/server/src/orchestration/commandInvariants.ts @@ -119,15 +119,13 @@ export function requireThreadArchived(input: { readonly threadId: ThreadId; }): Effect.Effect { return requireThread(input).pipe( - Effect.flatMap((thread) => - thread.archivedAt !== null - ? Effect.succeed(thread) - : Effect.fail( - invariantError( - input.command.type, - `Thread '${input.threadId}' is not archived for command '${input.command.type}'.`, - ), - ), + Effect.filterOrFail( + (thread) => thread.archivedAt !== null, + () => + invariantError( + input.command.type, + `Thread '${input.threadId}' is not archived for command '${input.command.type}'.`, + ), ), ); } @@ -138,15 +136,13 @@ export function requireThreadNotArchived(input: { readonly threadId: ThreadId; }): Effect.Effect { return requireThread(input).pipe( - Effect.flatMap((thread) => - thread.archivedAt === null - ? Effect.succeed(thread) - : Effect.fail( - invariantError( - input.command.type, - `Thread '${input.threadId}' is already archived and cannot handle command '${input.command.type}'.`, - ), - ), + Effect.filterOrFail( + (thread) => thread.archivedAt === null, + () => + invariantError( + input.command.type, + `Thread '${input.threadId}' is already archived and cannot handle command '${input.command.type}'.`, + ), ), ); } diff --git a/apps/server/src/orchestration/decider.pullRequests.test.ts b/apps/server/src/orchestration/decider.pullRequests.test.ts index b79ca01a8deadf66f64507400300cd13fafd955c..7a961af12d5be93f61428f1940d2e190b2c9acab 100644 --- a/apps/server/src/orchestration/decider.pullRequests.test.ts +++ b/apps/server/src/orchestration/decider.pullRequests.test.ts @@ -295,7 +295,7 @@ it.layer(NodeServices.layer)("pull request link decider", (it) => { for (const planned of events) { const event = { ...planned, sequence: model.snapshotSequence + 1 }; const encoded = yield* Schema.encodeEffect(OrchestrationEvent)(event); - const decoded = yield* Schema.decodeUnknownEffect(OrchestrationEvent)(encoded); + const decoded = yield* Schema.decodeEffect(OrchestrationEvent)(encoded); // Older detail-event unions must never receive the new PR discriminants. expect(isThreadDetailEvent(decoded)).toBe(false); model = yield* projectEvent(model, decoded); diff --git a/apps/server/src/orchestration/decider.ts b/apps/server/src/orchestration/decider.ts index b610f3868525bdf25fedb721a2ac06eeae389871..21df39a162e6b324a81fd01da1258599681ee755 100644 --- a/apps/server/src/orchestration/decider.ts +++ b/apps/server/src/orchestration/decider.ts @@ -488,12 +488,10 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" command.type === "thread.auto-settle" && (thread.settledOverride !== null || thread.autoSettleDisabledAt != null) ) { - return yield* Effect.fail( - new OrchestrationCommandInvariantError({ - commandType: command.type, - detail: `thread ${command.threadId} changed before automatic settlement`, - }), - ); + return yield* new OrchestrationCommandInvariantError({ + commandType: command.type, + detail: `thread ${command.threadId} changed before automatic settlement`, + }); } // The server owns settle eligibility. A stale command must not settle // a thread whose session is coming alive or working. @@ -645,36 +643,30 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" // structurally just a string): NaN fails every comparison, and an // unparseable snoozedUntil must never persist. if (!(Date.parse(command.snoozedUntil) > Date.parse(occurredAt))) { - return yield* Effect.fail( - new OrchestrationCommandInvariantError({ - commandType: command.type, - detail: `thread ${command.threadId} snooze wake time ${command.snoozedUntil} is not in the future`, - }), - ); + return yield* new OrchestrationCommandInvariantError({ + commandType: command.type, + detail: `thread ${command.threadId} snooze wake time ${command.snoozedUntil} is not in the future`, + }); } // Blocked-on-you work must not be snoozed away: a pending approval or // user-input request is the agent waiting on the user, and hiding it // defeats the request. (A running session IS snoozable — snooze only // affects visibility, never the agent.) if (openRequests(thread).size > 0) { - return yield* Effect.fail( - new OrchestrationCommandInvariantError({ - commandType: command.type, - detail: `thread ${command.threadId} has a pending approval or user-input request and cannot be snoozed`, - }), - ); + return yield* new OrchestrationCommandInvariantError({ + commandType: command.type, + detail: `thread ${command.threadId} has a pending approval or user-input request and cannot be snoozed`, + }); } // A queued turn start — a user message no turn has adopted yet — is // invisible pending work: no session, no pending flags. Snoozing in // that window would hide a just-requested turn exactly the way settle // would. if (hasQueuedTurnStartForThread(thread, occurredAt)) { - return yield* Effect.fail( - new OrchestrationCommandInvariantError({ - commandType: command.type, - detail: `thread ${command.threadId} has a queued turn start and cannot be snoozed`, - }), - ); + return yield* new OrchestrationCommandInvariantError({ + commandType: command.type, + detail: `thread ${command.threadId} has a queued turn start and cannot be snoozed`, + }); } // Re-snoozing an already-snoozed thread to the SAME wake time is a // duplicate (double-click, raced clients): re-emit with the original @@ -836,12 +828,10 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" // (rather than silently pinning) keeps a raced reorder-after-unpin // from resurrecting a pin the user just cleared. if (thread.pinnedAt == null) { - return yield* Effect.fail( - new OrchestrationCommandInvariantError({ - commandType: command.type, - detail: `thread ${command.threadId} is not pinned and cannot be reordered`, - }), - ); + return yield* new OrchestrationCommandInvariantError({ + commandType: command.type, + detail: `thread ${command.threadId} is not pinned and cannot be reordered`, + }); } // Idempotent by re-emission (see thread.settle): a duplicate drop on // the same slot keeps the existing updatedAt so it projects as a no-op. @@ -953,12 +943,10 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" if (command.linkedPullRequest != null) { const { linkedPullRequest: linked, ...metadata } = command; const project = readModel.projects.find((project) => project.id === thread.projectId); - let host = project?.repositoryIdentity?.canonicalKey.split("/")[0] ?? "unknown"; - try { - host = new URL(linked.url).hostname; - } catch { - // Historical clients can send links without a parseable URL. - } + // Historical clients can send links without a parseable URL. + const host = URL.canParse(linked.url) + ? new URL(linked.url).hostname + : (project?.repositoryIdentity?.canonicalKey.split("/")[0] ?? "unknown"); const hasMetadata = Object.entries(metadata).some( ([key, value]) => !["type", "commandId", "threadId"].includes(key) && value !== undefined, ); @@ -1872,12 +1860,10 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" sessionComingAlive || hasQueuedTurnStartForThread(thread, command.createdAt) ) { - return yield* Effect.fail( - new OrchestrationCommandInvariantError({ - commandType: command.type, - detail: `thread ${command.threadId} was re-engaged after settle; skipping session stop`, - }), - ); + return yield* new OrchestrationCommandInvariantError({ + commandType: command.type, + detail: `thread ${command.threadId} was re-engaged after settle; skipping session stop`, + }); } } return { diff --git a/apps/server/src/orchestration/workflowScriptQuery.ts b/apps/server/src/orchestration/workflowScriptQuery.ts index 06bbd35ccf62e751269c3c17a4f5ea9d9f15f2e7..184abc6147cefff38d4825bb231d242cda70f43c 100644 --- a/apps/server/src/orchestration/workflowScriptQuery.ts +++ b/apps/server/src/orchestration/workflowScriptQuery.ts @@ -32,9 +32,10 @@ export const readWorkflowScript = Effect.fn("orchestration.readWorkflowScript")( const requested = input.scriptPath; if (!NodePath.isAbsolute(requested) || NodePath.extname(requested) !== ".js") { - return yield* Effect.fail( - new OrchestrationGetWorkflowScriptError({ reason: "invalid-path", scriptPath: requested }), - ); + return yield* new OrchestrationGetWorkflowScriptError({ + reason: "invalid-path", + scriptPath: requested, + }); } const root = yield* Effect.tryPromise({ @@ -60,14 +61,16 @@ export const readWorkflowScript = Effect.fn("orchestration.readWorkflowScript")( }); if (resolved !== root && !resolved.startsWith(`${root}${NodePath.sep}`)) { - return yield* Effect.fail( - new OrchestrationGetWorkflowScriptError({ reason: "outside-root", scriptPath: resolved }), - ); + return yield* new OrchestrationGetWorkflowScriptError({ + reason: "outside-root", + scriptPath: resolved, + }); } if (NodePath.extname(resolved) !== ".js") { - return yield* Effect.fail( - new OrchestrationGetWorkflowScriptError({ reason: "not-js", scriptPath: resolved }), - ); + return yield* new OrchestrationGetWorkflowScriptError({ + reason: "not-js", + scriptPath: resolved, + }); } // TOCTOU-safe read (review finding): open FIRST, then verify what was diff --git a/apps/server/src/persistence/AuthPairingLinks.ts b/apps/server/src/persistence/AuthPairingLinks.ts index aae55dd2fe06e20d9477884a6351c1614e3476a0..7ad1d95ed7bc3f38132b60550cd68d3f85fb296f 100644 --- a/apps/server/src/persistence/AuthPairingLinks.ts +++ b/apps/server/src/persistence/AuthPairingLinks.ts @@ -268,7 +268,7 @@ export const make = Effect.gen(function* () { ), Effect.flatMap((rowOption) => Option.match(rowOption, { - onNone: () => Effect.succeed(Option.none()), + onNone: () => Effect.succeedNone, onSome: (row) => decodeAuthPairingLinkDbRow(row).pipe( Effect.mapError((cause) => @@ -278,7 +278,7 @@ export const make = Effect.gen(function* () { { pairingLinkId: row.id }, ), ), - Effect.map(Option.some), + Effect.asSome, ), }), ), @@ -329,7 +329,7 @@ export const make = Effect.gen(function* () { ), Effect.flatMap((rowOption) => Option.match(rowOption, { - onNone: () => Effect.succeed(Option.none()), + onNone: () => Effect.succeedNone, onSome: (row) => decodeAuthPairingLinkDbRow(row).pipe( Effect.mapError((cause) => @@ -339,7 +339,7 @@ export const make = Effect.gen(function* () { { pairingLinkId: row.id }, ), ), - Effect.map(Option.some), + Effect.asSome, ), }), ), diff --git a/apps/server/src/persistence/AuthSessions.ts b/apps/server/src/persistence/AuthSessions.ts index 836b1f8f2e3022756c04a6a390d902d0e1243553..bbc8fc75685ac59bc16d3a94a0a6148bca760cfb 100644 --- a/apps/server/src/persistence/AuthSessions.ts +++ b/apps/server/src/persistence/AuthSessions.ts @@ -422,7 +422,7 @@ export const make = Effect.gen(function* () { ), Effect.flatMap((rowOption) => Option.match(rowOption, { - onNone: () => Effect.succeed(Option.none()), + onNone: () => Effect.succeedNone, onSome: (row) => decodeAuthSessionDbRow(row).pipe( Effect.mapError((cause) => diff --git a/apps/server/src/persistence/Layers/ProjectionTurns.ts b/apps/server/src/persistence/Layers/ProjectionTurns.ts index bd57a4eaa30a7accd48f625c5a9f25dbe5b3d80d..94443c867f04823acafc1780e1b0cb6343fdae9e 100644 --- a/apps/server/src/persistence/Layers/ProjectionTurns.ts +++ b/apps/server/src/persistence/Layers/ProjectionTurns.ts @@ -317,9 +317,8 @@ const makeProjectionTurnRepository = Effect.gen(function* () { ), Effect.flatMap((rowOption) => Option.match(rowOption, { - onNone: () => Effect.succeed(Option.none()), - onSome: (row) => - Effect.succeed(Option.some(row as Schema.Schema.Type)), + onNone: () => Effect.succeedNone, + onSome: (row) => Effect.succeedSome(row as Schema.Schema.Type), }), ), ); diff --git a/apps/server/src/persistence/ProviderSessionRuntime.ts b/apps/server/src/persistence/ProviderSessionRuntime.ts index 2673512edf10720e34d52b11e79c595181781d6d..80588f58858fcdf54e7d09cbe1f36b8dd226b9ff 100644 --- a/apps/server/src/persistence/ProviderSessionRuntime.ts +++ b/apps/server/src/persistence/ProviderSessionRuntime.ts @@ -398,7 +398,7 @@ export const make = Effect.gen(function* () { ), Effect.flatMap((runtimeRowOption) => Option.match(runtimeRowOption, { - onNone: () => Effect.succeed(Option.none()), + onNone: () => Effect.succeedNone, onSome: (row) => decodeRuntimeRow(row).pipe( Effect.mapError((cause) => @@ -408,7 +408,7 @@ export const make = Effect.gen(function* () { { threadId: input.threadId }, ), ), - Effect.map((runtime) => Option.some(runtime)), + Effect.asSome, ), }), ), @@ -428,7 +428,7 @@ export const make = Effect.gen(function* () { // every consumer that enumerates sessions, such as the reaper. Effect.forEach(rows, (row) => decodeRuntimeRow(row).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catch((cause) => Effect.logWarning("provider.session.runtime.row-skipped", { threadId: row.threadId, diff --git a/apps/server/src/project/AgentSessionImporter.test.ts b/apps/server/src/project/AgentSessionImporter.test.ts index 4eb03a5cc036c868dd7681d7ce0b9d9af4136c15..7d623c91b326a57a5a299c3df03baa2857e170a8 100644 --- a/apps/server/src/project/AgentSessionImporter.test.ts +++ b/apps/server/src/project/AgentSessionImporter.test.ts @@ -232,7 +232,7 @@ it.layer(NodeServices.layer)("AgentSessionImporter", (it) => { upsert: (binding) => Effect.sync(() => void bindings.push(binding)), getProvider: () => Effect.die("unused"), recordImportedTranscript: () => Effect.void, - getBinding: () => Effect.succeed(Option.none()), + getBinding: () => Effect.succeedNone, listThreadIds: () => Effect.die("unused"), listBindings: () => Effect.die("unused"), }); @@ -456,7 +456,7 @@ it.layer(NodeServices.layer)("AgentSessionImporter", (it) => { upsert: () => Effect.die("must not replace an active binding"), getProvider: () => Effect.die("unused"), recordImportedTranscript: () => Effect.void, - getBinding: () => Effect.succeed(Option.some(runningBinding)), + getBinding: () => Effect.succeedSome(runningBinding), listThreadIds: () => Effect.die("unused"), listBindings: () => Effect.die("unused"), }); @@ -511,7 +511,7 @@ it.layer(NodeServices.layer)("AgentSessionImporter", (it) => { upsert: () => Effect.die("must not bind malformed or wrong-project sessions"), getProvider: () => Effect.die("unused"), recordImportedTranscript: () => Effect.die("unused"), - getBinding: () => Effect.succeed(Option.none()), + getBinding: () => Effect.succeedNone, listThreadIds: () => Effect.die("unused"), listBindings: () => Effect.die("unused"), }); diff --git a/apps/server/src/project/AgentSessionScanner.ts b/apps/server/src/project/AgentSessionScanner.ts index 975192e700331d8fd6036c935f35addb14588cdb..8a8f3b310e032ddb4197abb02ef8e1edca3a10f2 100644 --- a/apps/server/src/project/AgentSessionScanner.ts +++ b/apps/server/src/project/AgentSessionScanner.ts @@ -662,7 +662,7 @@ export const make = Effect.gen(function* () { fileSystem.readDirectory(directory).pipe(Effect.orElseSucceed((): ReadonlyArray => [])); const statOption = (target: string) => - fileSystem.stat(target).pipe(Effect.map(Option.some), Effect.orElseSucceed(Option.none)); + fileSystem.stat(target).pipe(Effect.asSome, Effect.orElseSucceed(Option.none)); /** Match directory aliases without assuming the host volume is case-insensitive. */ const directoryIdentity = Effect.fn("AgentSessionScanner.directoryIdentity")(function* ( diff --git a/apps/server/src/project/ProjectFaviconResolver.ts b/apps/server/src/project/ProjectFaviconResolver.ts index 4fd36cb67b945883da570ec298dfd50919c1c091..90cbcb574e813812f87687e95862748cc40f426d 100644 --- a/apps/server/src/project/ProjectFaviconResolver.ts +++ b/apps/server/src/project/ProjectFaviconResolver.ts @@ -143,7 +143,7 @@ const optionOnNotFound = ( effect: Effect.Effect, ): Effect.Effect, PlatformError.PlatformError, R> => effect.pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ PlatformError: (error) => error.reason._tag === "NotFound" ? Effect.succeed(Option.none()) : Effect.fail(error), @@ -175,7 +175,7 @@ export const make = Effect.gen(function* () { relativePath, }) ).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ WorkspacePathOutsideRootError: () => Effect.succeed( diff --git a/apps/server/src/project/RepositoryIdentityResolver.ts b/apps/server/src/project/RepositoryIdentityResolver.ts index 88ecb4186f679d04d82e7b752a0a690d488439ba..2d7f5d02d02e07018141e657ec93f8a1e4d66f38 100644 --- a/apps/server/src/project/RepositoryIdentityResolver.ts +++ b/apps/server/src/project/RepositoryIdentityResolver.ts @@ -141,6 +141,7 @@ export const make = Effect.fn("RepositoryIdentityResolver.make")(function* ( ) { const processRunner = yield* ProcessRunner.ProcessRunner; const cacheCapacity = options.cacheCapacity ?? DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY; + const refine = options.refine ?? Effect.succeed; const repositoryRootCache = yield* Cache.makeWith( (cwd) => @@ -161,10 +162,9 @@ export const make = Effect.fn("RepositoryIdentityResolver.make")(function* ( (cacheKey) => resolveRepositoryIdentityFromCacheKey(cacheKey).pipe( Effect.provideService(ProcessRunner.ProcessRunner, processRunner), - Effect.flatMap((identity) => - identity !== null && options.refine - ? options.refine(identity).pipe(Effect.catch(() => Effect.succeed(identity))) - : Effect.succeed(identity), + Effect.filterOrElse( + (identity): identity is null => identity === null, + (identity) => refine(identity).pipe(Effect.orElseSucceed(() => identity)), ), ), { diff --git a/apps/server/src/project/T3ProjectFileLoader.ts b/apps/server/src/project/T3ProjectFileLoader.ts index 105e6b09a317e367f165a393e6b59f52f0e73a50..4ad874b9090beadbbd1fbb6845995301b6819148 100644 --- a/apps/server/src/project/T3ProjectFileLoader.ts +++ b/apps/server/src/project/T3ProjectFileLoader.ts @@ -68,7 +68,7 @@ export const make = Effect.gen(function* () { function* (workspaceRoot) { const filePath = path.join(workspaceRoot, T3_PROJECT_FILE_NAME); const raw = yield* fileSystem.readFileString(filePath).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ PlatformError: (error) => error.reason._tag === "NotFound" @@ -87,7 +87,7 @@ export const make = Effect.gen(function* () { return Option.none(); } return yield* decodeT3ProjectFileJson(raw.value).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ SchemaError: (error) => logT3ProjectFileLoadError( diff --git a/apps/server/src/provider/AntigravityInstallation.ts b/apps/server/src/provider/AntigravityInstallation.ts index 75baf453da034cb2222972f6a98cec4ff15960d5..f3c3d2a4d70a208e8dbd76c38f80f5dc76fba8c0 100644 --- a/apps/server/src/provider/AntigravityInstallation.ts +++ b/apps/server/src/provider/AntigravityInstallation.ts @@ -315,7 +315,7 @@ export const makeAntigravityInstallation = Effect.fn("AntigravityInstallation.ma ); } const contents = yield* fs.readFileString(filePath); - return yield* Schema.decodeUnknownEffect(Schema.fromJsonString(schema))(contents); + return yield* Schema.decodeEffect(Schema.fromJsonString(schema))(contents); }); const executableFile = Effect.fn("AntigravityInstallation.executableFile")(function* ( diff --git a/apps/server/src/provider/Drivers/AntigravitySkills.ts b/apps/server/src/provider/Drivers/AntigravitySkills.ts index f9627e72fb55d3baeed2d213650b3e5f20b15d05..b113964675826a15cb5a7f0ddc1ce1e1717acb9e 100644 --- a/apps/server/src/provider/Drivers/AntigravitySkills.ts +++ b/apps/server/src/provider/Drivers/AntigravitySkills.ts @@ -86,7 +86,7 @@ const readIfPresent = ( Effect.catchTags({ PlatformError: (cause) => cause.reason._tag === "NotFound" - ? Effect.succeed(undefined) + ? Effect.undefined : Effect.fail( new AntigravitySkillsProbeError({ reason: "filesystem-error", path, cause }), ), diff --git a/apps/server/src/provider/Drivers/CursorDriver.ts b/apps/server/src/provider/Drivers/CursorDriver.ts index 59e2138c302ff396f9fda1c81f20613fe2b2ea83..d2ad661b4af4e206b32114c3ded45edce0f24224 100644 --- a/apps/server/src/provider/Drivers/CursorDriver.ts +++ b/apps/server/src/provider/Drivers/CursorDriver.ts @@ -140,12 +140,17 @@ export const CursorDriver: ProviderDriver = { processEnv, modelDiscovery.discover, ).pipe( - Effect.flatMap((snapshot) => - effectiveConfig.enabled && snapshot.installed && snapshot.auth.status === "authenticated" - ? readCursorUsageLimits(effectiveConfig, processEnv).pipe( - Effect.map((usageLimits) => ({ ...snapshot, usageLimits })), - ) - : Effect.succeed(snapshot), + Effect.filterOrElse( + (snapshot) => + !( + effectiveConfig.enabled && + snapshot.installed && + snapshot.auth.status === "authenticated" + ), + (snapshot) => + readCursorUsageLimits(effectiveConfig, processEnv).pipe( + Effect.map((usageLimits) => ({ ...snapshot, usageLimits })), + ), ), Effect.map(stampIdentity), Effect.provideService(HttpClient.HttpClient, httpClient), diff --git a/apps/server/src/provider/Drivers/GrokDriver.ts b/apps/server/src/provider/Drivers/GrokDriver.ts index 7c0ce5f34a00fe0574a6d6ce4061ae0e1bb4d8e4..70d7a3f6e3780070c029111f46584f42d846c808 100644 --- a/apps/server/src/provider/Drivers/GrokDriver.ts +++ b/apps/server/src/provider/Drivers/GrokDriver.ts @@ -131,17 +131,22 @@ export const GrokDriver: ProviderDriver = { const textGeneration = yield* makeGrokTextGeneration(effectiveConfig, processEnv); const checkProvider = checkGrokProviderStatus(effectiveConfig, processEnv, cwd).pipe( - Effect.flatMap((snapshot) => - effectiveConfig.enabled && snapshot.installed && snapshot.auth.status === "authenticated" - ? readGrokAccount(processEnv).pipe( - // The email lets clients recognize one account signed in on several environments. - Effect.map(({ email, usageLimits }) => ({ - ...snapshot, - auth: email ? { ...snapshot.auth, email } : snapshot.auth, - usageLimits, - })), - ) - : Effect.succeed(snapshot), + Effect.filterOrElse( + (snapshot) => + !( + effectiveConfig.enabled && + snapshot.installed && + snapshot.auth.status === "authenticated" + ), + (snapshot) => + readGrokAccount(processEnv).pipe( + // The email lets clients recognize one account signed in on several environments. + Effect.map(({ email, usageLimits }) => ({ + ...snapshot, + auth: email ? { ...snapshot.auth, email } : snapshot.auth, + usageLimits, + })), + ), ), Effect.map(stampIdentity), Effect.provideService(HttpClient.HttpClient, httpClient), diff --git a/apps/server/src/provider/Layers/AntigravityAdapter.ts b/apps/server/src/provider/Layers/AntigravityAdapter.ts index 0bab3a98a537e5e59798f2563f96d655bc9841de..e12a320c4d48bb064f8e748eea80fc211cf246d8 100644 --- a/apps/server/src/provider/Layers/AntigravityAdapter.ts +++ b/apps/server/src/provider/Layers/AntigravityAdapter.ts @@ -844,7 +844,7 @@ export const makeAntigravityAdapter = Effect.fn("makeAntigravityAdapter")(functi const model = yield* applyAntigravityAcpModelSelection({ runtime, model: input.modelSelection?.model, - defaultModel: yield* options.defaultModel ?? Effect.succeed(undefined), + defaultModel: yield* options.defaultModel ?? Effect.undefined, mapError: (cause) => cause, }); yield* runtime.setMode(antigravityPermissionMode(input.runtimeMode)); @@ -1035,7 +1035,7 @@ export const makeAntigravityAdapter = Effect.fn("makeAntigravityAdapter")(functi const model = resolveAntigravityModel({ configOptions, model: requestedModel, - defaultModel: yield* options.defaultModel ?? Effect.succeed(undefined), + defaultModel: yield* options.defaultModel ?? Effect.undefined, }); const availableModels = antigravityModelOptions(configOptions); if (model && !availableModels.some((option) => option.value === model)) { diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts index ca596e6501cfb40572089628d1d4aab5b47e6c19..5026203598369d1891ee3cbcdfa04f2cec2f4feb 100644 --- a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts +++ b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts @@ -1,3 +1,4 @@ +// @effect-diagnostics abortControllerInEffect:off - Tests hand-built AbortSignals to the SDK query stub to exercise cancellation. import { buildRuntimeInstructions } from "../RuntimeInstructions.ts"; // @effect-diagnostics nodeBuiltinImport:off import * as NodeFS from "node:fs"; diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.ts b/apps/server/src/provider/Layers/ClaudeAdapter.ts index 13645a33a05bbf917b0fc285451fd311c9ea9d5c..11a5322b4eeeef2c1f0310f96028884b321e5107 100644 --- a/apps/server/src/provider/Layers/ClaudeAdapter.ts +++ b/apps/server/src/provider/Layers/ClaudeAdapter.ts @@ -5357,6 +5357,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( ) => { // SDK history helpers read process.env. Isolate the provider's home instead // of changing the server's environment while other providers are running. + // @effect-diagnostics-next-line runEffectInsideEffect:off - SDK callback runs outside the fiber; the spawn is self-contained const result = await Effect.runPromise( spawnAndCollect( process.execPath, @@ -5547,7 +5548,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( for (const result of results) { if (result._tag === "Failure") { - return yield* Effect.fail(result.failure); + return yield* result.failure; } } }); diff --git a/apps/server/src/provider/Layers/CodexAdapter.test.ts b/apps/server/src/provider/Layers/CodexAdapter.test.ts index fc2e365f6e9c22496d1ca7bcda5e60567da98a1b..8380c2dc1fdd764ccbc1bb7e4d44d6194a64642c 100644 --- a/apps/server/src/provider/Layers/CodexAdapter.test.ts +++ b/apps/server/src/provider/Layers/CodexAdapter.test.ts @@ -225,7 +225,7 @@ const providerSessionDirectoryTestLayer = Layer.succeed(ProviderSessionDirectory recordImportedTranscript: () => Effect.die("unused"), getProvider: () => Effect.die(new Error("ProviderSessionDirectory.getProvider is not used in test")), - getBinding: () => Effect.succeed(Option.none()), + getBinding: () => Effect.succeedNone, listThreadIds: () => Effect.succeed([]), listBindings: () => Effect.succeed([]), }); diff --git a/apps/server/src/provider/Layers/CodexSessionRuntime.ts b/apps/server/src/provider/Layers/CodexSessionRuntime.ts index ba78589bfdb14ffc0ef76a0edeabbd218230b3dd..3e02726d7a6224a1005f7328491e1485e6f2701c 100644 --- a/apps/server/src/provider/Layers/CodexSessionRuntime.ts +++ b/apps/server/src/provider/Layers/CodexSessionRuntime.ts @@ -1518,7 +1518,7 @@ export const makeCodexSessionRuntime = ( } }), ), - Effect.catch(() => Effect.void), + Effect.ignore, Effect.forkIn(runtimeScope), ); }); diff --git a/apps/server/src/provider/Layers/GrokAdapter.ts b/apps/server/src/provider/Layers/GrokAdapter.ts index bec0fc39faa07057d59b6e7e9bc60e41c8b12484..601ce9ffa37ef5f8bc1241cdc5d0461e757ed3e8 100644 --- a/apps/server/src/provider/Layers/GrokAdapter.ts +++ b/apps/server/src/provider/Layers/GrokAdapter.ts @@ -776,46 +776,45 @@ export function makeGrokAdapter(grokSettings: GrokSettings, options?: GrokAdapte ); }); - const runTurnLivenessWatchdog = Effect.fn("GrokAdapter.runTurnLivenessWatchdog")( - function* (ctx: GrokSessionContext) { - while (true) { - if (ctx.stopped) { - return; - } - const turnId = ctx.livenessTurnId; - if ( - turnId === undefined || - ctx.interruptedTurnIds.has(turnId) || - !isLiveTurn(ctx, turnId) || - hasLivenessPause(ctx) - ) { - yield* Queue.take(ctx.livenessSignals); - continue; - } + const runTurnLivenessWatchdog = Effect.fn("GrokAdapter.runTurnLivenessWatchdog")(function* ( + ctx: GrokSessionContext, + ) { + while (true) { + if (ctx.stopped) { + return; + } + const turnId = ctx.livenessTurnId; + if ( + turnId === undefined || + ctx.interruptedTurnIds.has(turnId) || + !isLiveTurn(ctx, turnId) || + hasLivenessPause(ctx) + ) { + yield* Queue.take(ctx.livenessSignals); + continue; + } - const lastActivityAtNanos = ctx.lastTurnActivityAtNanos; - if (lastActivityAtNanos === undefined) { - yield* Queue.take(ctx.livenessSignals); - continue; - } - const nowNanos = yield* Clock.monotonicTimeNanos; - const remainingNanos = livenessTimeoutFor(ctx).nanos - (nowNanos - lastActivityAtNanos); - if (remainingNanos <= 0n) { - yield* settleStalledTurn(ctx, turnId); - continue; - } + const lastActivityAtNanos = ctx.lastTurnActivityAtNanos; + if (lastActivityAtNanos === undefined) { + yield* Queue.take(ctx.livenessSignals); + continue; + } + const nowNanos = yield* Clock.monotonicTimeNanos; + const remainingNanos = livenessTimeoutFor(ctx).nanos - (nowNanos - lastActivityAtNanos); + if (remainingNanos <= 0n) { + yield* settleStalledTurn(ctx, turnId); + continue; + } - const wakeReason = yield* Effect.raceFirst( - Effect.sleep(Duration.nanos(remainingNanos)).pipe(Effect.as("timeout" as const)), - Queue.take(ctx.livenessSignals).pipe(Effect.as("activity" as const)), - ); - if (wakeReason === "timeout") { - yield* settleStalledTurn(ctx, turnId); - } + const wakeReason = yield* Effect.raceFirst( + Effect.sleep(Duration.nanos(remainingNanos)).pipe(Effect.as("timeout" as const)), + Queue.take(ctx.livenessSignals).pipe(Effect.as("activity" as const)), + ); + if (wakeReason === "timeout") { + yield* settleStalledTurn(ctx, turnId); } - }, - Effect.catch(() => Effect.void), - ); + } + }, Effect.ignore()); const logNative = (threadId: ThreadId, method: string, payload: unknown) => Effect.gen(function* () { @@ -2008,7 +2007,7 @@ export function makeGrokAdapter(grokSettings: GrokSettings, options?: GrokAdapte errorMessage: errorMessage ?? "Grok prompt request failed.", }), ); - }).pipe(Effect.catch(() => Effect.void)), + }).pipe(Effect.ignore), ), ); }); diff --git a/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts b/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts index 601917d35864e10156d1cf2a4c9406846d3bbcb7..baded8094ba65bdc9ca68eb9e5e30b5c2a27c013 100644 --- a/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts +++ b/apps/server/src/provider/Layers/OpenCodeAdapter.test.ts @@ -578,7 +578,7 @@ const providerSessionDirectoryTestLayer = Layer.succeed(ProviderSessionDirectory recordImportedTranscript: () => Effect.die("unused"), getProvider: () => Effect.die(new Error("ProviderSessionDirectory.getProvider is not used in test")), - getBinding: () => Effect.succeed(Option.none()), + getBinding: () => Effect.succeedNone, listThreadIds: () => Effect.succeed([]), listBindings: () => Effect.succeed([]), }); diff --git a/apps/server/src/provider/Layers/OpenCodeAdapter.ts b/apps/server/src/provider/Layers/OpenCodeAdapter.ts index 41bf634c0d3ba64b1cb87e2fea1f5206528f8171..04535edbd3afb22d1e54a66e5daf75a692dd6b34 100644 --- a/apps/server/src/provider/Layers/OpenCodeAdapter.ts +++ b/apps/server/src/provider/Layers/OpenCodeAdapter.ts @@ -822,7 +822,7 @@ const abortOpenCodeDescendants = Effect.fn("abortOpenCodeDescendants")(function* .pipe( Effect.catchIf( (cause) => isOpenCodeNotFound(cause), - () => Effect.void, + () => Effect.undefined, ), Effect.result, ); @@ -1097,7 +1097,7 @@ export function makeOpenCodeAdapter( readonly observedAt: string; readonly event: Record; }, - ) => writeNativeEvent(threadId, event).pipe(Effect.catchCause(() => Effect.void)); + ) => writeNativeEvent(threadId, event).pipe(Effect.ignoreCause); const cancelIdleReconciliation = Effect.fn("cancelIdleReconciliation")(function* ( context: OpenCodeSessionContext, @@ -1262,7 +1262,7 @@ export function makeOpenCodeAdapter( yield* Effect.sleep(`${delayMs} millis`); } }).pipe( - Effect.catchCause(() => Effect.void), + Effect.ignoreCause, Effect.ensuring( Effect.sync(() => { if (context.pendingIdleReconciliation === pending) { @@ -1492,7 +1492,7 @@ export function makeOpenCodeAdapter( } yield* failPromptAdmissionRecovery(context, promptAdmission); }).pipe( - Effect.catchCause(() => Effect.void), + Effect.ignoreCause, Effect.ensuring( Effect.sync(() => { delete promptAdmission.recoveryFiber; @@ -1699,7 +1699,7 @@ export function makeOpenCodeAdapter( }), Effect.catchIf( (cause) => isOpenCodeNotFound(cause), - () => Effect.succeed(undefined), + () => Effect.undefined, ), ); let sessionId: string | undefined = candidateSessionId; @@ -2044,7 +2044,7 @@ export function makeOpenCodeAdapter( yield* Effect.sleep(`${delayMs} millis`); } }).pipe( - Effect.catchCause(() => Effect.void), + Effect.ignoreCause, Effect.ensuring( Effect.sync(() => { if (context.requestRelationRetries.get(requestId) === retry) { @@ -2163,7 +2163,7 @@ export function makeOpenCodeAdapter( return; } }).pipe( - Effect.catchCause(() => Effect.void), + Effect.ignoreCause, Effect.ensuring( Effect.sync(() => { if (context.pendingRequestRecovery === recovery) { @@ -2730,6 +2730,7 @@ export function makeOpenCodeAdapter( // the scope closes (explicit stop, unexpected exit, or layer // shutdown) and cancels the in-flight `event.subscribe` fetch so // the async iterable unwinds cleanly. + // @effect-diagnostics-next-line abortControllerInEffect:off - aborted by a scope finalizer to cancel the SDK's event.subscribe fetch const eventsAbortController = new AbortController(); let lastStreamError: unknown; let warnedAboutDisconnect = false; diff --git a/apps/server/src/provider/Layers/ProviderService.test.ts b/apps/server/src/provider/Layers/ProviderService.test.ts index b9997e1df3122ea903c60fc9c6b93c2827320a6d..36d884d8ac989c5efa478e2ae4fccccc46f5b078 100644 --- a/apps/server/src/provider/Layers/ProviderService.test.ts +++ b/apps/server/src/provider/Layers/ProviderService.test.ts @@ -4894,13 +4894,11 @@ const listThreadIds = vi.fn(() => Effect.succeed([activeSessionThreadId, historicalSessionThreadId]), ); const getBinding = vi.fn((threadId: ThreadId) => - Effect.succeed( - Option.some({ - threadId, - provider: CODEX_DRIVER, - providerInstanceId: codexInstanceId, - }), - ), + Effect.succeedSome({ + threadId, + provider: CODEX_DRIVER, + providerInstanceId: codexInstanceId, + }), ); const boundedListing = makeProviderServiceLayer({ directory: { diff --git a/apps/server/src/provider/Layers/ProviderService.ts b/apps/server/src/provider/Layers/ProviderService.ts index cdac979c4dfdbf14f7323d12d82823bc18b6f03d..5e5052d5ef23e5fff2d683c0a80b2905dad17055 100644 --- a/apps/server/src/provider/Layers/ProviderService.ts +++ b/apps/server/src/provider/Layers/ProviderService.ts @@ -2298,7 +2298,7 @@ const makeProviderService = Effect.fn("makeProviderService")(function* ( // Continuation is project-scopable, so decide it per session's project; // without orchestration the environment value is all there is. const stopSettings = yield* serverSettings.getSettings.pipe( - Effect.map(Option.some), + Effect.asSome, Effect.orElseSucceed(() => Option.none()), ); const continueAfterRestartFor = Effect.fn("continueAfterRestartFor")(function* ( diff --git a/apps/server/src/provider/Layers/ProviderSessionDirectory.ts b/apps/server/src/provider/Layers/ProviderSessionDirectory.ts index 29ec8d2ed1689c189f15443111b897dc88cdd024..03f1ece0b5e080ccf5b7f49271e8dfef2f42f091 100644 --- a/apps/server/src/provider/Layers/ProviderSessionDirectory.ts +++ b/apps/server/src/provider/Layers/ProviderSessionDirectory.ts @@ -93,9 +93,7 @@ const makeProviderSessionDirectory = Effect.gen(function* () { Option.match(runtime, { onNone: () => Effect.succeed(Option.none()), onSome: (value) => - toRuntimeBinding(value, "ProviderSessionDirectory.getBinding").pipe( - Effect.map((binding) => Option.some(binding)), - ), + toRuntimeBinding(value, "ProviderSessionDirectory.getBinding").pipe(Effect.asSome), }), ), ); diff --git a/apps/server/src/provider/Layers/claudeResetCredits.ts b/apps/server/src/provider/Layers/claudeResetCredits.ts index 925a25585491ecc9940bf8004362fcae03d76722..f02c17214c83382d4fa0652c9fc82883db250520 100644 --- a/apps/server/src/provider/Layers/claudeResetCredits.ts +++ b/apps/server/src/provider/Layers/claudeResetCredits.ts @@ -186,7 +186,7 @@ export const readClaudeResetCredits = Effect.fn("readClaudeResetCredits")( ); }, Effect.timeout("10 seconds"), - Effect.catch(() => Effect.succeed(undefined)), + Effect.orElseSucceed(() => undefined), ); /** The CLI keeps the account record beside its settings, or in the home directory by default. */ diff --git a/apps/server/src/provider/Layers/cursorUsageLimits.ts b/apps/server/src/provider/Layers/cursorUsageLimits.ts index 685378b9d79e84a65d91da15021c094da1791825..2fbaec59adb080e110b7820d2001a5db209901d8 100644 --- a/apps/server/src/provider/Layers/cursorUsageLimits.ts +++ b/apps/server/src/provider/Layers/cursorUsageLimits.ts @@ -127,14 +127,12 @@ export const readCursorUsageLimits = Effect.fn("readCursorUsageLimits")(function return cursorUsageResponseToLimits(body, checkedAt); }).pipe( Effect.timeout("10 seconds"), - Effect.catch(() => - Effect.succeed( - makeUnavailableUsageLimits({ - checkedAt, - reason: "probeFailed", - message: "Cursor could not read usage limits.", - }), - ), + Effect.orElseSucceed(() => + makeUnavailableUsageLimits({ + checkedAt, + reason: "probeFailed", + message: "Cursor could not read usage limits.", + }), ), ); }); diff --git a/apps/server/src/provider/Layers/grokUsageLimits.ts b/apps/server/src/provider/Layers/grokUsageLimits.ts index 311494cb8666addcb55e56df5647bdd127366245..7dc561d07ec00dcd0d2ea4cda760ef9d1a3a4504 100644 --- a/apps/server/src/provider/Layers/grokUsageLimits.ts +++ b/apps/server/src/provider/Layers/grokUsageLimits.ts @@ -169,7 +169,7 @@ export const readGrokAccount = Effect.fn("readGrokAccount")(function* ( return grokUsageResponseToLimits(body, checkedAt); }).pipe( Effect.timeout("10 seconds"), - Effect.catch(() => Effect.succeed(probeFailed)), + Effect.orElseSucceed(() => probeFailed), ); return { email, usageLimits }; }); diff --git a/apps/server/src/provider/ModelManifest.ts b/apps/server/src/provider/ModelManifest.ts index b698ed6c0c82932e80cc2db290b615292ffc8cb4..92a01336bbbba198fb76d9e6ecd1667f2585e53d 100644 --- a/apps/server/src/provider/ModelManifest.ts +++ b/apps/server/src/provider/ModelManifest.ts @@ -408,7 +408,7 @@ export const make = Effect.gen(function* () { fetchedAtMs = now; yield* encodeManifestCache({ fetchedAtMs: now, manifest: fetched }).pipe( Effect.flatMap((serialized) => fileSystem.writeFileString(cachePath, serialized)), - Effect.catchCause(() => Effect.void), + Effect.ignoreCause, ); return manifest; }); diff --git a/apps/server/src/provider/acp/AcpSessionRuntime.ts b/apps/server/src/provider/acp/AcpSessionRuntime.ts index c6b93cf20546c666f46daf95dde160fffd3a00be..0d0b81e910a308fa7001d21ab4fd00524a7873ca 100644 --- a/apps/server/src/provider/acp/AcpSessionRuntime.ts +++ b/apps/server/src/provider/acp/AcpSessionRuntime.ts @@ -780,17 +780,16 @@ export const make = ( resumePayload, acp.agent.resumeSession(resumePayload).pipe( Effect.timeoutOption(options.sessionLoadTimeout ?? defaultSessionLoadTimeout), - Effect.flatMap((result) => - Option.isSome(result) - ? Effect.succeed(result.value) - : Effect.fail( - new EffectAcpErrors.AcpTransportError({ - operation: "call-rpc", - method: "session/resume", - detail: "session/resume timed out waiting for the agent response.", - cause: undefined, - }), - ), + Effect.flatMap( + Effect.fromOption( + () => + new EffectAcpErrors.AcpTransportError({ + operation: "call-rpc", + method: "session/resume", + detail: "session/resume timed out waiting for the agent response.", + cause: undefined, + }), + ), ), ), ); @@ -834,19 +833,16 @@ export const make = ( ).pipe( Effect.ensuring(Fiber.interrupt(idleFiber).pipe(Effect.ignore)), Effect.timeoutOption(sessionLoadTimeout), - Effect.flatMap((result) => - Option.match(result, { - onNone: () => - Effect.fail( - new EffectAcpErrors.AcpTransportError({ - operation: "call-rpc", - method: "session/load", - detail: "session/load timed out waiting for RPC response or replay idle gap", - cause: undefined, - }), - ), - onSome: Effect.succeed, - }), + Effect.flatMap( + Effect.fromOption( + () => + new EffectAcpErrors.AcpTransportError({ + operation: "call-rpc", + method: "session/load", + detail: "session/load timed out waiting for RPC response or replay idle gap", + cause: undefined, + }), + ), ), Effect.tap((result) => logRequest({ @@ -1058,12 +1054,13 @@ export const make = ( ), (activePrompt) => Fiber.join(activePrompt.fiber).pipe( - Effect.catchCause((cause) => - options.cancelBehavior !== "wait-for-prompt" && Cause.hasInterruptsOnly(cause) - ? Effect.succeed({ - stopReason: "cancelled", - } satisfies EffectAcpSchema.PromptResponse) - : Effect.failCause(cause), + Effect.catchCauseIf( + (cause) => + options.cancelBehavior !== "wait-for-prompt" && Cause.hasInterruptsOnly(cause), + () => + Effect.succeed({ + stopReason: "cancelled", + } satisfies EffectAcpSchema.PromptResponse), ), Effect.tap(() => closeActiveAssistantSegment({ queue: eventQueue, assistantSegmentRef }), diff --git a/apps/server/src/provider/antigravityAuthSupport.ts b/apps/server/src/provider/antigravityAuthSupport.ts index fc7fcb0854cf0e186821cf922989686698d43dac..6eaa4ec7a49707ca58b1c3b3d6ba392d89f4a5ba 100644 --- a/apps/server/src/provider/antigravityAuthSupport.ts +++ b/apps/server/src/provider/antigravityAuthSupport.ts @@ -277,9 +277,7 @@ const linkAntigravityUserSkills = Effect.fn("linkAntigravityUserSkills")(functio yield* Effect.gen(function* () { const existing = yield* fs.readLink(link).pipe( Effect.map((value): string | undefined => path.resolve(path.dirname(link), value)), - Effect.catch((error) => - error.reason._tag === "NotFound" ? Effect.succeed(undefined) : Effect.fail(error), - ), + Effect.catchReason("PlatformError", "NotFound", () => Effect.undefined), ); if (existing === target) return; if (existing !== undefined) { diff --git a/apps/server/src/provider/makeManagedServerProvider.ts b/apps/server/src/provider/makeManagedServerProvider.ts index c180412ec42d64885c950e1cd26eaeb10641aa90..119c577474c8d56acd41922588cc14da8ebef522 100644 --- a/apps/server/src/provider/makeManagedServerProvider.ts +++ b/apps/server/src/provider/makeManagedServerProvider.ts @@ -252,6 +252,7 @@ export const makeManagedServerProvider = Effect.fn("makeManagedServerProvider")( yield* Effect.forever( getRefreshInterval.pipe( Effect.flatMap((refreshInterval) => + // @effect-diagnostics-next-line raceFirstWithSleepToTimeout:off - races the interval against a settings-change signal, not a timeout Effect.raceFirst( Effect.sleep( Duration.toMillis(Duration.fromInputUnsafe(refreshInterval)) <= 0 diff --git a/apps/server/src/provider/providerInstallation.ts b/apps/server/src/provider/providerInstallation.ts index cdec50fb0bb2b6e7c5bfc13749095c4f60b08fd4..42c5c3b5473842c367ff8ea5de418e971697f210 100644 --- a/apps/server/src/provider/providerInstallation.ts +++ b/apps/server/src/provider/providerInstallation.ts @@ -118,7 +118,7 @@ export const makeProviderInstallation = Effect.fn("makeProviderInstallation")(fu env: mergeProviderInstanceEnvironment(entry.environment), }).pipe( Effect.map((resolved) => [binaryPath, resolved]), - Effect.catch(() => Effect.succeed([binaryPath])), + Effect.orElseSucceed(() => [binaryPath]), ); }); yield* installation diff --git a/apps/server/src/pullRequest/GitHubPullRequestCli.ts b/apps/server/src/pullRequest/GitHubPullRequestCli.ts index 3ed2a200711cd6a8693893959a154422e58223ee..a609fd3d097baf8f7b41616697c812d3bcab3227 100644 --- a/apps/server/src/pullRequest/GitHubPullRequestCli.ts +++ b/apps/server/src/pullRequest/GitHubPullRequestCli.ts @@ -1595,31 +1595,30 @@ export const make = Effect.gen(function* () { decode: decodePullRequestCoreJson, }), ), - ).pipe( - Effect.flatMap((core) => { - if (!core.checksTruncated) return Effect.succeed(core); - // gh already pages check contexts. Keep its complete, deduplicated result for - // large check suites instead of letting the first 100 checks imply success. - return readLegacyDetail(input).pipe( - Effect.flatMap((detail) => - detail.headSha !== core.headSha - ? Effect.fail( - new GitHubPullRequestReadError({ - command: "gh", - cwd: input.cwd, - operation: "getPullRequestDetail", - cause: new Error("Pull request head changed while reading checks."), - }), - ) - : Effect.succeed({ - ...core, - checks: detail.checks, - checksState: detail.checksState, - checksTruncated: false, + // gh already pages check contexts. Keep its complete, deduplicated result for + // large check suites instead of letting the first 100 checks imply success. + Effect.filterOrElse( + (core) => !core.checksTruncated, + (core) => + readLegacyDetail(input).pipe( + Effect.filterOrFail( + (detail) => detail.headSha === core.headSha, + () => + new GitHubPullRequestReadError({ + command: "gh", + cwd: input.cwd, + operation: "getPullRequestDetail", + cause: new Error("Pull request head changed while reading checks."), }), + ), + Effect.map((detail) => ({ + ...core, + checks: detail.checks, + checksState: detail.checksState, + checksTruncated: false, + })), ), - ); - }), + ), ); }; @@ -1967,10 +1966,9 @@ export const make = Effect.gen(function* () { // the fallback out: an empty answer under one is already the answer. const hasQuery = (input.query?.trim().length ?? 0) > 0; return read(true).pipe( - Effect.flatMap((batch) => - batch.items.length === 0 && input.cursor === undefined && !hasQuery - ? read(false) - : Effect.succeed(batch), + Effect.filterOrElse( + (batch) => batch.items.length > 0 || input.cursor !== undefined || hasQuery, + () => read(false), ), Effect.flatMap((batch) => { // Match the search query's host support, and enrich only rows that survived paging. @@ -2131,6 +2129,7 @@ export const make = Effect.gen(function* () { }), ); }), + // @effect-diagnostics-next-line flatMapConditionalToFilterOrFail:off - the fallback needs a non-null stack, which a predicate that also reads includeDetails cannot refine. Effect.flatMap((stack) => { if (!input.includeDetails || stack === null) return Effect.succeed(stack); return github diff --git a/apps/server/src/pullRequest/GitLabPullRequestCli.ts b/apps/server/src/pullRequest/GitLabPullRequestCli.ts index 5a07bb1ae37ce04596cd29cd66810f6f4a8c7df0..c3b6070024978a013025490cd22301b21a11894a 100644 --- a/apps/server/src/pullRequest/GitLabPullRequestCli.ts +++ b/apps/server/src/pullRequest/GitLabPullRequestCli.ts @@ -1250,7 +1250,7 @@ export const make = Effect.gen(function* () { getMergeRequestDiffFileContents: (input) => Effect.gen(function* () { if (input.commit !== undefined && !isCommitSha(input.commit)) { - return yield* Effect.fail(new GitLabDiffCommitError({ command: "glab", cwd: input.cwd })); + return yield* new GitLabDiffCommitError({ command: "glab", cwd: input.cwd }); } const refs = yield* input.commit === undefined ? getDiffRefs(input) diff --git a/apps/server/src/pullRequest/PullRequestReadCache.ts b/apps/server/src/pullRequest/PullRequestReadCache.ts index 1b4ded39b9534bec0dfbda812911a5b6354b859c..724fbe490d7fc60b9f577e4e06d538466f1c8d6a 100644 --- a/apps/server/src/pullRequest/PullRequestReadCache.ts +++ b/apps/server/src/pullRequest/PullRequestReadCache.ts @@ -77,7 +77,7 @@ export const make = Effect.gen(function* () { () => backing .get("revisions") - .pipe(Effect.flatMap((raw) => Schema.decodeUnknownEffect(revisionCodec)(raw ?? "{}"))), + .pipe(Effect.flatMap((raw) => Schema.decodeEffect(revisionCodec)(raw ?? "{}"))), { capacity: 1, timeToLive: (exit) => (Exit.isSuccess(exit) ? Duration.infinity : Duration.zero), diff --git a/apps/server/src/pullRequest/PullRequestService.test.ts b/apps/server/src/pullRequest/PullRequestService.test.ts index a0d111bd710f510e437b908737358800248a03f6..4ba438a97da96ec4d1edf88c9d4b7adf6479d92f 100644 --- a/apps/server/src/pullRequest/PullRequestService.test.ts +++ b/apps/server/src/pullRequest/PullRequestService.test.ts @@ -3251,13 +3251,13 @@ it.effect("shares one cold viewer lookup across distinct concurrent lists", () = ], }); - yield* Effect.all( - ["all", "authored", "reviewing"].map((involvement) => + yield* Effect.forEach( + ["all", "authored", "reviewing"], + (involvement) => service.list({ state: "open", involvement: involvement as "all" | "authored" | "reviewing", }), - ), { concurrency: "unbounded" }, ); @@ -4328,6 +4328,7 @@ it.effect("keeps routed reads separate when the GitHub account changes", () => ], }); const readOperation = (input: Parameters[0]) => + // @effect-diagnostics-next-line unnecessaryEffectGen:off - the generator unifies the per-operation union of Effect types, which Effect.asVoid cannot infer through. Effect.gen(function* () { yield* service[operation](input); }); @@ -4391,6 +4392,7 @@ it.effect("isolates routed caches for two credentials belonging to the same acco ], }); const readOperation = (input: Parameters[0]) => + // @effect-diagnostics-next-line unnecessaryEffectGen:off - the generator unifies the per-operation union of Effect types, which Effect.asVoid cannot infer through. Effect.gen(function* () { yield* service[operation](input); }); diff --git a/apps/server/src/pullRequest/PullRequestService.ts b/apps/server/src/pullRequest/PullRequestService.ts index 6b4b2bd8bfd5ba8a92b50b029173428b40624243..866a45e49b09869e0edb99a67ea81eb8e75f15cd 100644 --- a/apps/server/src/pullRequest/PullRequestService.ts +++ b/apps/server/src/pullRequest/PullRequestService.ts @@ -2751,7 +2751,7 @@ export const make = Effect.gen(function* () { `project:${input.projectId}`, refScope(input), ]); - const decoded = yield* Schema.decodeUnknownEffect(codec)(payload).pipe(Effect.option); + const decoded = yield* Schema.decodeEffect(codec)(payload).pipe(Effect.option); return Option.isSome(decoded) ? decoded.value : yield* lookup; }); const summaryCodec = Schema.fromJsonString(PullRequestSummary); diff --git a/apps/server/src/relay/AgentAwarenessRelay.test.ts b/apps/server/src/relay/AgentAwarenessRelay.test.ts index 32cf68a61e1871d785ca83de65229d08ce333689..db4f1f53b7106c4246eb74a1b2d68e1c48ef6314 100644 --- a/apps/server/src/relay/AgentAwarenessRelay.test.ts +++ b/apps/server/src/relay/AgentAwarenessRelay.test.ts @@ -576,7 +576,7 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { yield* Deferred.await(releaseThreadShell); return Option.some(thread); }), - getProjectShellById: () => Effect.succeed(Option.some(project)), + getProjectShellById: () => Effect.succeedSome(project), } as unknown as ProjectionSnapshotQueryShape; const descriptor = { @@ -793,8 +793,8 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { threads: [thread], updatedAt: now, } satisfies OrchestrationShellSnapshot), - getThreadShellById: () => Effect.succeed(Option.some(thread)), - getProjectShellById: () => Effect.succeed(Option.some(project)), + getThreadShellById: () => Effect.succeedSome(thread), + getProjectShellById: () => Effect.succeedSome(project), } as unknown as ProjectionSnapshotQueryShape), ); @@ -915,7 +915,7 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { Layer.succeed(OrchestrationEngineService, {} as OrchestrationEngineShape), Layer.succeed(ProjectionSnapshotQuery, { getThreadShellById: () => Effect.sync(() => Option.fromNullishOr(currentThread)), - getProjectShellById: () => Effect.succeed(Option.some(project)), + getProjectShellById: () => Effect.succeedSome(project), } as unknown as ProjectionSnapshotQueryShape), ); diff --git a/apps/server/src/resourceTelemetry/HostResources.ts b/apps/server/src/resourceTelemetry/HostResources.ts index 032832dd4869d0bcca0a59ab88ad9aca32f6f1ce..0f61ac1899b6f4759895d0c177ba0273db12acb7 100644 --- a/apps/server/src/resourceTelemetry/HostResources.ts +++ b/apps/server/src/resourceTelemetry/HostResources.ts @@ -60,7 +60,7 @@ export const make = Effect.fn("makeHostResources")(function* () { if (platform === "linux") { const meminfo = yield* fs .readFileString("/proc/meminfo") - .pipe(Effect.catch(() => Effect.succeed(""))); + .pipe(Effect.orElseSucceed(() => "")); const available = /^MemAvailable:\s+(\d+)\s+kB$/m.exec(meminfo)?.[1]; if (available) availableMemoryBytes = Number(available) * 1024; } else if (platform === "darwin") { @@ -68,7 +68,7 @@ export const make = Effect.fn("makeHostResources")(function* () { .string(ChildProcess.make("/usr/bin/vm_stat", [], { stdin: "ignore", stderr: "ignore" })) .pipe( Effect.timeout("1 second"), - Effect.catch(() => Effect.succeed("")), + Effect.orElseSucceed(() => ""), ); availableMemoryBytes = darwinAvailableMemory(output) ?? availableMemoryBytes; } diff --git a/apps/server/src/server.test.ts b/apps/server/src/server.test.ts index 20c2a3d83251ff1d76c4b6536c5c694313f3a63b..12f37780701d6d73352c0a20220a152d0ebb5437 100644 --- a/apps/server/src/server.test.ts +++ b/apps/server/src/server.test.ts @@ -655,25 +655,25 @@ const buildAppUnderTest = (options?: { get: () => Effect.succeed(defaultVcsDriver), detect: (input) => defaultVcsDriver.detectRepository(input.cwd).pipe( - Effect.flatMap((repository) => - repository - ? Effect.succeed(repository) - : defaultVcsDriver.isInsideWorkTree(input.cwd).pipe( - Effect.map((isInsideWorkTree) => - isInsideWorkTree - ? { - kind: "git" as const, - rootPath: input.cwd, - metadataPath: null, - freshness: { - source: "live-local" as const, - observedAt: TEST_EPOCH, - expiresAt: Option.none(), - }, - } - : null, - ), + Effect.filterOrElse( + (repository) => repository !== null, + () => + defaultVcsDriver.isInsideWorkTree(input.cwd).pipe( + Effect.map((isInsideWorkTree) => + isInsideWorkTree + ? { + kind: "git" as const, + rootPath: input.cwd, + metadataPath: null, + freshness: { + source: "live-local" as const, + observedAt: TEST_EPOCH, + expiresAt: Option.none(), + }, + } + : null, ), + ), ), Effect.map((repository) => repository @@ -822,7 +822,7 @@ const buildAppUnderTest = (options?: { ...options?.layers?.providerAuth, }), Layer.mock(ProviderInstanceRegistry)({ - getInstance: () => Effect.succeed(undefined), + getInstance: () => Effect.undefined, listInstances: Effect.succeed([]), ...options?.layers?.providerInstanceRegistry, }), @@ -832,7 +832,7 @@ const buildAppUnderTest = (options?: { }), Layer.mock(ProviderSessionDirectory.ProviderSessionDirectory)({ upsert: () => Effect.void, - getBinding: () => Effect.succeed(Option.none()), + getBinding: () => Effect.succeedNone, listThreadIds: () => Effect.succeed([]), listBindings: () => Effect.succeed([]), ...options?.layers?.providerSessionDirectory, @@ -858,7 +858,7 @@ const buildAppUnderTest = (options?: { Layer.mergeAll( Layer.mock(ExternalLauncher.ExternalLauncher)({ resolveAvailableEditors: () => Effect.succeed([]), - resolveFileManagerRevealKind: () => Effect.sync((): undefined => undefined), + resolveFileManagerRevealKind: () => Effect.undefined, ...options?.layers?.externalLauncher, }), Layer.mock(RemoteOpenTargets.RemoteOpenTargets)({ @@ -1034,20 +1034,20 @@ const buildAppUnderTest = (options?: { }), searchThreads: () => Effect.succeed({ matches: [] }), getSnapshotSequence: () => Effect.succeed({ snapshotSequence: 0 }), - getProjectShellById: () => Effect.succeed(Option.none()), - getThreadShellById: () => Effect.succeed(Option.none()), - getThreadDetailById: () => Effect.succeed(Option.none()), - getThreadDetailSnapshot: () => Effect.succeed(Option.none()), + getProjectShellById: () => Effect.succeedNone, + getThreadShellById: () => Effect.succeedNone, + getThreadDetailById: () => Effect.succeedNone, + getThreadDetailSnapshot: () => Effect.succeedNone, getCounts: () => Effect.succeed({ projectCount: 0, threadCount: 0 }), getEventReplayStats: ({ fromSequenceExclusive, toSequenceInclusive }) => Effect.succeed({ eventCount: Math.max(0, toSequenceInclusive - fromSequenceExclusive), payloadBytes: 0, }), - getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), - getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), + getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, + getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, getImportedAgentSessionSources: () => Effect.succeed([]), - getThreadCheckpointContext: () => Effect.succeed(Option.none()), + getThreadCheckpointContext: () => Effect.succeedNone, ...options?.layers?.projectionSnapshotQuery, }), ), @@ -1201,7 +1201,7 @@ const buildAppUnderTest = (options?: { Layer.provide( Layer.mock(CloudCliTokenManager.CloudCliTokenManager)({ get: Effect.die(new Error("Unexpected T3 Connect CLI authorization request.")), - getExisting: Effect.succeed(Option.none()), + getExisting: Effect.succeedNone, hasCredential: Effect.succeed(false), clear: Effect.void, ...options?.layers?.cloudCliTokenManager, @@ -8972,8 +8972,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { yield* buildAppUnderTest({ layers: { projectionSnapshotQuery: { - getThreadDetailSnapshot: () => - Effect.succeed(Option.some({ snapshotSequence: 1, thread })), + getThreadDetailSnapshot: () => Effect.succeedSome({ snapshotSequence: 1, thread }), }, }, }); @@ -9146,16 +9145,13 @@ it.layer(NodeServices.layer)("server router seam", (it) => { streamDomainEvents: Stream.concat(Stream.make(event), Stream.never), }, projectionSnapshotQuery: { - getThreadDetailSnapshot: () => - Effect.succeed(Option.some({ snapshotSequence: 1, thread })), + getThreadDetailSnapshot: () => Effect.succeedSome({ snapshotSequence: 1, thread }), getThreadShellById: (threadId) => - Effect.succeed( - Option.some({ - ...makeDefaultOrchestrationThreadShell(), - id: threadId, - title: "Build complete", - }), - ), + Effect.succeedSome({ + ...makeDefaultOrchestrationThreadShell(), + id: threadId, + title: "Build complete", + }), }, }, }); @@ -9433,7 +9429,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { }, projectionSnapshotQuery: { getThreadDetailSnapshot: () => - Effect.succeed(Option.some({ snapshotSequence: 100_000, thread })), + Effect.succeedSome({ snapshotSequence: 100_000, thread }), }, }, }); @@ -9751,8 +9747,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { }), }, projectionSnapshotQuery: { - getThreadDetailSnapshot: () => - Effect.succeed(Option.some({ snapshotSequence: 5, thread })), + getThreadDetailSnapshot: () => Effect.succeedSome({ snapshotSequence: 5, thread }), }, }, }); @@ -9840,7 +9835,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { projectionSnapshotQuery: { getThreadDetailSnapshot: (_threadId, options) => { requestedTurnLimit = options?.turnLimit; - return Effect.succeed(Option.some({ snapshotSequence: 5, thread })); + return Effect.succeedSome({ snapshotSequence: 5, thread }); }, }, }, @@ -9933,7 +9928,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { readEvents: store.readFromSequence, }, projectionSnapshotQuery: { - getThreadDetailSnapshot: () => Effect.succeed(Option.none()), + getThreadDetailSnapshot: () => Effect.succeedNone, }, }, }); @@ -10163,8 +10158,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { replayStatsCalls += 1; return { eventCount: 5, payloadBytes: 8 * 1024 * 1024 + 1 }; }), - getThreadDetailSnapshot: () => - Effect.succeed(Option.some({ snapshotSequence: 5, thread })), + getThreadDetailSnapshot: () => Effect.succeedSome({ snapshotSequence: 5, thread }), getShellSnapshot: () => Effect.succeed({ snapshotSequence: 5, @@ -10468,7 +10462,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { ]), }, projectionSnapshotQuery: { - getThreadShellById: () => Effect.succeed(Option.none()), + getThreadShellById: () => Effect.succeedNone, }, }, }); @@ -10526,9 +10520,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { detail: "transient failure", }), ) - : Effect.succeed( - Option.some(makeDefaultOrchestrationThreadShell({ id: threadId })), - ); + : Effect.succeedSome(makeDefaultOrchestrationThreadShell({ id: threadId })); }), }, }, @@ -10588,7 +10580,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { ]), }, projectionSnapshotQuery: { - getProjectShellById: () => Effect.succeed(Option.none()), + getProjectShellById: () => Effect.succeedNone, }, }, }); @@ -10634,22 +10626,20 @@ it.layer(NodeServices.layer)("server router seam", (it) => { }, projectionSnapshotQuery: { getThreadShellById: () => - Effect.succeed( - Option.some( - makeDefaultOrchestrationThreadShell({ - id: threadId, + Effect.succeedSome( + makeDefaultOrchestrationThreadShell({ + id: threadId, + updatedAt: now, + session: { + threadId, + status: "ready", + providerName: "claudeAgent", + runtimeMode: "full-access", + activeTurnId: null, + lastError: null, updatedAt: now, - session: { - threadId, - status: "ready", - providerName: "claudeAgent", - runtimeMode: "full-access", - activeTurnId: null, - lastError: null, - updatedAt: now, - }, - }), - ), + }, + }), ), }, }, @@ -10782,8 +10772,8 @@ it.layer(NodeServices.layer)("server router seam", (it) => { }, projectionSnapshotQuery: { getThreadShellById: () => - Effect.succeed( - Option.some(makeDefaultOrchestrationThreadShell({ id: threadId, session: null })), + Effect.succeedSome( + makeDefaultOrchestrationThreadShell({ id: threadId, session: null }), ), }, }, @@ -10836,22 +10826,20 @@ it.layer(NodeServices.layer)("server router seam", (it) => { }, projectionSnapshotQuery: { getThreadShellById: () => - Effect.succeed( - Option.some( - makeDefaultOrchestrationThreadShell({ - id: threadId, + Effect.succeedSome( + makeDefaultOrchestrationThreadShell({ + id: threadId, + updatedAt: now, + session: { + threadId, + status: "stopped", + providerName: "claudeAgent", + runtimeMode: "full-access", + activeTurnId: null, + lastError: null, updatedAt: now, - session: { - threadId, - status: "stopped", - providerName: "claudeAgent", - runtimeMode: "full-access", - activeTurnId: null, - lastError: null, - updatedAt: now, - }, - }), - ), + }, + }), ), }, }, @@ -10902,22 +10890,20 @@ it.layer(NodeServices.layer)("server router seam", (it) => { }, projectionSnapshotQuery: { getThreadShellById: () => - Effect.succeed( - Option.some( - makeDefaultOrchestrationThreadShell({ - id: threadId, + Effect.succeedSome( + makeDefaultOrchestrationThreadShell({ + id: threadId, + updatedAt: now, + session: { + threadId, + status: "ready", + providerName: "claudeAgent", + runtimeMode: "full-access", + activeTurnId: null, + lastError: null, updatedAt: now, - session: { - threadId, - status: "ready", - providerName: "claudeAgent", - runtimeMode: "full-access", - activeTurnId: null, - lastError: null, - updatedAt: now, - }, - }), - ), + }, + }), ), }, }, @@ -11005,22 +10991,20 @@ it.layer(NodeServices.layer)("server router seam", (it) => { }, projectionSnapshotQuery: { getThreadShellById: () => - Effect.succeed( - Option.some( - makeDefaultOrchestrationThreadShell({ - id: threadId, + Effect.succeedSome( + makeDefaultOrchestrationThreadShell({ + id: threadId, + updatedAt: now, + session: { + threadId, + status: "ready", + providerName: "claudeAgent", + runtimeMode: "full-access", + activeTurnId: null, + lastError: null, updatedAt: now, - session: { - threadId, - status: "ready", - providerName: "claudeAgent", - runtimeMode: "full-access", - activeTurnId: null, - lastError: null, - updatedAt: now, - }, - }), - ), + }, + }), ), }, }, @@ -11077,22 +11061,20 @@ it.layer(NodeServices.layer)("server router seam", (it) => { }, projectionSnapshotQuery: { getThreadShellById: () => - Effect.succeed( - Option.some( - makeDefaultOrchestrationThreadShell({ - id: threadId, + Effect.succeedSome( + makeDefaultOrchestrationThreadShell({ + id: threadId, + updatedAt: now, + session: { + threadId, + status: "ready", + providerName: "claudeAgent", + runtimeMode: "full-access", + activeTurnId: null, + lastError: null, updatedAt: now, - session: { - threadId, - status: "ready", - providerName: "claudeAgent", - runtimeMode: "full-access", - activeTurnId: null, - lastError: null, - updatedAt: now, - }, - }), - ), + }, + }), ), }, }, diff --git a/apps/server/src/serverRuntimeStartup.reconcile.test.ts b/apps/server/src/serverRuntimeStartup.reconcile.test.ts index 37fd210ee6da28ae63001381a57d66ef40e03ba0..050650b11515b8c2f09d8db63fd4561cbdc1dc34 100644 --- a/apps/server/src/serverRuntimeStartup.reconcile.test.ts +++ b/apps/server/src/serverRuntimeStartup.reconcile.test.ts @@ -399,18 +399,16 @@ it.effect("does not continue archived or deleted marked sessions", () => { directory: { getBinding: (threadId) => { const thread = threadId === archived.id ? archived : deleted; - return Effect.succeed( - Option.some({ - threadId, - provider: ProviderDriverKind.make("codex"), - providerInstanceId, - status: "running" as const, - resumeCursor: { cursor: threadId }, - runtimePayload: { - continueAfterServerUpdate: thread.session.activeTurnId, - }, - }), - ); + return Effect.succeedSome({ + threadId, + provider: ProviderDriverKind.make("codex"), + providerInstanceId, + status: "running" as const, + resumeCursor: { cursor: threadId }, + runtimePayload: { + continueAfterServerUpdate: thread.session.activeTurnId, + }, + }); }, upsert: () => Effect.void, recordImportedTranscript: () => Effect.die("unused"), @@ -456,18 +454,16 @@ it.effect("retries continuation preparation before settling a persistent failure threads: [thread], directory: { getBinding: () => - Effect.succeed( - Option.some({ - threadId: thread.id, - provider: ProviderDriverKind.make("codex"), - providerInstanceId, - status: "running" as const, - resumeCursor: { cursor: thread.id }, - runtimePayload: { - continueAfterServerUpdate: thread.session.activeTurnId, - }, - }), - ), + Effect.succeedSome({ + threadId: thread.id, + provider: ProviderDriverKind.make("codex"), + providerInstanceId, + status: "running" as const, + resumeCursor: { cursor: thread.id }, + runtimePayload: { + continueAfterServerUpdate: thread.session.activeTurnId, + }, + }), upsert: () => Effect.void, recordImportedTranscript: () => Effect.die("unused"), getProvider: () => Effect.die("unused"), @@ -610,16 +606,14 @@ it.effect( directory: { getBinding: (candidate) => candidate === absent.id - ? Effect.succeed(Option.none()) + ? Effect.succeedNone : candidate === corrupt.id ? Effect.fail(corruptFailure) - : Effect.succeed( - Option.some({ - threadId: candidate, - provider: ProviderDriverKind.make("codex"), - providerInstanceId, - }), - ), + : Effect.succeedSome({ + threadId: candidate, + provider: ProviderDriverKind.make("codex"), + providerInstanceId, + }), upsert: () => Effect.fail(writeFailure), recordImportedTranscript: () => Effect.die("unused"), getProvider: () => Effect.die("unused"), @@ -657,7 +651,7 @@ it.effect("retries failed projections and continues after a persistent failure", return runReconciliation({ threads: [transient, persistent, later], directory: { - getBinding: () => Effect.succeed(Option.none()), + getBinding: () => Effect.succeedNone, upsert: () => Effect.void, recordImportedTranscript: () => Effect.die("unused"), getProvider: () => Effect.die("unused"), @@ -758,19 +752,17 @@ for (const scenario of [ continueAfterRestart: scenario !== "disabled", directory: { getBinding: () => - Effect.succeed( - Option.some({ - threadId: thread.id, - provider: ProviderDriverKind.make("codex"), - providerInstanceId, - status: scenario === "stopped binding" ? "stopped" : "running", - ...(scenario.includes("cursor") ? {} : { resumeCursor: { threadId: thread.id } }), - runtimePayload: { - activeTurnId: scenario === "marked superseded turn" ? "another-turn" : turnId, - ...(scenario.startsWith("marked") ? { continueAfterServerUpdate: turnId } : {}), - }, - }), - ), + Effect.succeedSome({ + threadId: thread.id, + provider: ProviderDriverKind.make("codex"), + providerInstanceId, + status: scenario === "stopped binding" ? "stopped" : "running", + ...(scenario.includes("cursor") ? {} : { resumeCursor: { threadId: thread.id } }), + runtimePayload: { + activeTurnId: scenario === "marked superseded turn" ? "another-turn" : turnId, + ...(scenario.startsWith("marked") ? { continueAfterServerUpdate: turnId } : {}), + }, + }), upsert: (binding) => Effect.sync(() => { upserts.push(binding); @@ -944,9 +936,7 @@ it.effect("settles failed opt-in recovery without retrying the provider turn", ( Effect.gen(function* () { sends.push(input); preparedPayloads.push(binding.runtimePayload); - return yield* Effect.fail( - new ProviderSessionNotFoundError({ threadId: input.threadId }), - ); + return yield* new ProviderSessionNotFoundError({ threadId: input.threadId }); }), }, directory: { diff --git a/apps/server/src/serverRuntimeStartup.test.ts b/apps/server/src/serverRuntimeStartup.test.ts index ab654e15c70bb42f1fbd2550dc2bc3da86e6a78e..df879a2cf307f19ea737920cf9f1908297a18714 100644 --- a/apps/server/src/serverRuntimeStartup.test.ts +++ b/apps/server/src/serverRuntimeStartup.test.ts @@ -174,27 +174,25 @@ it.effect("resolveAutoBootstrapWelcomeTargets returns existing project and threa getCounts: () => Effect.die("unused"), getEventReplayStats: () => Effect.die("unused"), getActiveProjectByWorkspaceRoot: () => - Effect.succeed( - Option.some({ - id: bootstrapProjectId, - title: "Startup Project", - workspaceRoot: "/tmp/startup-project", - defaultModelSelection: { - instanceId: ProviderInstanceId.make("codex"), - model: DEFAULT_MODEL, - }, - scripts: [], - createdAt: "2026-01-01T00:00:00.000Z", - updatedAt: "2026-01-01T00:00:00.000Z", - deletedAt: null, - }), - ), + Effect.succeedSome({ + id: bootstrapProjectId, + title: "Startup Project", + workspaceRoot: "/tmp/startup-project", + defaultModelSelection: { + instanceId: ProviderInstanceId.make("codex"), + model: DEFAULT_MODEL, + }, + scripts: [], + createdAt: "2026-01-01T00:00:00.000Z", + updatedAt: "2026-01-01T00:00:00.000Z", + deletedAt: null, + }), getProjectShells: () => Effect.die("unused"), getProjectShellById: () => Effect.die("unused"), - getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.some(bootstrapThreadId)), + getFirstActiveThreadIdByProjectId: () => Effect.succeedSome(bootstrapThreadId), getImportedAgentSessionSources: () => Effect.die("unused"), - getThreadCheckpointContext: () => Effect.succeed(Option.none()), - getFullThreadDiffContext: () => Effect.succeed(Option.none()), + getThreadCheckpointContext: () => Effect.succeedNone, + getFullThreadDiffContext: () => Effect.succeedNone, getThreadRuntimeContext: () => Effect.die("unused"), getTurnStartMessage: () => Effect.die("unused"), getThreadShellById: () => Effect.die("unused"), @@ -321,10 +319,10 @@ it.effect.each([ ), getProjectShells: () => Effect.die("unused"), getProjectShellById: () => Effect.die("unused"), - getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), + getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, getImportedAgentSessionSources: () => Effect.die("unused"), - getThreadCheckpointContext: () => Effect.succeed(Option.none()), - getFullThreadDiffContext: () => Effect.succeed(Option.none()), + getThreadCheckpointContext: () => Effect.succeedNone, + getFullThreadDiffContext: () => Effect.succeedNone, getThreadRuntimeContext: () => Effect.die("unused"), getTurnStartMessage: () => Effect.die("unused"), getThreadShellById: () => Effect.die("unused"), @@ -391,13 +389,13 @@ it.effect( getSnapshotSequence: () => Effect.die("unused"), getCounts: () => Effect.die("unused"), getEventReplayStats: () => Effect.die("unused"), - getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), + getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, getProjectShells: () => Effect.die("unused"), getProjectShellById: () => Effect.die("unused"), getFirstActiveThreadIdByProjectId: () => Effect.die("thread lookup failed"), getImportedAgentSessionSources: () => Effect.die("unused"), - getThreadCheckpointContext: () => Effect.succeed(Option.none()), - getFullThreadDiffContext: () => Effect.succeed(Option.none()), + getThreadCheckpointContext: () => Effect.succeedNone, + getFullThreadDiffContext: () => Effect.succeedNone, getThreadRuntimeContext: () => Effect.die("unused"), getTurnStartMessage: () => Effect.die("unused"), getThreadShellById: () => Effect.die("unused"), @@ -456,13 +454,13 @@ it.effect("resolveAutoBootstrapWelcomeTargets preserves typed UUID generation fa getSnapshotSequence: () => Effect.die("unused"), getCounts: () => Effect.die("unused"), getEventReplayStats: () => Effect.die("unused"), - getActiveProjectByWorkspaceRoot: () => Effect.succeed(Option.none()), + getActiveProjectByWorkspaceRoot: () => Effect.succeedNone, getProjectShells: () => Effect.die("unused"), getProjectShellById: () => Effect.die("unused"), - getFirstActiveThreadIdByProjectId: () => Effect.succeed(Option.none()), + getFirstActiveThreadIdByProjectId: () => Effect.succeedNone, getImportedAgentSessionSources: () => Effect.die("unused"), - getThreadCheckpointContext: () => Effect.succeed(Option.none()), - getFullThreadDiffContext: () => Effect.succeed(Option.none()), + getThreadCheckpointContext: () => Effect.succeedNone, + getFullThreadDiffContext: () => Effect.succeedNone, getThreadRuntimeContext: () => Effect.die("unused"), getTurnStartMessage: () => Effect.die("unused"), getThreadShellById: () => Effect.die("unused"), diff --git a/apps/server/src/serverRuntimeStartup.ts b/apps/server/src/serverRuntimeStartup.ts index 1468e1efecb04df8b1c63ada01e0af843bbb654c..be0cbbc6a6ba50f16074f7b22fc78dc8f208c7da 100644 --- a/apps/server/src/serverRuntimeStartup.ts +++ b/apps/server/src/serverRuntimeStartup.ts @@ -264,13 +264,13 @@ export const resolveAutoBootstrapWelcomeTargets = Effect.gen(function* () { bootstrapThreadId = existingThreadId.value; } }).pipe( - Effect.catchCause((cause) => - Cause.hasInterrupts(cause) - ? Effect.failCause(cause) - : Effect.logWarning("startup thread auto-bootstrap failed", { - bootstrapProjectId: nextProjectId, - cause, - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterrupts(cause), + (cause) => + Effect.logWarning("startup thread auto-bootstrap failed", { + bootstrapProjectId: nextProjectId, + cause, + }), ), ); }); @@ -312,11 +312,9 @@ const resolveStartupBrowserTarget = Effect.gen(function* () { ? `http://${formatHostForUrl(serverConfig.host)}:${serverConfig.port}` : localUrl; const baseTarget = serverConfig.devUrl?.toString() ?? bindUrl; - return yield* Effect.succeed(serverConfig.mode === "desktop" ? baseTarget : undefined).pipe( - Effect.flatMap((target) => - target ? Effect.succeed(target) : serverAuth.issueStartupPairingUrl(baseTarget), - ), - ); + return serverConfig.mode === "desktop" + ? baseTarget + : yield* serverAuth.issueStartupPairingUrl(baseTarget); }); const maybeOpenBrowser = (target: string) => @@ -487,7 +485,7 @@ export const reconcileProviderSessions = Effect.gen(function* () { const query = yield* ProjectionSnapshotQuery.ProjectionSnapshotQuery; const settings = yield* ServerSettings.ServerSettingsService; const restartSettings = yield* settings.getSettings.pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catch((cause) => Effect.logWarning("could not read restart continuation preference", { cause }).pipe( Effect.as(Option.none()), @@ -549,13 +547,13 @@ export const reconcileProviderSessions = Effect.gen(function* () { continue; } const binding = yield* directory.getBinding(thread.id).pipe( - Effect.catchCause((cause) => - Cause.hasInterrupts(cause) - ? Effect.failCause(cause) - : Effect.logWarning("failed to read orphaned provider session directory binding", { - threadId: thread.id, - cause, - }).pipe(Effect.as(Option.none())), + Effect.catchCauseIf( + (cause) => !Cause.hasInterrupts(cause), + (cause) => + Effect.logWarning("failed to read orphaned provider session directory binding", { + threadId: thread.id, + cause, + }).pipe(Effect.as(Option.none())), ), ); const continuationMarkerPresent = @@ -606,13 +604,13 @@ export const reconcileProviderSessions = Effect.gen(function* () { }); } }).pipe( - Effect.catchCause((cause) => - Cause.hasInterrupts(cause) - ? Effect.failCause(cause) - : Effect.logWarning( - "failed to reconcile orphaned provider session directory binding", - { threadId: thread.id, cause }, - ), + Effect.catchCauseIf( + (cause) => !Cause.hasInterrupts(cause), + (cause) => + Effect.logWarning("failed to reconcile orphaned provider session directory binding", { + threadId: thread.id, + cause, + }), ), ); @@ -633,13 +631,13 @@ export const reconcileProviderSessions = Effect.gen(function* () { }); }).pipe( Effect.retry({ times: 1 }), - Effect.catchCause((cause) => - Cause.hasInterrupts(cause) - ? Effect.failCause(cause) - : Effect.logWarning("failed to settle orphaned provider session projection", { - threadId: thread.id, - cause, - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterrupts(cause), + (cause) => + Effect.logWarning("failed to settle orphaned provider session projection", { + threadId: thread.id, + cause, + }), ), ); }); @@ -739,10 +737,9 @@ export const reconcileProviderSessions = Effect.gen(function* () { yield* settleAsError(ORPHANED_PROVIDER_SESSION_ERROR); } }).pipe( - Effect.catchCause((cause) => - Cause.hasInterrupts(cause) - ? Effect.failCause(cause) - : Effect.logWarning("provider session startup reconciliation failed", { cause }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterrupts(cause), + (cause) => Effect.logWarning("provider session startup reconciliation failed", { cause }), ), ); @@ -813,21 +810,20 @@ export const reconcileWorktreeSetups = Effect.gen(function* () { createdAt: interruptedAt, }) .pipe( - Effect.catchCause((cause) => - Cause.hasInterrupts(cause) - ? Effect.failCause(cause) - : Effect.logWarning("failed to settle interrupted worktree setup", { - threadId, - cause, - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterrupts(cause), + (cause) => + Effect.logWarning("failed to settle interrupted worktree setup", { + threadId, + cause, + }), ), ); } }).pipe( - Effect.catchCause((cause) => - Cause.hasInterrupts(cause) - ? Effect.failCause(cause) - : Effect.logWarning("worktree setup startup reconciliation failed", { cause }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterrupts(cause), + (cause) => Effect.logWarning("worktree setup startup reconciliation failed", { cause }), ), ); diff --git a/apps/server/src/serverRuntimeState.ts b/apps/server/src/serverRuntimeState.ts index 4afe10bd5b650b9985920b9ca7c82b8947aadcd8..26535ae348a2d60b320dbc947ab264000ce194d9 100644 --- a/apps/server/src/serverRuntimeState.ts +++ b/apps/server/src/serverRuntimeState.ts @@ -142,7 +142,7 @@ export const readPersistedServerRuntimeState = (path: string) => cause, }), ), - onSuccess: (contents) => Effect.succeed(Option.some(contents)), + onSuccess: (contents) => Effect.succeedSome(contents), }), ); if (Option.isNone(raw)) { @@ -155,7 +155,7 @@ export const readPersistedServerRuntimeState = (path: string) => } return yield* decodePersistedServerRuntimeState(trimmed).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.mapError( (cause) => new ServerRuntimeStateError({ diff --git a/apps/server/src/sourceControl/GitHubCli.test.ts b/apps/server/src/sourceControl/GitHubCli.test.ts index 5893c21ff772caf0cadb9d263ca01cb6df334679..eb31572de467d484e4b2e5cec79cb28b3498ab0f 100644 --- a/apps/server/src/sourceControl/GitHubCli.test.ts +++ b/apps/server/src/sourceControl/GitHubCli.test.ts @@ -155,8 +155,9 @@ describe("GitHubCli.layer", () => { capacity: 2, timeToLive: "1 minute", }); - const results = yield* Effect.all( - ["github.com", "github.example.test"].map((host, index) => + const results = yield* Effect.forEach( + ["github.com", "github.example.test"], + (host, index) => Cache.get(cache, host).pipe( Effect.provideService(GitHubCli.PinnedGitHubCredential, { host, @@ -164,7 +165,6 @@ describe("GitHubCli.layer", () => { credentialFingerprint: `fingerprint-${index}`, }), ), - ), { concurrency: 2 }, ); expect(results.map((result) => result.stdout)).toEqual(["credential-0", "credential-1"]); diff --git a/apps/server/src/sourceControl/SourceControlProviderRegistry.ts b/apps/server/src/sourceControl/SourceControlProviderRegistry.ts index d8893b29e0899d057421b3050196158b8ebf9c3d..422b2c991040e8081f115556862b59e08e340592 100644 --- a/apps/server/src/sourceControl/SourceControlProviderRegistry.ts +++ b/apps/server/src/sourceControl/SourceControlProviderRegistry.ts @@ -289,14 +289,14 @@ export const makeWithProviders = Effect.fn("makeSourceControlProviderRegistryWit get, resolveHandle, resolve: (input) => resolveHandle(input).pipe(Effect.map((handle) => handle.provider)), - discover: Effect.all( - discoverySpecs.map((spec) => + discover: Effect.forEach( + discoverySpecs, + (spec) => probeSourceControlProvider({ spec, process, cwd: config.cwd, }), - ), { concurrency: "unbounded" }, ), }); diff --git a/apps/server/src/storageCleanup.ts b/apps/server/src/storageCleanup.ts index 72af05dacf222b075837e05c669a61ba2a9b6687..400ae635018fad70757e43d9a99375b7e593bc46 100644 --- a/apps/server/src/storageCleanup.ts +++ b/apps/server/src/storageCleanup.ts @@ -419,10 +419,9 @@ export const make = Effect.gen(function* () { }); const worker = yield* makeDrainableWorker(() => sweep().pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("storage cleanup failed", { cause }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => Effect.logWarning("storage cleanup failed", { cause }), ), ), ); diff --git a/apps/server/src/telemetry/Identify.ts b/apps/server/src/telemetry/Identify.ts index c68dfbbcd9aee97981dbc807d4b94cbc8d5dcb45..d68812c17cc61011160270ce45500e38202c9c69 100644 --- a/apps/server/src/telemetry/Identify.ts +++ b/apps/server/src/telemetry/Identify.ts @@ -132,7 +132,7 @@ const readIdentityFile = ( filePath: string, ) => fileSystem.readFileString(filePath).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ PlatformError: (cause) => isNotFoundError(cause) @@ -278,7 +278,7 @@ export const getTelemetryIdentifierForHome = Effect.fn("getTelemetryIdentifierFo } const anonymousId = yield* upsertAnonymousId.pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ TelemetryIdentityReadError: (error) => logTelemetryIdentityError(error).pipe(Effect.as(Option.none())), diff --git a/apps/server/src/terminal/Manager.ts b/apps/server/src/terminal/Manager.ts index 9e8f98b2309e28d66f27569bf86ca1f768df9dd2..1a65587430d52700821b38207b7920b088572383 100644 --- a/apps/server/src/terminal/Manager.ts +++ b/apps/server/src/terminal/Manager.ts @@ -1934,16 +1934,7 @@ export const makeWithOptions = Effect.fn("TerminalManager.makeWithOptions")(func terminalId: string, ): Effect.fn.Return { return yield* Effect.flatMap(getSession(threadId, terminalId), (session) => - Option.match(session, { - onNone: () => - Effect.fail( - new TerminalSessionLookupError({ - threadId, - terminalId, - }), - ), - onSome: Effect.succeed, - }), + Effect.fromOption(session, () => new TerminalSessionLookupError({ threadId, terminalId })), ); }); @@ -2367,7 +2358,7 @@ export const makeWithOptions = Effect.fn("TerminalManager.makeWithOptions")(func } const inspectorOption = yield* acquireSubprocessInspector.pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catch((reason) => Effect.logWarning("failed to snapshot processes for terminal subprocess polling", { reason, @@ -2393,7 +2384,7 @@ export const makeWithOptions = Effect.fn("TerminalManager.makeWithOptions")(func ) { const terminalPid = session.pid; const inspectResult = yield* subprocessInspector(terminalPid).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catch((reason) => Effect.logWarning("failed to check terminal subprocess activity", { threadId: session.threadId, diff --git a/apps/server/src/textGeneration/AntigravityTextGeneration.ts b/apps/server/src/textGeneration/AntigravityTextGeneration.ts index 6fd59041ddb05b8f304c9ca151ad265c67cf1a28..dacc50bf383d980894fceb1db47b9ef8ebbead19 100644 --- a/apps/server/src/textGeneration/AntigravityTextGeneration.ts +++ b/apps/server/src/textGeneration/AntigravityTextGeneration.ts @@ -238,7 +238,7 @@ export const makeAntigravityTextGeneration = Effect.fn("makeAntigravityTextGener yield* applyAntigravityAcpModelSelection({ runtime, model: input.modelSelection.model, - defaultModel: yield* options.defaultModel ?? Effect.succeed(undefined), + defaultModel: yield* options.defaultModel ?? Effect.undefined, mapError: (cause) => new TextGenerationError({ operation, diff --git a/apps/server/src/textGeneration/CodexTextGeneration.test.ts b/apps/server/src/textGeneration/CodexTextGeneration.test.ts index 91c9cb94b5d5f7626c2320c2c173790f4672e00c..15220fd9d0aff572ead6f45b9b684ec470f89145 100644 --- a/apps/server/src/textGeneration/CodexTextGeneration.test.ts +++ b/apps/server/src/textGeneration/CodexTextGeneration.test.ts @@ -556,7 +556,7 @@ it.layer(CodexTextGenerationTestLayer)("CodexTextGeneration", (it) => { }), ), ), - Effect.ensuring(fs.remove(imagePath).pipe(Effect.catch(() => Effect.void))), + Effect.ensuring(fs.remove(imagePath).pipe(Effect.ignore)), ); expect(generated.branch).toBe("fix/ui-regression"); @@ -579,7 +579,7 @@ it.layer(CodexTextGenerationTestLayer)("CodexTextGeneration", (it) => { const { attachmentsDir } = yield* ServerConfig.ServerConfig; const missingAttachmentId = "thread-missing-attachment"; const missingPath = path.join(attachmentsDir, `${missingAttachmentId}.png`); - yield* fs.remove(missingPath).pipe(Effect.catch(() => Effect.void)); + yield* fs.remove(missingPath).pipe(Effect.ignore); const result = yield* textGeneration .generateBranchName({ diff --git a/apps/server/src/textGeneration/CodexTextGeneration.ts b/apps/server/src/textGeneration/CodexTextGeneration.ts index 4c9ac59d8422835416a2051e9c69ca976670b581..2410ddbf9be7a449b1de11f0f17a1a65caf27455 100644 --- a/apps/server/src/textGeneration/CodexTextGeneration.ts +++ b/apps/server/src/textGeneration/CodexTextGeneration.ts @@ -96,7 +96,7 @@ export const makeCodexTextGeneration = Effect.fn("makeCodexTextGeneration")(func ); const safeUnlink = (filePath: string): Effect.Effect => - fileSystem.remove(filePath).pipe(Effect.catch(() => Effect.void)); + fileSystem.remove(filePath).pipe(Effect.ignore); const encodeJsonForOperation = ( operation: @@ -263,8 +263,9 @@ export const makeCodexTextGeneration = Effect.fn("makeCodexTextGeneration")(func } }); - const cleanup = Effect.all( - [schemaPath, outputPath, ...cleanupPaths].map((filePath) => safeUnlink(filePath)), + const cleanup = Effect.forEach( + [schemaPath, outputPath, ...cleanupPaths], + (filePath) => safeUnlink(filePath), { concurrency: "unbounded", }, diff --git a/apps/server/src/textGeneration/ThreadTitleLinks.ts b/apps/server/src/textGeneration/ThreadTitleLinks.ts index 1b008bb38094f7591e24d4810a3a82a767aaee95..4e22605682f7cbf0dba5dfec74140b44245ba77f 100644 --- a/apps/server/src/textGeneration/ThreadTitleLinks.ts +++ b/apps/server/src/textGeneration/ThreadTitleLinks.ts @@ -14,12 +14,9 @@ export const resolveThreadTitleLinks = Effect.fn("resolveThreadTitleLinks")(func const providers = yield* SourceControlProviderRegistry.SourceControlProviderRegistry; const links = new Map>>(); for (const match of input.message.matchAll(/https:\/\/[^\s<>"')\]`]+/g)) { - let url: URL; - try { - url = new URL(match[0].replace(/[.,;!?]+$/, "")); - } catch { - continue; - } + const candidate = match[0].replace(/[.,;!?]+$/, ""); + if (!URL.canParse(candidate)) continue; + const url = new URL(candidate); url.hash = ""; url.search = ""; if (links.has(url.href)) continue; @@ -40,7 +37,7 @@ export const resolveThreadTitleLinks = Effect.fn("resolveThreadTitleLinks")(func ), Effect.map((summary) => `${url}\n${summary}`), Effect.timeout("3 seconds"), - Effect.catch(() => Effect.succeed(`${url}: unavailable`)), + Effect.orElseSucceed(() => `${url}: unavailable`), ), { concurrency: 2 }, ); diff --git a/apps/server/src/usage/UsageService.ts b/apps/server/src/usage/UsageService.ts index 949155c650f279d7898089b148f2b41c41555c68..1bf3e6f8c20284ef2685e4970ce6c908f7f98021 100644 --- a/apps/server/src/usage/UsageService.ts +++ b/apps/server/src/usage/UsageService.ts @@ -225,7 +225,7 @@ export const make = Effect.gen(function* () { yield* encodeRatesCache({ fetchedAtMs: now, document: fetched }).pipe( Effect.flatMap((serialized) => fileSystem.writeFileString(ratesCachePath, serialized)), - Effect.catchCause(() => Effect.void), + Effect.ignoreCause, ); }); @@ -372,7 +372,7 @@ export const make = Effect.gen(function* () { cacheDirty = false; }), // A cache we cannot write is a slower next start, not a failed read. - Effect.catchCause(() => Effect.void), + Effect.ignoreCause, ); }); diff --git a/apps/server/src/vcs/GitVcsDriver.ts b/apps/server/src/vcs/GitVcsDriver.ts index f450eef0dc248bc825a6a37f2344d4ec6b98283c..71ebeb1cfdb3c8828bb9c86eb7a7958e53e69345 100644 --- a/apps/server/src/vcs/GitVcsDriver.ts +++ b/apps/server/src/vcs/GitVcsDriver.ts @@ -1101,7 +1101,7 @@ export const makeVcsDriverShape = Effect.fn("makeGitVcsDriverShape")(function* ( /^warning: failed to remove \.\/: [^\n]+$/.test(cleaned.stderr.trim()) && (yield* fileSystem.readDirectory(input.cwd).pipe( Effect.map((entries) => entries.length === 0), - Effect.catch(() => Effect.succeed(false)), + Effect.orElseSucceed(() => false), )); if (!emptiedWorkspace) return yield* new VcsProcessExitError({ diff --git a/apps/server/src/vcs/GitVcsDriverCore.ts b/apps/server/src/vcs/GitVcsDriverCore.ts index 47fd3bd207259ed95092cb8b60422677f7269f5a..b15cd28b4e927fae2f6eb47825a14775d2c519e6 100644 --- a/apps/server/src/vcs/GitVcsDriverCore.ts +++ b/apps/server/src/vcs/GitVcsDriverCore.ts @@ -946,16 +946,14 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* return yield* execution.pipe( Effect.timeoutOption(timeoutMs), Effect.flatMap((result) => - Option.match(result, { - onNone: () => - Effect.fail( - new GitCommandError({ - ...gitCommandContext(commandInput), - detail: "Git command timed out.", - }), - ), - onSome: Effect.succeed, - }), + Effect.fromOption( + result, + () => + new GitCommandError({ + ...gitCommandContext(commandInput), + detail: "Git command timed out.", + }), + ), ), ); }, @@ -1004,11 +1002,9 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* : {}), ...(options.progress ? { progress: options.progress } : {}), }).pipe( - Effect.flatMap((result) => { - if (options.allowNonZeroExit || result.exitCode === 0) { - return Effect.succeed(result); - } - return Effect.fail( + Effect.filterOrFail( + (result) => options.allowNonZeroExit || result.exitCode === 0, + (result) => new GitCommandError({ ...gitCommandContext({ operation, cwd, args }), detail: options.fallbackErrorDetail ?? "Git command exited with a non-zero status.", @@ -1016,8 +1012,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* stdoutLength: result.stdout.length, stderrLength: result.stderr.length, }), - ); - }), + ), ); const executeGitWithStableDiagnostics = ( diff --git a/apps/server/src/vcs/VcsStatusBroadcaster.ts b/apps/server/src/vcs/VcsStatusBroadcaster.ts index 6668cc6a0ff53a6592dc60b88ffcac43da0c37ff..ae9abdd0cc4e784467c3b331bfc195c9742fc3c5 100644 --- a/apps/server/src/vcs/VcsStatusBroadcaster.ts +++ b/apps/server/src/vcs/VcsStatusBroadcaster.ts @@ -541,13 +541,13 @@ export const make = Effect.gen(function* () { const demandCwds = yield* Ref.get(demandCwdsRef); const shouldRun = needsInitialRefresh || - (yield* Effect.all( - [...demandCwds.keys()].map((demandCwd) => + (yield* Effect.forEach( + [...demandCwds.keys()], + (demandCwd) => backgroundPolicy.shouldRunScopeWork({ type: "vcs-status", cwd: demandCwd, }), - ), { concurrency: "unbounded" }, )).some(Boolean); if (!shouldRun) { diff --git a/apps/server/src/ws.ts b/apps/server/src/ws.ts index 17500460d9badb2008290a3481350f05afec2198..830bb35b86db7a595e0bb24fd5dd1950234d2bfb 100644 --- a/apps/server/src/ws.ts +++ b/apps/server/src/ws.ts @@ -854,27 +854,23 @@ const makeWsRpcLayer = ( case "project.meta-updated": return projectUpsertOrRemove(ProjectId.make(event.aggregateId), event.sequence); case "project.deleted": - return Effect.succeed( - Option.some({ - kind: "project-removed" as const, - sequence: event.sequence, - projectId: ProjectId.make(event.aggregateId), - }), - ); + return Effect.succeedSome({ + kind: "project-removed" as const, + sequence: event.sequence, + projectId: ProjectId.make(event.aggregateId), + }); case "thread.deleted": case "thread.archived": - return Effect.succeed( - Option.some({ - kind: "thread-removed" as const, - sequence: event.sequence, - threadId: ThreadId.make(event.aggregateId), - }), - ); + return Effect.succeedSome({ + kind: "thread-removed" as const, + sequence: event.sequence, + threadId: ThreadId.make(event.aggregateId), + }); case "thread.unarchived": return threadUpsertOrRemove(ThreadId.make(event.aggregateId), event.sequence); default: if (event.aggregateKind !== "thread") { - return Effect.succeed(Option.none()); + return Effect.succeedNone; } return threadUpsertOrRemove(ThreadId.make(event.aggregateId), event.sequence); } @@ -892,7 +888,7 @@ const makeWsRpcLayer = ( ): Effect.Effect, never, never> => read.pipe( Effect.retry({ times: 1 }), - Effect.map(Option.some), + Effect.asSome, Effect.tapError((error) => Effect.logWarning("orchestration shell projection refetch failed", { aggregateKind, diff --git a/apps/web/src/connection/platform.ts b/apps/web/src/connection/platform.ts index bcc2849dd04140bfe2b86d213b976bda4eb84da4..edd15c8df3eb9aad1a489792b7fed47467b6af12 100644 --- a/apps/web/src/connection/platform.ts +++ b/apps/web/src/connection/platform.ts @@ -213,7 +213,7 @@ const capabilitiesLayer = Layer.effectContext( }), }); const identity = RelayDeviceIdentity.of({ - deviceId: Effect.succeed(Option.none()), + deviceId: Effect.succeedNone, }); const primaryAuth = PrimaryEnvironmentAuth.of({ bearerToken: Effect.tryPromise({ diff --git a/apps/web/src/connection/storage.ts b/apps/web/src/connection/storage.ts index 6e777c663199561f687a794437f346b337dc15e8..4c6287e1b7394f2e65ab0a70bb1d5c52fc56f6a7 100644 --- a/apps/web/src/connection/storage.ts +++ b/apps/web/src/connection/storage.ts @@ -587,7 +587,7 @@ export const connectionStorageLayer = Layer.effectContext( Effect.tap(() => Effect.promise(() => projectFaviconCache.hydrate())), Effect.flatMap((raw) => { if (typeof raw !== "string") { - return Effect.succeed(Option.none()); + return Effect.succeedNone; } return decodeStoredShellSnapshot(raw).pipe( Effect.mapError((cause) => persistenceError("load-shell", cause)), @@ -623,7 +623,7 @@ export const connectionStorageLayer = Layer.effectContext( readDatabaseValue(database, SERVER_CONFIG_STORE_NAME, environmentId).pipe( Effect.flatMap((raw) => { if (typeof raw !== "string") { - return Effect.succeed(Option.none()); + return Effect.succeedNone; } return decodeStoredServerConfig(raw).pipe( Effect.mapError((cause) => persistenceError("load-server-config", cause)), @@ -661,7 +661,7 @@ export const connectionStorageLayer = Layer.effectContext( ).pipe( Effect.flatMap((raw) => { if (typeof raw !== "string") { - return Effect.succeed(Option.none()); + return Effect.succeedNone; } return decodeStoredThreadSnapshot(raw).pipe( Effect.mapError((cause) => persistenceError("load-thread", cause)), @@ -703,7 +703,7 @@ export const connectionStorageLayer = Layer.effectContext( readDatabaseValue(database, VCS_REFS_STORE_NAME, vcsRefsCacheKey(environmentId, cwd)).pipe( Effect.flatMap((raw) => { if (typeof raw !== "string") { - return Effect.succeed(Option.none()); + return Effect.succeedNone; } return decodeStoredVcsRefs(raw).pipe( Effect.mapError((cause) => persistenceError("load-vcs-refs", cause)), diff --git a/infra/relay/src/agentActivity/LiveActivities.test.ts b/infra/relay/src/agentActivity/LiveActivities.test.ts index 7f2bce87431e8ae026dfb1a667551a095c8d98fa..5cbd3edf728f417fd88d673b77e69cfd078f6a96 100644 --- a/infra/relay/src/agentActivity/LiveActivities.test.ts +++ b/infra/relay/src/agentActivity/LiveActivities.test.ts @@ -276,7 +276,7 @@ describe("LiveActivities", () => { liveActivities.register({ userId: "user-1", registration }), ); const targetListError = yield* Effect.flip(liveActivities.listTargets({ userId: "user-1" })); - const deliveryErrors = yield* Effect.all( + const deliveryErrors = yield* Effect.forEach( [ liveActivities.markDelivery({ userId: "user-1", @@ -297,7 +297,8 @@ describe("LiveActivities", () => { kind: "push_notification", invalidatedAt: "2026-05-25T00:00:10.000Z", }), - ].map(Effect.flip), + ], + (effect) => Effect.flip(effect), { concurrency: 1 }, ); diff --git a/infra/relay/src/clientConfig.test.ts b/infra/relay/src/clientConfig.test.ts index 6fd28dcd948c58cbbe22730267e86f3554523f0c..cfc5e665f0b6691c3b0d5e2cb0d75113f33e11ed 100644 --- a/infra/relay/src/clientConfig.test.ts +++ b/infra/relay/src/clientConfig.test.ts @@ -82,13 +82,7 @@ describe("PublishClientConfig", () => { ), ); - yield* stack - .deploy( - Effect.gen(function* () { - return yield* PublishClientConfig(clientConfig("v1")); - }), - ) - .pipe(configured); + yield* stack.deploy(PublishClientConfig(clientConfig("v1"))).pipe(configured); const first = yield* fs.readFileString(target); expect(first).toContain("KEEP=yes\n"); expect(first).toContain("T3CODE_RELAY_URL=https://relay.example.com\n"); @@ -97,23 +91,11 @@ describe("PublishClientConfig", () => { // Same input: the action is skipped, so a change made by hand survives. yield* fs.writeFileString(target, `${first}MANUAL=1\n`); - yield* stack - .deploy( - Effect.gen(function* () { - return yield* PublishClientConfig(clientConfig("v1")); - }), - ) - .pipe(configured); + yield* stack.deploy(PublishClientConfig(clientConfig("v1"))).pipe(configured); expect(yield* fs.readFileString(target)).toContain("MANUAL=1\n"); // A rotated token changes the input, so it runs again and replaces the line. - yield* stack - .deploy( - Effect.gen(function* () { - return yield* PublishClientConfig(clientConfig("v2")); - }), - ) - .pipe(configured); + yield* stack.deploy(PublishClientConfig(clientConfig("v2"))).pipe(configured); const third = yield* fs.readFileString(target); expect(third).toContain("T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN=client-v2\n"); expect(third).not.toContain("client-v1"); @@ -128,11 +110,7 @@ describe("PublishClientConfig", () => { const dir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-relay-client-config-" }); const target = path.join(dir, "client.env"); const exit = yield* stack - .deploy( - Effect.gen(function* () { - return yield* PublishClientConfig({ ...clientConfig("v1"), url: undefined }); - }), - ) + .deploy(PublishClientConfig({ ...clientConfig("v1"), url: undefined })) .pipe( Effect.provide( ConfigProvider.layer( @@ -155,11 +133,9 @@ describe("PublishClientConfig", () => { const target = path.join(dir, "client.env"); const exit = yield* stack .deploy( - Effect.gen(function* () { - return yield* PublishClientConfig({ - ...clientConfig("v1"), - clientTracingDataset: "relay-traces\nINJECTED=1", - }); + PublishClientConfig({ + ...clientConfig("v1"), + clientTracingDataset: "relay-traces\nINJECTED=1", }), ) .pipe( diff --git a/infra/relay/src/environments/ManagedEndpointAllocations.ts b/infra/relay/src/environments/ManagedEndpointAllocations.ts index 4767dd30072ed8e8c51af300bd148d4c8176dadf..588333d3e15aafa1ee0f420bc562e6f316ff18a1 100644 --- a/infra/relay/src/environments/ManagedEndpointAllocations.ts +++ b/infra/relay/src/environments/ManagedEndpointAllocations.ts @@ -567,9 +567,7 @@ export const make = Effect.gen(function* () { .pipe( Effect.mapError(lockError), Effect.flatMap((rows) => - rows.length === 0 - ? Effect.succeed(Option.none()) - : effect.pipe(Effect.map(Option.some)), + rows.length === 0 ? Effect.succeedNone : Effect.asSome(effect), ), ), ) diff --git a/infra/relay/src/environments/ManagedEndpointProvider.test.ts b/infra/relay/src/environments/ManagedEndpointProvider.test.ts index fc02d56b2ff76d4b3be103cf2803bf48ad74e7a2..d7cb7d67f9dd6af3758eecbf551bd2b2a97ee048 100644 --- a/infra/relay/src/environments/ManagedEndpointProvider.test.ts +++ b/infra/relay/src/environments/ManagedEndpointProvider.test.ts @@ -6,7 +6,6 @@ import * as Alchemy from "alchemy"; import * as Cloudflare from "alchemy/Cloudflare"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; -import * as Option from "effect/Option"; import * as Redacted from "effect/Redacted"; import * as RelayConfiguration from "../Config.ts"; @@ -302,8 +301,8 @@ function makeAllocations(calls: AllocationCall[] = []) { Effect.suspend(() => { const current = allocations.get(allocationKey(input)); return current?.tunnelId === input.tunnelId && current.generation === input.generation - ? effect.pipe(Effect.map(Option.some)) - : Effect.succeed(Option.none()); + ? Effect.asSome(effect) + : Effect.succeedNone; }), claimDeprovision: (input) => Effect.sync(() => { @@ -1189,7 +1188,7 @@ describe("ManagedEndpointProvider", () => { const changed = ManagedEndpointAllocations.ManagedEndpointAllocations.of({ ...allocations, withClaimedTunnel: (input, effect) => - loseClaim ? Effect.succeed(Option.none()) : allocations.withClaimedTunnel(input, effect), + loseClaim ? Effect.succeedNone : allocations.withClaimedTunnel(input, effect), }); const layer = providerLayer(makePersistentTunnelClient(), makeDnsClient(), changed); @@ -1320,7 +1319,7 @@ describe("ManagedEndpointProvider", () => { const allocations = makeAllocations(); const changed = ManagedEndpointAllocations.ManagedEndpointAllocations.of({ ...allocations, - withClaimedTunnel: () => Effect.succeed(Option.none()), + withClaimedTunnel: () => Effect.succeedNone, }); const layer = providerLayer(makePersistentTunnelClient(tunnelCalls), makeDnsClient(), changed); @@ -1349,9 +1348,7 @@ describe("ManagedEndpointProvider", () => { const changed = ManagedEndpointAllocations.ManagedEndpointAllocations.of({ ...allocations, withClaimedTunnel: (input, effect) => - ++lockCount === 1 - ? allocations.withClaimedTunnel(input, effect) - : Effect.succeed(Option.none()), + ++lockCount === 1 ? allocations.withClaimedTunnel(input, effect) : Effect.succeedNone, }); const layer = providerLayer(makePersistentTunnelClient(), makeDnsClient(dnsCalls), changed); diff --git a/infra/relay/src/environments/ManagedEndpointProvider.ts b/infra/relay/src/environments/ManagedEndpointProvider.ts index bc0f4c7457cbbddce500c2ad497d2c4e8c38ab00..892ac09a6e50aed57319a5da18809bef03b8020d 100644 --- a/infra/relay/src/environments/ManagedEndpointProvider.ts +++ b/infra/relay/src/environments/ManagedEndpointProvider.ts @@ -471,8 +471,9 @@ export const make = Effect.gen(function* () { ? updateExistingDnsRecords(records, preferredDnsRecordId, dnsRecord) : Effect.fail(createError), ), - Effect.flatMap((dnsRecordId) => - dnsRecordId === null ? Effect.fail(createError) : Effect.succeed(dnsRecordId), + Effect.filterOrFail( + (dnsRecordId) => dnsRecordId !== null, + () => createError, ), ), }), @@ -578,19 +579,19 @@ export const make = Effect.gen(function* () { ? Effect.succeed("missing" as const) : Effect.fail(error), }), - Effect.flatMap((result) => - result === "missing" - ? Effect.succeed(result) - : allocations - .markReady({ - ...input, - generation: allocation.generation, - }) - .pipe( - Effect.map((updated) => - updated ? ("configured" as const) : ("stale" as const), - ), + Effect.filterOrElse( + (result): result is "missing" => result === "missing", + () => + allocations + .markReady({ + ...input, + generation: allocation.generation, + }) + .pipe( + Effect.map((updated) => + updated ? ("configured" as const) : ("stale" as const), ), + ), ), ), ) @@ -791,11 +792,11 @@ export const make = Effect.gen(function* () { const expectedStatus = input.expectedStatus; const inactiveBefore = input.expectedInactiveBefore; const currentTunnel = yield* tunnels.get(tunnelId).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catchTags({ ManagedEndpointTunnelClientError: (cause) => isManagedEndpointNotFound(cause.cause) - ? Effect.succeed(Option.none()) + ? Effect.succeedNone : Effect.fail( new ManagedEndpointDeprovisioningFailed({ ...input, diff --git a/packages/client-runtime/src/authorization/layer.test.ts b/packages/client-runtime/src/authorization/layer.test.ts index 5cd2d89d1ac2effc7909cf350cce18ce8884f11d..d31f37db2eb7a7c9ccf42c3bccc37fd6209e8774 100644 --- a/packages/client-runtime/src/authorization/layer.test.ts +++ b/packages/client-runtime/src/authorization/layer.test.ts @@ -199,7 +199,7 @@ const makeHarness = Effect.fn("TestRemoteAuthorization.makeHarness")(function* ( clerkToken: input.clerkToken ?? Effect.succeed("clerk-session"), }), Layer.succeed(ClientCapabilities.RelayDeviceIdentity, { - deviceId: Effect.succeed(Option.some("device-1")), + deviceId: Effect.succeedSome("device-1"), }), Layer.succeed(TokenStore.RemoteDpopAccessTokenStore, tokenStore), Layer.succeed( diff --git a/packages/client-runtime/src/connection/registry.test.ts b/packages/client-runtime/src/connection/registry.test.ts index 729d27060187424197ceb6e442f188b30166487c..c07a15101f605ffad2e3a460f8ff1d7f718f8e4f 100644 --- a/packages/client-runtime/src/connection/registry.test.ts +++ b/packages/client-runtime/src/connection/registry.test.ts @@ -275,12 +275,12 @@ const makeHarness = Effect.fn("TestEnvironmentRegistry.makeHarness")(function* ( next.set(environmentId, snapshot); return next; }), - loadThread: (_environmentId, _threadId) => Effect.succeed(Option.none()), + loadThread: (_environmentId, _threadId) => Effect.succeedNone, saveThread: (_environmentId, _thread) => Effect.void, removeThread: (_environmentId, _threadId) => Effect.void, - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: () => Effect.void, diff --git a/packages/client-runtime/src/connection/resolver.ts b/packages/client-runtime/src/connection/resolver.ts index af1a417fc594f9a2428ac949735249184fb62f32..120f63cc07ef3aa53e1ab1151b61e4eab6344e29 100644 --- a/packages/client-runtime/src/connection/resolver.ts +++ b/packages/client-runtime/src/connection/resolver.ts @@ -110,10 +110,9 @@ const makeBearerBroker = Effect.fn("clientRuntime.connection.broker.makeBearer") entry: ConnectionCatalogEntry & { readonly target: BearerConnectionTarget }, ) { const target = entry.target; - const profile = yield* Option.match(entry.profile, { - onNone: () => Effect.fail(profileMissingError(target.connectionId)), - onSome: Effect.succeed, - }); + const profile = yield* Effect.fromOption(entry.profile, () => + profileMissingError(target.connectionId), + ); if (!isBearerProfile(profile)) { return yield* new ConnectionBlockedError({ reason: "configuration", @@ -186,10 +185,9 @@ const makeSshBroker = Effect.fn("clientRuntime.connection.broker.makeSsh")(funct entry: ConnectionCatalogEntry & { readonly target: SshConnectionTarget }, ) { const target = entry.target; - const profile = yield* Option.match(entry.profile, { - onNone: () => Effect.fail(profileMissingError(target.connectionId)), - onSome: Effect.succeed, - }); + const profile = yield* Effect.fromOption(entry.profile, () => + profileMissingError(target.connectionId), + ); if (!isSshProfile(profile)) { return yield* new ConnectionBlockedError({ reason: "configuration", diff --git a/packages/client-runtime/src/connection/supervisor.test.ts b/packages/client-runtime/src/connection/supervisor.test.ts index 882e67aac33c83c3080b40f72ccf0634d58b821e..7ad709c3a54bd3148c38c1c04d63005ac5ca6c44 100644 --- a/packages/client-runtime/src/connection/supervisor.test.ts +++ b/packages/client-runtime/src/connection/supervisor.test.ts @@ -1290,11 +1290,11 @@ describe("EnvironmentSupervisor", () => { Layer.succeed(ManagedRelayDpopSigner, signer), Layer.succeed(ManagedRelayClient, relay), Layer.succeed(ClientCapabilities.CloudSession, { - identity: Effect.succeed(Option.some({ accountId: "test-account" })), + identity: Effect.succeedSome({ accountId: "test-account" }), clerkToken: Effect.succeed("clerk-token"), }), Layer.succeed(ClientCapabilities.RelayDeviceIdentity, { - deviceId: Effect.succeed(Option.none()), + deviceId: Effect.succeedNone, }), TokenStore.layer({ get: () => Ref.get(token), diff --git a/packages/client-runtime/src/connection/supervisor.ts b/packages/client-runtime/src/connection/supervisor.ts index 45ef022920569209d04710f7a4b6968b4256c986..b75413bbb7de409bfac7a1446332a9b151fcc82c 100644 --- a/packages/client-runtime/src/connection/supervisor.ts +++ b/packages/client-runtime/src/connection/supervisor.ts @@ -606,6 +606,7 @@ export const make = Effect.fn("EnvironmentSupervisor.make")(function* ( }, Effect.ensuring(clearLease)); const waitForRetrySignal = Effect.fnUntraced(function* (delayMs: number) { + // @effect-diagnostics-next-line raceFirstWithSleepToTimeout:off - the sleep is the retry delay (false), not a timeout around the signal loop return yield* Effect.raceFirst( Effect.sleep(delayMs).pipe(Effect.as(false)), Effect.gen(function* () { diff --git a/packages/client-runtime/src/platform/storageDocument.test.ts b/packages/client-runtime/src/platform/storageDocument.test.ts index a0c7de3d093bd767f9a5334df0cac73b48348bfc..811ad170b6dd4eda877c6a362b06def53ef9608c 100644 --- a/packages/client-runtime/src/platform/storageDocument.test.ts +++ b/packages/client-runtime/src/platform/storageDocument.test.ts @@ -207,7 +207,7 @@ describe("ConnectionCatalogDocument", () => { remoteDpopTokens: [token], }; const schema = Schema.fromJsonString(ConnectionCatalogDocument); - const restored = Schema.decodeUnknownSync(schema)(Schema.encodeSync(schema)(document)); + const restored = Schema.decodeSync(schema)(Schema.encodeSync(schema)(document)); expect(restored).toEqual(document); expect(restored.remoteDpopTokens[0]?.accountId).toBe(accountId); diff --git a/packages/client-runtime/src/relay/discovery.test.ts b/packages/client-runtime/src/relay/discovery.test.ts index fdbcceb7df867cc1c26eaaa13c67d3b5b1fa2816..8251b2dc36d9ea3eb474a6a17e0cbc2f9ed9ce3b 100644 --- a/packages/client-runtime/src/relay/discovery.test.ts +++ b/packages/client-runtime/src/relay/discovery.test.ts @@ -134,15 +134,13 @@ const makeHarness = Effect.fn("RelayDiscoveryTest.makeHarness")(function* () { ), ), clerkToken: Ref.get(clerkToken).pipe( - Effect.flatMap((token) => - token === null - ? Effect.fail( - new ConnectionBlockedError({ - reason: "authentication", - detail: "Signed out.", - }), - ) - : Effect.succeed(token), + Effect.filterOrFail( + (token) => token !== null, + () => + new ConnectionBlockedError({ + reason: "authentication", + detail: "Signed out.", + }), ), ), }), @@ -289,7 +287,7 @@ describe("RelayEnvironmentDiscovery", () => { Layer.mergeAll( Layer.succeed(ManagedRelay.ManagedRelayClient, client), Layer.succeed(ClientCapabilities.CloudSession, { - identity: Effect.succeed(Option.some({ accountId: "account-1" })), + identity: Effect.succeedSome({ accountId: "account-1" }), clerkToken: Effect.succeed("clerk-token"), }), Layer.succeed(Connectivity.Connectivity, { diff --git a/packages/client-runtime/src/relay/discovery.ts b/packages/client-runtime/src/relay/discovery.ts index 8a8eae1d6cd956684c9e10f4b7cdb5b5c92ccd9d..8aee35347e2b5ddc54ef7d067eae27bbc49dc76e 100644 --- a/packages/client-runtime/src/relay/discovery.ts +++ b/packages/client-runtime/src/relay/discovery.ts @@ -241,7 +241,7 @@ export const make = Effect.fn("RelayEnvironmentDiscovery.make")(function* () { })); return; } - return yield* Effect.fail(failure); + return yield* failure; } const clerkToken = tokenResult.success; if ((yield* Ref.get(accountGeneration)) !== generation) { diff --git a/packages/client-runtime/src/relay/managedRelay.ts b/packages/client-runtime/src/relay/managedRelay.ts index eadece417681263a9e595dc8a942f0cc783cab0f..9c414ad9e60558690330946608f4b9018b8ddeeb 100644 --- a/packages/client-runtime/src/relay/managedRelay.ts +++ b/packages/client-runtime/src/relay/managedRelay.ts @@ -666,11 +666,8 @@ export const make = Effect.fn("ManagedRelayClient.make")(function* ( authorize(input).pipe( Effect.flatMap((authorization) => request(authorization).pipe( - Effect.catch((error) => { - if (!isRejectedDpopAccessToken(error)) { - return Effect.fail(error); - } - return invalidateAccessToken(authorization.accessToken).pipe( + Effect.catchIf(isRejectedDpopAccessToken, (error) => + invalidateAccessToken(authorization.accessToken).pipe( Effect.tap((invalidated) => Effect.annotateCurrentSpan({ "relay.token_cache.invalidated": invalidated, @@ -686,8 +683,8 @@ export const make = Effect.fn("ManagedRelayClient.make")(function* ( : Effect.void, ), Effect.andThen(refreshRejectedToken ? attempt(false) : Effect.fail(error)), - ); - }), + ), + ), ), ), ); diff --git a/packages/client-runtime/src/relay/managedRelayState.ts b/packages/client-runtime/src/relay/managedRelayState.ts index cb6d5983d394a5a6cb69b8df5530d73a47b90e73..cd092689cdd4b9a2378df88feff8562dd642d590 100644 --- a/packages/client-runtime/src/relay/managedRelayState.ts +++ b/packages/client-runtime/src/relay/managedRelayState.ts @@ -180,14 +180,12 @@ function readSessionClerkToken( session: ManagedRelaySession, ): Effect.Effect { return session.readClerkToken().pipe( - Effect.flatMap((token) => - token - ? Effect.succeed(token) - : Effect.fail( - new ManagedRelaySessionError({ - message: "The T3 Connect session token is unavailable.", - }), - ), + Effect.filterOrFail( + (token): token is string => Boolean(token), + () => + new ManagedRelaySessionError({ + message: "The T3 Connect session token is unavailable.", + }), ), ); } diff --git a/packages/client-runtime/src/rpc/session.test.ts b/packages/client-runtime/src/rpc/session.test.ts index 65b537921f092e31e2dbe45f55060e0d9176a382..9d49b8e6240ae754b9566bce78a20dbb55d34cf9 100644 --- a/packages/client-runtime/src/rpc/session.test.ts +++ b/packages/client-runtime/src/rpc/session.test.ts @@ -161,7 +161,7 @@ const RpcRequest = Schema.TaggedStruct("Request", { }); const decodeJson = Schema.decodeUnknownSync(Schema.fromJsonString(Schema.Unknown)); const isRpcRequest = Schema.is(RpcRequest); -const isPing = Schema.is(Schema.Struct({ _tag: Schema.Literal("Ping") })); +const isPing = Schema.is(Schema.TaggedStruct("Ping", {})); const encodeJson = Schema.encodeUnknownSync(Schema.fromJsonString(Schema.Unknown)); const encodeServerConfig = Schema.encodeSync(ServerConfig); const encodeServerConfigStreamEvent = Schema.encodeSync(ServerConfigStreamEvent); @@ -823,14 +823,14 @@ describe("RpcSessionFactory", () => { retryNow: Effect.void, } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); const cache = Persistence.EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.none()), + loadShell: () => Effect.succeedNone, saveShell: () => Effect.void, - loadThread: () => Effect.succeed(Option.none()), + loadThread: () => Effect.succeedNone, saveThread: () => Effect.void, removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: () => Effect.void, diff --git a/packages/client-runtime/src/rpc/session.ts b/packages/client-runtime/src/rpc/session.ts index 252bf0242236ddde29911865b4bb6a8db9e6518e..93bb2ff2f7d3a37d017457e8a5d60656e63c1a02 100644 --- a/packages/client-runtime/src/rpc/session.ts +++ b/packages/client-runtime/src/rpc/session.ts @@ -291,13 +291,13 @@ export const make = Effect.fn("RpcSessionFactory.make")(function* ( Effect.flatMap(() => Effect.fail(configSubscriptionEndedError)), ), ).pipe( - Effect.flatMap((config) => - config.environment.environmentId === connection.environmentId - ? Effect.succeed(config) - : environmentMismatchError({ - expected: connection.environmentId, - actual: config.environment.environmentId, - }), + Effect.filterOrElse( + (config) => config.environment.environmentId === connection.environmentId, + (config) => + environmentMismatchError({ + expected: connection.environmentId, + actual: config.environment.environmentId, + }), ), Effect.withSpan("environment.initialSync"), ); diff --git a/packages/client-runtime/src/state/pullRequestRouting.ts b/packages/client-runtime/src/state/pullRequestRouting.ts index af625bbad56e01013e9affde4f7d9de909cf8972..999c6133f5dac3b64fa2e7ab7da1de58815cf742 100644 --- a/packages/client-runtime/src/state/pullRequestRouting.ts +++ b/packages/client-runtime/src/state/pullRequestRouting.ts @@ -339,15 +339,11 @@ export function createPullRequestRouter() { } const operation = run(id); return yield* (reads.has(tag) ? operation.pipe(readTimeout(id)) : operation).pipe( - Effect.catch((error) => { - if ( - (reads.has(tag) || rejectedBeforeDispatch(error)) && - index + 1 < candidates.length - ) { - return visit(index + 1); - } - return Effect.fail(error); - }), + Effect.catchIf( + (error) => + (reads.has(tag) || rejectedBeforeDispatch(error)) && index + 1 < candidates.length, + () => visit(index + 1), + ), Effect.map((result) => typeof result === "object" && result !== null && "projectId" in result ? { diff --git a/packages/client-runtime/src/state/pullRequests.test.ts b/packages/client-runtime/src/state/pullRequests.test.ts index 75e6b38f4a2e5ec20184853fa3e75a1773e4530b..b6d8c1733f1cce1a9cdf65523a3dfc4d8ef832a8 100644 --- a/packages/client-runtime/src/state/pullRequests.test.ts +++ b/packages/client-runtime/src/state/pullRequests.test.ts @@ -1171,7 +1171,7 @@ it.effect("updates cached labels after successful edits without rereading the ho if (failDetail) { yield* detailRefreshStarted.open; yield* releaseDetailRefresh.await; - return yield* Effect.fail(new MutationRefused()); + return yield* new MutationRefused(); } return { title: "keep this title", labels: [existing] }; }), diff --git a/packages/client-runtime/src/state/server.test.ts b/packages/client-runtime/src/state/server.test.ts index 878f8c902f91ed984ccc18c93496d6ed28557c93..7443850b1b5e5a665ffd151cd0ce43fe8e8402e7 100644 --- a/packages/client-runtime/src/state/server.test.ts +++ b/packages/client-runtime/src/state/server.test.ts @@ -778,14 +778,14 @@ describe("server state projection", () => { } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); const savedConfigs = yield* Queue.unbounded(); const cache = Persistence.EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.none()), + loadShell: () => Effect.succeedNone, saveShell: () => Effect.void, - loadThread: () => Effect.succeed(Option.none()), + loadThread: () => Effect.succeedNone, saveThread: () => Effect.void, removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.some(CONFIG)), + loadServerConfig: () => Effect.succeedSome(CONFIG), saveServerConfig: (_environmentId, config) => Queue.offer(savedConfigs, config), - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: () => Effect.void, @@ -839,14 +839,14 @@ describe("server state projection", () => { } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); const savedConfigs = yield* Queue.unbounded(); const cache = Persistence.EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.none()), + loadShell: () => Effect.succeedNone, saveShell: () => Effect.void, - loadThread: () => Effect.succeed(Option.none()), + loadThread: () => Effect.succeedNone, saveThread: () => Effect.void, removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.some(CONFIG)), + loadServerConfig: () => Effect.succeedSome(CONFIG), saveServerConfig: (_environmentId, config) => Queue.offer(savedConfigs, config), - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: () => Effect.void, diff --git a/packages/client-runtime/src/state/serverUsage.test.ts b/packages/client-runtime/src/state/serverUsage.test.ts index 9cf2667d8bc202f53fa5d6077c1800031c6b4aa9..2df10f7cf8606d40e2857b70317fa05b7e962aa3 100644 --- a/packages/client-runtime/src/state/serverUsage.test.ts +++ b/packages/client-runtime/src/state/serverUsage.test.ts @@ -125,14 +125,14 @@ const makeHarness = Effect.fn("ServerUsageTest.makeHarness")(function* ( Stream.provideService(stream, EnvironmentSupervisor, supervisor), } as EnvironmentRegistry["Service"]); const cache = EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.none()), + loadShell: () => Effect.succeedNone, saveShell: () => Effect.void, - loadThread: () => Effect.succeed(Option.none()), + loadThread: () => Effect.succeedNone, saveThread: () => Effect.void, removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: () => Effect.void, diff --git a/packages/client-runtime/src/state/session.ts b/packages/client-runtime/src/state/session.ts index 9a0a9dfe9a5fc9c8e8c8652996115d3a5836a5f0..5787bd0e60b774a54d0f853f8845c9c4e2a0402e 100644 --- a/packages/client-runtime/src/state/session.ts +++ b/packages/client-runtime/src/state/session.ts @@ -20,7 +20,7 @@ function initialConfigOption( initialConfig: Effect.Effect, ): Effect.Effect> { return initialConfig.pipe( - Effect.map(Option.some), + Effect.asSome, Effect.catch((error) => Effect.logWarning("Could not load the initial environment configuration.").pipe( Effect.annotateLogs({ ...safeErrorLogAttributes(error) }), diff --git a/packages/client-runtime/src/state/shell-sync.test.ts b/packages/client-runtime/src/state/shell-sync.test.ts index 0d933c39f8ba0ca2293609bb5f56c79ca0eae5ec..9483d86de0fff801a08b0c629bd7f16ba03ac1b7 100644 --- a/packages/client-runtime/src/state/shell-sync.test.ts +++ b/packages/client-runtime/src/state/shell-sync.test.ts @@ -80,14 +80,14 @@ describe("environment shell synchronization", () => { retryNow: Effect.void, } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); const cache = Persistence.EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.none()), + loadShell: () => Effect.succeedNone, saveShell: () => Effect.never, - loadThread: () => Effect.succeed(Option.none()), + loadThread: () => Effect.succeedNone, saveThread: () => Effect.void, removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: () => Effect.void, @@ -96,7 +96,7 @@ describe("environment shell synchronization", () => { // Cold cache with no HTTP snapshot available → falls back to the // socket-embedded snapshot. const snapshotLoader = ShellSnapshotLoader.of({ - load: () => Effect.succeed(Option.none()), + load: () => Effect.succeedNone, }); const shellState = yield* makeEnvironmentShellState().pipe( Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor), @@ -176,14 +176,14 @@ describe("environment shell synchronization", () => { retryNow: Effect.void, } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); const cache = Persistence.EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.none()), + loadShell: () => Effect.succeedNone, saveShell: () => Effect.void, - loadThread: () => Effect.succeed(Option.none()), + loadThread: () => Effect.succeedNone, saveThread: () => Effect.void, removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: () => Effect.void, @@ -194,7 +194,7 @@ describe("environment shell synchronization", () => { Effect.provideService(Persistence.EnvironmentCacheStore, cache), Effect.provideService( ShellSnapshotLoader, - ShellSnapshotLoader.of({ load: () => Effect.succeed(Option.none()) }), + ShellSnapshotLoader.of({ load: () => Effect.succeedNone }), ), ); yield* SubscriptionRef.set(supervisorState, { @@ -284,14 +284,14 @@ describe("environment shell synchronization", () => { retryNow: Effect.void, } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); const cache = Persistence.EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.some(cachedSnapshot)), + loadShell: () => Effect.succeedSome(cachedSnapshot), saveShell: () => Effect.void, - loadThread: () => Effect.succeed(Option.none()), + loadThread: () => Effect.succeedNone, saveThread: () => Effect.void, removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: () => Effect.void, @@ -364,14 +364,14 @@ describe("environment shell synchronization", () => { retryNow: Effect.void, } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); const cache = Persistence.EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.some(LIVE_SHELL_SNAPSHOT)), + loadShell: () => Effect.succeedSome(LIVE_SHELL_SNAPSHOT), saveShell: () => Effect.void, - loadThread: () => Effect.succeed(Option.none()), + loadThread: () => Effect.succeedNone, saveThread: () => Effect.void, removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: () => Effect.void, diff --git a/packages/client-runtime/src/state/sourceControl.test.ts b/packages/client-runtime/src/state/sourceControl.test.ts index 33c566bf82b6c53e79d45507b762eeeb70c0cfdf..ff9824106a7802c9a124ff7c523164278f42636b 100644 --- a/packages/client-runtime/src/state/sourceControl.test.ts +++ b/packages/client-runtime/src/state/sourceControl.test.ts @@ -101,14 +101,14 @@ describe("source control environment atoms", () => { } as unknown as EnvironmentRegistry.EnvironmentRegistry["Service"]); const removed = new Array(); const cache = Persistence.EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.none()), + loadShell: () => Effect.succeedNone, saveShell: () => Effect.void, - loadThread: () => Effect.succeed(Option.none()), + loadThread: () => Effect.succeedNone, saveThread: () => Effect.void, removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: (environmentId, cwd) => Effect.sync(() => { diff --git a/packages/client-runtime/src/state/threads-atoms.test.ts b/packages/client-runtime/src/state/threads-atoms.test.ts index 6fd61c8d1cfde2e794b5ddfd2e8502361a27ef14..34e6857041d3a561fb5fb0654ed3e53b2ac6c930 100644 --- a/packages/client-runtime/src/state/threads-atoms.test.ts +++ b/packages/client-runtime/src/state/threads-atoms.test.ts @@ -200,7 +200,7 @@ const makeHarness = Effect.fn("TestThreadAtoms.makeHarness")(function* (options? Layer.succeed( EnvironmentCacheStore, EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.none()), + loadShell: () => Effect.succeedNone, saveShell: () => Effect.void, loadThread: () => Effect.sync(() => { @@ -209,9 +209,9 @@ const makeHarness = Effect.fn("TestThreadAtoms.makeHarness")(function* (options? }), saveThread: () => Effect.void, removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: () => Effect.void, diff --git a/packages/client-runtime/src/state/threads-pagination.test.ts b/packages/client-runtime/src/state/threads-pagination.test.ts index a473deb1f97b3fa2ba32aed2475953702c336d80..4dbc3329994eddc886cf7adc12f8356e5c799a1d 100644 --- a/packages/client-runtime/src/state/threads-pagination.test.ts +++ b/packages/client-runtime/src/state/threads-pagination.test.ts @@ -197,16 +197,16 @@ const makeHarness = Effect.fn("TestThreadPagination.makeHarness")(function* (opt retryNow: Effect.void, } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); const cache = Persistence.EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.none()), + loadShell: () => Effect.succeedNone, saveShell: () => Effect.void, loadThread: () => Effect.succeed(options?.cached !== undefined ? Option.some(options.cached) : Option.none()), saveThread: (_environmentId, thread) => Ref.update(savedThreads, (current) => [...current, thread]), removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: () => Effect.void, diff --git a/packages/client-runtime/src/state/threads-sync.test.ts b/packages/client-runtime/src/state/threads-sync.test.ts index c41651576dad363ebb973fa8d6355278d6f81a87..848357ec2bef5dc37f59f7786056cbf830e30cd7 100644 --- a/packages/client-runtime/src/state/threads-sync.test.ts +++ b/packages/client-runtime/src/state/threads-sync.test.ts @@ -216,7 +216,7 @@ const makeHarness = Effect.fn("TestEnvironmentThreads.makeHarness")(function* (o retryNow: Ref.update(retryCount, (count) => count + 1), } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]); const cache = Persistence.EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.none()), + loadShell: () => Effect.succeedNone, saveShell: () => Effect.void, loadThread: (_environmentId, threadId) => options?.loadCached ?? @@ -234,9 +234,9 @@ const makeHarness = Effect.fn("TestEnvironmentThreads.makeHarness")(function* (o ), removeThread: (_environmentId, threadId) => Ref.update(removedThreads, (current) => [...current, threadId]), - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: () => Effect.void, diff --git a/packages/client-runtime/src/state/vcs.test.ts b/packages/client-runtime/src/state/vcs.test.ts index d7a4692fc3170f4aff71e247f9ab343f8c6b7ac8..7b11c48f3ca53f8f67b8158951cd42571568f945 100644 --- a/packages/client-runtime/src/state/vcs.test.ts +++ b/packages/client-runtime/src/state/vcs.test.ts @@ -98,12 +98,12 @@ function cacheWithRefs( overrides: Partial = {}, ) { return Persistence.EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.none()), + loadShell: () => Effect.succeedNone, saveShell: () => Effect.void, - loadThread: () => Effect.succeed(Option.none()), + loadThread: () => Effect.succeedNone, saveThread: () => Effect.void, removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, loadVcsRefs: () => Effect.succeed(refs), saveVcsRefs: () => Effect.void, diff --git a/packages/client-runtime/src/state/vcsAction.test.ts b/packages/client-runtime/src/state/vcsAction.test.ts index 24aa314b1cfffecd33f6bcfeb9d78899b9324806..2d840ba71ff4bc368625dd2637853d947f8a1623 100644 --- a/packages/client-runtime/src/state/vcsAction.test.ts +++ b/packages/client-runtime/src/state/vcsAction.test.ts @@ -99,14 +99,14 @@ function progress(event: T): T { function cacheStore(onClearVcsRefs: (environmentId: EnvironmentId) => void) { return Persistence.EnvironmentCacheStore.of({ - loadShell: () => Effect.succeed(Option.none()), + loadShell: () => Effect.succeedNone, saveShell: () => Effect.void, - loadThread: () => Effect.succeed(Option.none()), + loadThread: () => Effect.succeedNone, saveThread: () => Effect.void, removeThread: () => Effect.void, - loadServerConfig: () => Effect.succeed(Option.none()), + loadServerConfig: () => Effect.succeedNone, saveServerConfig: () => Effect.void, - loadVcsRefs: () => Effect.succeed(Option.none()), + loadVcsRefs: () => Effect.succeedNone, saveVcsRefs: () => Effect.void, removeVcsRefs: () => Effect.void, clearVcsRefs: (environmentId) => Effect.sync(() => onClearVcsRefs(environmentId)), diff --git a/packages/contracts/src/pullRequest.test.ts b/packages/contracts/src/pullRequest.test.ts index 9ea74eb78736f5f89e0f5d894a51a0c0c2265478..3d979e852abc58c88749a11386273d71622e6f81 100644 --- a/packages/contracts/src/pullRequest.test.ts +++ b/packages/contracts/src/pullRequest.test.ts @@ -92,7 +92,7 @@ describe("PullRequestListResult", () => { it("round-trips through the JSON codec the RPC serializes with", () => { const codec = Schema.toCodecJson(PullRequestListResult); - const decoded = Schema.decodeUnknownSync(codec)(Schema.encodeUnknownSync(codec)(LIST_RESULT)); + const decoded = Schema.decodeSync(codec)(Schema.encodeUnknownSync(codec)(LIST_RESULT)); expect(decoded).toStrictEqual(LIST_RESULT); }); diff --git a/packages/contracts/src/rpc.test.ts b/packages/contracts/src/rpc.test.ts index 6a9adebd85d6eb6f47a678237ccb61a477b82a67..bdc3448b85636c22ed080bd46ca61ade43399c7c 100644 --- a/packages/contracts/src/rpc.test.ts +++ b/packages/contracts/src/rpc.test.ts @@ -13,19 +13,19 @@ import { WsSubscribeServerConfigRpc } from "./rpc.ts"; describe("subscribeServerConfig payload compatibility", () => { it("is accepted by a server whose schema predates the field", () => { const oldServerPayload = Schema.Struct({}); - const decoded = Schema.decodeUnknownExit(oldServerPayload)({ environmentThemes: true }); + const decoded = Schema.decodeExit(oldServerPayload)({ environmentThemes: true }); expect(Exit.isSuccess(decoded)).toBe(true); }); it("is carried by a server that declares it", () => { - const decoded = Schema.decodeUnknownSync(WsSubscribeServerConfigRpc.payloadSchema)({ + const decoded = Schema.decodeSync(WsSubscribeServerConfigRpc.payloadSchema)({ environmentThemes: true, }); expect(decoded).toEqual({ environmentThemes: true }); }); it("stays optional, so a client that never sends it still subscribes", () => { - const decoded = Schema.decodeUnknownSync(WsSubscribeServerConfigRpc.payloadSchema)({}); + const decoded = Schema.decodeSync(WsSubscribeServerConfigRpc.payloadSchema)({}); expect(decoded).toEqual({}); }); }); diff --git a/packages/contracts/src/settings.ts b/packages/contracts/src/settings.ts index 3e301201910cb8879d0efca58b340c3c5180a9fb..94bc277349233dc2851ebc8a252dfdfaefa81988 100644 --- a/packages/contracts/src/settings.ts +++ b/packages/contracts/src/settings.ts @@ -1082,9 +1082,7 @@ export type StorageCleanupSettings = typeof StorageCleanupSettings.Type; export const ServerSettings = Schema.Struct({ worktreeCleanup: WorktreeCleanup.pipe(Schema.withDecodingDefault(Effect.succeed(null))), storageCleanup: StorageCleanupSettings.pipe( - Schema.withDecodingDefault( - Effect.succeed(Schema.decodeUnknownSync(StorageCleanupSettings)({})), - ), + Schema.withDecodingDefault(Effect.succeed(Schema.decodeSync(StorageCleanupSettings)({}))), ), // How assistant text reaches clients during a turn. Deliberately a fresh // key (was `enableLegacyTokenStreaming`, before that diff --git a/packages/effect-acp/src/client.ts b/packages/effect-acp/src/client.ts index 27203c20c6277709db6c81e3eb3f956570cedb11..c3f4e7644fc9c3e1d971e67277ff764b71bd0403 100644 --- a/packages/effect-acp/src/client.ts +++ b/packages/effect-acp/src/client.ts @@ -346,11 +346,9 @@ export const make = Effect.fn("effect-acp/AcpClient.make")(function* ( registration: BufferedNotificationHandler, notification: A, ) => - Effect.forEach( - registration.handlers, - (handler) => handler(notification).pipe(Effect.catch(() => Effect.void)), - { discard: true }, - ); + Effect.forEach(registration.handlers, (handler) => handler(notification).pipe(Effect.ignore), { + discard: true, + }); const flushBufferedNotifications = (registration: BufferedNotificationHandler) => Effect.suspend(() => { diff --git a/packages/effect-acp/src/protocol.ts b/packages/effect-acp/src/protocol.ts index f74a8ea29e37594966ff0682ab4c432bf048cfbd..a84c2f5a57b4e542989a6b0d72e805b52211788d 100644 --- a/packages/effect-acp/src/protocol.ts +++ b/packages/effect-acp/src/protocol.ts @@ -216,7 +216,7 @@ export const makeAcpPatchedProtocol = Effect.fn("makeAcpPatchedProtocol")(functi Queue.offer(notificationQueue, notification).pipe( Effect.andThen( options.onNotification - ? options.onNotification(notification).pipe(Effect.catch(() => Effect.void)) + ? options.onNotification(notification).pipe(Effect.ignore) : Effect.void, ), Effect.asVoid, diff --git a/packages/effect-codex-app-server/src/_internal/shared.ts b/packages/effect-codex-app-server/src/_internal/shared.ts index 8bcb59467d3da5d70b3aa5e4a39e46c73213bf97..334dc4a43bd347e0c012a61eebce6a4770f70a75 100644 --- a/packages/effect-codex-app-server/src/_internal/shared.ts +++ b/packages/effect-codex-app-server/src/_internal/shared.ts @@ -45,7 +45,7 @@ export const encodeOptionalPayload = ( ): Effect.Effect => { if (!schema) { if (payload === undefined) { - return Effect.sync(() => undefined); + return Effect.undefined; } return Effect.fail( CodexError.CodexAppServerRequestError.unexpectedPayload(method, "encode-payload", payload), diff --git a/packages/effect-codex-app-server/src/client.ts b/packages/effect-codex-app-server/src/client.ts index 78d7196261394b5589485682e0b442564bae31a9..d4ff21b8c59e86ee6c5db0a3c5b2c9aec2a3c315 100644 --- a/packages/effect-codex-app-server/src/client.ts +++ b/packages/effect-codex-app-server/src/client.ts @@ -153,14 +153,12 @@ const make = Effect.fn("effect-codex-app-server/CodexAppServerClient.make")(func Effect.flatMap((decoded) => Effect.forEach(handlers, (handler) => handler(decoded), { discard: true }), ), - Effect.catch(() => Effect.void), + Effect.ignore, ); } return unknownNotificationHandler - ? unknownNotificationHandler(notification.method, notification.params).pipe( - Effect.catch(() => Effect.void), - ) + ? unknownNotificationHandler(notification.method, notification.params).pipe(Effect.ignore) : Effect.void; }; diff --git a/packages/effect-codex-app-server/src/schema.test.ts b/packages/effect-codex-app-server/src/schema.test.ts index 8e2160fdd51796a1ce162b594fda4bc3aab4eec7..b12b33e0285409056bda21a5e7680ea6a422bc4c 100644 --- a/packages/effect-codex-app-server/src/schema.test.ts +++ b/packages/effect-codex-app-server/src/schema.test.ts @@ -29,7 +29,7 @@ it("keeps async questions in live notifications and thread history", () => { CodexSchema.V2ThreadReadResponse__ThreadItem, CodexSchema.V2ThreadResumeResponse__ThreadItem, ]) { - assert.deepEqual(Schema.decodeUnknownSync(schema)(item), item); + assert.deepEqual(Schema.decodeSync(schema)(item), item); } }); diff --git a/packages/shared/src/httpReadiness.ts b/packages/shared/src/httpReadiness.ts index 5aad9d488aaebec19fc6c4bbfd28e7acc30df62f..4aeda1c4757a0a0720f66eb456ae9fa42a95785c 100644 --- a/packages/shared/src/httpReadiness.ts +++ b/packages/shared/src/httpReadiness.ts @@ -106,17 +106,13 @@ export const waitForHttpReady = Effect.fn("shared.httpReadiness.waitForHttpReady Effect.timeoutOption(Duration.millis(probeTimeoutMs)), Effect.mapError((cause) => fail(cause)), ); - return yield* Option.match(responseOption, { - onSome: Effect.succeed, - onNone: () => - Effect.fail( - fail({ - kind: "probe-timeout", - attempt, - probeTimeoutMs, - }), - ), - }); + return yield* Effect.fromOption(responseOption, () => + fail({ + kind: "probe-timeout", + attempt, + probeTimeoutMs, + }), + ); }).pipe( Effect.mapError((cause) => (isMadeError(cause) ? cause : fail(cause))), Effect.tapError((cause) => diff --git a/packages/shared/src/relayClient.ts b/packages/shared/src/relayClient.ts index b33078fc5548d9aafcd942e769203d343cf47d92..4d1ce988086d28b177cf083007e45f84897a8903 100644 --- a/packages/shared/src/relayClient.ts +++ b/packages/shared/src/relayClient.ts @@ -331,9 +331,7 @@ export const makeCloudflaredRelayClient = Effect.fn("cloudflared.make")(function for (let attempt = 0; attempt < INSTALL_LOCK_RETRY_COUNT; attempt += 1) { const acquired = yield* fileSystem.writeFileString(lockPath, "", { flag: "wx" }).pipe( Effect.as(true), - Effect.catch((error) => - isAlreadyExists(error) ? Effect.succeed(false) : Effect.fail(error), - ), + Effect.catchIf(isAlreadyExists, () => Effect.succeed(false)), ); if (acquired) return; @@ -445,16 +443,16 @@ export const makeCloudflaredRelayClient = Effect.fn("cloudflared.make")(function }).pipe( Effect.scoped, Effect.ensuring(fileSystem.remove(lockPath, { force: true }).pipe(Effect.ignore)), - Effect.catch((cause) => - cause instanceof RelayClientInstallError - ? Effect.fail(cause) - : Effect.fail( - new RelayClientInstallError({ - reason: "write_failed", - message: "Could not install the relay client.", - cause, - }), - ), + Effect.catchIf( + (cause) => !(cause instanceof RelayClientInstallError), + (cause) => + Effect.fail( + new RelayClientInstallError({ + reason: "write_failed", + message: "Could not install the relay client.", + cause, + }), + ), ), ); }); diff --git a/packages/shared/src/relayTracing.ts b/packages/shared/src/relayTracing.ts index 1259984ea3c9255b1e0cc6a4f3a175b983706f9c..907773bc15a59c4e05aa59719bd8b7eec871fcee 100644 --- a/packages/shared/src/relayTracing.ts +++ b/packages/shared/src/relayTracing.ts @@ -151,6 +151,6 @@ export function makeRelayClientTracingLayer( return Layer.effect( RelayClientTracer, - Tracer.Tracer.pipe(Effect.map(nonInterferingTracer), Effect.map(Option.some)), + Tracer.Tracer.pipe(Effect.map(nonInterferingTracer), Effect.asSome), ).pipe(Layer.provide(tracerLayer)); } diff --git a/packages/shared/src/schemaJson.ts b/packages/shared/src/schemaJson.ts index 076ff67b62dfe24ed64c2bed0adcbc113417f644..c811c1da1081d59bf313e36147afd1e818e3e331 100644 --- a/packages/shared/src/schemaJson.ts +++ b/packages/shared/src/schemaJson.ts @@ -1,7 +1,6 @@ import * as Cause from "effect/Cause"; import * as Effect from "effect/Effect"; import * as Exit from "effect/Exit"; -import * as Option from "effect/Option"; import * as Result from "effect/Result"; import * as Schema from "effect/Schema"; import * as SchemaGetter from "effect/SchemaGetter"; @@ -187,7 +186,7 @@ const parseLenientJsonGetter = SchemaGetter.onSome((input: string) => { ); return decodeJsonString(stripped).pipe( - Effect.map(Option.some), + Effect.asSome, Effect.mapError((error) => error.issue), ); }); diff --git a/tsconfig.base.json b/tsconfig.base.json index a84f620ba861562620ced1d40cc3372c0a24ba97..7897a92f557f4e5d5c7ecf4c31926fa6546bc181 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -50,7 +50,8 @@ "globalTimers": "error", "globalTimersInEffect": "error", "globalFetch": "error", - "globalFetchInEffect": "error" + "globalFetchInEffect": "error", + "schemaNumber": "off" } } ]