Prototype: tokenize(text) returns lowercase ASCII [a-z0-9]+ runs in order with duplicates, using whole-string JavaScript toLowerCase then regex. Non-ASCII characters and punctuation separate runs; return a new list.
Final: SearchEngine accepts nonempty document IDs. add returns false on duplicate preserving text; get exact original text or null; update/delete return false when missing. search(term,limit=10) requires exactly one query token even when limit zero. searchPhrase(phrase,limit=10) matches consecutive tokens, counts overlapping starts, and returns [] for no query tokens. Both return fresh [docId,score] pairs ordered score descending then ID, at most limit. Negative/noninteger limits raise RangeError. stats returns [documentCount,totalTokenCount], counting empty documents. Updates/deletion/reuse leave no stale matches or counts.
Every class is exported by name from exercise.mjs. Keep instances independent. Unless explicitly documented otherwise, input values have the stated collection/string types and do not contain null elements. Invalid calls preserve state; validate before mutation. Return fresh mutable snapshots unless stated otherwise.
Complete the starter code, then run the checks.
Results and errors will appear here.