var __defProp = Object.defineProperty;
var __returnValue = (v) => v;
function __exportSetter(name, newValue) {
  this[name] = __returnValue.bind(null, newValue);
}
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, {
      get: all[name],
      enumerable: true,
      configurable: true,
      set: __exportSetter.bind(all, name)
    });
};
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
  if (typeof require !== "undefined")
    return require.apply(this, arguments);
  throw Error('Dynamic require of "' + x + '" is not supported');
});

// src/logger.ts
function isProduction() {
  if (typeof process !== "undefined" && process.env) {
    return false;
  }
  return false;
}
function setLogLevel(level) {
  config.level = level;
}
function getLogLevel() {
  return config.level;
}
function configureLogger(options) {
  config = { ...config, ...options };
}
function enableLogging() {
  config.level = "debug";
}
function disableLogging() {
  config.level = "none";
}
function setDebugMode(enabled) {
  config.level = enabled ? "debug" : "error";
}
function isDebugMode() {
  return config.level === "debug";
}
function shouldLog(level) {
  return LOG_LEVEL_ORDER[level] >= LOG_LEVEL_ORDER[config.level];
}
function formatTag(tag, level) {
  const timestamp = config.timestamps ? `${new Date().toISOString()} ` : "";
  if (config.colors && level !== "none") {
    const color = LOG_LEVEL_COLORS[level];
    return `${timestamp}${color}[${tag}]${RESET_COLOR}`;
  }
  return `${timestamp}[${tag}]`;
}

class Logger {
  tag;
  constructor(tag) {
    this.tag = tag;
  }
  debug(...args) {
    if (!shouldLog("debug"))
      return;
    if (config.handler) {
      config.handler.debug(this.tag, ...args);
    } else {
      console.log(formatTag(this.tag, "debug"), ...args);
    }
  }
  info(...args) {
    if (!shouldLog("info"))
      return;
    if (config.handler) {
      config.handler.info(this.tag, ...args);
    } else {
      console.info(formatTag(this.tag, "info"), ...args);
    }
  }
  warn(...args) {
    if (!shouldLog("warn"))
      return;
    if (config.handler) {
      config.handler.warn(this.tag, ...args);
    } else {
      console.warn(formatTag(this.tag, "warn"), ...args);
    }
  }
  error(...args) {
    if (!shouldLog("error"))
      return;
    if (config.handler) {
      config.handler.error(this.tag, ...args);
    } else {
      console.error(formatTag(this.tag, "error"), ...args);
    }
  }
  time(label, fn) {
    if (!shouldLog("debug")) {
      return fn();
    }
    const start = performance.now();
    try {
      return fn();
    } finally {
      const duration = performance.now() - start;
      this.debug(`${label}: ${duration.toFixed(2)}ms`);
    }
  }
  async timeAsync(label, fn) {
    if (!shouldLog("debug")) {
      return fn();
    }
    const start = performance.now();
    try {
      return await fn();
    } finally {
      const duration = performance.now() - start;
      this.debug(`${label}: ${duration.toFixed(2)}ms`);
    }
  }
  child(subTag) {
    return new Logger(`${this.tag}:${subTag}`);
  }
  debugIf(condition, ...args) {
    if (condition)
      this.debug(...args);
  }
  warnIf(condition, ...args) {
    if (condition)
      this.warn(...args);
  }
  errorIf(condition, ...args) {
    if (condition)
      this.error(...args);
  }
  loggedOnce = new Set;
  warnOnce(key, ...args) {
    if (this.loggedOnce.has(key))
      return;
    this.loggedOnce.add(key);
    this.warn(...args);
  }
  debugOnce(key, ...args) {
    if (this.loggedOnce.has(key))
      return;
    this.loggedOnce.add(key);
    this.debug(...args);
  }
}
function createLogger(tag) {
  return new Logger(tag);
}
var LOG_LEVEL_ORDER, LOG_LEVEL_COLORS, RESET_COLOR = "\x1B[0m", config, logger, log, frameworkLoggers;
var init_logger = __esm(() => {
  LOG_LEVEL_ORDER = {
    debug: 0,
    info: 1,
    warn: 2,
    error: 3,
    none: 4
  };
  LOG_LEVEL_COLORS = {
    debug: "\x1B[36m",
    info: "\x1B[32m",
    warn: "\x1B[33m",
    error: "\x1B[31m"
  };
  config = {
    level: isProduction() ? "error" : "info",
    colors: true,
    timestamps: false
  };
  logger = createLogger("Hypen");
  log = {
    debug: (tag, ...args) => {
      if (!shouldLog("debug"))
        return;
      console.log(formatTag(tag, "debug"), ...args);
    },
    info: (tag, ...args) => {
      if (!shouldLog("info"))
        return;
      console.info(formatTag(tag, "info"), ...args);
    },
    warn: (tag, ...args) => {
      if (!shouldLog("warn"))
        return;
      console.warn(formatTag(tag, "warn"), ...args);
    },
    error: (tag, ...args) => {
      if (!shouldLog("error"))
        return;
      console.error(formatTag(tag, "error"), ...args);
    }
  };
  frameworkLoggers = {
    hypen: createLogger("Hypen"),
    engine: createLogger("Engine"),
    router: createLogger("Router"),
    state: createLogger("State"),
    events: createLogger("Events"),
    remote: createLogger("Remote"),
    renderer: createLogger("Renderer"),
    module: createLogger("Module"),
    lifecycle: createLogger("Lifecycle"),
    loader: createLogger("Loader"),
    context: createLogger("Context"),
    discovery: createLogger("Discovery"),
    plugin: createLogger("Plugin"),
    canvas: createLogger("Canvas"),
    debug: createLogger("Debug")
  };
});

