Source: lib/core/runtime/navigation/MemoryNavigationDelegate.js

import { NavigationDelegate } from './NavigationDelegate.js';

/**
 * In-memory navigation delegate for Node.js / SSR / headless environments.
 * Manages route location state without DOM or window dependencies.
 * @augments NavigationDelegate
 */
export class MemoryNavigationDelegate extends NavigationDelegate {
  /**
   * @param {string} [initialHash] - Initial location hash string.
   */
  constructor(initialHash = '#/') {
    super();
    const initial = initialHash || '#/';
    this.history = [initial];
    this.cursorIndex = 0;
    this.currentHash = initial;
    this.title = '';
    this.hashListeners = new Set();
    this.linkClickListeners = new Set();
    this.activeRouters = new Set();
  }

  /** @override */
  getHash() {
    return this.history[this.cursorIndex] || this.currentHash || '#/';
  }

  /** @override */
  setHash(hash, options = {}) {
    const targetHash = hash || '#/';
    if (options.replace) {
      if (this.history.length === 0) {
        this.history.push(targetHash);
        this.cursorIndex = 0;
      } else {
        this.history[this.cursorIndex] = targetHash;
      }
    } else {
      this.history = this.history.slice(0, this.cursorIndex + 1);
      this.history.push(targetHash);
      this.cursorIndex = this.history.length - 1;
    }
    this.currentHash = this.history[this.cursorIndex];
    for (const listener of this.hashListeners) {
      listener(this.currentHash);
    }
  }

  /** @override */
  back() {
    this.go(-1);
  }

  /** @override */
  forward() {
    this.go(1);
  }

  /** @override */
  go(delta) {
    if (typeof delta !== 'number' || isNaN(delta) || delta === 0) {
      return;
    }
    const targetIndex = this.cursorIndex + delta;
    if (targetIndex < 0 || targetIndex >= this.history.length) {
      return;
    }
    this.cursorIndex = targetIndex;
    this.currentHash = this.history[this.cursorIndex];
    for (const listener of this.hashListeners) {
      listener(this.currentHash);
    }
  }

  /** @override */
  onHashChange(callback) {
    this.hashListeners.add(callback);
    return () => this.hashListeners.delete(callback);
  }

  /** @override */
  onLinkClick(callback) {
    this.linkClickListeners.add(callback);
    return () => this.linkClickListeners.delete(callback);
  }

  /** @override */
  setTitle(title) {
    this.title = title;
  }

  /** @override */
  registerRouter(router) {
    this.activeRouters.add(router);
  }

  /** @override */
  unregisterRouter(router) {
    this.activeRouters.delete(router);
  }

  /** @override */
  getActiveRouters() {
    return this.activeRouters;
  }

  /** @override */
  destroy() {
    this.hashListeners.clear();
    this.linkClickListeners.clear();
    this.activeRouters.clear();
  }
}