@jagreehal/workflow 1.12.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1197 -20
- package/dist/duration.cjs +2 -0
- package/dist/duration.cjs.map +1 -0
- package/dist/duration.d.cts +246 -0
- package/dist/duration.d.ts +246 -0
- package/dist/duration.js +2 -0
- package/dist/duration.js.map +1 -0
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/match.cjs +2 -0
- package/dist/match.cjs.map +1 -0
- package/dist/match.d.cts +216 -0
- package/dist/match.d.ts +216 -0
- package/dist/match.js +2 -0
- package/dist/match.js.map +1 -0
- package/dist/schedule.cjs +2 -0
- package/dist/schedule.cjs.map +1 -0
- package/dist/schedule.d.cts +387 -0
- package/dist/schedule.d.ts +387 -0
- package/dist/schedule.js +2 -0
- package/dist/schedule.js.map +1 -0
- package/docs/api.md +30 -0
- package/docs/coming-from-neverthrow.md +103 -10
- package/docs/effect-features-to-port.md +210 -0
- package/docs/match-examples.test.ts +558 -0
- package/docs/match.md +417 -0
- package/docs/policies-examples.test.ts +750 -0
- package/docs/policies.md +508 -0
- package/docs/resource-management-examples.test.ts +729 -0
- package/docs/resource-management.md +509 -0
- package/docs/schedule-examples.test.ts +736 -0
- package/docs/schedule.md +467 -0
- package/docs/tagged-error-examples.test.ts +494 -0
- package/docs/tagged-error.md +730 -0
- package/docs/visualization-examples.test.ts +663 -0
- package/docs/visualization.md +395 -0
- package/docs/visualize-examples.md +1 -1
- package/package.json +17 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schedule.ts","../src/duration.ts"],"sourcesContent":["/**\n * @jagreehal/workflow/schedule\n *\n * Composable scheduling primitives inspired by Effect's Schedule module.\n * Build complex retry and polling strategies by composing simple building blocks.\n *\n * @example\n * ```typescript\n * // \"Exponential backoff with jitter, max 5 attempts, then poll every minute\"\n * const schedule = Schedule.exponential(Duration.millis(100))\n * .pipe(Schedule.jittered(0.2))\n * .pipe(Schedule.upTo(5))\n * .pipe(Schedule.andThen(Schedule.spaced(Duration.minutes(1))))\n *\n * // Use with workflows\n * const result = await step(fetchData, { schedule })\n * ```\n */\n\nimport { Duration, millis, toMillis, multiply, add, type Duration as DurationType } from \"./duration\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * The state maintained by a schedule during execution.\n */\nexport interface ScheduleState {\n /** Number of iterations completed */\n readonly iterations: number;\n /** Total elapsed time since schedule started */\n readonly elapsed: DurationType;\n /** The last input received (if any) */\n readonly lastInput?: unknown;\n /** The last output produced (if any) */\n readonly lastOutput?: unknown;\n /** Combinator-specific metadata (used by andThen, union, intersect, etc.) */\n readonly meta?: unknown;\n}\n\n/**\n * Decision returned by a schedule step.\n * Combinators can optionally provide `nextState` to thread custom state through execution.\n */\nexport type ScheduleDecision<Out> =\n | { readonly _tag: \"Continue\"; readonly delay: DurationType; readonly output: Out; readonly nextState?: ScheduleState }\n | { readonly _tag: \"Done\" };\n\n/**\n * A Schedule defines a recurring pattern with delays and outputs.\n *\n * @typeParam In - The input type (from retry attempts, etc.)\n * @typeParam Out - The output type (often the delay or iteration count)\n */\nexport interface Schedule<In = unknown, Out = unknown> {\n readonly _tag: \"Schedule\";\n /** Compute the next step given input and current state */\n readonly step: (input: In, state: ScheduleState) => ScheduleDecision<Out>;\n /** Initial state for this schedule */\n readonly initial: ScheduleState;\n}\n\n// =============================================================================\n// Internal Helpers\n// =============================================================================\n\nconst initialState: ScheduleState = {\n iterations: 0,\n elapsed: Duration.zero,\n lastInput: undefined,\n lastOutput: undefined,\n};\n\nfunction continueWith<Out>(delay: DurationType, output: Out): ScheduleDecision<Out> {\n return { _tag: \"Continue\", delay, output };\n}\n\nfunction done<Out>(): ScheduleDecision<Out> {\n return { _tag: \"Done\" };\n}\n\nfunction updateState(state: ScheduleState, delay: DurationType, input: unknown, output: unknown): ScheduleState {\n return {\n iterations: state.iterations + 1,\n elapsed: add(state.elapsed, delay),\n lastInput: input,\n lastOutput: output,\n };\n}\n\n// =============================================================================\n// Base Schedules\n// =============================================================================\n\n/**\n * A schedule that repeats forever with no delay.\n */\nexport function forever<In = unknown>(): Schedule<In, number> {\n return {\n _tag: \"Schedule\",\n initial: initialState,\n step: (_input, state) => continueWith(Duration.zero, state.iterations),\n };\n}\n\n/**\n * A schedule that repeats exactly n times.\n *\n * @example\n * ```typescript\n * const thrice = Schedule.recurs(3)\n * ```\n */\nexport function recurs<In = unknown>(n: number): Schedule<In, number> {\n return {\n _tag: \"Schedule\",\n initial: initialState,\n step: (_input, state) =>\n state.iterations < n ? continueWith(Duration.zero, state.iterations) : done(),\n };\n}\n\n/**\n * A schedule that repeats once.\n */\nexport function once<In = unknown>(): Schedule<In, void> {\n return {\n _tag: \"Schedule\",\n initial: initialState,\n step: (_input, state) =>\n state.iterations === 0 ? continueWith(Duration.zero, undefined) : done(),\n };\n}\n\n/**\n * A schedule that always stops immediately.\n */\nexport function stop<In = unknown>(): Schedule<In, never> {\n return {\n _tag: \"Schedule\",\n initial: initialState,\n step: () => done(),\n };\n}\n\n// =============================================================================\n// Delay-Based Schedules\n// =============================================================================\n\n/**\n * A schedule that waits a fixed duration between each iteration.\n *\n * @example\n * ```typescript\n * const pollEvery5s = Schedule.spaced(Duration.seconds(5))\n * ```\n */\nexport function spaced<In = unknown>(delay: DurationType): Schedule<In, number> {\n return {\n _tag: \"Schedule\",\n initial: initialState,\n step: (_input, state) => continueWith(delay, state.iterations),\n };\n}\n\n/**\n * Alias for spaced - a fixed interval schedule.\n */\nexport const fixed = spaced;\n\n/**\n * A schedule with exponentially increasing delays.\n * delay(n) = base * factor^n\n *\n * @example\n * ```typescript\n * // 100ms, 200ms, 400ms, 800ms, ...\n * const exponential = Schedule.exponential(Duration.millis(100))\n *\n * // With custom factor: 100ms, 300ms, 900ms, ...\n * const triple = Schedule.exponential(Duration.millis(100), 3)\n * ```\n */\nexport function exponential<In = unknown>(\n base: DurationType,\n factor: number = 2\n): Schedule<In, DurationType> {\n return {\n _tag: \"Schedule\",\n initial: initialState,\n step: (_input, state) => {\n const delay = multiply(base, Math.pow(factor, state.iterations));\n return continueWith(delay, delay);\n },\n };\n}\n\n/**\n * A schedule with linearly increasing delays.\n * delay(n) = base * (n + 1)\n *\n * @example\n * ```typescript\n * // 100ms, 200ms, 300ms, 400ms, ...\n * const linear = Schedule.linear(Duration.millis(100))\n * ```\n */\nexport function linear<In = unknown>(base: DurationType): Schedule<In, DurationType> {\n return {\n _tag: \"Schedule\",\n initial: initialState,\n step: (_input, state) => {\n const delay = multiply(base, state.iterations + 1);\n return continueWith(delay, delay);\n },\n };\n}\n\n/**\n * A schedule with fibonacci-sequence delays.\n * delay(n) follows fibonacci: base, base, 2*base, 3*base, 5*base, ...\n *\n * @example\n * ```typescript\n * // 100ms, 100ms, 200ms, 300ms, 500ms, 800ms, ...\n * const fib = Schedule.fibonacci(Duration.millis(100))\n * ```\n */\nexport function fibonacci<In = unknown>(base: DurationType): Schedule<In, DurationType> {\n // Precompute fibonacci numbers for reasonable iteration counts\n const fibNumbers: number[] = [1, 1];\n for (let i = 2; i < 50; i++) {\n fibNumbers[i] = fibNumbers[i - 1] + fibNumbers[i - 2];\n }\n\n return {\n _tag: \"Schedule\",\n initial: initialState,\n step: (_input, state) => {\n const fibIndex = Math.min(state.iterations, fibNumbers.length - 1);\n const delay = multiply(base, fibNumbers[fibIndex]);\n return continueWith(delay, delay);\n },\n };\n}\n\n// =============================================================================\n// Combinators: Limits\n// =============================================================================\n\n/**\n * Limit a schedule to at most n iterations.\n *\n * @example\n * ```typescript\n * // Exponential backoff, max 5 attempts\n * const limited = Schedule.exponential(Duration.millis(100))\n * .pipe(Schedule.upTo(5))\n * ```\n */\nexport function upTo<In, Out>(\n n: number\n): (schedule: Schedule<In, Out>) => Schedule<In, Out> {\n return (schedule) => ({\n _tag: \"Schedule\",\n initial: schedule.initial,\n step: (input, state) => {\n if (state.iterations >= n) return done();\n return schedule.step(input, state);\n },\n });\n}\n\n/**\n * Limit a schedule by total elapsed time.\n *\n * @example\n * ```typescript\n * // Retry for at most 30 seconds\n * const timeLimited = Schedule.spaced(Duration.seconds(1))\n * .pipe(Schedule.upToElapsed(Duration.seconds(30)))\n * ```\n */\nexport function upToElapsed<In, Out>(\n maxElapsed: DurationType\n): (schedule: Schedule<In, Out>) => Schedule<In, Out> {\n return (schedule) => ({\n _tag: \"Schedule\",\n initial: schedule.initial,\n step: (input, state) => {\n if (toMillis(state.elapsed) >= toMillis(maxElapsed)) return done();\n return schedule.step(input, state);\n },\n });\n}\n\n/**\n * Cap the maximum delay of a schedule.\n *\n * @example\n * ```typescript\n * // Exponential, but never more than 30 seconds\n * const capped = Schedule.exponential(Duration.millis(100))\n * .pipe(Schedule.maxDelay(Duration.seconds(30)))\n * ```\n */\nexport function maxDelay<In, Out>(\n max: DurationType\n): (schedule: Schedule<In, Out>) => Schedule<In, Out> {\n return (schedule) => ({\n _tag: \"Schedule\",\n initial: schedule.initial,\n step: (input, state) => {\n const decision = schedule.step(input, state);\n if (decision._tag === \"Done\") return decision;\n const capped = toMillis(decision.delay) > toMillis(max) ? max : decision.delay;\n return continueWith(capped, decision.output);\n },\n });\n}\n\n/**\n * Set a minimum delay for a schedule.\n *\n * @example\n * ```typescript\n * const atLeast1s = schedule.pipe(Schedule.minDelay(Duration.seconds(1)))\n * ```\n */\nexport function minDelay<In, Out>(\n min: DurationType\n): (schedule: Schedule<In, Out>) => Schedule<In, Out> {\n return (schedule) => ({\n _tag: \"Schedule\",\n initial: schedule.initial,\n step: (input, state) => {\n const decision = schedule.step(input, state);\n if (decision._tag === \"Done\") return decision;\n const floored = toMillis(decision.delay) < toMillis(min) ? min : decision.delay;\n return continueWith(floored, decision.output);\n },\n });\n}\n\n// =============================================================================\n// Combinators: Conditions\n// =============================================================================\n\n/**\n * Continue while a predicate on the input returns true.\n *\n * @example\n * ```typescript\n * // Retry while the error is transient\n * const retryTransient = Schedule.exponential(Duration.millis(100))\n * .pipe(Schedule.whileInput((err: Error) => err.message.includes(\"ECONNRESET\")))\n * ```\n */\nexport function whileInput<In, Out>(\n predicate: (input: In) => boolean\n): (schedule: Schedule<In, Out>) => Schedule<In, Out> {\n return (schedule) => ({\n _tag: \"Schedule\",\n initial: schedule.initial,\n step: (input, state) => {\n if (!predicate(input)) return done();\n return schedule.step(input, state);\n },\n });\n}\n\n/**\n * Continue while a predicate on the output returns true.\n *\n * @example\n * ```typescript\n * // Poll until status is \"ready\"\n * const untilReady = Schedule.spaced(Duration.seconds(1))\n * .pipe(Schedule.whileOutput((status: string) => status !== \"ready\"))\n * ```\n */\nexport function whileOutput<In, Out>(\n predicate: (output: Out) => boolean\n): (schedule: Schedule<In, Out>) => Schedule<In, Out> {\n return (schedule) => ({\n _tag: \"Schedule\",\n initial: schedule.initial,\n step: (input, state) => {\n const decision = schedule.step(input, state);\n if (decision._tag === \"Done\") return decision;\n if (!predicate(decision.output)) return done();\n return decision;\n },\n });\n}\n\n/**\n * Continue until a predicate on the input returns true.\n * Opposite of whileInput.\n */\nexport function untilInput<In, Out>(\n predicate: (input: In) => boolean\n): (schedule: Schedule<In, Out>) => Schedule<In, Out> {\n return whileInput((input: In) => !predicate(input));\n}\n\n/**\n * Continue until a predicate on the output returns true.\n * Opposite of whileOutput.\n */\nexport function untilOutput<In, Out>(\n predicate: (output: Out) => boolean\n): (schedule: Schedule<In, Out>) => Schedule<In, Out> {\n return whileOutput((output: Out) => !predicate(output));\n}\n\n// =============================================================================\n// Combinators: Jitter\n// =============================================================================\n\n/**\n * Add random jitter to delays to prevent thundering herd.\n *\n * @param factor - Jitter factor (0.0 to 1.0). Delay will be multiplied by (1 - factor) to (1 + factor).\n *\n * @example\n * ```typescript\n * // ±20% jitter\n * const jittered = Schedule.exponential(Duration.millis(100))\n * .pipe(Schedule.jittered(0.2))\n * ```\n */\nexport function jittered<In, Out>(\n factor: number = 0.2\n): (schedule: Schedule<In, Out>) => Schedule<In, Out> {\n return (schedule) => ({\n _tag: \"Schedule\",\n initial: schedule.initial,\n step: (input, state) => {\n const decision = schedule.step(input, state);\n if (decision._tag === \"Done\") return decision;\n\n // Random factor between (1 - factor) and (1 + factor)\n const jitterMultiplier = 1 - factor + Math.random() * factor * 2;\n const jitteredDelay = millis(toMillis(decision.delay) * jitterMultiplier);\n\n return continueWith(jitteredDelay, decision.output);\n },\n });\n}\n\n/**\n * Add a fixed random delay to each iteration.\n *\n * @example\n * ```typescript\n * // Add 0-500ms random delay\n * const randomized = schedule.pipe(Schedule.addDelay(Duration.millis(500)))\n * ```\n */\nexport function addDelay<In, Out>(\n maxExtra: DurationType\n): (schedule: Schedule<In, Out>) => Schedule<In, Out> {\n return (schedule) => ({\n _tag: \"Schedule\",\n initial: schedule.initial,\n step: (input, state) => {\n const decision = schedule.step(input, state);\n if (decision._tag === \"Done\") return decision;\n\n const extra = millis(Math.random() * toMillis(maxExtra));\n const newDelay = add(decision.delay, extra);\n\n return continueWith(newDelay, decision.output);\n },\n });\n}\n\n// =============================================================================\n// Combinators: Composition\n// =============================================================================\n\n/** Internal metadata for andThen combinator */\ninterface AndThenMeta {\n readonly inSecondPhase: boolean;\n readonly firstState: ScheduleState; // Preserves first schedule's state (including its meta)\n readonly secondState: ScheduleState;\n}\n\n/**\n * Chain two schedules: run the first until done, then run the second.\n * The second schedule starts with fresh state when it begins.\n *\n * @example\n * ```typescript\n * // 5 exponential retries, then poll every minute\n * const strategy = Schedule.exponential(Duration.millis(100))\n * .pipe(Schedule.upTo(5))\n * .pipe(Schedule.andThen(Schedule.spaced(Duration.minutes(1))))\n * ```\n */\nexport function andThen<In, Out1, Out2>(\n second: Schedule<In, Out2>\n): (first: Schedule<In, Out1>) => Schedule<In, Out1 | Out2> {\n return (first) => {\n const initialMeta: AndThenMeta = {\n inSecondPhase: false,\n firstState: first.initial, // Preserve first's initial state (including its meta)\n secondState: second.initial,\n };\n\n return {\n _tag: \"Schedule\",\n initial: { ...initialState, meta: initialMeta },\n step: (input, state) => {\n const meta = (state.meta as AndThenMeta) ?? initialMeta;\n\n if (!meta.inSecondPhase) {\n // Pass first's isolated state (not outer state) to preserve its meta\n const decision = first.step(input, meta.firstState);\n if (decision._tag === \"Continue\") {\n // Update first's state - use nextState if provided, otherwise compute it\n const nextFirstState = decision.nextState ?? updateState(meta.firstState, decision.delay, input, decision.output);\n const nextState: ScheduleState = {\n ...updateState(state, decision.delay, input, decision.output),\n meta: { ...meta, firstState: nextFirstState },\n };\n return { _tag: \"Continue\", delay: decision.delay, output: decision.output as Out1 | Out2, nextState };\n }\n // First schedule done, switch to second with fresh state\n const secondDecision = second.step(input, second.initial);\n if (secondDecision._tag === \"Done\") return done();\n\n const nextSecondState = secondDecision.nextState ?? updateState(second.initial, secondDecision.delay, input, secondDecision.output);\n const nextState: ScheduleState = {\n ...updateState(state, secondDecision.delay, input, secondDecision.output),\n meta: { inSecondPhase: true, firstState: meta.firstState, secondState: nextSecondState },\n };\n return { _tag: \"Continue\", delay: secondDecision.delay, output: secondDecision.output as Out1 | Out2, nextState };\n }\n\n // In second phase - use secondState from meta\n const decision = second.step(input, meta.secondState);\n if (decision._tag === \"Done\") return done();\n\n // Update second's state\n const nextSecondState = decision.nextState ?? updateState(meta.secondState, decision.delay, input, decision.output);\n const nextState: ScheduleState = {\n ...updateState(state, decision.delay, input, decision.output),\n meta: { ...meta, secondState: nextSecondState },\n };\n\n return { _tag: \"Continue\", delay: decision.delay, output: decision.output as Out1 | Out2, nextState };\n },\n };\n };\n}\n\n/** Internal metadata for union/intersect combinators */\ninterface ParallelMeta {\n readonly firstState: ScheduleState;\n readonly secondState: ScheduleState;\n}\n\n/**\n * Run two schedules in parallel, using the shorter delay.\n * Both schedules advance their state independently.\n *\n * @example\n * ```typescript\n * // Use whichever delay is shorter\n * const union = Schedule.union(\n * Schedule.exponential(Duration.millis(100)),\n * Schedule.spaced(Duration.seconds(1))\n * )\n * ```\n */\nexport function union<In, Out1, Out2>(\n first: Schedule<In, Out1>,\n second: Schedule<In, Out2>\n): Schedule<In, [Out1, Out2]> {\n const initialMeta: ParallelMeta = {\n firstState: first.initial,\n secondState: second.initial,\n };\n\n return {\n _tag: \"Schedule\",\n initial: { ...initialState, meta: initialMeta },\n step: (input, state) => {\n const meta = (state.meta as ParallelMeta) ?? initialMeta;\n\n const firstDecision = first.step(input, meta.firstState);\n const secondDecision = second.step(input, meta.secondState);\n\n if (firstDecision._tag === \"Done\" && secondDecision._tag === \"Done\") {\n return done();\n }\n\n // Compute next child states - respect nextState if child provides custom state threading\n const nextFirstState = firstDecision._tag === \"Continue\"\n ? (firstDecision.nextState ?? updateState(meta.firstState, firstDecision.delay, input, firstDecision.output))\n : meta.firstState;\n const nextSecondState = secondDecision._tag === \"Continue\"\n ? (secondDecision.nextState ?? updateState(meta.secondState, secondDecision.delay, input, secondDecision.output))\n : meta.secondState;\n\n // Take the shorter delay\n const firstDelay = firstDecision._tag === \"Continue\" ? toMillis(firstDecision.delay) : Infinity;\n const secondDelay = secondDecision._tag === \"Continue\" ? toMillis(secondDecision.delay) : Infinity;\n\n const delay = millis(Math.min(firstDelay, secondDelay));\n const output: [Out1, Out2] = [\n firstDecision._tag === \"Continue\" ? firstDecision.output : (undefined as Out1),\n secondDecision._tag === \"Continue\" ? secondDecision.output : (undefined as Out2),\n ];\n\n const nextState: ScheduleState = {\n ...updateState(state, delay, input, output),\n meta: { firstState: nextFirstState, secondState: nextSecondState },\n };\n\n return { _tag: \"Continue\", delay, output, nextState };\n },\n };\n}\n\n/**\n * Run two schedules in parallel, using the longer delay.\n * Both schedules advance their state independently. Stops when either schedule stops.\n *\n * @example\n * ```typescript\n * // Use whichever delay is longer\n * const intersect = Schedule.intersect(\n * Schedule.exponential(Duration.millis(100)),\n * Schedule.spaced(Duration.seconds(1))\n * )\n * ```\n */\nexport function intersect<In, Out1, Out2>(\n first: Schedule<In, Out1>,\n second: Schedule<In, Out2>\n): Schedule<In, [Out1, Out2]> {\n const initialMeta: ParallelMeta = {\n firstState: first.initial,\n secondState: second.initial,\n };\n\n return {\n _tag: \"Schedule\",\n initial: { ...initialState, meta: initialMeta },\n step: (input, state) => {\n const meta = (state.meta as ParallelMeta) ?? initialMeta;\n\n const firstDecision = first.step(input, meta.firstState);\n const secondDecision = second.step(input, meta.secondState);\n\n // Both must continue\n if (firstDecision._tag === \"Done\" || secondDecision._tag === \"Done\") {\n return done();\n }\n\n // Compute next child states - respect nextState if child provides custom state threading\n const nextFirstState = firstDecision.nextState ?? updateState(meta.firstState, firstDecision.delay, input, firstDecision.output);\n const nextSecondState = secondDecision.nextState ?? updateState(meta.secondState, secondDecision.delay, input, secondDecision.output);\n\n // Take the longer delay\n const delay = millis(Math.max(toMillis(firstDecision.delay), toMillis(secondDecision.delay)));\n const output: [Out1, Out2] = [firstDecision.output, secondDecision.output];\n\n const nextState: ScheduleState = {\n ...updateState(state, delay, input, output),\n meta: { firstState: nextFirstState, secondState: nextSecondState },\n };\n\n return { _tag: \"Continue\", delay, output, nextState };\n },\n };\n}\n\n// =============================================================================\n// Combinators: Transformations\n// =============================================================================\n\n/**\n * Transform the output of a schedule.\n *\n * @example\n * ```typescript\n * const withIteration = Schedule.spaced(Duration.seconds(1))\n * .pipe(Schedule.map((n) => ({ attempt: n + 1 })))\n * ```\n */\nexport function map<In, Out, NewOut>(\n fn: (output: Out) => NewOut\n): (schedule: Schedule<In, Out>) => Schedule<In, NewOut> {\n return (schedule) => ({\n _tag: \"Schedule\",\n initial: schedule.initial,\n step: (input, state) => {\n const decision = schedule.step(input, state);\n if (decision._tag === \"Done\") return done();\n return continueWith(decision.delay, fn(decision.output));\n },\n });\n}\n\n/**\n * Perform a side effect on each iteration.\n *\n * @example\n * ```typescript\n * const logged = Schedule.exponential(Duration.millis(100))\n * .pipe(Schedule.tap((delay) => console.log(`Waiting ${delay}ms`)))\n * ```\n */\nexport function tap<In, Out>(\n fn: (output: Out, state: ScheduleState) => void\n): (schedule: Schedule<In, Out>) => Schedule<In, Out> {\n return (schedule) => ({\n _tag: \"Schedule\",\n initial: schedule.initial,\n step: (input, state) => {\n const decision = schedule.step(input, state);\n if (decision._tag === \"Continue\") {\n fn(decision.output, state);\n }\n return decision;\n },\n });\n}\n\n/**\n * Modify the delay of a schedule.\n *\n * @example\n * ```typescript\n * const doubled = schedule.pipe(Schedule.modifyDelay(d => Duration.multiply(d, 2)))\n * ```\n */\nexport function modifyDelay<In, Out>(\n fn: (delay: DurationType, output: Out) => DurationType\n): (schedule: Schedule<In, Out>) => Schedule<In, Out> {\n return (schedule) => ({\n _tag: \"Schedule\",\n initial: schedule.initial,\n step: (input, state) => {\n const decision = schedule.step(input, state);\n if (decision._tag === \"Done\") return decision;\n return continueWith(fn(decision.delay, decision.output), decision.output);\n },\n });\n}\n\n// =============================================================================\n// Running Schedules\n// =============================================================================\n\n/**\n * Create an iterator for running a schedule step-by-step.\n *\n * @example\n * ```typescript\n * const runner = Schedule.run(mySchedule)\n *\n * while (true) {\n * const next = runner.next(lastResult)\n * if (next.done) break\n * await sleep(next.value.delay)\n * }\n * ```\n */\nexport function run<In, Out>(\n schedule: Schedule<In, Out>\n): { next: (input: In) => { done: false; value: { delay: DurationType; output: Out } } | { done: true } } {\n let state = schedule.initial;\n\n return {\n next(input: In) {\n const decision = schedule.step(input, state);\n if (decision._tag === \"Done\") {\n return { done: true as const };\n }\n\n // Use combinator-provided nextState if available, otherwise compute standard update\n state = decision.nextState ?? updateState(state, decision.delay, input, decision.output);\n return {\n done: false as const,\n value: { delay: decision.delay, output: decision.output },\n };\n },\n };\n}\n\n/**\n * Get all delays from a schedule (for testing/visualization).\n * Runs the schedule with undefined input until it completes or reaches maxIterations.\n */\nexport function delays<Out>(\n schedule: Schedule<undefined, Out>,\n maxIterations: number = 100\n): DurationType[] {\n const result: DurationType[] = [];\n const runner = run(schedule);\n\n for (let i = 0; i < maxIterations; i++) {\n const next = runner.next(undefined);\n if (next.done) break;\n result.push(next.value.delay);\n }\n\n return result;\n}\n\n// =============================================================================\n// Pipe Support\n// =============================================================================\n\n/**\n * Type guard to check if a value is a Schedule.\n */\nfunction isSchedule(value: unknown): value is Schedule<unknown, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"_tag\" in value &&\n (value as { _tag: unknown })._tag === \"Schedule\"\n );\n}\n\n/**\n * A schedule with pipe support for method chaining.\n */\ntype PipedSchedule<In, Out> = Schedule<In, Out> & {\n pipe: <NewOut>(fn: (s: Schedule<In, Out>) => Schedule<In, NewOut>) => PipedSchedule<In, NewOut>;\n} & {\n pipe: <R>(fn: (s: Schedule<In, Out>) => R) => R;\n};\n\n/**\n * Create a schedule with pipe support for method chaining.\n */\nfunction withPipe<In, Out>(schedule: Schedule<In, Out>): PipedSchedule<In, Out> {\n return {\n ...schedule,\n pipe(fn: (s: Schedule<In, Out>) => unknown): unknown {\n const result = fn(schedule);\n if (isSchedule(result)) {\n return withPipe(result as Schedule<unknown, unknown>);\n }\n return result;\n },\n } as PipedSchedule<In, Out>;\n}\n\n// Wrapped versions with pipe support\nconst spacedWithPipe = <In = unknown>(delay: DurationType) => withPipe(spaced<In>(delay));\nconst exponentialWithPipe = <In = unknown>(base: DurationType, factor?: number) => withPipe(exponential<In>(base, factor));\nconst linearWithPipe = <In = unknown>(base: DurationType) => withPipe(linear<In>(base));\nconst fibonacciWithPipe = <In = unknown>(base: DurationType) => withPipe(fibonacci<In>(base));\nconst recursWithPipe = <In = unknown>(n: number) => withPipe(recurs<In>(n));\nconst foreverWithPipe = <In = unknown>() => withPipe(forever<In>());\nconst onceWithPipe = <In = unknown>() => withPipe(once<In>());\nconst unionWithPipe = <In, Out1, Out2>(first: Schedule<In, Out1>, second: Schedule<In, Out2>) => withPipe(union(first, second));\nconst intersectWithPipe = <In, Out1, Out2>(first: Schedule<In, Out1>, second: Schedule<In, Out2>) => withPipe(intersect(first, second));\n\n// =============================================================================\n// Namespace Export\n// =============================================================================\n\n/**\n * Schedule namespace for composable scheduling.\n *\n * @example\n * ```typescript\n * import { Schedule, Duration } from \"@jagreehal/workflow\";\n *\n * // Exponential backoff with jitter, max 5 attempts\n * const retry = Schedule.exponential(Duration.millis(100))\n * .pipe(Schedule.jittered(0.2))\n * .pipe(Schedule.upTo(5))\n * .pipe(Schedule.maxDelay(Duration.seconds(30)))\n *\n * // Then poll every minute forever\n * const poll = Schedule.spaced(Duration.minutes(1))\n *\n * // Chain them\n * const strategy = retry.pipe(Schedule.andThen(poll))\n * ```\n */\nexport const Schedule = {\n // Base schedules\n forever: foreverWithPipe,\n once: onceWithPipe,\n recurs: recursWithPipe,\n stop,\n\n // Delay-based\n spaced: spacedWithPipe,\n fixed: spacedWithPipe,\n exponential: exponentialWithPipe,\n linear: linearWithPipe,\n fibonacci: fibonacciWithPipe,\n\n // Limits\n upTo,\n upToElapsed,\n maxDelay,\n minDelay,\n\n // Conditions\n whileInput,\n whileOutput,\n untilInput,\n untilOutput,\n\n // Jitter\n jittered,\n addDelay,\n\n // Composition\n andThen,\n union: unionWithPipe,\n intersect: intersectWithPipe,\n\n // Transformations\n map,\n tap,\n modifyDelay,\n\n // Running\n run,\n delays,\n} as const;\n\nexport type { Schedule as ScheduleType };\n","/**\n * @jagreehal/workflow/duration\n *\n * Type-safe duration handling inspired by Effect's Duration module.\n * Prevents unit confusion (milliseconds vs seconds) with explicit constructors.\n */\n\n// =============================================================================\n// Duration Type\n// =============================================================================\n\n/**\n * A type-safe representation of a time duration.\n * Use the constructor functions (millis, seconds, etc.) to create durations.\n */\nexport interface Duration {\n readonly _tag: \"Duration\";\n readonly millis: number;\n}\n\n// =============================================================================\n// Constructors\n// =============================================================================\n\n/**\n * Create a Duration from milliseconds.\n *\n * @example\n * ```typescript\n * const d = Duration.millis(500)\n * ```\n */\nexport function millis(ms: number): Duration {\n return { _tag: \"Duration\", millis: ms };\n}\n\n/**\n * Create a Duration from seconds.\n *\n * @example\n * ```typescript\n * const d = Duration.seconds(5) // 5000ms\n * ```\n */\nexport function seconds(s: number): Duration {\n return { _tag: \"Duration\", millis: s * 1000 };\n}\n\n/**\n * Create a Duration from minutes.\n *\n * @example\n * ```typescript\n * const d = Duration.minutes(2) // 120000ms\n * ```\n */\nexport function minutes(m: number): Duration {\n return { _tag: \"Duration\", millis: m * 60 * 1000 };\n}\n\n/**\n * Create a Duration from hours.\n *\n * @example\n * ```typescript\n * const d = Duration.hours(1) // 3600000ms\n * ```\n */\nexport function hours(h: number): Duration {\n return { _tag: \"Duration\", millis: h * 60 * 60 * 1000 };\n}\n\n/**\n * Create a Duration from days.\n *\n * @example\n * ```typescript\n * const d = Duration.days(1) // 86400000ms\n * ```\n */\nexport function days(d: number): Duration {\n return { _tag: \"Duration\", millis: d * 24 * 60 * 60 * 1000 };\n}\n\n/**\n * Zero duration.\n */\nexport const zero: Duration = { _tag: \"Duration\", millis: 0 };\n\n/**\n * Infinite duration (represented as Infinity milliseconds).\n */\nexport const infinity: Duration = { _tag: \"Duration\", millis: Infinity };\n\n// =============================================================================\n// Conversions\n// =============================================================================\n\n/**\n * Convert a Duration to milliseconds.\n */\nexport function toMillis(duration: Duration): number {\n return duration.millis;\n}\n\n/**\n * Convert a Duration to seconds.\n */\nexport function toSeconds(duration: Duration): number {\n return duration.millis / 1000;\n}\n\n/**\n * Convert a Duration to minutes.\n */\nexport function toMinutes(duration: Duration): number {\n return duration.millis / (60 * 1000);\n}\n\n/**\n * Convert a Duration to hours.\n */\nexport function toHours(duration: Duration): number {\n return duration.millis / (60 * 60 * 1000);\n}\n\n/**\n * Convert a Duration to days.\n */\nexport function toDays(duration: Duration): number {\n return duration.millis / (24 * 60 * 60 * 1000);\n}\n\n// =============================================================================\n// Operations\n// =============================================================================\n\n/**\n * Add two durations.\n *\n * @example\n * ```typescript\n * const total = Duration.add(Duration.seconds(5), Duration.millis(500))\n * // 5500ms\n * ```\n */\nexport function add(a: Duration, b: Duration): Duration {\n return { _tag: \"Duration\", millis: a.millis + b.millis };\n}\n\n/**\n * Subtract duration b from duration a.\n * Result is clamped to zero (no negative durations).\n *\n * @example\n * ```typescript\n * const remaining = Duration.subtract(Duration.seconds(5), Duration.seconds(2))\n * // 3000ms\n * ```\n */\nexport function subtract(a: Duration, b: Duration): Duration {\n return { _tag: \"Duration\", millis: Math.max(0, a.millis - b.millis) };\n}\n\n/**\n * Multiply a duration by a factor.\n *\n * @example\n * ```typescript\n * const doubled = Duration.multiply(Duration.seconds(5), 2)\n * // 10000ms\n * ```\n */\nexport function multiply(duration: Duration, factor: number): Duration {\n return { _tag: \"Duration\", millis: duration.millis * factor };\n}\n\n/**\n * Divide a duration by a divisor.\n *\n * @example\n * ```typescript\n * const half = Duration.divide(Duration.seconds(10), 2)\n * // 5000ms\n * ```\n */\nexport function divide(duration: Duration, divisor: number): Duration {\n return { _tag: \"Duration\", millis: duration.millis / divisor };\n}\n\n// =============================================================================\n// Comparisons\n// =============================================================================\n\n/**\n * Check if duration a is less than duration b.\n */\nexport function lessThan(a: Duration, b: Duration): boolean {\n return a.millis < b.millis;\n}\n\n/**\n * Check if duration a is less than or equal to duration b.\n */\nexport function lessThanOrEqual(a: Duration, b: Duration): boolean {\n return a.millis <= b.millis;\n}\n\n/**\n * Check if duration a is greater than duration b.\n */\nexport function greaterThan(a: Duration, b: Duration): boolean {\n return a.millis > b.millis;\n}\n\n/**\n * Check if duration a is greater than or equal to duration b.\n */\nexport function greaterThanOrEqual(a: Duration, b: Duration): boolean {\n return a.millis >= b.millis;\n}\n\n/**\n * Check if two durations are equal.\n */\nexport function equals(a: Duration, b: Duration): boolean {\n return a.millis === b.millis;\n}\n\n/**\n * Get the minimum of two durations.\n */\nexport function min(a: Duration, b: Duration): Duration {\n return a.millis <= b.millis ? a : b;\n}\n\n/**\n * Get the maximum of two durations.\n */\nexport function max(a: Duration, b: Duration): Duration {\n return a.millis >= b.millis ? a : b;\n}\n\n/**\n * Clamp a duration between a minimum and maximum.\n */\nexport function clamp(duration: Duration, minimum: Duration, maximum: Duration): Duration {\n return min(max(duration, minimum), maximum);\n}\n\n// =============================================================================\n// Predicates\n// =============================================================================\n\n/**\n * Check if a duration is zero.\n */\nexport function isZero(duration: Duration): boolean {\n return duration.millis === 0;\n}\n\n/**\n * Check if a duration is infinite.\n */\nexport function isInfinite(duration: Duration): boolean {\n return duration.millis === Infinity;\n}\n\n/**\n * Check if a duration is finite and positive.\n */\nexport function isFinite(duration: Duration): boolean {\n return Number.isFinite(duration.millis) && duration.millis > 0;\n}\n\n/**\n * Type guard to check if a value is a Duration.\n */\nexport function isDuration(value: unknown): value is Duration {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"_tag\" in value &&\n value._tag === \"Duration\" &&\n \"millis\" in value &&\n typeof value.millis === \"number\"\n );\n}\n\n// =============================================================================\n// Formatting\n// =============================================================================\n\n/**\n * Format a duration as a human-readable string.\n *\n * @example\n * ```typescript\n * Duration.format(Duration.seconds(90)) // \"1m 30s\"\n * Duration.format(Duration.millis(500)) // \"500ms\"\n * ```\n */\nexport function format(duration: Duration): string {\n const ms = duration.millis;\n\n if (ms === Infinity) return \"∞\";\n if (ms === 0) return \"0ms\";\n\n const days = Math.floor(ms / (24 * 60 * 60 * 1000));\n const hours = Math.floor((ms % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000));\n const minutes = Math.floor((ms % (60 * 60 * 1000)) / (60 * 1000));\n const seconds = Math.floor((ms % (60 * 1000)) / 1000);\n const millis = ms % 1000;\n\n const parts: string[] = [];\n if (days > 0) parts.push(`${days}d`);\n if (hours > 0) parts.push(`${hours}h`);\n if (minutes > 0) parts.push(`${minutes}m`);\n if (seconds > 0) parts.push(`${seconds}s`);\n if (millis > 0 && parts.length === 0) parts.push(`${millis}ms`);\n\n return parts.join(\" \") || \"0ms\";\n}\n\n// =============================================================================\n// Parsing\n// =============================================================================\n\n/**\n * Parse a duration from a string like \"100ms\", \"5s\", \"2m\", \"1h\", \"1d\".\n * Returns undefined if parsing fails.\n *\n * @example\n * ```typescript\n * Duration.parse(\"5s\") // Duration.seconds(5)\n * Duration.parse(\"100ms\") // Duration.millis(100)\n * Duration.parse(\"2m\") // Duration.minutes(2)\n * ```\n */\nexport function parse(input: string): Duration | undefined {\n const match = input.trim().match(/^(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d)$/i);\n if (!match) return undefined;\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n\n switch (unit) {\n case \"ms\":\n return millis(value);\n case \"s\":\n return seconds(value);\n case \"m\":\n return minutes(value);\n case \"h\":\n return hours(value);\n case \"d\":\n return days(value);\n default:\n return undefined;\n }\n}\n\n// =============================================================================\n// Namespace Export\n// =============================================================================\n\n/**\n * Duration namespace with all functions for convenient access.\n *\n * @example\n * ```typescript\n * import { Duration } from \"@jagreehal/workflow\";\n *\n * const timeout = Duration.seconds(30);\n * const delay = Duration.millis(100);\n * const total = Duration.add(timeout, delay);\n *\n * console.log(Duration.format(total)); // \"30s 100ms\"\n * ```\n */\nexport const Duration = {\n // Constructors\n millis,\n seconds,\n minutes,\n hours,\n days,\n zero,\n infinity,\n\n // Conversions\n toMillis,\n toSeconds,\n toMinutes,\n toHours,\n toDays,\n\n // Operations\n add,\n subtract,\n multiply,\n divide,\n\n // Comparisons\n lessThan,\n lessThanOrEqual,\n greaterThan,\n greaterThanOrEqual,\n equals,\n min,\n max,\n clamp,\n\n // Predicates\n isZero,\n isInfinite,\n isFinite,\n isDuration,\n\n // Formatting\n format,\n parse,\n} as const;\n\nexport type { Duration as DurationType };\n"],"mappings":"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","names":["schedule_exports","__export","Schedule","addDelay","andThen","delays","exponential","fibonacci","fixed","forever","intersect","jittered","linear","map","maxDelay","minDelay","modifyDelay","once","recurs","run","spaced","stop","tap","union","untilInput","untilOutput","upTo","upToElapsed","whileInput","whileOutput","__toCommonJS","millis","ms","seconds","s","minutes","m","hours","h","days","d","zero","infinity","toMillis","duration","toSeconds","toMinutes","toHours","toDays","add","a","b","subtract","multiply","factor","divide","divisor","lessThan","lessThanOrEqual","greaterThan","greaterThanOrEqual","equals","min","max","clamp","minimum","maximum","isZero","isInfinite","isFinite","isDuration","value","format","parts","parse","input","match","Duration","initialState","Duration","continueWith","delay","output","done","updateState","state","input","add","forever","_input","recurs","n","once","stop","spaced","fixed","exponential","base","factor","multiply","linear","fibonacci","fibNumbers","i","fibIndex","upTo","schedule","upToElapsed","maxElapsed","toMillis","maxDelay","max","decision","capped","minDelay","min","floored","whileInput","predicate","whileOutput","untilInput","untilOutput","jittered","jitterMultiplier","jitteredDelay","millis","addDelay","maxExtra","extra","newDelay","andThen","second","first","initialMeta","meta","nextFirstState","nextState","secondDecision","nextSecondState","union","firstDecision","firstDelay","secondDelay","intersect","map","fn","tap","modifyDelay","run","delays","maxIterations","result","runner","next","isSchedule","value","withPipe","spacedWithPipe","exponentialWithPipe","linearWithPipe","fibonacciWithPipe","recursWithPipe","foreverWithPipe","onceWithPipe","unionWithPipe","intersectWithPipe","Schedule"]}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { DurationType as Duration } from './duration.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @jagreehal/workflow/schedule
|
|
5
|
+
*
|
|
6
|
+
* Composable scheduling primitives inspired by Effect's Schedule module.
|
|
7
|
+
* Build complex retry and polling strategies by composing simple building blocks.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // "Exponential backoff with jitter, max 5 attempts, then poll every minute"
|
|
12
|
+
* const schedule = Schedule.exponential(Duration.millis(100))
|
|
13
|
+
* .pipe(Schedule.jittered(0.2))
|
|
14
|
+
* .pipe(Schedule.upTo(5))
|
|
15
|
+
* .pipe(Schedule.andThen(Schedule.spaced(Duration.minutes(1))))
|
|
16
|
+
*
|
|
17
|
+
* // Use with workflows
|
|
18
|
+
* const result = await step(fetchData, { schedule })
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The state maintained by a schedule during execution.
|
|
24
|
+
*/
|
|
25
|
+
interface ScheduleState {
|
|
26
|
+
/** Number of iterations completed */
|
|
27
|
+
readonly iterations: number;
|
|
28
|
+
/** Total elapsed time since schedule started */
|
|
29
|
+
readonly elapsed: Duration;
|
|
30
|
+
/** The last input received (if any) */
|
|
31
|
+
readonly lastInput?: unknown;
|
|
32
|
+
/** The last output produced (if any) */
|
|
33
|
+
readonly lastOutput?: unknown;
|
|
34
|
+
/** Combinator-specific metadata (used by andThen, union, intersect, etc.) */
|
|
35
|
+
readonly meta?: unknown;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Decision returned by a schedule step.
|
|
39
|
+
* Combinators can optionally provide `nextState` to thread custom state through execution.
|
|
40
|
+
*/
|
|
41
|
+
type ScheduleDecision<Out> = {
|
|
42
|
+
readonly _tag: "Continue";
|
|
43
|
+
readonly delay: Duration;
|
|
44
|
+
readonly output: Out;
|
|
45
|
+
readonly nextState?: ScheduleState;
|
|
46
|
+
} | {
|
|
47
|
+
readonly _tag: "Done";
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* A schedule that repeats forever with no delay.
|
|
51
|
+
*/
|
|
52
|
+
declare function forever<In = unknown>(): Schedule<In, number>;
|
|
53
|
+
/**
|
|
54
|
+
* A schedule that repeats exactly n times.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const thrice = Schedule.recurs(3)
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare function recurs<In = unknown>(n: number): Schedule<In, number>;
|
|
62
|
+
/**
|
|
63
|
+
* A schedule that repeats once.
|
|
64
|
+
*/
|
|
65
|
+
declare function once<In = unknown>(): Schedule<In, void>;
|
|
66
|
+
/**
|
|
67
|
+
* A schedule that always stops immediately.
|
|
68
|
+
*/
|
|
69
|
+
declare function stop<In = unknown>(): Schedule<In, never>;
|
|
70
|
+
/**
|
|
71
|
+
* A schedule that waits a fixed duration between each iteration.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* const pollEvery5s = Schedule.spaced(Duration.seconds(5))
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
declare function spaced<In = unknown>(delay: Duration): Schedule<In, number>;
|
|
79
|
+
/**
|
|
80
|
+
* Alias for spaced - a fixed interval schedule.
|
|
81
|
+
*/
|
|
82
|
+
declare const fixed: typeof spaced;
|
|
83
|
+
/**
|
|
84
|
+
* A schedule with exponentially increasing delays.
|
|
85
|
+
* delay(n) = base * factor^n
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* // 100ms, 200ms, 400ms, 800ms, ...
|
|
90
|
+
* const exponential = Schedule.exponential(Duration.millis(100))
|
|
91
|
+
*
|
|
92
|
+
* // With custom factor: 100ms, 300ms, 900ms, ...
|
|
93
|
+
* const triple = Schedule.exponential(Duration.millis(100), 3)
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
declare function exponential<In = unknown>(base: Duration, factor?: number): Schedule<In, Duration>;
|
|
97
|
+
/**
|
|
98
|
+
* A schedule with linearly increasing delays.
|
|
99
|
+
* delay(n) = base * (n + 1)
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* // 100ms, 200ms, 300ms, 400ms, ...
|
|
104
|
+
* const linear = Schedule.linear(Duration.millis(100))
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
declare function linear<In = unknown>(base: Duration): Schedule<In, Duration>;
|
|
108
|
+
/**
|
|
109
|
+
* A schedule with fibonacci-sequence delays.
|
|
110
|
+
* delay(n) follows fibonacci: base, base, 2*base, 3*base, 5*base, ...
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* // 100ms, 100ms, 200ms, 300ms, 500ms, 800ms, ...
|
|
115
|
+
* const fib = Schedule.fibonacci(Duration.millis(100))
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
declare function fibonacci<In = unknown>(base: Duration): Schedule<In, Duration>;
|
|
119
|
+
/**
|
|
120
|
+
* Limit a schedule to at most n iterations.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* // Exponential backoff, max 5 attempts
|
|
125
|
+
* const limited = Schedule.exponential(Duration.millis(100))
|
|
126
|
+
* .pipe(Schedule.upTo(5))
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
declare function upTo<In, Out>(n: number): (schedule: Schedule<In, Out>) => Schedule<In, Out>;
|
|
130
|
+
/**
|
|
131
|
+
* Limit a schedule by total elapsed time.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* // Retry for at most 30 seconds
|
|
136
|
+
* const timeLimited = Schedule.spaced(Duration.seconds(1))
|
|
137
|
+
* .pipe(Schedule.upToElapsed(Duration.seconds(30)))
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
declare function upToElapsed<In, Out>(maxElapsed: Duration): (schedule: Schedule<In, Out>) => Schedule<In, Out>;
|
|
141
|
+
/**
|
|
142
|
+
* Cap the maximum delay of a schedule.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* // Exponential, but never more than 30 seconds
|
|
147
|
+
* const capped = Schedule.exponential(Duration.millis(100))
|
|
148
|
+
* .pipe(Schedule.maxDelay(Duration.seconds(30)))
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
declare function maxDelay<In, Out>(max: Duration): (schedule: Schedule<In, Out>) => Schedule<In, Out>;
|
|
152
|
+
/**
|
|
153
|
+
* Set a minimum delay for a schedule.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* const atLeast1s = schedule.pipe(Schedule.minDelay(Duration.seconds(1)))
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
declare function minDelay<In, Out>(min: Duration): (schedule: Schedule<In, Out>) => Schedule<In, Out>;
|
|
161
|
+
/**
|
|
162
|
+
* Continue while a predicate on the input returns true.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* // Retry while the error is transient
|
|
167
|
+
* const retryTransient = Schedule.exponential(Duration.millis(100))
|
|
168
|
+
* .pipe(Schedule.whileInput((err: Error) => err.message.includes("ECONNRESET")))
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
declare function whileInput<In, Out>(predicate: (input: In) => boolean): (schedule: Schedule<In, Out>) => Schedule<In, Out>;
|
|
172
|
+
/**
|
|
173
|
+
* Continue while a predicate on the output returns true.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* // Poll until status is "ready"
|
|
178
|
+
* const untilReady = Schedule.spaced(Duration.seconds(1))
|
|
179
|
+
* .pipe(Schedule.whileOutput((status: string) => status !== "ready"))
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
declare function whileOutput<In, Out>(predicate: (output: Out) => boolean): (schedule: Schedule<In, Out>) => Schedule<In, Out>;
|
|
183
|
+
/**
|
|
184
|
+
* Continue until a predicate on the input returns true.
|
|
185
|
+
* Opposite of whileInput.
|
|
186
|
+
*/
|
|
187
|
+
declare function untilInput<In, Out>(predicate: (input: In) => boolean): (schedule: Schedule<In, Out>) => Schedule<In, Out>;
|
|
188
|
+
/**
|
|
189
|
+
* Continue until a predicate on the output returns true.
|
|
190
|
+
* Opposite of whileOutput.
|
|
191
|
+
*/
|
|
192
|
+
declare function untilOutput<In, Out>(predicate: (output: Out) => boolean): (schedule: Schedule<In, Out>) => Schedule<In, Out>;
|
|
193
|
+
/**
|
|
194
|
+
* Add random jitter to delays to prevent thundering herd.
|
|
195
|
+
*
|
|
196
|
+
* @param factor - Jitter factor (0.0 to 1.0). Delay will be multiplied by (1 - factor) to (1 + factor).
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* // ±20% jitter
|
|
201
|
+
* const jittered = Schedule.exponential(Duration.millis(100))
|
|
202
|
+
* .pipe(Schedule.jittered(0.2))
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
declare function jittered<In, Out>(factor?: number): (schedule: Schedule<In, Out>) => Schedule<In, Out>;
|
|
206
|
+
/**
|
|
207
|
+
* Add a fixed random delay to each iteration.
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* // Add 0-500ms random delay
|
|
212
|
+
* const randomized = schedule.pipe(Schedule.addDelay(Duration.millis(500)))
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
declare function addDelay<In, Out>(maxExtra: Duration): (schedule: Schedule<In, Out>) => Schedule<In, Out>;
|
|
216
|
+
/**
|
|
217
|
+
* Chain two schedules: run the first until done, then run the second.
|
|
218
|
+
* The second schedule starts with fresh state when it begins.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```typescript
|
|
222
|
+
* // 5 exponential retries, then poll every minute
|
|
223
|
+
* const strategy = Schedule.exponential(Duration.millis(100))
|
|
224
|
+
* .pipe(Schedule.upTo(5))
|
|
225
|
+
* .pipe(Schedule.andThen(Schedule.spaced(Duration.minutes(1))))
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
declare function andThen<In, Out1, Out2>(second: Schedule<In, Out2>): (first: Schedule<In, Out1>) => Schedule<In, Out1 | Out2>;
|
|
229
|
+
/**
|
|
230
|
+
* Run two schedules in parallel, using the shorter delay.
|
|
231
|
+
* Both schedules advance their state independently.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```typescript
|
|
235
|
+
* // Use whichever delay is shorter
|
|
236
|
+
* const union = Schedule.union(
|
|
237
|
+
* Schedule.exponential(Duration.millis(100)),
|
|
238
|
+
* Schedule.spaced(Duration.seconds(1))
|
|
239
|
+
* )
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
declare function union<In, Out1, Out2>(first: Schedule<In, Out1>, second: Schedule<In, Out2>): Schedule<In, [Out1, Out2]>;
|
|
243
|
+
/**
|
|
244
|
+
* Run two schedules in parallel, using the longer delay.
|
|
245
|
+
* Both schedules advance their state independently. Stops when either schedule stops.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* // Use whichever delay is longer
|
|
250
|
+
* const intersect = Schedule.intersect(
|
|
251
|
+
* Schedule.exponential(Duration.millis(100)),
|
|
252
|
+
* Schedule.spaced(Duration.seconds(1))
|
|
253
|
+
* )
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
declare function intersect<In, Out1, Out2>(first: Schedule<In, Out1>, second: Schedule<In, Out2>): Schedule<In, [Out1, Out2]>;
|
|
257
|
+
/**
|
|
258
|
+
* Transform the output of a schedule.
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```typescript
|
|
262
|
+
* const withIteration = Schedule.spaced(Duration.seconds(1))
|
|
263
|
+
* .pipe(Schedule.map((n) => ({ attempt: n + 1 })))
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
declare function map<In, Out, NewOut>(fn: (output: Out) => NewOut): (schedule: Schedule<In, Out>) => Schedule<In, NewOut>;
|
|
267
|
+
/**
|
|
268
|
+
* Perform a side effect on each iteration.
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* ```typescript
|
|
272
|
+
* const logged = Schedule.exponential(Duration.millis(100))
|
|
273
|
+
* .pipe(Schedule.tap((delay) => console.log(`Waiting ${delay}ms`)))
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
declare function tap<In, Out>(fn: (output: Out, state: ScheduleState) => void): (schedule: Schedule<In, Out>) => Schedule<In, Out>;
|
|
277
|
+
/**
|
|
278
|
+
* Modify the delay of a schedule.
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```typescript
|
|
282
|
+
* const doubled = schedule.pipe(Schedule.modifyDelay(d => Duration.multiply(d, 2)))
|
|
283
|
+
* ```
|
|
284
|
+
*/
|
|
285
|
+
declare function modifyDelay<In, Out>(fn: (delay: Duration, output: Out) => Duration): (schedule: Schedule<In, Out>) => Schedule<In, Out>;
|
|
286
|
+
/**
|
|
287
|
+
* Create an iterator for running a schedule step-by-step.
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```typescript
|
|
291
|
+
* const runner = Schedule.run(mySchedule)
|
|
292
|
+
*
|
|
293
|
+
* while (true) {
|
|
294
|
+
* const next = runner.next(lastResult)
|
|
295
|
+
* if (next.done) break
|
|
296
|
+
* await sleep(next.value.delay)
|
|
297
|
+
* }
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
300
|
+
declare function run<In, Out>(schedule: Schedule<In, Out>): {
|
|
301
|
+
next: (input: In) => {
|
|
302
|
+
done: false;
|
|
303
|
+
value: {
|
|
304
|
+
delay: Duration;
|
|
305
|
+
output: Out;
|
|
306
|
+
};
|
|
307
|
+
} | {
|
|
308
|
+
done: true;
|
|
309
|
+
};
|
|
310
|
+
};
|
|
311
|
+
/**
|
|
312
|
+
* Get all delays from a schedule (for testing/visualization).
|
|
313
|
+
* Runs the schedule with undefined input until it completes or reaches maxIterations.
|
|
314
|
+
*/
|
|
315
|
+
declare function delays<Out>(schedule: Schedule<undefined, Out>, maxIterations?: number): Duration[];
|
|
316
|
+
/**
|
|
317
|
+
* A schedule with pipe support for method chaining.
|
|
318
|
+
*/
|
|
319
|
+
type PipedSchedule<In, Out> = Schedule<In, Out> & {
|
|
320
|
+
pipe: <NewOut>(fn: (s: Schedule<In, Out>) => Schedule<In, NewOut>) => PipedSchedule<In, NewOut>;
|
|
321
|
+
} & {
|
|
322
|
+
pipe: <R>(fn: (s: Schedule<In, Out>) => R) => R;
|
|
323
|
+
};
|
|
324
|
+
/**
|
|
325
|
+
* A Schedule defines a recurring pattern with delays and outputs.
|
|
326
|
+
*
|
|
327
|
+
* @typeParam In - The input type (from retry attempts, etc.)
|
|
328
|
+
* @typeParam Out - The output type (often the delay or iteration count)
|
|
329
|
+
*/
|
|
330
|
+
interface Schedule<In = unknown, Out = unknown> {
|
|
331
|
+
readonly _tag: "Schedule";
|
|
332
|
+
/** Compute the next step given input and current state */
|
|
333
|
+
readonly step: (input: In, state: ScheduleState) => ScheduleDecision<Out>;
|
|
334
|
+
/** Initial state for this schedule */
|
|
335
|
+
readonly initial: ScheduleState;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Schedule namespace for composable scheduling.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```typescript
|
|
342
|
+
* import { Schedule, Duration } from "@jagreehal/workflow";
|
|
343
|
+
*
|
|
344
|
+
* // Exponential backoff with jitter, max 5 attempts
|
|
345
|
+
* const retry = Schedule.exponential(Duration.millis(100))
|
|
346
|
+
* .pipe(Schedule.jittered(0.2))
|
|
347
|
+
* .pipe(Schedule.upTo(5))
|
|
348
|
+
* .pipe(Schedule.maxDelay(Duration.seconds(30)))
|
|
349
|
+
*
|
|
350
|
+
* // Then poll every minute forever
|
|
351
|
+
* const poll = Schedule.spaced(Duration.minutes(1))
|
|
352
|
+
*
|
|
353
|
+
* // Chain them
|
|
354
|
+
* const strategy = retry.pipe(Schedule.andThen(poll))
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
357
|
+
declare const Schedule: {
|
|
358
|
+
readonly forever: <In = unknown>() => PipedSchedule<In, number>;
|
|
359
|
+
readonly once: <In = unknown>() => PipedSchedule<In, void>;
|
|
360
|
+
readonly recurs: <In = unknown>(n: number) => PipedSchedule<In, number>;
|
|
361
|
+
readonly stop: typeof stop;
|
|
362
|
+
readonly spaced: <In = unknown>(delay: Duration) => PipedSchedule<In, number>;
|
|
363
|
+
readonly fixed: <In = unknown>(delay: Duration) => PipedSchedule<In, number>;
|
|
364
|
+
readonly exponential: <In = unknown>(base: Duration, factor?: number) => PipedSchedule<In, Duration>;
|
|
365
|
+
readonly linear: <In = unknown>(base: Duration) => PipedSchedule<In, Duration>;
|
|
366
|
+
readonly fibonacci: <In = unknown>(base: Duration) => PipedSchedule<In, Duration>;
|
|
367
|
+
readonly upTo: typeof upTo;
|
|
368
|
+
readonly upToElapsed: typeof upToElapsed;
|
|
369
|
+
readonly maxDelay: typeof maxDelay;
|
|
370
|
+
readonly minDelay: typeof minDelay;
|
|
371
|
+
readonly whileInput: typeof whileInput;
|
|
372
|
+
readonly whileOutput: typeof whileOutput;
|
|
373
|
+
readonly untilInput: typeof untilInput;
|
|
374
|
+
readonly untilOutput: typeof untilOutput;
|
|
375
|
+
readonly jittered: typeof jittered;
|
|
376
|
+
readonly addDelay: typeof addDelay;
|
|
377
|
+
readonly andThen: typeof andThen;
|
|
378
|
+
readonly union: <In, Out1, Out2>(first: Schedule<In, Out1>, second: Schedule<In, Out2>) => PipedSchedule<In, [Out1, Out2]>;
|
|
379
|
+
readonly intersect: <In, Out1, Out2>(first: Schedule<In, Out1>, second: Schedule<In, Out2>) => PipedSchedule<In, [Out1, Out2]>;
|
|
380
|
+
readonly map: typeof map;
|
|
381
|
+
readonly tap: typeof tap;
|
|
382
|
+
readonly modifyDelay: typeof modifyDelay;
|
|
383
|
+
readonly run: typeof run;
|
|
384
|
+
readonly delays: typeof delays;
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
export { Schedule, type ScheduleDecision, type ScheduleState, Schedule as ScheduleType, addDelay, andThen, delays, exponential, fibonacci, fixed, forever, intersect, jittered, linear, map, maxDelay, minDelay, modifyDelay, once, recurs, run, spaced, stop, tap, union, untilInput, untilOutput, upTo, upToElapsed, whileInput, whileOutput };
|