// src/result.ts
function Ok(value) {
  return { ok: true, value };
}
function Err(error) {
  return { ok: false, error };
}
function classifyEngineError(err) {
  const message = err instanceof Error ? err.message : String(err);
  if (message.startsWith("Parse error:")) {
    return new ParseError(message);
  }
  if (message.startsWith("Invalid state:") || message.startsWith("Invalid state patch:")) {
    return new StateError(message);
  }
  if (message.startsWith("Parent node not found:")) {
    return new RenderError(message);
  }
  return new RenderError(message);
}
var HypenError, ActionError, ConnectionError, StateError, ParseError, RenderError;
var init_result = __esm(() => {
  HypenError = class HypenError extends Error {
    code;
    context;
    cause;
    constructor(code, message, options) {
      super(message);
      this.name = "HypenError";
      this.code = code;
      this.context = options?.context;
      this.cause = options?.cause;
      Object.setPrototypeOf(this, new.target.prototype);
    }
  };
  ActionError = class ActionError extends HypenError {
    actionName;
    constructor(actionName, cause) {
      super("ACTION_ERROR", `Action handler "${actionName}" failed: ${cause instanceof Error ? cause.message : String(cause)}`, {
        context: { actionName },
        cause: cause instanceof Error ? cause : undefined
      });
      this.name = "ActionError";
      this.actionName = actionName;
    }
  };
  ConnectionError = class ConnectionError extends HypenError {
    url;
    attempt;
    constructor(url, cause, attempt) {
      super("CONNECTION_ERROR", `Connection to "${url}" failed${attempt ? ` (attempt ${attempt})` : ""}: ${cause instanceof Error ? cause.message : String(cause)}`, {
        context: { url, attempt },
        cause: cause instanceof Error ? cause : undefined
      });
      this.name = "ConnectionError";
      this.url = url;
      this.attempt = attempt;
    }
  };
  StateError = class StateError extends HypenError {
    path;
    constructor(message, path, cause) {
      super("STATE_ERROR", message, {
        context: { path },
        cause: cause instanceof Error ? cause : undefined
      });
      this.name = "StateError";
      this.path = path;
    }
  };
  ParseError = class ParseError extends HypenError {
    source;
    constructor(message, source, cause) {
      super("PARSE_ERROR", message, {
        context: { source },
        cause: cause instanceof Error ? cause : undefined
      });
      this.name = "ParseError";
      this.source = source;
    }
  };
  RenderError = class RenderError extends HypenError {
    constructor(message, cause) {
      super("RENDER_ERROR", message, {
        cause: cause instanceof Error ? cause : undefined
      });
      this.name = "RenderError";
    }
  };
});

// src/disposable.ts
init_logger();
var log2 = frameworkLoggers.lifecycle;
function isDisposable(obj) {
  return obj !== null && typeof obj === "object" && "dispose" in obj && typeof obj.dispose === "function";
}

