Skip to content

ExecutionPlan.ts

Describes ordered fallback steps for running effects or streams.

An ExecutionPlan contains one or more steps. Each step provides a Context or Layer, and may also define attempt limits, retry schedules, or predicates that decide whether to keep trying. The runtime tries steps in order until the workflow succeeds or the plan is exhausted. This module also supports merging plans and reading metadata for the active step and attempt.

Since v3.16.0



Combines multiple execution plans by concatenating their steps in order.

When to use

Use to combine separately defined fallback plans into one ordered plan before applying it to an effect or stream.

Details

The resulting plan tries every step from the first plan, then every step from the next plan, and so on.

See

  • make for building a plan from individual steps instead of combining existing plans

Signature

declare const merge: <const Plans extends NonEmptyReadonlyArray<ExecutionPlan<any>>>(
...plans: Plans
) => ExecutionPlan<{
provides: make.PlanProvides<Plans>
input: make.PlanInput<Plans>
error: Plans[number] extends ExecutionPlan<infer T> ? T["error"] : never
requirements: Plans[number] extends ExecutionPlan<infer T> ? T["requirements"] : never
}>

Source

Since v3.16.0

Create an ExecutionPlan, which can be used with Effect.withExecutionPlan or Stream.withExecutionPlan, allowing you to provide different resources for each step of execution until the effect succeeds or the plan is exhausted.

Example (Creating an execution plan)

import { Effect, ExecutionPlan, Schedule } from "effect"
import type { Layer } from "effect"
import type { LanguageModel } from "effect/unstable/ai"
declare const layerBad: Layer.Layer<LanguageModel.LanguageModel>
declare const layerGood: Layer.Layer<LanguageModel.LanguageModel>
const ThePlan = ExecutionPlan.make(
{
// First try with the bad layer 2 times with a 3 second delay between attempts
provide: layerBad,
attempts: 2,
schedule: Schedule.spaced(3000)
},
// Then try with the bad layer 3 times with a 1 second delay between attempts
{
provide: layerBad,
attempts: 3,
schedule: Schedule.spaced(1000)
},
// Finally try with the good layer.
//
// If `attempts` is omitted, the plan will only attempt once, unless a schedule is provided.
{
provide: layerGood
}
)
declare const effect: Effect.Effect<void, never, LanguageModel.LanguageModel>
const withPlan: Effect.Effect<void> = Effect.withExecutionPlan(effect, ThePlan)

Signature

declare const make: <const Steps extends NonEmptyReadonlyArray<make.Step>>(
...steps: Steps & { [K in keyof Steps]: make.Step }
) => ExecutionPlan<{
provides: make.StepProvides<Steps>
input: make.StepInput<Steps>
error:
| (Steps[number]["provide"] extends Context.Context<infer _P> | Layer.Layer<infer _P, infer E, infer _R>
? E
: never)
| (Steps[number]["while"] extends (input: infer _I) => Effect.Effect<infer _A, infer _E, infer _R> ? _E : never)
requirements:
| (Steps[number]["provide"] extends Layer.Layer<infer _A, infer _E, infer R> ? R : never)
| (Steps[number]["while"] extends (input: infer _I) => Effect.Effect<infer _A, infer _E, infer R> ? R : never)
| (Steps[number]["schedule"] extends Schedule.Schedule<infer _O, infer _I, infer R> ? R : never)
}>

Source

Since v3.16.0

Returns true if a value is an ExecutionPlan by checking for the ExecutionPlan.TypeId marker.

When to use

Use when accepting an unknown value and you need to narrow it to an ExecutionPlan before reading plan fields or passing it to plan-consuming APIs.

Gotchas

This is a structural marker check; it does not validate the marker value or the shape of the plan steps.

See

  • make for constructing execution plans that satisfy this guard
  • TypeId for the runtime marker checked by this guard

Signature

declare const isExecutionPlan: (u: unknown) => u is ExecutionPlan<any>

Source

Since v3.16.0

Context reference containing metadata for the currently running execution-plan attempt.

When to use

Use to read the active plan step and attempt while code is running under an execution plan.

Signature

declare const CurrentMetadata: Context.Reference<Metadata>

Source

Since v4.0.0

Metadata describing the currently running execution-plan attempt.

Details

attempt is the current 1-based attempt number, and stepIndex is the 0-based index of the plan step currently being evaluated.

Signature

export interface Metadata {
readonly attempt: number
readonly stepIndex: number
}

Source

Since v4.0.0

Base type-level configuration carried by an ExecutionPlan.

Details

provides tracks services supplied by plan steps, input tracks the error input consumed by schedules and while predicates, error tracks failures from plan layers or predicates, and requirements tracks services needed to build or run the plan.

Signature

type ConfigBase = {
provides: any
input: any
error: any
requirements: any
}

