Tiny, side effecting inventory core written in plain JavaScript. Started as a freeCodeCamp exercise and expanded to include strict parsing, safe integer guards, and a unified result contract for all operations.
- Practice writing small, reliable stateful code (no frameworks)
- Show a clear result contract instead of console-only output
- Tracks items as
{name, quantity}in memory - Normalizes inputs (name trimmed + lowercase; quantity must be a positive safe integer)
- Provides two mutating APIs:
- addProduct(name, quantity)
- removeProduct(name, quantity)
- Returns the same JSON shape for every outcome (created, updated, removed, not_found, insufficient)
Browser (drop in)
<script src="./main.js"></script>
<script>
addProduct("Widget", 5); //created
addProduct("widget", 2); //updated (same normalized name)
removeProduct("widget", 3); //updated
console.log(inventory); //[{name: 'widget', quantity: 4}]
</script>State lives in the global inventory array exposed by main.js
addProduct(name, quantity)
- Adds a new item or increases quantity of an existing type (existing product)
- Returns a result object (see contract below)
In code these are frozen with Object.freeze to prevent mutation
const OP = Object.freeze({ ADD: "add", REMOVE: "remove" });
const STATUS = Object.freeze({
created: "created",
updated: "updated",
removed: "removed",
notFound: "not_found",
insufficient: "insufficient",
});Every call returns the same shape:
{ op: 'add' | 'remove', //which API
status: 'created'| 'updated' | 'removed' | 'not_found' | 'insufficient', // outcome
name: string, // normalized product name
requested: number, // user's input (always positive)
applied: number, // +q for add, -q for remove, 0 if blocked
previous: number | null, // qty before op (null if not_found/new)
current: number | null, // qty after op (null if not_found)
shortage: number | null, // remove-only: (requested - previous) when insufficient
changed: boolean, // whether the inventory mutated
}- When previous is not null: current === previous + applied
- applied is requested for adds, 0 for blocked operations (not_found, insufficient), and -requested for successful removes
- shortage is null except when status === 'insufficient', where shortage === requested - previous and shortage > 0
- changed is true only for created, updated, removed
Create
addProduct("Bolts", 5);
// { op: 'add', status:'created', name: 'bolts', requested: 5, applied: 5,
// previous: null, current: 5, changed: true }Update (add more)
addProduct("bolts", 3);
// { op: 'add', status: 'updated', name: 'bolts', requested:3, applied: 3,
// previous: 5, current: 8, shortage: null, changed: true }Insufficient remove
removeProduct("bolts", 10);
// { op: 'remove', status: 'insufficient', name: 'bolts', requested: 10, applied: 0,
// previous: 8, current: 8, shortage: 2, changed: false }Exact remove
removeProduct("bolts", 8);
// { op: 'remove', status: 'removed', name: 'bolts', requested: 8, applied: -8,
// previous: 8, current: 0, shortage: null, changed: true }Not found
removeProduct("nuts", 1);
// { op: 'remove', status: 'not_found', name: 'nuts', requested: 1, applied: 0,
// previous: null, current: null, shortage: null, changed: false }These are programmer/data errors and will throw exceptions:
- parseName:
- non-string -> TypeError
- empty after trim -> RangeError
- parseQuantity:
- non-numeric / non-digit string -> TypeError
- ≤ 0 or not a safe integer -> RangeError
- Add overflow:
- previous + requested is not a safe integer -> RangeError
Business rule failures (not_found, insufficient) do not throw; they return a result with status explaining what happened.
- In-memory only; data resets on page reload. State is kept in a module level
inventoryarray for simplicity - Case-insensitive names by design (stored lowercase)
This project was focused on data validation and invariants
- Instance factory (multiple inventories via a closure)
- Add automated tests, learn Node's built in test runner and codify these invariants
- Optional 'safe' wrapper that never throws and returns {status: 'error', ...} envelopes for boundary use
- Inspired by a freeCodeCamp JavaScript exercise; expanded with input validation, safe integer checks, and unified return contract
MIT