@xpulse/config β Component Spec
Status: CONCEPT Β· Updated March 2026
Parent: GLOBAL_concept-ecosystem.md
Overview
Reads and validates xpulse.json, resolves ${ENV_VAR} references, and exposes
the configuration via config.get().
One responsibility: know xpulse.json and make it accessible β nothing more.
Dependencies
1 @xpulse /config2 βββ @xpulse /dotenv β load .env before ${VAR } resolution 3 βββ @xpulse /event β fire config :loaded
API
Loading
1 import config from '@xpulse/config' ;2 3 4 await config.load ();5 6 7 await config.load ({ path : '/app/xpulse.json' });
Merge Order
config.load() builds the configuration in three layers:
1 1. Hardcoded DEFAULTS β lowest priority2 2. node_modules/@xpulse/*/xpulse.json β merged alphabetically 3 3. App's own xpulse.json β highest priority, overrides everything
All installed @xpulse/* packages can ship default configuration that becomes
active automatically β without the app needing to configure anything.
The app's own xpulse.json always takes precedence.
Example: @xpulse/doc ships all docs sources as defaults. To disable one:
1 { "docs" : { "sources" : { "event" : { "docs" : false } } } }
Standard keys directly
1 config.name 2 config.type
All keys via get()
1 config.get ('name' ) 2 config.get ('type' ) 3 config.get ('http.port' ) 4 config.get ('theme.default' ) 5 config.get ('i18n.locales' ) 6 config.get ('sources.chat.url' ) 7 config.get ('release.current' ) 8 9 10 config.get ('http.port' , '3000' ) 11 config.get ('missing.key' , null )
Full object
${ENV_VAR} Resolution
xpulse.json can reference .env values:
1 { 2 "name" : "xpulse-web" , 3 "type" : "page" , 4 "http" : { 5 "port" : "${PORT}" 6 } , 7 "sources" : { 8 "chat" : { 9 "url" : "${CHAT_URL}" , 10 "health" : "${CHAT_HEALTH_URL}" 11 } 12 } 13 }
config.load() resolves all ${VAR} references β after @xpulse/dotenv loading:
1 await config.load ();2 config.get ('http.port' ) 3 config.get ('sources.chat.url' )
Unresolved variables (not in .env and not in process.env) remain
as ${VAR} and produce a warning.
Fallback Values (Convention)
Key
Fallback
type
'tool'
theme.default
'dark'
i18n.default
'de'
i18n.locales
['de']
paths.templates
'src/templates/'
paths.locales
'src/locales/'
paths.public
'src/public/'
http.port
process.env.PORT || '3000'
sources.*.docs
false
sources.*.type
'tool'
Validation
config.load() validates known standard keys:
Error
Behaviour
xpulse.json not found
throws Error
Invalid JSON
throws Error
name missing
throws Error
Unknown keys
ignored, no warning
${VAR} unresolvable
warning, value remains ${VAR}
Emitted Events
Event
Payload
When
config:loaded
{ name, type, env, files, packages }
after successful config.load()
1 import event from '@xpulse/event' ;2 3 event.on ('config:loaded' , ({ name, type, env, files, packages } ) => { 4 5 6 7 8 9 });
Debug Integration
When @xpulse/debug is installed as a devDependency, @xpulse/config
automatically provides a DataCollector. @xpulse/debug discovers it via
node_modules/@xpulse/config/src/datacollectors/ β no configuration needed.
1 "optionalDependencies" : { 2 "@xpulse/debug" : "^1.0.0" 3 }
The ConfigCollector (name: 'config', icon: 'π§') shows in the Web Profiler:
Summary (kv): Name, Type, ENV (NODE_ENV or APP_ENV), debug config,
HTTP port, log enabled, log level
Full Config (resolved) (raw): the complete config.all() output as
formatted JSON β including DEFAULTS and resolved ${ENV_VAR} values
Object values in the summary (e.g. the debug block) are serialized as JSON.
No toolbar badge.
Package Structure
1 @xpulse/config/ 2 src / 3 index.js β default export: config object 4 defaults.js β fallback values (internal) 5 resolver.js β ${VAR} resolution (internal) 6 validator.js β validate standard keys (internal) 7 test/ 8 config.test .js 9 resolver.test .js 10 validator.test .js 11 docs/ 12 index.md 13 api.md 14 _meta.json 15 Dockerfile 16 Makefile 17 README.md 18 package.json 19 xpulse.json