///
import { type Cell, Default, handler, lift, recipe, str } from "commontools";
interface InventoryEntry {
sku: string;
stock: number;
reorderLevel: number;
}
interface InventoryReorderArgs {
inventory: Default;
}
interface StockChangeEvent {
sku?: string;
quantity?: number;
}
interface ThresholdChangeEvent {
sku?: string;
threshold?: number;
}
interface LowStockReportEntry extends InventoryEntry {
recommendedOrder: number;
}
const defaultInventory: InventoryEntry[] = [
{ sku: "WIDGET-ALPHA", stock: 12, reorderLevel: 5 },
{ sku: "WIDGET-BETA", stock: 4, reorderLevel: 6 },
{ sku: "WIDGET-GAMMA", stock: 9, reorderLevel: 4 },
];
const sanitizeSku = (value: unknown): string | null => {
if (typeof value !== "string") return null;
const trimmed = value.trim();
if (!trimmed) return null;
return trimmed.toUpperCase();
};
const toNonNegativeInteger = (value: unknown): number | null => {
if (typeof value !== "number" || !Number.isFinite(value)) {
return null;
}
const integer = Math.floor(value);
return integer >= 0 ? integer : null;
};
const toPositiveInteger = (value: unknown): number | null => {
const integer = toNonNegativeInteger(value);
if (integer === null || integer === 0) return null;
return integer;
};
const cloneInventory = (
entries: readonly InventoryEntry[],
): InventoryEntry[] => entries.map((entry) => ({ ...entry }));
const sanitizeInventoryEntry = (
value: unknown,
): InventoryEntry | null => {
const source = value as InventoryEntry | undefined;
const sku = sanitizeSku(source?.sku);
if (!sku) return null;
const stock = toNonNegativeInteger(source?.stock) ?? 0;
const reorderLevel = toNonNegativeInteger(source?.reorderLevel) ?? 0;
return { sku, stock, reorderLevel };
};
const sanitizeInventoryList = (value: unknown): InventoryEntry[] => {
if (!Array.isArray(value)) {
return cloneInventory(defaultInventory);
}
const seen = new Set();
const sanitized: InventoryEntry[] = [];
for (const raw of value) {
const entry = sanitizeInventoryEntry(raw);
if (!entry || seen.has(entry.sku)) continue;
sanitized.push(entry);
seen.add(entry.sku);
}
if (sanitized.length === 0) {
return cloneInventory(defaultInventory);
}
return sanitized;
};
const receiveShipment = handler(
(
event: StockChangeEvent | undefined,
context: { inventory: Cell },
) => {
const sku = sanitizeSku(event?.sku);
const quantity = toPositiveInteger(event?.quantity);
if (!sku || quantity === null) return;
const current = sanitizeInventoryList(context.inventory.get());
let mutated = false;
const updated = current.map((entry) => {
if (entry.sku !== sku) return entry;
mutated = true;
return { ...entry, stock: entry.stock + quantity };
});
if (!mutated) return;
context.inventory.set(updated);
},
);
const recordSale = handler(
(
event: StockChangeEvent | undefined,
context: { inventory: Cell },
) => {
const sku = sanitizeSku(event?.sku);
const quantity = toPositiveInteger(event?.quantity);
if (!sku || quantity === null) return;
const current = sanitizeInventoryList(context.inventory.get());
let mutated = false;
const updated = current.map((entry) => {
if (entry.sku !== sku) return entry;
const nextStock = entry.stock - quantity;
mutated = entry.stock !== nextStock;
return { ...entry, stock: nextStock > 0 ? nextStock : 0 };
});
if (!mutated) return;
context.inventory.set(updated);
},
);
const updateThreshold = handler(
(
event: ThresholdChangeEvent | undefined,
context: { inventory: Cell },
) => {
const sku = sanitizeSku(event?.sku);
const threshold = toNonNegativeInteger(event?.threshold);
if (!sku || threshold === null) return;
const current = sanitizeInventoryList(context.inventory.get());
let mutated = false;
const updated = current.map((entry) => {
if (entry.sku !== sku) return entry;
if (entry.reorderLevel === threshold) return entry;
mutated = true;
return { ...entry, reorderLevel: threshold };
});
if (!mutated) return;
context.inventory.set(updated);
},
);
export const inventoryReorderThreshold = recipe(
"Inventory Reorder Threshold",
({ inventory }) => {
const inventoryView = lift(sanitizeInventoryList)(inventory);
const lowStockEntries = lift((entries: InventoryEntry[]) =>
entries.filter((entry) => entry.stock <= entry.reorderLevel)
)(inventoryView);
const lowStockSkus = lift((entries: InventoryEntry[]) =>
entries.map((entry) => entry.sku)
)(lowStockEntries);
const lowStockReport = lift((entries: InventoryEntry[]) =>
entries.map((entry) => ({
sku: entry.sku,
stock: entry.stock,
reorderLevel: entry.reorderLevel,
recommendedOrder: entry.reorderLevel - entry.stock + 1,
}))
)(lowStockEntries);
const alertCount = lift((entries: InventoryEntry[]) => entries.length)(
lowStockEntries,
);
const needsAttention = lift((count: number) => count > 0)(alertCount);
const alertLabel = str`${alertCount} items below reorder threshold`;
const alertMessage = lift((skus: string[]) => {
if (skus.length === 0) return "All items healthy";
return `Reorder needed for ${skus.join(", ")}`;
})(lowStockSkus);
return {
inventory,
inventoryView,
lowStockEntries,
lowStockSkus,
lowStockReport,
alertCount,
needsAttention,
alertLabel,
alertMessage,
receiveShipment: receiveShipment({ inventory }),
recordSale: recordSale({ inventory }),
setThreshold: updateThreshold({ inventory }),
};
},
);
export type { InventoryEntry, InventoryReorderArgs, LowStockReportEntry };