mirror of
https://github.com/anomalyco/opencode.git
synced 2026-10-25 19:20:00 +00:00
43 lines
1.5 KiB
TypeScript
43 lines
1.5 KiB
TypeScript
import { Effect } from "effect"
|
|
import { TestClock } from "effect/testing"
|
|
|
|
// Defers on a real macrotask so pubsub delivery, fiber hops, and filesystem
|
|
// work can complete between TestClock advances.
|
|
const settle = Effect.promise(() => new Promise((resolve) => setTimeout(resolve, 1)))
|
|
|
|
// One advance step: let pending real work finish, then fire due TestClock timers.
|
|
const tick = Effect.gen(function* () {
|
|
yield* settle
|
|
yield* TestClock.adjust("500 millis")
|
|
})
|
|
|
|
/**
|
|
* Drives every pending TestClock timer to completion: stream debounces register
|
|
* their sleeps from separate fiber hops that a single adjust can miss, so the
|
|
* loop alternates real-macrotask settles with adjusts until the condition
|
|
* holds. Extra adjusts are harmless when nothing is pending.
|
|
*
|
|
* Bounded by real time, not a step count: the condition can also wait on real
|
|
* work such as a plugin import, which needs many more steps on a loaded CI
|
|
* runner than locally.
|
|
*/
|
|
export const advance = Effect.fnUntraced(function* (condition: () => boolean) {
|
|
const deadline = performance.now() + 15_000
|
|
while (performance.now() < deadline) {
|
|
if (condition()) return
|
|
yield* tick
|
|
}
|
|
if (condition()) return
|
|
return yield* Effect.die(new Error("condition never became true within 15s"))
|
|
})
|
|
|
|
/**
|
|
* Advances far enough that any pending debounced work would have completed,
|
|
* so a no-op assertion afterwards is meaningful.
|
|
*/
|
|
export const drain = Effect.gen(function* () {
|
|
for (let round = 0; round < 4; round++) {
|
|
yield* tick
|
|
}
|
|
yield* settle
|
|
})
|