Registers a module descriptor with the registry.
Idempotent: a module already registered under the same id wins, and a second call is a
no-op.
Registering after init() — a plugin whose bundle loads on demand — stores the module.
A lifecycle module is not initialised for the application already running, which it
joined by itself, but destroy() reaches it: it is torn down with the others, and
initialised like them from the next init() on. A UI slot is not drawn before the next
mount, the toolbar being built once per boot, and a warning says so.
Throws a GeoLeafError when the module is neither a lifecycle module (init and
destroy, both functions) nor a UI-only slot ({ id, ui }).
The module to register.
Resolves the dependency graph and initialises all registered modules in topological order.
Rejects with a GeoLeafError if a circular dependency is detected — the message
includes the full cycle path (e.g. "A → B → A") — or if a module declares a
dependency that is not registered.
Returns a Promise that resolves when all modules have completed init().
If any module's init() rejects, the returned Promise rejects with
that error (subsequent modules are not initialised) — unless
options.onModuleError answers "continue": the failure is then isolated to that
module and to the modules that depend on it (see ModuleInitOptions).
Engine-agnostic map adapter.
Read-only configuration access.
Optionaloptions: ModuleInitOptions
onModuleError, to isolate a module failure instead of rejecting.
Retrieves a registered module by its id, cast to type T.
Throws a GeoLeafError when no module with the given id is registered.
Use has() first when presence is uncertain.
The module's id string.
Returns true if a module with the given id is registered.
The module's id string.
Returns true if init() has already been called on this registry.
Use this guard before calling register() in contexts where the registry
may have already been initialised (e.g. double-boot scenarios).
Returns all registered modules in insertion order.
The returned array is read-only — use register() to add modules.
Collects and returns the ui slot of every module that declares one.
Used by the UI orchestrator to build the mobile toolbar and the filter panel tabs without coupling the UI layer to specific module classes.
Returns an empty array when no module declares a UI slot.
Destroys the modules that started, in reverse initialisation order, and re-arms the
registry: a later init() runs every module again.
Each module's destroy() is called exactly once. Errors in individual
destroy() calls are logged but do not interrupt the teardown sequence —
all modules are destroyed regardless of individual failures. A module that never ran is
not destroyed: one skipped because a module it depends on failed (see
ModuleInitOptions), or one an initialisation stopped by shouldContinue never
reached.
Refused — logged, nothing torn down — while init() is still running.
Returns the schema of a registered module, or null if no module
with the given id is registered.
The returned object is a lightweight metadata view — it does not expose
internal module state. IModuleSchema is enriched as capability
schemas (config, capabilities, metadata) get defined.
Module id (e.g. 'poi', 'route', 'search').
Returns a read-only snapshot of the modules that are running, in insertion order.
Includes both built-in modules (registered before init()) and lazily
registered plugin modules (registered after init()). Leaves out a module whose
capability gate (isEnabled()) answers false and — under
onModuleError: () => "continue" — a module whose init() failed and the modules
skipped because they depend on it.
Orchestrates the GeoLeaf module lifecycle.
The registry is the single entry point for module management:
init()is called.init(), the registry performs a topological sort of all registered modules and calls eachmodule.init()in the resolved order.destroy(), modules are torn down in reverse initialisation order.The registry is exposed on the
GeoLeafnamespace to allow third-party modules to self-register:Example