///
import { Cell, Default, handler, lift, recipe, str } from "commontools";
interface RingBufferCounterArgs {
value: Default;
history: Default;
capacity: Default;
}
const normalizeCapacityValue = (raw: unknown): number => {
if (typeof raw !== "number" || !Number.isFinite(raw)) {
return 3;
}
const normalized = Math.floor(raw);
return normalized > 0 ? normalized : 1;
};
const incrementAndTrim = handler(
(
event: { amount?: number } | undefined,
context: {
value: Cell;
history: Cell;
limit: Cell;
},
) => {
const delta = typeof event?.amount === "number" ? event.amount : 1;
const current = context.value.get();
const base = typeof current === "number" ? current : 0;
const next = base + delta;
context.value.set(next);
const capacity = normalizeCapacityValue(context.limit.get());
const existing = context.history.get();
const list = Array.isArray(existing) ? existing : [];
const trimmed = [
...list.slice(-Math.max(capacity - 1, 0)),
next,
];
context.history.set(trimmed);
},
);
const resizeBuffer = handler(
(
event: { capacity?: number } | undefined,
context: { capacity: Cell; history: Cell },
) => {
if (
typeof event?.capacity !== "number" ||
!Number.isFinite(event.capacity)
) {
return;
}
const nextCapacity = normalizeCapacityValue(event.capacity);
context.capacity.set(nextCapacity);
const existing = context.history.get();
const list = Array.isArray(existing) ? existing : [];
context.history.set(list.slice(-nextCapacity));
},
);
export const counterWithRingBufferHistory = recipe(
"Counter With Ring Buffer History",
({ value, history, capacity }) => {
const currentValue = lift((count: number | undefined) =>
typeof count === "number" ? count : 0
)(value);
const historyView = lift((entries: number[] | undefined) =>
Array.isArray(entries) ? entries : []
)(history);
const limit = lift((raw: number | undefined) =>
normalizeCapacityValue(raw)
)(capacity);
const label = str`Value ${currentValue} | limit ${limit}`;
return {
value,
history,
capacity,
currentValue,
historyView,
limit,
label,
increment: incrementAndTrim({ value, history, limit }),
resize: resizeBuffer({ capacity, history }),
};
},
);