Hone logo
Hone
Problems

Time-Travel Debugger in JavaScript

Imagine you're debugging a complex application where state changes happen rapidly and are difficult to trace. A "time-travel debugger" allows you to step backward and forward through the application's state history, inspecting variables and understanding how the application arrived at its current state. This challenge asks you to build a simplified version of such a debugger, focusing on tracking and replaying state changes.

Problem Description

You are tasked with creating a TimeTravelDebugger class in JavaScript. This class will track the state of an application over time, allowing you to "travel" to different points in its history. The debugger should be able to:

  1. Record State: Accept a function appUpdate that represents a single update to the application's state. Each time recordState is called with appUpdate, the debugger should execute appUpdate and store the resulting state along with the previous state.
  2. Travel Through Time: Provide methods to move forward (nextState()) and backward (previousState()) through the recorded states.
  3. Inspect Current State: Offer a method to retrieve the current state (getCurrentState()).
  4. Reset: Provide a method to clear the history and start fresh.

The appUpdate function should accept the previous state as its argument and return the new state. The debugger should maintain a history of states, allowing for both forward and backward traversal.

Key Requirements:

  • The debugger must handle cases where there are no previous states (at the beginning of the history) or no future states (at the end of the history).
  • The debugger should not modify the original appUpdate function.
  • The debugger should store the result of appUpdate as the state, not the function itself.

Expected Behavior:

  • recordState should execute the provided function and store the new state.
  • nextState should move to the next state in the history, if available.
  • previousState should move to the previous state in the history, if available.
  • getCurrentState should return the current state.
  • reset should clear the history.

Edge Cases to Consider:

  • Calling nextState() or previousState() when at the beginning or end of the history.
  • appUpdate functions that return the same state repeatedly.
  • appUpdate functions that throw errors. (The debugger should gracefully handle these, potentially by stopping state recording.)

Examples

Example 1:

Input:
const appUpdate = (prevState) => prevState + 1;
const debugger = new TimeTravelDebugger(appUpdate);
debugger.recordState();
debugger.recordState();
debugger.recordState();

Output:
debugger.getCurrentState() // 3
debugger.nextState();
debugger.getCurrentState() // 4
debugger.previousState();
debugger.getCurrentState() // 3

Explanation: The appUpdate function increments the previous state. The debugger records three states (0, 1, 2, 3). nextState() moves to 4, and previousState() moves back to 3.

Example 2:

Input:
const appUpdate = (prevState) => {
  if (prevState < 5) {
    return prevState + 1;
  } else {
    return prevState;
  }
};
const debugger = new TimeTravelDebugger(appUpdate);
debugger.recordState();
debugger.recordState();
debugger.recordState();
debugger.recordState();
debugger.recordState();

Output:
debugger.getCurrentState() // 5
debugger.nextState();
debugger.getCurrentState() // 5
debugger.previousState();
debugger.previousState();
debugger.getCurrentState() // 4

Explanation: The appUpdate function increments the state until it reaches 5, then stops. The debugger records states 0, 1, 2, 3, 4, 5. After reaching 5, nextState() has no effect.

Example 3: (Edge Case)

Input:
const appUpdate = (prevState) => prevState; // Returns the same state
const debugger = new TimeTravelDebugger(appUpdate);
debugger.recordState();
debugger.recordState();

Output:
debugger.getCurrentState() // 0
debugger.nextState();
debugger.getCurrentState() // 0
debugger.previousState();
debugger.getCurrentState() // 0

Explanation: The appUpdate function always returns the same state. The debugger records two identical states. Moving forward or backward has no effect.

Constraints

  • The appUpdate function should be a pure function (no side effects).
  • The debugger should maintain a maximum history of 100 states to prevent excessive memory usage.
  • The appUpdate function should not take more than 100ms to execute. (This is a design consideration for the appUpdate function, not a constraint on the debugger itself, but it's important to be aware of).
  • The debugger should handle errors thrown by appUpdate gracefully, preventing the debugger from crashing.

Notes

  • Consider using an array to store the state history.
  • Think about how to handle the initial state (before any recordState calls). You can either require an initial state to be passed to the constructor or assume a default initial state (e.g., null or undefined).
  • Error handling is important. Consider what should happen if appUpdate throws an error. Should the debugger stop recording states? Should it continue?
  • This is a simplified debugger. A real debugger would have many more features, such as breakpoints, variable inspection, and more sophisticated stepping capabilities. Focus on the core functionality of state tracking and time travel.
Loading editor...
javascript