feat(http-recorder): prepare public beta release (#31018)
This commit is contained in:
@@ -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
|
||||
}),
|
||||
),
|
||||
),
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user