/// import { type Cell, cell, Default, handler, lift, recipe, str, } from "commontools"; interface ScenarioArgumentOverrideArgs { value: Default; step: Default; } interface IncrementEvent { amount?: unknown; } interface StepChangeEvent { step?: unknown; } interface ApplyOverrideEvent { note?: unknown; } interface SanitizedArguments { value: number; step: number; } const toFiniteInteger = (input: unknown, fallback: number): number => { if (typeof input !== "number" || !Number.isFinite(input)) { return fallback; } const rounded = Math.trunc(input); return Number.isFinite(rounded) ? rounded : fallback; }; const sanitizeCounterValue = (input: unknown): number => { return toFiniteInteger(input, 0); }; const sanitizeStepValue = (input: unknown): number => { const normalized = Math.abs(toFiniteInteger(input, 1)); return normalized === 0 ? 1 : normalized; }; const resolveIncrementAmount = ( payload: unknown, fallback: number, ): number => { if (typeof payload !== "number" || !Number.isFinite(payload)) { return fallback; } const normalized = Math.trunc(payload); return normalized === 0 ? fallback : normalized; }; const sanitizeHistory = (input: unknown): number[] => { if (!Array.isArray(input)) return []; const entries: number[] = []; for (const value of input) { entries.push(sanitizeCounterValue(value)); } return entries; }; const sanitizeOverrideCount = (value: unknown): number => { const current = sanitizeCounterValue(value); return current >= 0 ? current : 0; }; const nextOverrideCount = (value: unknown): number => { return sanitizeOverrideCount(value) + 1; }; const incrementCounter = handler( ( event: IncrementEvent | undefined, context: { value: Cell; step: Cell; history: Cell; }, ) => { const baseStep = sanitizeStepValue(context.step.get()); const applied = resolveIncrementAmount(event?.amount, baseStep); const current = sanitizeCounterValue(context.value.get()); const next = current + applied; context.value.set(next); const history = sanitizeHistory(context.history.get()); context.history.set([...history, next]); }, ); const changeStepFromEvent = handler( ( event: StepChangeEvent | undefined, context: { step: Cell }, ) => { if (!event || !("step" in event)) return; const next = sanitizeStepValue(event.step); context.step.set(next); }, ); const applyArgumentOverrides = handler( ( event: ApplyOverrideEvent | undefined, context: { args: Cell; value: Cell; step: Cell; history: Cell; overrides: Cell; note: Cell; }, ) => { const sanitized = context.args.get(); const nextValue = sanitizeCounterValue(sanitized.value); const nextStep = sanitizeStepValue(sanitized.step); context.value.set(nextValue); context.step.set(nextStep); context.history.set([nextValue]); const overrides = nextOverrideCount(context.overrides.get()); context.overrides.set(overrides); const note = typeof event?.note === "string" && event.note.trim().length > 0 ? event.note.trim() : `override-${overrides}`; context.note.set(`Applied ${note} -> value ${nextValue} step ${nextStep}`); }, ); export const counterWithScenarioArgumentOverrides = recipe< ScenarioArgumentOverrideArgs >( "Counter With Scenario Driven Argument Overrides", ({ value, step }) => { const sanitizedArguments = lift(( inputs: { value?: number; step?: number }, ) => ({ value: sanitizeCounterValue(inputs.value), step: sanitizeStepValue(inputs.step), }))({ value, step }); const sanitizedValue = sanitizedArguments.key("value"); const sanitizedStep = sanitizedArguments.key("step"); const runtimeValue = cell(0); const runtimeStep = cell(1); const historyStore = cell([]); const overrideSource = cell(0); const overrideCount = lift((value: number | undefined) => sanitizeOverrideCount(value) )(overrideSource); const overrideNote = cell("initial arguments applied"); const currentValue = lift((input: number | undefined) => sanitizeCounterValue(input) )(runtimeValue); const activeStep = lift((input: number | undefined) => sanitizeStepValue(input) )(runtimeStep); const history = lift(sanitizeHistory)(historyStore); const historyCount = lift((entries: number[]) => entries.length)(history); const lastRecorded = lift((entries: number[]) => entries.length > 0 ? entries[entries.length - 1] : 0 )(history); const argumentLabel = str`Argument baseline value ${sanitizedValue} step ${sanitizedStep}`; const summary = str`Current ${currentValue} step ${activeStep} overrides ${overrideCount} history ${historyCount}`; return { argumentInputs: { value, step }, argumentState: sanitizedArguments, currentValue, activeStep, history, historyCount, lastRecorded, overrideCount, overrideNote, argumentLabel, summary, controls: { increment: incrementCounter({ value: runtimeValue, step: runtimeStep, history: historyStore, }), changeStep: changeStepFromEvent({ step: runtimeStep }), applyArgumentOverrides: applyArgumentOverrides({ args: sanitizedArguments, value: runtimeValue, step: runtimeStep, history: historyStore, overrides: overrideSource, note: overrideNote, }), }, }; }, );