Tutorial: collect something, then make something
import { Inventory } from '@cranberry-forge/satchel';
const catalog = [
{ id: 'copper', name: 'Copper ore', maxStack: 8, weight: 0.6 },
{ id: 'glass', name: 'Skyglass', width: 1, height: 2, maxStack: 4, weight: 0.8 },
{ id: 'lantern', name: 'Lantern', width: 2, height: 2, weight: 2 }
];
const pack = new Inventory({ catalog, columns: 6, rows: 4, maxWeight: 16 });
// Call from a Three.js raycast or overlap handler. Confirm success before
// removing the world object; a full pack must never destroy the pickup.
const result = pack.add('copper', 3);
if (result.ok) copperPickup.removeFromParent();
pack.add('glass', 2);
const recipe = {
ingredients: [{ itemId: 'copper', quantity: 3 }, { itemId: 'glass', quantity: 2 }],
outputs: [{ itemId: 'lantern', quantity: 1 }]
};
const crafted = pack.craft(recipe);
if (!crafted.ok) showMessage(crafted.reason);Every operation plans changes in a private draft. If an output cannot fit or a weight limit is exceeded, the entire action fails without consuming ingredients, changing stack IDs, or notifying subscribers. transfer() commits both inventories before notifying either one. These guarantees concern synchronous inventory state, not persistence, networking or callback side effects.