Welcome 👋
Many Neo configs accept almost anything: a class, a class name, a config object, an object with an ntype, or a ready instance. They all end up as one instance. That conversion is a single static method, Neo.util.ClassSystem.beforeSetInstance(). It is used across the engine, it is short, and it has no test. Pinning it is a great way to learn how Neo creates things.
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/ClassSystem.mjs, static beforeSetInstance(config, DefaultClass = null, defaultValues = {})
- New test file:
test/playwright/unit/util/ClassSystem.spec.mjs
- Pattern to copy:
test/playwright/unit/util/Array.spec.mjs (the setup() call and imports)
- Tip: define a tiny test class in the spec (extend
Neo.core.Base, give it a className and an ntype, register it with Neo.setupClass) so every row has something to create. Give it static observable = true, as src/util/HashHistory.mjs does: core.Base has no on() of its own, and the row that passes listeners needs one.
What to cover
config |
DefaultClass |
Returns |
null |
a class |
a new DefaultClass instance built with defaultValues |
null |
a class name string |
the name is resolved with Neo.ns(), then as above |
| a class |
— |
Neo.create(config, defaultValues) |
{ntype: '…', …} |
— |
Neo.ntype({...defaultValues, ...config}): keys in config win over defaults |
{…} without ntype |
a class |
Neo.create({module: DefaultClass, ...defaultValues, ...config}) |
| an existing instance |
— |
the same instance; when defaultValues.listeners is given, those listeners are attached with on() |
null |
none |
null, returned unchanged |
Done when
Out of scope
The callers of beforeSetInstance and Neo.create itself.
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).
Live latest-open sweep: the latest 20 open issues at 2026-09-23T14:24Z; no equivalent (the ClassSystem title search returns closed history only). A2A: no claim on this scope. Memory Core: no prior decision.
Origin Session ID: 3be453e4-8b04-4865-be62-4cff34f4e0c6
Welcome 👋
Many Neo configs accept almost anything: a class, a class name, a config object, an object with an
ntype, or a ready instance. They all end up as one instance. That conversion is a single static method,Neo.util.ClassSystem.beforeSetInstance(). It is used across the engine, it is short, and it has no test. Pinning it is a great way to learn how Neo creates things.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/ClassSystem.mjs,static beforeSetInstance(config, DefaultClass = null, defaultValues = {})test/playwright/unit/util/ClassSystem.spec.mjstest/playwright/unit/util/Array.spec.mjs(thesetup()call and imports)Neo.core.Base, give it aclassNameand anntype, register it withNeo.setupClass) so every row has something to create. Give itstatic observable = true, assrc/util/HashHistory.mjsdoes:core.Basehas noon()of its own, and the row that passeslistenersneeds one.What to cover
configDefaultClassnullDefaultClassinstance built withdefaultValuesnullNeo.ns(), then as aboveNeo.create(config, defaultValues){ntype: '…', …}Neo.ntype({...defaultValues, ...config}): keys inconfigwin over defaults{…}withoutntypeNeo.create({module: DefaultClass, ...defaultValues, ...config})defaultValues.listenersis given, those listeners are attached withon()nullnull, returned unchangedDone when
test/playwright/unit/util/ClassSystem.spec.mjsexists, following the pattern file's setup.test(...)per table row, each checking the class of what comes back (and identity for the instance row).src/.npm run test-unit -- test/playwright/unit/util/ClassSystem.spec.mjspasses.npm run test-unitpasses.Out of scope
The callers of
beforeSetInstanceandNeo.createitself.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). Live latest-open sweep: the latest 20 open issues at 2026-09-23T14:24Z; no equivalent (theClassSystemtitle search returns closed history only). A2A: no claim on this scope. Memory Core: no prior decision.Origin Session ID: 3be453e4-8b04-4865-be62-4cff34f4e0c6