class DisposableStack {
  stack = [];
  disposed = false;
  add(disposable) {
    if (this.disposed) {
      disposable.dispose();
      return disposable;
    }
    this.stack.push(disposable);
    return disposable;
  }
  addCallback(callback) {
    this.add({ dispose: callback });
  }
  addValue(value, dispose) {
    this.add({ dispose: () => dispose(value) });
    return value;
  }
  dispose() {
    if (this.disposed)
      return;
    this.disposed = true;
    while (this.stack.length > 0) {
      const item = this.stack.pop();
      try {
        item.dispose();
      } catch (error) {
        log2.error("Error during dispose:", error);
      }
    }
  }
  get isDisposed() {
    return this.disposed;
  }
  get size() {
    return this.stack.length;
  }
}
function disposableListener(target, event, handler, options) {
  target.addEventListener(event, handler, options);
  return {
    dispose: () => target.removeEventListener(event, handler, options)
  };
}
function disposableTimeout(callback, ms) {
  const id = setTimeout(callback, ms);
  return {
    id,
    dispose: () => clearTimeout(id)
  };
}
function disposableInterval(callback, ms) {
  const id = setInterval(callback, ms);
  return {
    id,
    dispose: () => clearInterval(id)
  };
}
function disposableWebSocket(ws) {
  return {
    dispose: () => {
      if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
        ws.close();
      }
    }
  };
}
function disposableAbortController() {
  const controller = new AbortController;
  return {
    controller,
    signal: controller.signal,
    dispose: () => controller.abort()
  };
}
function disposableSubscription(unsubscribe) {
  return { dispose: unsubscribe };
}
var ELEMENT_DISPOSABLES = Symbol("hypen.disposables");
function getElementDisposables(element) {
  const existing = element[ELEMENT_DISPOSABLES];
  if (existing instanceof DisposableStack) {
    return existing;
  }
  const stack = new DisposableStack;
  element[ELEMENT_DISPOSABLES] = stack;
  return stack;
}
function disposeElement(element) {
  const stack = element[ELEMENT_DISPOSABLES];
  if (stack instanceof DisposableStack) {
    stack.dispose();
    delete element[ELEMENT_DISPOSABLES];
  }
}
function hasElementDisposables(element) {
  return element[ELEMENT_DISPOSABLES] instanceof DisposableStack;
}

class DisposableMixin {
  disposables = new DisposableStack;
  track(disposable) {
    return this.disposables.add(disposable);
  }
  onDispose(callback) {
    this.disposables.addCallback(callback);
  }
  dispose() {
    this.disposables.dispose();
  }
}
function compositeDisposable(...disposables) {
  return {
    dispose: () => {
      for (const d of disposables) {
        try {
          d.dispose();
        } catch (error) {
          log2.error("Error during dispose:", error);
        }
      }
    }
  };
}
async function using(resource, fn) {
  const r = typeof resource === "function" ? resource() : resource;
  try {
    return await fn(r);
  } finally {
    r.dispose();
  }
}
function usingSync(resource, fn) {
  const r = typeof resource === "function" ? resource() : resource;
  try {
    return fn(r);
  } finally {
    r.dispose();
  }
}

// src/remote/client.ts
init_result();

// src/retry.ts
init_result();
var DEFAULT_OPTIONS = {
  maxAttempts: 3,
  delayMs: 1000,
  backoff: "exponential",
  maxDelayMs: 30000,
  jitter: 0.1
};
function calculateDelay(attempt, options) {
  let delay;
  switch (options.backoff) {
    case "exponential":
      delay = options.delayMs * Math.pow(2, attempt - 1);
      break;
    case "linear":
      delay = options.delayMs * attempt;
      break;
    case "none":
      delay = options.delayMs;
      break;
  }
  if (options.jitter > 0) {
    const jitterRange = delay * options.jitter;
    delay += (Math.random() * 2 - 1) * jitterRange;
  }
  return Math.min(delay, options.maxDelayMs);
}
function sleep(ms, signal) {
  return new Promise((resolve, reject) => {
    if (signal?.aborted) {
      reject(new Error("Retry aborted"));
      return;
    }
    const timeoutId = setTimeout(resolve, ms);
    signal?.addEventListener("abort", () => {
      clearTimeout(timeoutId);
      reject(new Error("Retry aborted"));
    });
  });
}
async function retry(fn, options = {}) {
  const opts = { ...DEFAULT_OPTIONS, ...options };
  let lastError = new Error("No attempts made");
  for (let attempt = 1;attempt <= opts.maxAttempts; attempt++) {
    try {
      if (opts.signal?.aborted) {
        throw new Error("Retry aborted");
      }
      return await fn();
    } catch (e) {
      lastError = e instanceof Error ? e : new Error(String(e));
      if (opts.shouldRetry && !opts.shouldRetry(lastError)) {
        throw lastError;
      }
      if (attempt === opts.maxAttempts) {
        break;
      }
      const delayMs = calculateDelay(attempt, opts);
      opts.onRetry?.(attempt, lastError, delayMs);
      await sleep(delayMs, opts.signal);
    }
  }
  throw lastError;
}
var RetryConditions = {
  networkErrors: (error) => {
    const message = error.message.toLowerCase();
    return message.includes("network") || message.includes("fetch") || message.includes("timeout") || message.includes("econnrefused") || message.includes("econnreset") || message.includes("socket");
  },
  httpRetryable: (error) => {
    const status = error.status;
    if (!status)
      return false;
    return [408, 429, 500, 502, 503, 504].includes(status);
  },
  websocketErrors: (error) => {
    const message = error.message.toLowerCase();
    return message.includes("websocket") || message.includes("connection") || message.includes("close");
  },
  any: (...conditions) => (error) => conditions.some((c) => c(error)),
  all: (...conditions) => (error) => conditions.every((c) => c(error))
};
var RetryPresets = {
  aggressive: {
    maxAttempts: 10,
    delayMs: 500,
    backoff: "exponential",
    maxDelayMs: 60000,
    jitter: 0.2
  },
  conservative: {
    maxAttempts: 3,
    delayMs: 2000,
    backoff: "linear",
    maxDelayMs: 1e4,
    jitter: 0.1
  },
  fast: {
    maxAttempts: 5,
    delayMs: 100,
    backoff: "exponential",
    maxDelayMs: 2000,
    jitter: 0
  },
  websocket: {
    maxAttempts: 10,
    delayMs: 1000,
    backoff: "exponential",
    maxDelayMs: 30000,
    jitter: 0.1,
    shouldRetry: RetryConditions.websocketErrors
  }
};

