The Lifecycle Contract for Data Overlays in the DataLayerManager: 4 States Explained

The DataLayerManager in the gods-eye-view repository enforces a strict lifecycle contract that moves every data overlay through disabled, enabling, enabled, and disabling states, using a serial queue and deterministic event pipeline to prevent race conditions and guarantee safe transitions.

The DataLayerManager class defined in src/data/manager.js governs every real-time data overlay rendered on the CesiumJS globe in bilawalsidhu/gods-eye-view. Its lifecycle contract for data overlays specifies exactly how an overlay advances from initialization through activation to teardown, while an “uncertain” flag captures any transition that fails or aborts. Understanding these states and their enforcement mechanisms is essential for building reliable UI controls and custom data modules.

Four Primary Lifecycle States

The contract defines four mutually exclusive states stored in the lifecycleState field, declared at src/data/manager.js#L78-L88.

disabled

This is the default and terminal rest state. An overlay in the disabled state is fully off, releases resources, and does not render. The manager sets this state after a successful disable() call or during initialization.

enabling

The enabling state means the manager is actively executing the enable sequence: init → enable → first update. The helper _setLifecycleTransition at src/data/manager.js#L62-L71 records this transition and immediately notifies listening UI panels via _refreshTogglePanel.

enabled

Once the enable sequence completes without error, _settleLifecycle at src/data/manager.js#L74-L85 finalizes the entry as enabled. In this state the overlay is visible, actively polling for periodic updates, and fully integrated with the CesiumJS globe.

disabling

The disabling state indicates an in-progress teardown. The manager enters this state through _setLifecycleTransition(..., 'disabling') while the overlay module’s disable logic runs.

lifecycleUncertain Flag

Whenever a lifecycle step throws an exception or returns false—such as a rejected enable, disable, or init—the manager sets entry.lifecycleUncertain = true. According to the contract comment block at src/data/manager.js#L15-L21, this flag signals that the overlay’s authoritative state cannot be trusted and the transition should be treated as failed.

Serial Queue and Phase Pipeline Enforcement

The lifecycle contract is enforced by a serial queue stored on each overlay entry as entry.toggleChain. This queue guarantees that only one lifecycle transition runs at a time for a given overlay, eliminating race conditions like double-polling or rapid enable-then-disable glitches.

Each transition follows a deterministic six-phase pipeline:

  1. Visibility-Will-Change – The manager emits a visibility-will-change event so listeners can prepare.
  2. Guard Check – _visibilityBlockReason may abort the transition before any module code runs.
  3. Lifecycle Transition – _setLifecycleTransition records the new state (enabling or disabling) and refreshes toggle panels.
  4. Module Lifecycle Calls – The overlay’s module receives ordered calls such as entry.module.init(), entry.module.enable(), entry.module.update(), or entry.module.disable(). Concrete implementations like the OpenSky traffic feed in src/data/traffic.js implement these hooks.
  5. Error Handling – Any thrown exception or rejected promise triggers a visibility-failed event and sets entry.lifecycleUncertain = true.
  6. Settlement – On success, _settleLifecycle records the final enabled or disabled state and clears uncertainty.

Event emission for these phases is centralized at src/data/manager.js#L490-L517.

Public API for Data Overlay Lifecycle Control

The contract is surfaced through two public methods that both return promises resolving only after the lifecycle settles.

setEnabled

setEnabled(layerId, shouldEnable, options) requests an absolute state change. Pass true to activate an overlay or false to deactivate it.

// Enable the OpenSky traffic overlay
await dataManager.setEnabled('openSky', true, {
  origin: 'user',
  notificationToken: 'ui-button',
});

// Disable the same overlay
await dataManager.setEnabled('openSky', false, {
  origin: 'user',
  notificationToken: 'ui-button',
});

toggle

toggle(layerId, options) flips the current effective visibility without requiring the caller to know the existing state. Implemented at src/data/manager.js#L174-L190, it delegates to the same internal pipeline used by setEnabled.

// Toggle the overlay relative to its current state
await dataManager.toggle('openSky', { origin: 'user' });

Both methods return a promise that resolves after the manager has moved through the appropriate lifecycle phases, guaranteeing the overlay is either fully active or fully inactive.

Event-Driven State Synchronization

UI components such as those in src/ui.js consume a structured event stream to stay synchronized with the lifecycle contract. The manager emits the following events during transitions:

  • visibility-will-change – Fired before the guard check and module calls begin.
  • visibility – Fired after successful settlement.
  • visibility-failed – Fired when a lifecycle step throws or rejects.
  • visibility-cancelled – Fired when a transition is aborted by user action or logic.
  • visibility-blocked – Fired when _visibilityBlockReason prevents the transition.

Listeners can use these events to update toggle buttons, persist user preferences, or display error indicators without polling the manager directly.

Summary

  • The DataLayerManager in src/data/manager.js defines a strict lifecycle contract for data overlays with four primary states: disabled, enabling, enabled, and disabling.
  • A serial queue (entry.toggleChain) prevents concurrent transitions and eliminates race conditions.
  • The six-phase pipeline includes event emission, guard checks, state recording via _setLifecycleTransition, module hooks, error handling, and final settlement via _settleLifecycle.
  • The uncertain flag (entry.lifecycleUncertain) marks entries where a lifecycle step failed or returned false.
  • Public methods setEnabled and toggle return promises that resolve only after the lifecycle settles, providing a safe async boundary for UI code.

Frequently Asked Questions

What happens if an overlay fails during the enable sequence?

If init, enable, or the first update throws or returns false, the manager emits a visibility-failed event and sets entry.lifecycleUncertain = true according to src/data/manager.js#L74-L85. The overlay does not reach the enabled state, and the entry remains in an untrustworthy condition until the next successful transition.

How does the DataLayerManager prevent rapid toggle race conditions?

Each overlay entry owns a serial queue (entry.toggleChain) that chains lifecycle transitions sequentially. Because the pipeline enforces one transition at a time, rapid calls to setEnabled or toggle cannot overlap or execute out of order, preventing double-polling and visual glitches.

What is the difference between setEnabled and toggle?

setEnabled(layerId, shouldEnable) drives the overlay toward an absolute boolean state and is used when the caller already knows the desired outcome. toggle(layerId), defined at src/data/manager.js#L174-L190, determines the current effective visibility and then delegates to the same internal pipeline to flip it, making it ideal for simple button interactions.

Which lifecycle events should a UI panel listen for?

Panels should listen for visibility-will-change to show loading indicators, visibility to confirm active state, visibility-failed to display errors, and visibility-blocked to explain why a transition was prevented. The src/ui.js file demonstrates this pattern by reacting to these events to refresh controls and panels.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →