feat(http-recorder): prepare public beta release (#31018)

This commit is contained in:
Kit Langton
2026-06-06 03:01:26 +00:00
committed by GitHub
parent ba57718b05
commit 54f4974546
36 changed files with 2277 additions and 713 deletions
+26 -12
View File
@@ -1,7 +1,7 @@
import { Effect, Ref, Scope } from "effect"
import type * as CassetteService from "./cassette"
import type { CassetteNotFoundError } from "./cassette"
import type { Interaction } from "./schema"
import { Effect, Scope, SynchronizedRef } from "effect"
import type * as CassetteService from "./cassette.js"
import type { CassetteNotFoundError } from "./cassette.js"
import type { Interaction } from "./schema.js"
const isCI = () => {
const value = process.env.CI
@@ -18,9 +18,9 @@ export const resolveAutoMode = (
})
export interface ReplayState<T> {
readonly load: Effect.Effect<ReadonlyArray<T>, CassetteNotFoundError>
readonly cursor: Effect.Effect<number>
readonly advance: Effect.Effect<void>
readonly claim: <E>(
validate: (interaction: T | undefined, index: number, interactions: ReadonlyArray<T>) => Effect.Effect<void, E>,
) => Effect.Effect<{ readonly interaction: T; readonly index: number }, CassetteNotFoundError | E>
}
export const makeReplayState = <T>(
@@ -30,19 +30,33 @@ export const makeReplayState = <T>(
): Effect.Effect<ReplayState<T>, never, Scope.Scope> =>
Effect.gen(function* () {
const load = yield* Effect.cached(cassette.read(name).pipe(Effect.map(project)))
const position = yield* Ref.make(0)
const position = yield* SynchronizedRef.make(0)
yield* Effect.addFinalizer(() =>
Effect.gen(function* () {
const used = yield* Ref.get(position)
if (used === 0) return
const used = yield* SynchronizedRef.get(position)
if (used === 0) return yield* Effect.void
const interactions = yield* load.pipe(Effect.orDie)
if (used < interactions.length)
yield* Effect.die(
return yield* Effect.die(
new Error(`Unused recorded interactions in ${name}: used ${used} of ${interactions.length}`),
)
return yield* Effect.void
}),
)
return { load, cursor: Ref.get(position), advance: Ref.update(position, (n) => n + 1) }
return {
claim: (validate) =>
Effect.flatMap(load, (interactions) =>
SynchronizedRef.modifyEffect(position, (index) =>
Effect.gen(function* () {
const interaction = interactions[index]
yield* validate(interaction, index, interactions)
if (interaction === undefined)
return yield* Effect.die("Replay validation accepted a missing interaction")
return [{ interaction, index }, index + 1] as const
}),
),
),
}
})