Source

Since v4.0.0

A ExecutionPlan can be used with Effect.withExecutionPlan or Stream.withExecutionPlan, allowing you to provide different resources for each step of execution until the effect succeeds or the plan is exhausted.

Example (Defining fallback execution steps)

import { Effect, ExecutionPlan, Schedule } from "effect"
import type { Layer } from "effect"
import type { LanguageModel } from "effect/unstable/ai"
declare const layerBad: Layer.Layer<LanguageModel.LanguageModel>
declare const layerGood: Layer.Layer<LanguageModel.LanguageModel>
const ThePlan = ExecutionPlan.make(
{
// First try with the bad layer 2 times with a 3 second delay between attempts
provide: layerBad,
attempts: 2,
schedule: Schedule.spaced(3000)
},
// Then try with the bad layer 3 times with a 1 second delay between attempts
{
provide: layerBad,
attempts: 3,
schedule: Schedule.spaced(1000)
},
// Finally try with the good layer.
//
// If `attempts` is omitted, the plan will only attempt once, unless a schedule is provided.
{
provide: layerGood
}
)
declare const effect: Effect.Effect<void, never, LanguageModel.LanguageModel>
const withPlan: Effect.Effect<void> = Effect.withExecutionPlan(effect, ThePlan)

Signature

export interface ExecutionPlan<
Config extends {
provides: any
input: any
error: any
requirements: any
}
> extends Pipeable {
readonly [TypeId]: TypeId
readonly steps: NonEmptyReadonlyArray<{
readonly provide:
| Context.Context<Config["provides"]>
| Layer.Layer<Config["provides"], Config["error"], Config["requirements"]>
readonly attempts?: number | undefined
readonly while?:
| ((input: Config["input"]) => Effect.Effect<boolean, Config["error"], Config["requirements"]>)
| undefined
readonly schedule?: Schedule.Schedule<any, Config["input"], Config["requirements"]> | undefined
}>
/**
* Returns an equivalent `ExecutionPlan` with the requirements satisfied, using the current context.
*/
readonly captureRequirements: Effect.Effect<
ExecutionPlan<{
provides: Config["provides"]
input: Config["input"]
error: Config["error"]
requirements: never
}>,
never,
Config["requirements"]
>
}

Source

Since v3.16.0

Runtime type identifier attached to ExecutionPlan values and used by isExecutionPlan.

Signature

declare const TypeId: "~effect/ExecutionPlan"

Source

Since v3.16.0

String literal type used as the runtime type identifier for ExecutionPlan values.

Signature

type TypeId = "~effect/ExecutionPlan"

Source

Since v3.16.0

Namespace containing type helpers used by ExecutionPlan.make.

Source

Since v3.16.0

Input shape for a single execution-plan step.

Details

Each step provides a Context or Layer and may limit attempts, add a while predicate for retry decisions, or attach a Schedule for retry timing.

Signature

type Step = {
readonly provide: Context.Context<any> | Context.Context<never> | Layer.Any
readonly attempts?: number | undefined
readonly while?: ((input: any) => boolean | Effect.Effect<boolean, any, any>) | undefined
readonly schedule?: Schedule.Schedule<any, any, any> | undefined
}

Source

Since v3.16.0

Computes the intersection of services provided by a list of execution-plan steps.

Signature

type StepProvides<Steps, Out> = Steps extends readonly [infer Step, ...infer Rest]
? StepProvides<
Rest,
Out &
(Step extends { readonly provide: Context.Context<infer P> | Layer.Layer<infer P, infer _E, infer _R> }
? P
: unknown)
>
: Out

Source

Since v3.16.1

Computes the intersection of services provided by a list of execution plans.

Signature

type PlanProvides<Plans, Out> = Plans extends readonly [infer Plan, ...infer Rest]
? PlanProvides<Rest, Out & (Plan extends ExecutionPlan<infer T> ? T["provides"] : unknown)>
: Out

Source

Since v3.16.1

Computes the input type consumed by the while predicates and schedules in a list of execution-plan steps.

Signature

type StepInput<Steps, Out> = Steps extends readonly [infer Step, ...infer Rest]
? StepInput<
Rest,
Out &
((Step extends { readonly while: (input: infer I) => infer _ } ? I : unknown) &
(Step extends { readonly schedule: Schedule.Schedule<infer _O, infer I, infer _R> } ? I : unknown))
>
: Out

Source

Since v3.16.0

Computes the combined input type consumed by a list of execution plans.

Signature

type PlanInput<Plans, Out> = Plans extends readonly [infer Plan, ...infer Rest]
? PlanInput<Rest, Out & (Plan extends ExecutionPlan<infer T> ? T["input"] : unknown)>
: Out

Source

Since v3.16.0