diff options
| author | Luke Parker <[email protected]> | 2026-03-19 09:54:01 +1000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-03-19 09:54:01 +1000 |
| commit | 5d2f8d77f964d7be6b9c9e0602f0eb6bb68993b9 (patch) | |
| tree | 4677a8eb6e6d093579489bcd71c9dd812aca0da1 /packages | |
| parent | 81be544981d04cc48b2aa5193c1b2b7096ec8bc4 (diff) | |
| download | opencode-5d2f8d77f964d7be6b9c9e0602f0eb6bb68993b9.tar.gz opencode-5d2f8d77f964d7be6b9c9e0602f0eb6bb68993b9.zip | |
fix: restore recent test regressions and upgrade effect beta (#18158)
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/app/e2e/prompt/prompt-multiline.spec.ts | 18 | ||||
| -rw-r--r-- | packages/app/package.json | 2 | ||||
| -rw-r--r-- | packages/app/src/pages/layout.tsx | 1 | ||||
| -rw-r--r-- | packages/desktop-electron/package.json | 2 | ||||
| -rw-r--r-- | packages/opencode/package.json | 2 | ||||
| -rw-r--r-- | packages/opencode/src/file/watcher.ts | 9 | ||||
| -rw-r--r-- | packages/opencode/src/util/process.ts | 1 | ||||
| -rw-r--r-- | packages/opencode/test/file/watcher.test.ts | 54 | ||||
| -rw-r--r-- | packages/opencode/test/util/process.test.ts | 16 |
9 files changed, 67 insertions, 38 deletions
diff --git a/packages/app/e2e/prompt/prompt-multiline.spec.ts b/packages/app/e2e/prompt/prompt-multiline.spec.ts index 216aa3fda..3584773bb 100644 --- a/packages/app/e2e/prompt/prompt-multiline.spec.ts +++ b/packages/app/e2e/prompt/prompt-multiline.spec.ts @@ -7,12 +7,18 @@ test("shift+enter inserts a newline without submitting", async ({ page, gotoSess await expect(page).toHaveURL(/\/session\/?$/) const prompt = page.locator(promptSelector) - await prompt.click() - await page.keyboard.type("line one") - await page.keyboard.press("Shift+Enter") - await page.keyboard.type("line two") + await prompt.focus() + await expect(prompt).toBeFocused() + + await prompt.pressSequentially("line one") + await expect(prompt).toBeFocused() + + await prompt.press("Shift+Enter") + await expect(page).toHaveURL(/\/session\/?$/) + await expect(prompt).toBeFocused() + + await prompt.pressSequentially("line two") await expect(page).toHaveURL(/\/session\/?$/) - await expect(prompt).toContainText("line one") - await expect(prompt).toContainText("line two") + await expect.poll(() => prompt.evaluate((el) => el.innerText)).toBe("line one\nline two") }) diff --git a/packages/app/package.json b/packages/app/package.json index 878cfb8e3..545d31309 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -56,7 +56,7 @@ "@solidjs/router": "catalog:", "@thisbeyond/solid-dnd": "0.7.5", "diff": "catalog:", - "effect": "4.0.0-beta.31", + "effect": "catalog:", "fuzzysort": "catalog:", "ghostty-web": "github:anomalyco/ghostty-web#main", "luxon": "catalog:", diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index c84c7272d..4c3a00dfb 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -566,6 +566,7 @@ export default function Layout(props: ParentProps) { const [autoselecting] = createResource(async () => { await ready.promise await layout.ready.promise + if (!untrack(() => state.autoselect)) return const list = layout.projects.list() const last = server.projects.last() diff --git a/packages/desktop-electron/package.json b/packages/desktop-electron/package.json index 2af3196e1..bcb80d9e6 100644 --- a/packages/desktop-electron/package.json +++ b/packages/desktop-electron/package.json @@ -30,7 +30,7 @@ "@solid-primitives/storage": "catalog:", "@solidjs/meta": "catalog:", "@solidjs/router": "0.15.4", - "effect": "4.0.0-beta.31", + "effect": "catalog:", "electron-log": "^5", "electron-store": "^10", "electron-updater": "^6", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index a6caca8ad..049573e3e 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -82,7 +82,6 @@ "@ai-sdk/xai": "2.0.51", "@aws-sdk/credential-providers": "3.993.0", "@clack/prompts": "1.0.0-alpha.1", - "@effect/platform-node": "4.0.0-beta.31", "@gitlab/gitlab-ai-provider": "3.6.0", "@gitlab/opencode-gitlab-auth": "1.3.3", "@hono/standard-validator": "0.1.5", @@ -98,6 +97,7 @@ "@openrouter/ai-sdk-provider": "1.5.4", "@opentui/core": "0.1.87", "@opentui/solid": "0.1.87", + "@effect/platform-node": "catalog:", "@parcel/watcher": "2.5.1", "@pierre/diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts index ad683303c..16ab3c6d3 100644 --- a/packages/opencode/src/file/watcher.ts +++ b/packages/opencode/src/file/watcher.ts @@ -51,6 +51,13 @@ export namespace FileWatcher { if (process.platform === "linux") return "inotify" } + function protecteds(dir: string) { + return Protected.paths().filter((item) => { + const rel = path.relative(dir, item) + return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel) + }) + } + export const hasNativeBinding = () => !!watcher() export class Service extends ServiceMap.Service<Service, {}>()("@opencode/FileWatcher") {} @@ -105,7 +112,7 @@ export namespace FileWatcher { const cfgIgnores = cfg.watcher?.ignore ?? [] if (yield* Flag.OPENCODE_EXPERIMENTAL_FILEWATCHER) { - yield* subscribe(instance.directory, [...FileIgnore.PATTERNS, ...cfgIgnores, ...Protected.paths()]) + yield* subscribe(instance.directory, [...FileIgnore.PATTERNS, ...cfgIgnores, ...protecteds(instance.directory)]) } if (instance.project.vcs === "git") { diff --git a/packages/opencode/src/util/process.ts b/packages/opencode/src/util/process.ts index 8cf1f5b9f..7e6be0e20 100644 --- a/packages/opencode/src/util/process.ts +++ b/packages/opencode/src/util/process.ts @@ -98,6 +98,7 @@ export namespace Process { reject(error) }) }) + void exited.catch(() => undefined) if (opts.abort) { opts.abort.addEventListener("abort", abort, { once: true }) diff --git a/packages/opencode/test/file/watcher.test.ts b/packages/opencode/test/file/watcher.test.ts index 8a3d30d31..2cd27643e 100644 --- a/packages/opencode/test/file/watcher.test.ts +++ b/packages/opencode/test/file/watcher.test.ts @@ -2,7 +2,7 @@ import { $ } from "bun" import { afterEach, describe, expect, test } from "bun:test" import fs from "fs/promises" import path from "path" -import { Deferred, Effect, Fiber, Option } from "effect" +import { Deferred, Effect, Option } from "effect" import { tmpdir } from "../fixture/fixture" import { watcherConfigLayer, withServices } from "../fixture/instance" import { FileWatcher } from "../../src/file/watcher" @@ -25,6 +25,7 @@ function withWatcher<E>(directory: string, body: Effect.Effect<void, E>) { directory, FileWatcher.layer, async (rt) => { + await rt.runPromise(FileWatcher.Service.use(() => Effect.void)) await Effect.runPromise(ready(directory)) await Effect.runPromise(body) }, @@ -54,24 +55,29 @@ function listen(directory: string, check: (evt: WatcherEvent) => boolean, hit: ( } function wait(directory: string, check: (evt: WatcherEvent) => boolean) { - return Effect.callback<WatcherEvent>((resume) => { - const cleanup = listen(directory, check, (evt) => { - cleanup() - resume(Effect.succeed(evt)) + return Effect.gen(function* () { + const deferred = yield* Deferred.make<WatcherEvent>() + const cleanup = yield* Effect.sync(() => { + let off = () => {} + off = listen(directory, check, (evt) => { + off() + Deferred.doneUnsafe(deferred, Effect.succeed(evt)) + }) + return off }) - return Effect.sync(cleanup) - }).pipe(Effect.timeout("5 seconds")) + return { cleanup, deferred } + }) } function nextUpdate<E>(directory: string, check: (evt: WatcherEvent) => boolean, trigger: Effect.Effect<void, E>) { return Effect.acquireUseRelease( - wait(directory, check).pipe(Effect.forkChild({ startImmediately: true })), - (fiber) => + wait(directory, check), + ({ deferred }) => Effect.gen(function* () { yield* trigger - return yield* Fiber.join(fiber) + return yield* Deferred.await(deferred).pipe(Effect.timeout("5 seconds")) }), - Fiber.interrupt, + ({ cleanup }) => Effect.sync(cleanup), ) } @@ -82,23 +88,15 @@ function noUpdate<E>( trigger: Effect.Effect<void, E>, ms = 500, ) { - return Effect.gen(function* () { - const deferred = yield* Deferred.make<WatcherEvent>() - - yield* Effect.acquireUseRelease( - Effect.sync(() => - listen(directory, check, (evt) => { - Effect.runSync(Deferred.succeed(deferred, evt)) - }), - ), - () => - Effect.gen(function* () { - yield* trigger - expect(yield* Deferred.await(deferred).pipe(Effect.timeoutOption(`${ms} millis`))).toEqual(Option.none()) - }), - (cleanup) => Effect.sync(cleanup), - ) - }) + return Effect.acquireUseRelease( + wait(directory, check), + ({ deferred }) => + Effect.gen(function* () { + yield* trigger + expect(yield* Deferred.await(deferred).pipe(Effect.timeoutOption(`${ms} millis`))).toEqual(Option.none()) + }), + ({ cleanup }) => Effect.sync(cleanup), + ) } function ready(directory: string) { diff --git a/packages/opencode/test/util/process.test.ts b/packages/opencode/test/util/process.test.ts index b9bc50f9b..1d08cba6b 100644 --- a/packages/opencode/test/util/process.test.ts +++ b/packages/opencode/test/util/process.test.ts @@ -109,4 +109,20 @@ describe("util.process", () => { expect(await proc.exited).toBe(0) }) + + test("rejects missing commands without leaking unhandled errors", async () => { + await using tmp = await tmpdir() + const cmd = path.join(tmp.path, "missing" + (process.platform === "win32" ? ".cmd" : "")) + const err = await Process.spawn([cmd], { + stdin: "pipe", + stdout: "pipe", + stderr: "pipe", + }).exited.catch((err) => err) + + expect(err).toBeInstanceOf(Error) + if (!(err instanceof Error)) throw err + expect(err).toMatchObject({ + code: "ENOENT", + }) + }) }) |