// src/remote/client.ts
init_logger();
var log3 = frameworkLoggers.remote;

class RemoteEngine {
  ws = null;
  url;
  state = "disconnected";
  options;
  reconnectAttempts = 0;
  disposables = new DisposableStack;
  reconnectDisposable = null;
  currentSessionId = null;
  sessionOptions;
  patchCallbacks = [];
  stateCallbacks = [];
  connectionCallbacks = [];
  disconnectionCallbacks = [];
  errorCallbacks = [];
  sessionEstablishedCallbacks = [];
  sessionExpiredCallbacks = [];
  currentState = null;
  currentRevision = 0;
  moduleName = "";
  constructor(url, options = {}) {
    this.url = url;
    this.options = {
      autoReconnect: options.autoReconnect ?? true,
      reconnectInterval: options.reconnectInterval ?? 3000,
      maxReconnectAttempts: options.maxReconnectAttempts ?? 10,
      session: options.session
    };
    this.sessionOptions = options.session;
    if (options.session?.id) {
      this.currentSessionId = options.session.id;
    }
  }
  async connect() {
    if (this.state === "connected" || this.state === "connecting") {
      return Ok(undefined);
    }
    this.state = "connecting";
    return new Promise((resolve) => {
      try {
        this.ws = new WebSocket(this.url);
        this.disposables.add(disposableWebSocket(this.ws));
        const messageHandler = (event) => {
          this.handleMessage(event.data);
        };
        this.disposables.add(disposableListener(this.ws, "message", messageHandler));
        const errorHandler = () => {
          this.state = "error";
          const error = new ConnectionError(this.url, new Error("WebSocket error"));
          this.errorCallbacks.forEach((cb) => cb(error));
          resolve(Err(error));
        };
        this.disposables.add(disposableListener(this.ws, "error", errorHandler));
        const closeHandler = () => {
          this.state = "disconnected";
          this.disconnectionCallbacks.forEach((cb) => cb());
          this.attemptReconnect();
        };
        this.disposables.add(disposableListener(this.ws, "close", closeHandler));
        this.ws.onopen = () => {
          this.state = "connected";
          this.reconnectAttempts = 0;
          if (this.reconnectDisposable) {
            this.reconnectDisposable.dispose();
            this.reconnectDisposable = null;
          }
          this.sendHello();
          this.connectionCallbacks.forEach((cb) => cb());
          resolve(Ok(undefined));
        };
      } catch (e) {
        this.state = "error";
        const error = new ConnectionError(this.url, e);
        resolve(Err(error));
      }
    });
  }
  sendHello() {
    if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
      return;
    const hello = {
      type: "hello",
      sessionId: this.currentSessionId ?? this.sessionOptions?.id,
      props: this.sessionOptions?.props
    };
    this.ws.send(JSON.stringify(hello));
  }
  disconnect() {
    if (this.reconnectDisposable) {
      this.reconnectDisposable.dispose();
      this.reconnectDisposable = null;
    }
    if (this.ws) {
      if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
        this.ws.close();
      }
      this.ws = null;
    }
    this.state = "disconnected";
  }
  dispose() {
    this.disconnect();
    this.disposables.dispose();
  }
  dispatchAction(action, payload) {
    if (this.state !== "connected" || !this.ws) {
      log3.warn("Cannot dispatch action: not connected");
      return;
    }
    const message = {
      type: "dispatchAction",
      module: this.moduleName,
      action,
      payload
    };
    this.ws.send(JSON.stringify(message));
  }
  subscribeState() {
    if (this.state !== "connected" || !this.ws) {
      log3.warn("Cannot subscribe to state: not connected");
      return;
    }
    const message = { type: "subscribeState" };
    this.ws.send(JSON.stringify(message));
  }
  updateState(state) {
    if (this.state !== "connected" || !this.ws) {
      log3.warn("Cannot update state: not connected");
      return;
    }
    const message = {
      type: "updateState",
      module: this.moduleName,
      state
    };
    this.ws.send(JSON.stringify(message));
  }
  onPatches(callback) {
    this.patchCallbacks.push(callback);
    return this;
  }
  onStateUpdate(callback) {
    this.stateCallbacks.push(callback);
    return this;
  }
  onConnect(callback) {
    this.connectionCallbacks.push(callback);
    return this;
  }
  onDisconnect(callback) {
    this.disconnectionCallbacks.push(callback);
    return this;
  }
  onError(callback) {
    this.errorCallbacks.push(callback);
    return this;
  }
  onSessionEstablished(callback) {
    this.sessionEstablishedCallbacks.push(callback);
    return this;
  }
  onSessionExpired(callback) {
    this.sessionExpiredCallbacks.push(callback);
    return this;
  }
  getConnectionState() {
    return this.state;
  }
  getCurrentState() {
    return this.currentState;
  }
  getRevision() {
    return this.currentRevision;
  }
  getSessionId() {
    return this.currentSessionId;
  }
  handleMessage(data) {
    try {
      const message = JSON.parse(data);
      switch (message.type) {
        case "sessionAck":
          this.handleSessionAck(message);
          break;
        case "sessionExpired":
          this.handleSessionExpired(message);
          break;
        case "initialTree":
          this.handleInitialTree(message);
          break;
        case "patch":
          this.handlePatch(message);
          break;
        case "stateUpdate":
          this.currentState = message.state;
          this.stateCallbacks.forEach((cb) => cb(this.currentState));
          break;
      }
    } catch (e) {
      log3.error("Error handling remote message:", e);
      const error = e instanceof Error ? e : new Error(String(e));
      this.errorCallbacks.forEach((cb) => cb(error));
    }
  }
  handleSessionAck(message) {
    this.currentSessionId = message.sessionId;
    const info = {
      sessionId: message.sessionId,
      isNew: message.isNew,
      isRestored: message.isRestored
    };
    this.sessionEstablishedCallbacks.forEach((cb) => cb(info));
  }
  handleSessionExpired(message) {
    this.currentSessionId = null;
    this.sessionExpiredCallbacks.forEach((cb) => cb(message.reason));
  }
  handleInitialTree(message) {
    this.moduleName = message.module;
    this.currentState = message.state;
    this.currentRevision = message.revision;
    if (message.patches.length > 0) {
      this.patchCallbacks.forEach((cb) => cb(message.patches));
    }
    this.stateCallbacks.forEach((cb) => cb(message.state));
  }
  handlePatch(message) {
    if (message.revision <= this.currentRevision) {
      log3.warn(`Out of order patch: expected > ${this.currentRevision}, got ${message.revision}`);
      return;
    }
    this.currentRevision = message.revision;
    if (message.patches.length > 0) {
      this.patchCallbacks.forEach((cb) => cb(message.patches));
    }
  }
  attemptReconnect() {
    if (!this.options.autoReconnect) {
      return;
    }
    this.reconnectDisposable = disposableTimeout(() => {
      this.reconnectDisposable = null;
      retry(async () => {
        const result = await this.connect();
        if (!result.ok) {
          throw result.error;
        }
      }, {
        maxAttempts: this.options.maxReconnectAttempts,
        delayMs: this.options.reconnectInterval,
        backoff: "exponential",
        maxDelayMs: 30000,
        jitter: 0.1,
        onRetry: (attempt, error) => {
          log3.debug(`Reconnection attempt ${attempt}/${this.options.maxReconnectAttempts} failed: ${error.message}`);
        }
      }).catch((error) => {
        log3.error("Max reconnection attempts reached:", error.message);
        this.errorCallbacks.forEach((cb) => cb(new ConnectionError(this.url, error, this.options.maxReconnectAttempts)));
      });
    }, this.options.reconnectInterval);
  }
}
export {
  RemoteEngine
};

//# debugId=C5E3F786F5F26DAB64756E2164756E21
