Welcome 👋
Neo apps route by the URL hash, and Neo can run one app across several browser windows. Neo.util.HashHistory remembers the recent routes: one stack per window, newest first. It is about sixty lines of plain logic with no test, which makes it a good first spec.
This is a good first issue for Hacktoberfest 2026. It needs Node.js and a clone of this repository: no Docker, no AI setup.
[!IMPORTANT]
Reserved for first-time Neo.mjs contributors. The maintainer team will not claim, edit, or implement this issue through 2026-10-31. Comment here to claim it, and ask anything on this issue.
Set up once: follow Set up the engine from a clone, including npm run bundle-browser-deps: without it the unit suite selects zero tests.
Where things live
- Source:
src/util/HashHistory.mjs, a singleton with push(data), first(), second(), getAt(), getCount(), getStack() and a change event
- New test file:
test/playwright/unit/util/HashHistory.spec.mjs
- Pattern to copy:
test/playwright/unit/util/Array.spec.mjs (the setup() call and imports)
- Tip: it is a singleton, so its stacks survive from one test to the next. Start each test with
HashHistory.stacks = {}.
What to cover
| Action |
Expected |
push({hashString: 'a', windowId: 'w1'}), then 'b' |
first('w1') is the 'b' entry, second('w1') the 'a' entry |
the same hashString twice in a row |
stored once, and no second change event |
change event |
fires with (newEntry, previousEntry), and previousEntry is null on the first push |
pushes for 'w1' and 'w2' |
two independent stacks: getCount('w1') counts only its own |
51 different pushes (maxItems is 50) |
the oldest entry is dropped, and the count stays 50 |
getAt(0, 'w1') |
the newest entry |
a read with no windowId |
the first window's stack |
Something to look at while you are in there: push() runs delete data[windowId]. That deletes a property named after the window id's value (for example data['w1']), not the windowId property itself. Pin what the code does today. If you think it meant delete data.windowId, say so on this issue before changing src/: that is a behaviour change, and #5492 introduced the line.
Done when
Out of scope
The router and controllers that read the history (src/controller/Base.mjs), and browser integration.
While you wait for review
You just pinned one small piece of a multi-threaded application engine: in Neo.mjs your app's code runs inside a web worker, and the main thread only paints. Two things worth ten minutes:
- See the part nobody expects. Run
npm run build-themes -- -n -e dev -t all and npm run server-start, open apps/workstation/, and drag a tab out of the browser window. It becomes its own OS window, still running, with the same component instances and the same worker. Benefits: Introduction explains why that is possible.
- Your coding agent can go much further here. The skills this repository's AI maintainers work with are published on npm as
neo-agent-skills, and they need no Knowledge Base and no Memory Core. With Claude or GPT beside you, the help wanted issues are within reach after this one.
Maintainer notes
Curated under #18985 (Hacktoberfest 2026). Found work: no unit or component spec names this module on dev (2026-09-23). The delete data[windowId] line dates from e4466745b2 (#5492); it is recorded as a question, not asserted as a bug.
Live latest-open sweep: the latest 20 open issues at 2026-09-23T14:24Z; no equivalent (the HashHistory title search returns five closed issues). A2A: no claim on this scope. Memory Core: no prior decision.
Origin Session ID: 3be453e4-8b04-4865-be62-4cff34f4e0c6
Welcome 👋
Neo apps route by the URL hash, and Neo can run one app across several browser windows.
Neo.util.HashHistoryremembers the recent routes: one stack per window, newest first. It is about sixty lines of plain logic with no test, which makes it a good first spec.This is a good first issue for Hacktoberfest 2026. It needs Node.js and a clone of this repository: no Docker, no AI setup.
Set up once: follow Set up the engine from a clone, including
npm run bundle-browser-deps: without it the unit suite selects zero tests.Where things live
src/util/HashHistory.mjs, a singleton withpush(data),first(),second(),getAt(),getCount(),getStack()and achangeeventtest/playwright/unit/util/HashHistory.spec.mjstest/playwright/unit/util/Array.spec.mjs(thesetup()call and imports)HashHistory.stacks = {}.What to cover
push({hashString: 'a', windowId: 'w1'}), then'b'first('w1')is the'b'entry,second('w1')the'a'entryhashStringtwice in a rowchangeeventchangeevent(newEntry, previousEntry), andpreviousEntryisnullon the first push'w1'and'w2'getCount('w1')counts only its ownmaxItemsis 50)getAt(0, 'w1')windowIdSomething to look at while you are in there:
push()runsdelete data[windowId]. That deletes a property named after the window id's value (for exampledata['w1']), not thewindowIdproperty itself. Pin what the code does today. If you think it meantdelete data.windowId, say so on this issue before changingsrc/: that is a behaviour change, and #5492 introduced the line.Done when
test/playwright/unit/util/HashHistory.spec.mjsexists, following the pattern file's setup.test(...)per table row.npm run test-unit -- test/playwright/unit/util/HashHistory.spec.mjspasses.npm run test-unitpasses.Out of scope
The router and controllers that read the history (
src/controller/Base.mjs), and browser integration.While you wait for review
You just pinned one small piece of a multi-threaded application engine: in Neo.mjs your app's code runs inside a web worker, and the main thread only paints. Two things worth ten minutes:
npm run build-themes -- -n -e dev -t allandnpm run server-start, openapps/workstation/, and drag a tab out of the browser window. It becomes its own OS window, still running, with the same component instances and the same worker. Benefits: Introduction explains why that is possible.neo-agent-skills, and they need no Knowledge Base and no Memory Core. With Claude or GPT beside you, thehelp wantedissues are within reach after this one.Maintainer notes
Curated under #18985 (Hacktoberfest 2026). Found work: no unit or component spec names this module on
dev(2026-09-23). Thedelete data[windowId]line dates frome4466745b2(#5492); it is recorded as a question, not asserted as a bug. Live latest-open sweep: the latest 20 open issues at 2026-09-23T14:24Z; no equivalent (theHashHistorytitle search returns five closed issues). A2A: no claim on this scope. Memory Core: no prior decision.Origin Session ID: 3be453e4-8b04-4865-be62-4cff34f4e0c6