///
import { Cell, cell, Default, handler, lift, recipe, str } from "commontools";
type GateMode = "enabled" | "disabled";
interface DerivedHandlerGateArgs {
value: Default;
gateMode: Default;
}
const ensureNumber = (value: unknown, fallback: number): number =>
typeof value === "number" && Number.isFinite(value) ? value : fallback;
const normalizeAmount = (value: unknown): number =>
typeof value === "number" && Number.isFinite(value) ? value : 1;
const clampGateMode = (value: unknown): GateMode =>
value === "disabled" ? "disabled" : "enabled";
const applyIncrement = handler(
(
event: { amount?: number } | undefined,
context: {
value: Cell;
log: Cell;
blockedCount: Cell;
appliedCount: Cell;
canIncrement: Cell;
},
) => {
const amount = normalizeAmount(event?.amount);
const historyRaw = context.log.get();
const history = Array.isArray(historyRaw) ? historyRaw : [];
const allowed = context.canIncrement.get() === true;
const currentValue = ensureNumber(context.value.get(), 0);
const blocked = ensureNumber(context.blockedCount.get(), 0);
const applied = ensureNumber(context.appliedCount.get(), 0);
if (!allowed) {
context.blockedCount.set(blocked + 1);
context.log.set([...history, `blocked:${amount}`]);
return;
}
const next = currentValue + amount;
context.value.set(next);
context.appliedCount.set(applied + 1);
context.log.set([...history, `applied:${next}`]);
},
);
const setGateMode = handler(
(
event: { mode?: GateMode } | undefined,
context: { gateMode: Cell },
) => {
const current = clampGateMode(context.gateMode.get());
const next = event?.mode === "disabled"
? "disabled"
: event?.mode === "enabled"
? "enabled"
: current === "enabled"
? "disabled"
: "enabled";
if (next !== current) {
context.gateMode.set(next);
}
},
);
export const counterWithDerivedHandlerGate = recipe(
"Counter With Derived Handler Gate",
({ value, gateMode }) => {
const attemptLog = cell([]);
const blockedCount = cell(0);
const appliedCount = cell(0);
const safeValue = lift((input: number | undefined) =>
typeof input === "number" && Number.isFinite(input) ? input : 0
)(value);
const safeGateMode = lift((mode: GateMode | string | undefined) =>
mode === "disabled" ? "disabled" : "enabled"
)(gateMode);
const isActive = lift((mode: GateMode) => mode === "enabled")(
safeGateMode,
);
const statusLabel = lift((mode: GateMode) =>
mode === "disabled" ? "disabled" : "enabled"
)(safeGateMode);
const attemptHistory = lift((entries: string[] | undefined) =>
Array.isArray(entries) ? entries : []
)(attemptLog);
const blockedAttempts = lift((count: number | undefined) =>
typeof count === "number" && Number.isFinite(count) ? count : 0
)(blockedCount);
const appliedAttempts = lift((count: number | undefined) =>
typeof count === "number" && Number.isFinite(count) ? count : 0
)(appliedCount);
const increment = applyIncrement({
value,
log: attemptLog,
blockedCount,
appliedCount,
canIncrement: isActive,
});
const toggleGate = setGateMode({ gateMode });
return {
value,
gateMode: safeGateMode,
current: safeValue,
isActive,
status: statusLabel,
blockedAttempts,
appliedAttempts,
attemptHistory,
label: str`Count ${safeValue} (${statusLabel})`,
increment,
toggleGate,
};
},
);