var __defProp = Object.defineProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, {
      get: all[name],
      enumerable: true,
      configurable: true,
      set: (newValue) => all[name] = () => newValue
    });
};
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);

// src/state.ts
function deepClone(obj) {
  if (obj === null || typeof obj !== "object") {
    return obj;
  }
  if (typeof obj === "function") {
    return obj;
  }
  if (typeof obj.__getSnapshot === "function") {
    return obj.__getSnapshot();
  }
  if (obj instanceof WeakMap || obj instanceof WeakSet) {
    return obj;
  }
  const visited = new WeakMap;
  function cloneInternal(value) {
    if (value === null || typeof value !== "object") {
      return value;
    }
    if (typeof value === "function") {
      return value;
    }
    if (visited.has(value)) {
      return visited.get(value);
    }
    if (value instanceof WeakMap || value instanceof WeakSet) {
      return value;
    }
    if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
      try {
        return structuredClone(value);
      } catch {}
    }
    if (Array.isArray(value)) {
      const arrClone = [];
      visited.set(value, arrClone);
      for (let i = 0;i < value.length; i++) {
        arrClone[i] = cloneInternal(value[i]);
      }
      return arrClone;
    }
    const objClone = {};
    visited.set(value, objClone);
    for (const key in value) {
      if (Object.prototype.hasOwnProperty.call(value, key)) {
        objClone[key] = cloneInternal(value[key]);
      }
    }
    const symbolKeys = Object.getOwnPropertySymbols(value);
    for (const sym of symbolKeys) {
      objClone[sym] = cloneInternal(value[sym]);
    }
    return objClone;
  }
  return cloneInternal(obj);
}
function diffState(oldState, newState, basePath = "") {
  const paths = [];
  const newValues = {};
  function diff(oldVal, newVal, path) {
    if (oldVal === newVal)
      return;
    if (typeof oldVal !== "object" || typeof newVal !== "object" || oldVal === null || newVal === null) {
      if (oldVal !== newVal) {
        paths.push(path);
        newValues[path] = newVal;
      }
      return;
    }
    if (Array.isArray(oldVal) || Array.isArray(newVal)) {
      if (!Array.isArray(oldVal) || !Array.isArray(newVal) || oldVal.length !== newVal.length) {
        paths.push(path);
        newValues[path] = newVal;
        return;
      }
      for (let i = 0;i < newVal.length; i++) {
        const itemPath = path ? `${path}.${i}` : `${i}`;
        diff(oldVal[i], newVal[i], itemPath);
      }
      return;
    }
    const oldKeys = new Set(Object.keys(oldVal));
    const newKeys = new Set(Object.keys(newVal));
    for (const key of newKeys) {
      const propPath = path ? `${path}.${key}` : key;
      if (!oldKeys.has(key)) {
        paths.push(propPath);
        newValues[propPath] = newVal[key];
      } else {
        diff(oldVal[key], newVal[key], propPath);
      }
    }
    for (const key of oldKeys) {
      if (!newKeys.has(key)) {
        const propPath = path ? `${path}.${key}` : key;
        paths.push(propPath);
        newValues[propPath] = undefined;
      }
    }
  }
  diff(oldState, newState, basePath);
  return { paths, newValues };
}
function createObservableState(initialState, options) {
  const opts = options || { onChange: () => {} };
  if (initialState === null || initialState === undefined) {
    initialState = {};
  }
  if (initialState instanceof Number || initialState instanceof String || initialState instanceof Boolean) {
    throw new TypeError("Cannot create observable state from primitive wrapper objects (Number, String, Boolean). " + "Use plain primitives or regular objects instead.");
  }
  initialState = deepClone(initialState);
  let lastSnapshot = deepClone(initialState);
  const pathPrefix = opts.pathPrefix || "";
  let batchDepth = 0;
  let pendingChange = null;
  function notifyChange() {
    if (batchDepth > 0)
      return;
    const change = diffState(lastSnapshot, state, pathPrefix);
    if (change.paths.length > 0) {
      lastSnapshot = deepClone(state);
      if (pendingChange) {
        change.paths.push(...pendingChange.paths);
        Object.assign(change.newValues, pendingChange.newValues);
        pendingChange = null;
      }
      opts.onChange(change);
    }
  }
  let notificationPending = false;
  function scheduleBatch() {
    if (batchDepth === 0) {
      if (!notificationPending) {
        notificationPending = true;
        queueMicrotask(() => {
          notificationPending = false;
          if (batchDepth === 0) {
            notifyChange();
          }
        });
      }
    }
  }
  const proxyCache = new WeakMap;
  function createProxy(target, basePath) {
    const cached = proxyCache.get(target);
    if (cached)
      return cached;
    const proxy = new Proxy(target, {
      get(obj, prop) {
        if (prop === IS_PROXY)
          return true;
        if (prop === RAW_TARGET)
          return obj;
        if (prop === "__beginBatch") {
          return () => {
            batchDepth++;
          };
        }
        if (prop === "__endBatch") {
          return () => {
            batchDepth--;
            if (batchDepth === 0) {
              notifyChange();
            }
          };
        }
        if (prop === "__getSnapshot") {
          return () => deepClone(obj);
        }
        const value = obj[prop];
        if (value && typeof value === "object") {
          if (value[IS_PROXY]) {
            return value;
          }
          if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet) {
            return value;
          }
          const cachedNested = proxyCache.get(value);
          if (cachedNested) {
            return cachedNested;
          }
          const nestedProxy = createProxy(value, basePath ? `${basePath}.${String(prop)}` : String(prop));
          return nestedProxy;
        }
        return value;
      },
      set(obj, prop, value) {
        const oldValue = obj[prop];
        if (value && typeof value === "object" && value[IS_PROXY]) {
          value = value[RAW_TARGET];
        }
        obj[prop] = value;
        if (oldValue !== value) {
          scheduleBatch();
        }
        return true;
      },
      deleteProperty(obj, prop) {
        if (prop in obj) {
          delete obj[prop];
          scheduleBatch();
        }
        return true;
      }
    });
    proxyCache.set(target, proxy);
    return proxy;
  }
  const state = createProxy(initialState, pathPrefix);
  return state;
}
function batchStateUpdates(state, fn) {
  const s = state;
  if (s.__beginBatch && s.__endBatch) {
    s.__beginBatch();
    try {
      fn();
    } finally {
      s.__endBatch();
    }
  } else {
    fn();
  }
}
function getStateSnapshot(state) {
  const s = state;
  if (s.__getSnapshot) {
    return s.__getSnapshot();
  }
  return deepClone(state);
}
function isStateProxy(value) {
  return value !== null && typeof value === "object" && value[IS_PROXY] === true;
}
function unwrapProxy(value) {
  if (value !== null && typeof value === "object" && value[IS_PROXY]) {
    return value[RAW_TARGET];
  }
  return value;
}
var IS_PROXY, RAW_TARGET;
var init_state = __esm(() => {
  IS_PROXY = Symbol.for("hypen.isProxy");
  RAW_TARGET = Symbol.for("hypen.rawTarget");
});

// src/result.ts
function Ok(value) {
  return { ok: true, value };
}
function Err(error) {
  return { ok: false, error };
}
function isOk(result) {
  return result.ok;
}
function isErr(result) {
  return !result.ok;
}
async function fromPromise(promise, mapError) {
  try {
    const value = await promise;
    return Ok(value);
  } catch (e) {
    if (mapError) {
      return Err(mapError(e));
    }
    return Err(e);
  }
}
function fromTry(fn, mapError) {
  try {
    return Ok(fn());
  } catch (e) {
    if (mapError) {
      return Err(mapError(e));
    }
    return Err(e);
  }
}
function map(result, fn) {
  if (result.ok) {
    return Ok(fn(result.value));
  }
  return result;
}
function mapErr(result, fn) {
  if (!result.ok) {
    return Err(fn(result.error));
  }
  return result;
}
function flatMap(result, fn) {
  if (result.ok) {
    return fn(result.value);
  }
  return result;
}
function unwrap(result) {
  if (result.ok) {
    return result.value;
  }
  throw result.error;
}
function unwrapOr(result, defaultValue) {
  if (result.ok) {
    return result.value;
  }
  return defaultValue;
}
function unwrapOrElse(result, fn) {
  if (result.ok) {
    return result.value;
  }
  return fn(result.error);
}
function match(result, handlers) {
  if (result.ok) {
    return handlers.ok(result.value);
  }
  return handlers.err(result.error);
}
function all(results) {
  const values = [];
  for (const result of results) {
    if (!result.ok) {
      return result;
    }
    values.push(result.value);
  }
  return Ok(values);
}
var HypenError, ActionError, ConnectionError, StateError;
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;
    }
  };
});

// 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 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" : "debug",
    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 = {
    engine: createLogger("Engine"),
    router: createLogger("Router"),
    state: createLogger("State"),
    events: createLogger("Events"),
    remote: createLogger("Remote"),
    renderer: createLogger("Renderer"),
    module: createLogger("Module"),
    lifecycle: createLogger("Lifecycle")
  };
});

// src/app.ts
var exports_app = {};
__export(exports_app, {
  app: () => app,
  HypenModuleInstance: () => HypenModuleInstance,
  HypenAppBuilder: () => HypenAppBuilder,
  HypenApp: () => HypenApp
});

class HypenAppBuilder {
  initialState;
  options;
  createdHandler;
  actionHandlers = new Map;
  destroyedHandler;
  disconnectHandler;
  reconnectHandler;
  expireHandler;
  errorHandler;
  template;
  constructor(initialState, options) {
    this.initialState = initialState;
    this.options = options || {};
  }
  onCreated(fn) {
    this.createdHandler = fn;
    return this;
  }
  onAction(name, fn) {
    this.actionHandlers.set(name, fn);
    return this;
  }
  onDestroyed(fn) {
    this.destroyedHandler = fn;
    return this;
  }
  onDisconnect(fn) {
    this.disconnectHandler = fn;
    return this;
  }
  onReconnect(fn) {
    this.reconnectHandler = fn;
    return this;
  }
  onExpire(fn) {
    this.expireHandler = fn;
    return this;
  }
  onError(fn) {
    this.errorHandler = fn;
    return this;
  }
  ui(template) {
    this.template = template;
    return this.build();
  }
  build() {
    const stateKeys = this.initialState !== null && typeof this.initialState === "object" ? Object.keys(this.initialState) : [];
    return {
      name: this.options.name,
      actions: Array.from(this.actionHandlers.keys()),
      stateKeys,
      persist: this.options.persist,
      version: this.options.version,
      initialState: this.initialState,
      template: this.template,
      handlers: {
        onCreated: this.createdHandler,
        onAction: this.actionHandlers,
        onDestroyed: this.destroyedHandler,
        onDisconnect: this.disconnectHandler,
        onReconnect: this.reconnectHandler,
        onExpire: this.expireHandler,
        onError: this.errorHandler
      }
    };
  }
}

class HypenApp {
  defineState(initial, options) {
    return new HypenAppBuilder(initial, options);
  }
}

class HypenModuleInstance {
  engine;
  definition;
  state;
  isDestroyed = false;
  routerContext;
  globalContext;
  stateChangeCallbacks = [];
  constructor(engine, definition, routerContext, globalContext) {
    this.engine = engine;
    this.definition = definition;
    this.routerContext = routerContext;
    this.globalContext = globalContext;
    this.state = createObservableState(definition.initialState, {
      onChange: (change) => {
        this.engine.notifyStateChange(change.paths, change.newValues);
        this.stateChangeCallbacks.forEach((cb) => cb());
      }
    });
    this.engine.setModule(definition.name || "AnonymousModule", definition.actions, definition.stateKeys, getStateSnapshot(this.state));
    for (const [actionName, handler] of definition.handlers.onAction) {
      log2.debug(`Registering action handler: ${actionName} for module ${definition.name}`);
      this.engine.onAction(actionName, async (action) => {
        log2.debug(`Action handler fired: ${actionName}`, action);
        const actionCtx = {
          name: action.name,
          payload: action.payload,
          sender: action.sender
        };
        const next = {
          router: this.routerContext?.root || null
        };
        const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
        const result = await this.executeAction(actionName, handler, {
          action: actionCtx,
          state: this.state,
          next,
          context
        });
        if (!result.ok) {
          const shouldRethrow = await this.handleError(result.error, { actionName });
          if (shouldRethrow) {
            throw result.error;
          }
        } else {
          log2.debug(`Action handler completed: ${actionName}`);
        }
      });
    }
    this.callCreatedHandler();
  }
  createGlobalContextAPI() {
    if (!this.globalContext) {
      throw new Error("Global context not available");
    }
    const ctx = this.globalContext;
    const api = {
      getModule: (id) => ctx.getModule(id),
      hasModule: (id) => ctx.hasModule(id),
      getModuleIds: () => ctx.getModuleIds(),
      getGlobalState: () => ctx.getGlobalState(),
      emit: (event, payload) => ctx.emit(event, payload),
      on: (event, handler) => ctx.on(event, handler)
    };
    if (ctx.__router) {
      api.__router = ctx.__router;
    }
    if (ctx.__hypenEngine) {
      api.__hypenEngine = ctx.__hypenEngine;
    }
    return api;
  }
  async executeAction(actionName, handler, ctx) {
    try {
      const result = handler(ctx);
      await result;
      return Ok(undefined);
    } catch (e) {
      return Err(new ActionError(actionName, e));
    }
  }
  async handleError(error, context) {
    const errorCtx = {
      error,
      state: this.state,
      actionName: context.actionName,
      lifecycle: context.lifecycle
    };
    if (this.definition.handlers.onError) {
      try {
        const result = await this.definition.handlers.onError(errorCtx);
        if (result && typeof result === "object") {
          if ("handled" in result && result.handled) {
            return false;
          }
          if ("rethrow" in result && result.rethrow) {
            return true;
          }
        }
      } catch (handlerError) {
        log2.error("Error in onError handler:", handlerError);
      }
    }
    if (this.globalContext) {
      const eventContext = context.actionName ? `action:${context.actionName}` : context.lifecycle ? `lifecycle:${context.lifecycle}` : "unknown";
      this.globalContext.emit("error", {
        message: error.message,
        error,
        context: eventContext
      });
    }
    log2.error(`${context.actionName ? `Action "${context.actionName}"` : `Lifecycle "${context.lifecycle}"`} error:`, error);
    return false;
  }
  async callCreatedHandler() {
    if (this.definition.handlers.onCreated) {
      const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
      await this.definition.handlers.onCreated(this.state, context);
    }
  }
  onStateChange(callback) {
    this.stateChangeCallbacks.push(callback);
  }
  async destroy() {
    if (this.isDestroyed)
      return;
    if (this.definition.handlers.onDestroyed) {
      await this.definition.handlers.onDestroyed(this.state);
    }
    this.isDestroyed = true;
  }
  getState() {
    return getStateSnapshot(this.state);
  }
  getLiveState() {
    return this.state;
  }
  updateState(patch) {
    Object.assign(this.state, patch);
  }
}
var log2, app;
var init_app = __esm(() => {
  init_result();
  init_state();
  init_logger();
  log2 = createLogger("ModuleInstance");
  app = new HypenApp;
});

// src/engine.ts
import { WasmEngine } from "../wasm-node/hypen_engine.js";
function unwrapForWasm(value) {
  if (value === null || typeof value !== "object") {
    return value;
  }
  if (typeof value.__getSnapshot === "function") {
    return value.__getSnapshot();
  }
  try {
    return structuredClone(value);
  } catch {
    return JSON.parse(JSON.stringify(value));
  }
}

class Engine {
  wasmEngine = null;
  initialized = false;
  async init() {
    if (this.initialized)
      return;
    this.wasmEngine = new WasmEngine;
    this.initialized = true;
  }
  ensureInitialized() {
    if (!this.wasmEngine) {
      throw new Error("Engine not initialized. Call init() first.");
    }
    return this.wasmEngine;
  }
  setRenderCallback(callback) {
    const engine = this.ensureInitialized();
    engine.setRenderCallback((patches) => {
      callback(patches);
    });
  }
  setComponentResolver(resolver) {
    const engine = this.ensureInitialized();
    engine.setComponentResolver((componentName, contextPath) => {
      const result = resolver(componentName, contextPath);
      return result;
    });
  }
  renderSource(source) {
    const engine = this.ensureInitialized();
    engine.renderSource(source);
  }
  renderLazyComponent(source) {
    const engine = this.ensureInitialized();
    engine.renderLazyComponent(source);
  }
  renderInto(source, parentNodeId, state) {
    const engine = this.ensureInitialized();
    engine.renderInto(source, parentNodeId, unwrapForWasm(state));
  }
  notifyStateChange(paths, values) {
    const engine = this.ensureInitialized();
    if (paths.length === 0) {
      return;
    }
    engine.updateStateSparse(paths, unwrapForWasm(values));
    console.debug("[Hypen] State changed (sparse):", paths);
  }
  notifyStateChangeFull(paths, currentState) {
    const engine = this.ensureInitialized();
    engine.updateState(unwrapForWasm(currentState));
    if (paths.length > 0) {
      console.debug("[Hypen] State changed (full):", paths);
    }
  }
  updateState(statePatch) {
    const engine = this.ensureInitialized();
    engine.updateState(unwrapForWasm(statePatch));
  }
  dispatchAction(name, payload) {
    const engine = this.ensureInitialized();
    engine.dispatchAction(name, payload ?? null);
  }
  onAction(actionName, handler) {
    const engine = this.ensureInitialized();
    engine.onAction(actionName, (action) => {
      Promise.resolve(handler(action)).catch(console.error);
    });
  }
  setModule(name, actions, stateKeys, initialState) {
    const engine = this.ensureInitialized();
    engine.setModule(name, actions, stateKeys, initialState);
  }
  getRevision() {
    const engine = this.ensureInitialized();
    return Number(engine.getRevision());
  }
  clearTree() {
    const engine = this.ensureInitialized();
    engine.clearTree();
  }
  debugParseComponent(source) {
    const engine = this.ensureInitialized();
    return engine.debugParseComponent(source);
  }
}

// src/engine.browser.ts
var wasmInit = null;
var WasmEngineClass = null;
function mapToObject(value) {
  if (value instanceof Map) {
    const obj = {};
    for (const [key, val] of value.entries()) {
      obj[key] = mapToObject(val);
    }
    return obj;
  } else if (Array.isArray(value)) {
    return value.map(mapToObject);
  } else if (value && typeof value === "object" && value.constructor === Object) {
    const obj = {};
    for (const [key, val] of Object.entries(value)) {
      obj[key] = mapToObject(val);
    }
    return obj;
  }
  return value;
}

class Engine2 {
  wasmEngine = null;
  initialized = false;
  async init(options = {}) {
    if (this.initialized)
      return;
    const cdnBase = "https://unpkg.com/@hypen-space/core@0.2.12/wasm-browser";
    const jsUrl = options.jsUrl ?? `${cdnBase}/hypen_engine.js`;
    const wasmUrl = options.wasmUrl ?? `${cdnBase}/hypen_engine_bg.wasm`;
    try {
      const wasmModule = await import(jsUrl);
      wasmInit = wasmModule.default;
      WasmEngineClass = wasmModule.WasmEngine;
      await wasmInit(wasmUrl);
      this.wasmEngine = new WasmEngineClass;
      this.initialized = true;
    } catch (error) {
      console.error("[Hypen] Failed to initialize WASM engine:", error);
      throw error;
    }
  }
  ensureInitialized() {
    if (!this.wasmEngine) {
      throw new Error("Engine not initialized. Call init() first.");
    }
    return this.wasmEngine;
  }
  setRenderCallback(callback) {
    const engine = this.ensureInitialized();
    engine.setRenderCallback((patches) => {
      callback(patches);
    });
  }
  setComponentResolver(resolver) {
    const engine = this.ensureInitialized();
    engine.setComponentResolver((componentName, contextPath) => {
      const result = resolver(componentName, contextPath);
      return result;
    });
  }
  renderSource(source) {
    const engine = this.ensureInitialized();
    engine.renderSource(source);
  }
  renderLazyComponent(source) {
    const engine = this.ensureInitialized();
    engine.renderLazyComponent(source);
  }
  renderInto(source, parentNodeId, state) {
    const engine = this.ensureInitialized();
    const safeState = JSON.parse(JSON.stringify(state));
    engine.renderInto(source, parentNodeId, safeState);
  }
  notifyStateChange(paths, currentState) {
    const engine = this.ensureInitialized();
    const plainObject = JSON.parse(JSON.stringify(currentState));
    engine.updateState(plainObject);
    if (paths.length > 0) {
      console.debug("[Hypen] State changed:", paths);
    }
  }
  updateState(statePatch) {
    const engine = this.ensureInitialized();
    const plainObject = JSON.parse(JSON.stringify(statePatch));
    engine.updateState(plainObject);
  }
  dispatchAction(name, payload) {
    const engine = this.ensureInitialized();
    console.log(`[Engine] Action dispatched: ${name}`);
    engine.dispatchAction(name, payload ?? null);
  }
  onAction(actionName, handler) {
    const engine = this.ensureInitialized();
    engine.onAction(actionName, (action) => {
      const normalizedAction = {
        ...action,
        payload: action.payload ? mapToObject(action.payload) : action.payload
      };
      Promise.resolve(handler(normalizedAction)).catch(console.error);
    });
  }
  setModule(name, actions, stateKeys, initialState) {
    const engine = this.ensureInitialized();
    engine.setModule(name, actions, stateKeys, initialState);
  }
  getRevision() {
    const engine = this.ensureInitialized();
    return engine.getRevision();
  }
  clearTree() {
    const engine = this.ensureInitialized();
    engine.clearTree();
  }
  debugParseComponent(source) {
    const engine = this.ensureInitialized();
    return engine.debugParseComponent(source);
  }
}

// src/renderer.ts
class BaseRenderer {
  nodes = new Map;
  getNode(id) {
    return this.nodes.get(id);
  }
  clear() {
    this.nodes.clear();
  }
  applyPatch(patch) {
    switch (patch.type) {
      case "create":
        this.onCreate(patch.id, patch.elementType, patch.props || {});
        break;
      case "setProp":
        this.onSetProp(patch.id, patch.name, patch.value);
        break;
      case "setText":
        this.onSetText(patch.id, patch.text);
        break;
      case "insert":
        this.onInsert(patch.parentId, patch.id, patch.beforeId);
        break;
      case "move":
        this.onMove(patch.parentId, patch.id, patch.beforeId);
        break;
      case "remove":
        this.onRemove(patch.id);
        break;
      case "attachEvent":
        this.onAttachEvent(patch.id, patch.eventName);
        break;
      case "detachEvent":
        this.onDetachEvent(patch.id, patch.eventName);
        break;
    }
  }
}

class ConsoleRenderer {
  applyPatches(patches) {
    console.group("Patches:");
    for (const patch of patches) {
      console.log(patch);
    }
    console.groupEnd();
  }
}

// src/router.ts
init_state();

class HypenRouter {
  state;
  subscribers = new Set;
  isInitialized = false;
  isUpdating = false;
  constructor() {
    this.state = createObservableState({
      currentPath: "/",
      params: {},
      query: {},
      previousPath: null
    }, {
      onChange: () => {
        this.notifySubscribers();
      }
    });
    if (typeof window !== "undefined") {
      this.initializeBrowserSync();
    }
  }
  initializeBrowserSync() {
    const initialPath = this.getPathFromBrowser();
    this.state.currentPath = initialPath;
    this.state.params = {};
    this.state.query = this.parseQuery();
    window.addEventListener("popstate", () => {
      const newPath = this.getPathFromBrowser();
      this.updatePath(newPath, false);
    });
    window.addEventListener("hashchange", () => {
      if (this.isUpdating)
        return;
      const newPath = this.getPathFromBrowser();
      this.updatePath(newPath, false);
    });
    this.isInitialized = true;
    console.log("Router initialized at:", initialPath);
  }
  getPathFromBrowser() {
    if (typeof window === "undefined")
      return "/";
    const hash = window.location.hash.slice(1);
    if (hash)
      return hash;
    return window.location.pathname;
  }
  parseQuery() {
    if (typeof window === "undefined")
      return {};
    const query = {};
    const searchParams = new URLSearchParams(window.location.search);
    searchParams.forEach((value, key) => {
      query[key] = value;
    });
    return query;
  }
  push(path) {
    console.log("Router.push:", path);
    this.updatePath(path, true);
  }
  replace(path) {
    console.log("Router.replace:", path);
    this.updatePath(path, true, true);
  }
  back() {
    console.log("Router.back");
    if (typeof window !== "undefined") {
      window.history.back();
    }
  }
  forward() {
    console.log("Router.forward");
    if (typeof window !== "undefined") {
      window.history.forward();
    }
  }
  updatePath(path, updateBrowser, replace = false) {
    if (this.isUpdating)
      return;
    this.isUpdating = true;
    try {
      const oldPath = this.state.currentPath;
      this.state.previousPath = oldPath;
      this.state.currentPath = path;
      this.state.query = this.parseQuery();
      this.notifySubscribers();
      if (updateBrowser && typeof window !== "undefined") {
        const url = "#" + path;
        if (replace) {
          window.history.replaceState(null, "", url);
        } else {
          window.history.pushState(null, "", url);
        }
        const hashChangeEvent = new HashChangeEvent("hashchange", {
          oldURL: window.location.href.replace(window.location.hash, "#" + oldPath),
          newURL: window.location.href
        });
        window.dispatchEvent(hashChangeEvent);
      }
    } finally {
      this.isUpdating = false;
    }
  }
  getCurrentPath() {
    return this.state.currentPath;
  }
  getParams() {
    return { ...this.state.params };
  }
  getQuery() {
    return { ...this.state.query };
  }
  getState() {
    return getStateSnapshot(this.state);
  }
  matchPath(pattern, path) {
    if (!pattern || typeof pattern !== "string") {
      return null;
    }
    if (!path || typeof path !== "string") {
      return null;
    }
    if (pattern === path) {
      return {
        params: {},
        query: this.state.query,
        path
      };
    }
    if (pattern.endsWith("/*")) {
      const prefix = pattern.slice(0, -2);
      if (path === prefix || path.startsWith(prefix + "/")) {
        return {
          params: {},
          query: this.state.query,
          path
        };
      }
      return null;
    }
    const paramNames = [];
    const regexPattern = pattern.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, name) => {
      paramNames.push(name);
      return "([^/]+)";
    }).replace(/\*/g, ".*");
    const regex = new RegExp(`^${regexPattern}$`);
    const match2 = path.match(regex);
    if (!match2)
      return null;
    const params = {};
    paramNames.forEach((name, i) => {
      const value = match2[i + 1];
      if (value !== undefined) {
        params[name] = decodeURIComponent(value);
      }
    });
    return {
      params,
      query: this.state.query,
      path
    };
  }
  onNavigate(callback) {
    this.subscribers.add(callback);
    try {
      callback(this.getState());
    } catch (error) {
      console.error("[HypenRouter] Error in route subscriber:", error);
    }
    return () => {
      this.subscribers.delete(callback);
    };
  }
  notifySubscribers() {
    const routeState = this.getState();
    this.subscribers.forEach((callback) => {
      try {
        callback(routeState);
      } catch (error) {
        console.error("[HypenRouter] Error in route subscriber:", error);
      }
    });
  }
  isActive(pattern) {
    return this.matchPath(pattern, this.state.currentPath) !== null;
  }
  buildUrl(path, query) {
    if (!query || Object.keys(query).length === 0) {
      return path;
    }
    const queryString = new URLSearchParams(query).toString();
    return `${path}?${queryString}`;
  }
}

// src/events.ts
class TypedEventEmitter {
  eventBus = new Map;
  emit(event, payload) {
    const handlers = this.eventBus.get(event);
    if (!handlers || handlers.size === 0) {
      return;
    }
    handlers.forEach((handler) => {
      try {
        handler(payload);
      } catch (error) {
        console.error(`Error in event handler for "${String(event)}":`, error);
      }
    });
  }
  on(event, handler) {
    if (!this.eventBus.has(event)) {
      this.eventBus.set(event, new Set);
    }
    const handlers = this.eventBus.get(event);
    handlers.add(handler);
    return () => {
      handlers.delete(handler);
      if (handlers.size === 0) {
        this.eventBus.delete(event);
      }
    };
  }
  once(event, handler) {
    const wrappedHandler = (payload) => {
      handler(payload);
      unsubscribe();
    };
    const unsubscribe = this.on(event, wrappedHandler);
    return unsubscribe;
  }
  off(event, handler) {
    const handlers = this.eventBus.get(event);
    if (handlers) {
      handlers.delete(handler);
      if (handlers.size === 0) {
        this.eventBus.delete(event);
      }
    }
  }
  removeAllListeners(event) {
    this.eventBus.delete(event);
  }
  clearAll() {
    this.eventBus.clear();
  }
  listenerCount(event) {
    return this.eventBus.get(event)?.size ?? 0;
  }
  eventNames() {
    return Array.from(this.eventBus.keys());
  }
}
function createEventEmitter() {
  return new TypedEventEmitter;
}

// src/context.ts
class HypenGlobalContext {
  modules = new Map;
  typedEvents;
  legacyEventBus = new Map;
  constructor() {
    this.typedEvents = new TypedEventEmitter;
  }
  get events() {
    return this.typedEvents;
  }
  registerModule(id, instance) {
    if (this.modules.has(id)) {
      console.warn(`Module "${id}" is already registered. Overwriting.`);
    }
    this.modules.set(id, instance);
    console.log(`Registered module: ${id}`);
  }
  unregisterModule(id) {
    this.modules.delete(id);
    console.log(`Unregistered module: ${id}`);
  }
  getModule(id) {
    const module = this.modules.get(id);
    if (!module) {
      throw new Error(`Module "${id}" not found. Available modules: ${Array.from(this.modules.keys()).join(", ")}`);
    }
    return {
      state: module.getLiveState(),
      setState: (patch) => module.updateState(patch),
      getState: () => module.getState()
    };
  }
  hasModule(id) {
    return this.modules.has(id);
  }
  getModuleIds() {
    return Array.from(this.modules.keys());
  }
  getGlobalState() {
    const state = {};
    this.modules.forEach((module, id) => {
      state[id] = module.getState();
    });
    return state;
  }
  emit(event, payload) {
    const handlers = this.legacyEventBus.get(event);
    if (!handlers || handlers.size === 0) {
      console.log(`Event "${event}" emitted but no listeners`);
    } else {
      console.log(`Emitting event: ${event}`, payload);
      handlers.forEach((handler) => {
        try {
          handler(payload);
        } catch (error) {
          console.error(`Error in event handler for "${event}":`, error);
        }
      });
    }
    if (this.typedEvents.listenerCount(event) > 0) {
      this.typedEvents.emit(event, payload);
    }
  }
  on(event, handler) {
    if (!this.legacyEventBus.has(event)) {
      this.legacyEventBus.set(event, new Set);
    }
    const handlers = this.legacyEventBus.get(event);
    handlers.add(handler);
    console.log(`Listening to event: ${event}`);
    return () => {
      handlers.delete(handler);
      if (handlers.size === 0) {
        this.legacyEventBus.delete(event);
      }
    };
  }
  off(event, handler) {
    const handlers = this.legacyEventBus.get(event);
    if (handlers) {
      handlers.delete(handler);
      if (handlers.size === 0) {
        this.legacyEventBus.delete(event);
      }
    }
  }
  clearEvent(event) {
    this.legacyEventBus.delete(event);
  }
  clearAllEvents() {
    this.legacyEventBus.clear();
  }
  debug() {
    return {
      modules: this.getModuleIds(),
      events: Array.from(this.legacyEventBus.keys()),
      typedEvents: this.typedEvents.eventNames(),
      state: this.getGlobalState()
    };
  }
}

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

// src/disposable.ts
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) {
        console.error("[DisposableStack] 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) {
          console.error("[compositeDisposable] 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/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;
}
async function retryResult(fn, options = {}) {
  try {
    const value = await retry(fn, options);
    return Ok(value);
  } catch (e) {
    return Err(e instanceof Error ? e : new Error(String(e)));
  }
}
function withRetry(fn, options = {}) {
  return (...args) => retry(() => fn(...args), options);
}
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
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) {
      console.warn("Cannot dispatch action: not connected");
      return;
    }
    const message = {
      type: "dispatchAction",
      module: this.moduleName,
      action,
      payload
    };
    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) {
      console.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) {
      console.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) => {
          console.log(`Reconnection attempt ${attempt}/${this.options.maxReconnectAttempts} failed: ${error.message}`);
        }
      }).catch((error) => {
        console.error("Max reconnection attempts reached:", error.message);
        this.errorCallbacks.forEach((cb) => cb(new ConnectionError(this.url, error, this.options.maxReconnectAttempts)));
      });
    }, this.options.reconnectInterval);
  }
}

// src/loader.ts
import { existsSync, readdirSync, readFileSync } from "fs";
import { join } from "path";

class ComponentLoader {
  components = new Map;
  register(name, module, template, path) {
    this.components.set(name, {
      name,
      module,
      template,
      path: path || name
    });
  }
  get(name) {
    return this.components.get(name);
  }
  has(name) {
    return this.components.has(name);
  }
  getNames() {
    return Array.from(this.components.keys());
  }
  getAll() {
    return Array.from(this.components.values());
  }
  clear() {
    this.components.clear();
  }
  async loadFromDirectory(name, dirPath) {
    try {
      const modulePath = join(dirPath, "component.ts");
      const moduleExport = await import(modulePath);
      const module = moduleExport.default;
      const templatePath = join(dirPath, "component.hypen");
      const template = readFileSync(templatePath, "utf-8");
      this.register(name, module, template, dirPath);
      console.log(`✓ Loaded component: ${name} from ${dirPath}`);
    } catch (error) {
      console.error(`✗ Failed to load component ${name} from ${dirPath}:`, error);
      throw error;
    }
  }
  async loadFromComponentsDir(baseDir) {
    try {
      if (!existsSync(baseDir)) {
        console.warn(`⚠ Components directory not found: ${baseDir}`);
        return;
      }
      const entries = readdirSync(baseDir, { withFileTypes: true });
      for (const entry of entries) {
        if (!entry.isDirectory())
          continue;
        const componentDir = join(baseDir, entry.name);
        const hypenPath = join(componentDir, "component.hypen");
        if (existsSync(hypenPath)) {
          await this.loadFromDirectory(entry.name, componentDir);
        }
      }
      console.log(`✓ Loaded ${this.components.size} components from ${baseDir}`);
    } catch (error) {
      console.error(`✗ Failed to load components from ${baseDir}:`, error);
      throw error;
    }
  }
}
var componentLoader = new ComponentLoader;

// src/resolver.ts
class ComponentResolver {
  cache = new Map;
  options;
  constructor(options = {}) {
    this.options = {
      baseDir: options.baseDir || process.cwd(),
      cache: options.cache ?? true,
      customFetch: options.customFetch || this.defaultFetch.bind(this)
    };
  }
  async resolve(importStmt) {
    const sourcePath = this.getSourcePath(importStmt.source);
    if (this.options.cache && this.cache.has(sourcePath)) {
      const cached = this.cache.get(sourcePath);
      return this.extractComponents(importStmt.clause, cached);
    }
    let component;
    if (importStmt.source.type === "local") {
      component = await this.resolveLocal(importStmt.source.path);
    } else {
      component = await this.resolveUrl(importStmt.source.url);
    }
    if (this.options.cache) {
      this.cache.set(sourcePath, component);
    }
    return this.extractComponents(importStmt.clause, component);
  }
  async resolveLocal(path) {
    throw new Error(`Dynamic local component resolution not yet implemented: ${path}
` + `Please use the build-components.ts script to generate component imports.`);
  }
  async resolveUrl(url) {
    try {
      const response = await this.options.customFetch(url);
      const data = JSON.parse(response);
      if (!data.module || !data.template) {
        throw new Error(`Invalid component format from ${url}. Expected { module, template }`);
      }
      return data;
    } catch (error) {
      throw new Error(`Failed to resolve component from ${url}: ${error instanceof Error ? error.message : String(error)}`);
    }
  }
  async defaultFetch(url) {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    return response.text();
  }
  extractComponents(clause, component) {
    if (clause.type === "default") {
      return {
        [clause.name]: component
      };
    } else {
      const result = {};
      for (const name of clause.names) {
        result[name] = component;
      }
      return result;
    }
  }
  getSourcePath(source) {
    return source.type === "local" ? source.path : source.url;
  }
  clearCache() {
    this.cache.clear();
  }
  static parseImports(text) {
    const imports = [];
    const importRegex = /import\s+(?:(\{[^}]*\})|(\w+))\s+from\s+["']([^"']+)["']/g;
    let match2;
    while ((match2 = importRegex.exec(text)) !== null) {
      const [, namedImports, defaultImport, source] = match2;
      if (!source)
        continue;
      let clause;
      if (namedImports) {
        const names = namedImports.slice(1, -1).split(",").map((n) => n.trim()).filter((n) => n.length > 0);
        clause = { type: "named", names };
      } else if (defaultImport) {
        clause = { type: "default", name: defaultImport };
      } else {
        continue;
      }
      const sourceObj = source.startsWith("http://") || source.startsWith("https://") ? { type: "url", url: source } : { type: "local", path: source };
      imports.push({ clause, source: sourceObj });
    }
    return imports;
  }
}

// src/discovery.ts
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, watch } from "fs";
import { join as join2, basename, resolve } from "path";
function removeImports(text) {
  return text.replace(/import\s+(?:\{[^}]+\}|\w+)\s+from\s+["'][^"']+["']\s*/g, "");
}
async function discoverComponents(baseDir, options = {}) {
  const {
    patterns = ["single-file", "folder", "sibling", "index"],
    recursive = false,
    debug = false
  } = options;
  const log3 = debug ? (...args) => console.log("[discovery]", ...args) : () => {};
  const resolvedDir = resolve(baseDir);
  const components = [];
  const seen = new Set;
  log3("Scanning directory:", resolvedDir);
  log3("Patterns:", patterns);
  const addTwoFileComponent = (name, hypenPath, modulePath) => {
    if (seen.has(name)) {
      log3(`Skipping duplicate: ${name}`);
      return;
    }
    seen.add(name);
    const templateRaw = readFileSync2(hypenPath, "utf-8");
    const template = removeImports(templateRaw).trim();
    components.push({
      name,
      hypenPath,
      modulePath,
      template,
      hasModule: modulePath !== null,
      isSingleFile: false
    });
    log3(`Found: ${name} (two-file, ${modulePath ? "with module" : "stateless"})`);
  };
  const addSingleFileComponent = (name, modulePath, template) => {
    if (seen.has(name)) {
      log3(`Skipping duplicate: ${name}`);
      return;
    }
    seen.add(name);
    components.push({
      name,
      hypenPath: null,
      modulePath,
      template,
      hasModule: true,
      isSingleFile: true
    });
    log3(`Found: ${name} (single-file with inline template)`);
  };
  const scanForFolderComponents = (dir) => {
    if (!existsSync2(dir))
      return;
    const entries = readdirSync2(dir, { withFileTypes: true });
    for (const entry of entries) {
      if (!entry.isDirectory())
        continue;
      const folderPath = join2(dir, entry.name);
      const componentName = entry.name;
      if (patterns.includes("folder")) {
        const hypenPath = join2(folderPath, "component.hypen");
        if (existsSync2(hypenPath)) {
          const modulePath = join2(folderPath, "component.ts");
          addTwoFileComponent(componentName, hypenPath, existsSync2(modulePath) ? modulePath : null);
          continue;
        }
      }
      if (patterns.includes("index")) {
        const hypenPath = join2(folderPath, "index.hypen");
        if (existsSync2(hypenPath)) {
          const modulePath = join2(folderPath, "index.ts");
          addTwoFileComponent(componentName, hypenPath, existsSync2(modulePath) ? modulePath : null);
          continue;
        }
      }
      if (recursive) {
        scanForFolderComponents(folderPath);
      }
    }
  };
  const scanForSiblingComponents = (dir) => {
    if (!existsSync2(dir))
      return;
    const entries = readdirSync2(dir, { withFileTypes: true });
    for (const entry of entries) {
      if (entry.isDirectory()) {
        if (recursive) {
          scanForSiblingComponents(join2(dir, entry.name));
        }
        continue;
      }
      if (!entry.name.endsWith(".hypen"))
        continue;
      const hypenPath = join2(dir, entry.name);
      const baseName = basename(entry.name, ".hypen");
      if (baseName === "component" || baseName === "index")
        continue;
      const modulePath = join2(dir, `${baseName}.ts`);
      addTwoFileComponent(baseName, hypenPath, existsSync2(modulePath) ? modulePath : null);
    }
  };
  const scanForSingleFileComponents = async (dir) => {
    if (!existsSync2(dir))
      return;
    const entries = readdirSync2(dir, { withFileTypes: true });
    for (const entry of entries) {
      if (entry.isDirectory()) {
        if (recursive) {
          await scanForSingleFileComponents(join2(dir, entry.name));
        }
        continue;
      }
      if (!entry.name.endsWith(".ts"))
        continue;
      if (entry.name.startsWith(".") || entry.name.includes(".test.") || entry.name.includes(".spec."))
        continue;
      const baseName = basename(entry.name, ".ts");
      if (baseName === "component" || baseName === "index")
        continue;
      const hypenPath = join2(dir, `${baseName}.hypen`);
      if (existsSync2(hypenPath))
        continue;
      const modulePath = join2(dir, entry.name);
      const content = readFileSync2(modulePath, "utf-8");
      if (content.includes(".ui(") || content.includes(".ui(hypen")) {
        try {
          const moduleExport = await import(modulePath);
          const module = moduleExport.default;
          if (module && typeof module === "object" && module.template) {
            addSingleFileComponent(baseName, modulePath, module.template);
          }
        } catch (e) {
          log3(`Failed to import potential single-file component: ${entry.name}`, e);
        }
      }
    }
  };
  if (patterns.includes("folder") || patterns.includes("index")) {
    scanForFolderComponents(resolvedDir);
  }
  if (patterns.includes("sibling")) {
    scanForSiblingComponents(resolvedDir);
  }
  if (patterns.includes("single-file")) {
    await scanForSingleFileComponents(resolvedDir);
  }
  log3(`Discovered ${components.length} components`);
  return components;
}
async function loadDiscoveredComponents(components) {
  const { app: app2 } = await Promise.resolve().then(() => (init_app(), exports_app));
  const loaded = new Map;
  for (const component of components) {
    let module;
    let template = component.template;
    if (component.modulePath) {
      const moduleExport = await import(component.modulePath);
      module = moduleExport.default;
      if (component.isSingleFile && module.template) {
        template = module.template;
      }
    } else {
      module = app2.defineState({}).build();
    }
    loaded.set(component.name, {
      name: component.name,
      module,
      template
    });
  }
  return loaded;
}
function watchComponents(baseDir, options = {}) {
  const resolvedDir = resolve(baseDir);
  const {
    onChange,
    onAdd,
    onRemove,
    onUpdate,
    debug = false,
    ...discoveryOptions
  } = options;
  const log3 = debug ? (...args) => console.log("[discovery:watch]", ...args) : () => {};
  let currentComponents = new Map;
  let debounceTimer = null;
  const initialScan = async () => {
    const components = await discoverComponents(resolvedDir, discoveryOptions);
    currentComponents = new Map(components.map((c) => [c.name, c]));
    onChange?.(components);
  };
  const rescan = async () => {
    if (debounceTimer) {
      clearTimeout(debounceTimer);
    }
    debounceTimer = setTimeout(async () => {
      log3("Rescanning...");
      const newComponents = await discoverComponents(resolvedDir, discoveryOptions);
      const newMap = new Map(newComponents.map((c) => [c.name, c]));
      for (const [name, component] of newMap) {
        const existing = currentComponents.get(name);
        if (!existing) {
          log3("Added:", name);
          onAdd?.(component);
        } else if (existing.template !== component.template || existing.modulePath !== component.modulePath) {
          log3("Updated:", name);
          onUpdate?.(component);
        }
      }
      for (const name of currentComponents.keys()) {
        if (!newMap.has(name)) {
          log3("Removed:", name);
          onRemove?.(name);
        }
      }
      currentComponents = newMap;
      onChange?.(newComponents);
    }, 100);
  };
  const watcher = watch(resolvedDir, { recursive: true }, (event, filename) => {
    if (!filename)
      return;
    if (filename.endsWith(".hypen") || filename.endsWith(".ts")) {
      log3("File changed:", filename);
      rescan();
    }
  });
  initialScan();
  return {
    stop: () => {
      watcher.close();
      if (debounceTimer) {
        clearTimeout(debounceTimer);
      }
    }
  };
}
async function generateComponentsCode(baseDir, options = {}) {
  const components = await discoverComponents(baseDir, options);
  const resolvedDir = resolve(baseDir);
  let code = `/**
 * Auto-generated component imports
 * Generated by @hypen-space/core discovery
 */

`;
  for (const component of components) {
    const relativePath = component.modulePath ? "./" + component.modulePath.replace(resolvedDir + "/", "").replace(/\.ts$/, ".js") : null;
    if (relativePath) {
      code += `import ${component.name}Module from "${relativePath}";
`;
    }
  }
  code += `
import { app } from "@hypen-space/core";

`;
  for (const component of components) {
    if (component.isSingleFile) {
      code += `export const ${component.name} = {
  module: ${component.name}Module,
  template: ${component.name}Module.template,
};

`;
    } else if (component.hasModule) {
      const templateJson = JSON.stringify(component.template);
      code += `export const ${component.name} = {
  module: ${component.name}Module,
  template: ${templateJson},
};

`;
    } else {
      const templateJson = JSON.stringify(component.template);
      code += `const ${component.name}Module = app.defineState({}).build();
export const ${component.name} = {
  module: ${component.name}Module,
  template: ${templateJson},
};

`;
    }
  }
  return code;
}

// src/plugin.ts
import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
import { dirname, basename as basename2, join as join3, resolve as resolve2 } from "path";
function findModulePath(hypenPath, patterns) {
  const dir = dirname(hypenPath);
  const baseName = basename2(hypenPath, ".hypen");
  const parentDir = dirname(dir);
  const folderName = basename2(dir);
  for (const pattern of patterns) {
    let candidatePath = null;
    switch (pattern) {
      case "sibling":
        candidatePath = join3(dir, `${baseName}.ts`);
        break;
      case "component":
        if (baseName === "component") {
          candidatePath = join3(dir, "component.ts");
        }
        break;
      case "index":
        if (baseName === "index") {
          candidatePath = join3(dir, "index.ts");
        }
        break;
    }
    if (candidatePath && existsSync3(candidatePath)) {
      return candidatePath;
    }
  }
  return null;
}
function getComponentName(hypenPath) {
  const baseName = basename2(hypenPath, ".hypen");
  if (baseName === "component" || baseName === "index") {
    return basename2(dirname(hypenPath));
  }
  return baseName;
}
function parseImports(text) {
  const imports = [];
  const importRegex = /import\s+(?:\{([^}]+)\}|(\w+))\s+from\s+["']([^"']+)["']/g;
  let match2;
  while ((match2 = importRegex.exec(text)) !== null) {
    const [, namedImports, defaultImport, source] = match2;
    if (!source)
      continue;
    let names;
    if (namedImports) {
      names = namedImports.split(",").map((n) => n.trim()).filter((n) => n.length > 0);
    } else if (defaultImport) {
      names = [defaultImport];
    } else {
      continue;
    }
    imports.push({ names, source });
  }
  return imports;
}
function removeImports2(text) {
  return text.replace(/import\s+(?:\{[^}]+\}|\w+)\s+from\s+["'][^"']+["']\s*/g, "");
}
function hypenPlugin(options = {}) {
  const { debug = false, patterns = ["sibling", "component", "index"] } = options;
  const log3 = debug ? (...args) => console.log("[hypen-plugin]", ...args) : () => {};
  return {
    name: "hypen-loader",
    async setup(build) {
      build.onLoad({ filter: /\.hypen$/ }, async (args) => {
        const hypenPath = resolve2(args.path);
        log3("Loading:", hypenPath);
        const templateRaw = readFileSync3(hypenPath, "utf-8");
        const imports = parseImports(templateRaw);
        const template = removeImports2(templateRaw).trim();
        if (imports.length > 0) {
          log3("Found imports:", imports);
        }
        const componentName = getComponentName(hypenPath);
        log3("Component name:", componentName);
        const modulePath = findModulePath(hypenPath, patterns);
        log3("Module path:", modulePath);
        let contents;
        if (modulePath) {
          const relativeModulePath = modulePath.replace(/\.ts$/, ".js");
          contents = `
import _module from "${relativeModulePath}";
export const module = _module;
export const template = ${JSON.stringify(template)};
export const name = ${JSON.stringify(componentName)};
export default { module: _module, template: ${JSON.stringify(template)}, name: ${JSON.stringify(componentName)} };
`;
        } else {
          log3("No module found, creating stateless component");
          contents = `
import { app } from "@hypen-space/core";
const _module = app.defineState({}).build();
export const module = _module;
export const template = ${JSON.stringify(template)};
export const name = ${JSON.stringify(componentName)};
export default { module: _module, template: ${JSON.stringify(template)}, name: ${JSON.stringify(componentName)} };
`;
        }
        return {
          contents,
          loader: "js"
        };
      });
    }
  };
}
var defaultHypenPlugin = hypenPlugin();
function registerHypenPlugin(options) {
  Bun.plugin(hypenPlugin(options));
}
var plugin_default = hypenPlugin;

// src/components/builtin.ts
init_app();
var Router = app.defineState({
  currentPath: "/",
  matchedRoute: null,
  routeParams: {}
}, { name: "__Router" }).onCreated((state, context) => {
  if (!context) {
    console.error("[Router] Requires global context");
    return;
  }
  const router = context.__router;
  if (!router) {
    console.error("[Router] Router not found in context");
    return;
  }
  const hypenEngine = context.__hypenEngine;
  const updateRouteVisibility = async (currentPath) => {
    const routeElements = document.querySelectorAll('[data-hypen-type="route"]');
    let matchFound = false;
    for (let index = 0;index < routeElements.length; index++) {
      const routeEl = routeElements[index];
      const htmlEl = routeEl;
      const routePath = htmlEl.dataset.routePath || "/";
      const isMatch = routePath === currentPath;
      htmlEl.style.display = isMatch ? "flex" : "none";
      if (isMatch) {
        matchFound = true;
        const componentName = htmlEl.dataset.routeComponent;
        const isLazy = htmlEl.dataset.routeLazy === "true";
        const hasContent = htmlEl.children.length > 0;
        if (componentName && !hasContent && hypenEngine) {
          try {
            await hypenEngine.renderLazyRoute(routePath, componentName, htmlEl);
          } catch (err) {
            console.error(`[Router] Failed to render route ${routePath}:`, err);
          }
        }
      }
    }
    if (!matchFound) {
      console.warn(`[Router] No route matched path: ${currentPath}. Available routes:`, Array.from(routeElements).map((el) => el.dataset.routePath));
    }
  };
  setTimeout(() => {
    updateRouteVisibility(state.currentPath);
  }, 100);
  router.onNavigate((routeState) => {
    state.currentPath = routeState.currentPath;
    state.routeParams = routeState.params;
    updateRouteVisibility(routeState.currentPath);
  });
}).build();
var Route = app.defineState({}, { name: "__Route" }).build();
var Link = app.defineState({
  to: "/",
  isActive: false
}, { name: "__Link" }).onAction("navigate", ({ state, context }) => {
  const router = context?.__router;
  if (!router) {
    console.error("[Link] Requires router context");
    return;
  }
  const targetPath = state.to;
  router.push(targetPath);
}).onCreated((state, context) => {
  if (!context)
    return;
  const router = context.__router;
  if (router) {
    router.onNavigate((routeState) => {
      state.isActive = routeState.currentPath === state.to;
    });
  }
}).build();

// src/index.ts
init_app();

// src/hypen.ts
function createBindingProxy(root) {
  const handler = {
    get(_, prop) {
      if (prop === Symbol.toPrimitive || prop === "toString" || prop === "valueOf") {
        return () => `\${${root}}`;
      }
      if (typeof prop === "symbol") {
        return;
      }
      if (prop === "toJSON") {
        return () => `\${${root}}`;
      }
      return createBindingProxy(`${root}.${prop}`);
    },
    has() {
      return true;
    },
    ownKeys() {
      return [];
    },
    getOwnPropertyDescriptor() {
      return {
        configurable: true,
        enumerable: true
      };
    }
  };
  return new Proxy({}, handler);
}
var state = createBindingProxy("state");
var item = createBindingProxy("item");
var index = {
  [Symbol.toPrimitive]: () => "${index}",
  toString: () => "${index}",
  valueOf: () => "${index}",
  toJSON: () => "${index}"
};
function hypen(strings, ...expressions) {
  let result = strings[0];
  for (let i = 0;i < expressions.length; i++) {
    const expr = expressions[i];
    result += String(expr);
    result += strings[i + 1];
  }
  return result.trim();
}

// src/index.ts
init_state();

// src/remote/server.ts
init_app();

// src/remote/session.ts
class SessionManager {
  activeSessions = new Map;
  pendingSessions = new Map;
  sessionConnections = new Map;
  config;
  constructor(config2 = {}) {
    this.config = {
      ttl: config2.ttl ?? 3600,
      concurrent: config2.concurrent ?? "kick-old",
      generateId: config2.generateId ?? (() => crypto.randomUUID())
    };
  }
  getTtl() {
    return this.config.ttl;
  }
  getConcurrentPolicy() {
    return this.config.concurrent;
  }
  createSession(props) {
    const now = new Date;
    const session = {
      id: this.config.generateId(),
      ttl: this.config.ttl,
      createdAt: now,
      lastConnectedAt: now,
      props
    };
    this.activeSessions.set(session.id, session);
    return session;
  }
  getActiveSession(id) {
    return this.activeSessions.get(id) ?? null;
  }
  getPendingSession(id) {
    return this.pendingSessions.get(id) ?? null;
  }
  hasSession(id) {
    return this.activeSessions.has(id) || this.pendingSessions.has(id);
  }
  suspendSession(sessionId, savedState, onExpire) {
    const session = this.activeSessions.get(sessionId);
    if (!session)
      return;
    this.activeSessions.delete(sessionId);
    const expiryTimer = setTimeout(async () => {
      const pending = this.pendingSessions.get(sessionId);
      if (pending) {
        this.pendingSessions.delete(sessionId);
        await onExpire(pending.session);
      }
    }, session.ttl * 1000);
    this.pendingSessions.set(sessionId, {
      session,
      savedState,
      expiryTimer
    });
  }
  resumeSession(sessionId) {
    const pending = this.pendingSessions.get(sessionId);
    if (!pending)
      return null;
    clearTimeout(pending.expiryTimer);
    this.pendingSessions.delete(sessionId);
    pending.session.lastConnectedAt = new Date;
    this.activeSessions.set(sessionId, pending.session);
    return {
      session: pending.session,
      savedState: pending.savedState
    };
  }
  destroySession(sessionId) {
    this.activeSessions.delete(sessionId);
    const pending = this.pendingSessions.get(sessionId);
    if (pending) {
      clearTimeout(pending.expiryTimer);
      this.pendingSessions.delete(sessionId);
    }
    this.sessionConnections.delete(sessionId);
  }
  trackConnection(sessionId, ws) {
    let connections = this.sessionConnections.get(sessionId);
    if (!connections) {
      connections = new Set;
      this.sessionConnections.set(sessionId, connections);
    }
    connections.add(ws);
  }
  untrackConnection(sessionId, ws) {
    const connections = this.sessionConnections.get(sessionId);
    if (connections) {
      connections.delete(ws);
      if (connections.size === 0) {
        this.sessionConnections.delete(sessionId);
      }
    }
  }
  getConnections(sessionId) {
    return this.sessionConnections.get(sessionId);
  }
  getConnectionCount(sessionId) {
    return this.sessionConnections.get(sessionId)?.size ?? 0;
  }
  getStats() {
    let totalConnections = 0;
    for (const connections of this.sessionConnections.values()) {
      totalConnections += connections.size;
    }
    return {
      activeSessions: this.activeSessions.size,
      pendingSessions: this.pendingSessions.size,
      totalConnections
    };
  }
  destroy() {
    for (const pending of this.pendingSessions.values()) {
      clearTimeout(pending.expiryTimer);
    }
    this.activeSessions.clear();
    this.pendingSessions.clear();
    this.sessionConnections.clear();
  }
}

// src/remote/server.ts
class RemoteServer {
  _module = null;
  _moduleName = "App";
  _ui = "";
  _config = {};
  _sessionConfig = {};
  _onConnectionCallbacks = [];
  _onDisconnectionCallbacks = [];
  clients = new Map;
  nextClientId = 1;
  server = null;
  sessionManager = null;
  module(name, module) {
    this._moduleName = name;
    this._module = module;
    return this;
  }
  ui(dsl) {
    this._ui = dsl;
    return this;
  }
  config(config2) {
    this._config = { ...this._config, ...config2 };
    return this;
  }
  session(config2) {
    this._sessionConfig = config2;
    return this;
  }
  onConnection(callback) {
    this._onConnectionCallbacks.push(callback);
    return this;
  }
  onDisconnection(callback) {
    this._onDisconnectionCallbacks.push(callback);
    return this;
  }
  listen(port) {
    if (!this._module) {
      throw new Error("Module not set. Call .module() before .listen()");
    }
    if (!this._ui) {
      throw new Error("UI not set. Call .ui() before .listen()");
    }
    this.sessionManager = new SessionManager(this._sessionConfig);
    const finalPort = port ?? this._config.port ?? 3000;
    const hostname = this._config.hostname ?? "0.0.0.0";
    this.server = Bun.serve({
      port: finalPort,
      hostname,
      websocket: {
        open: (ws) => this.handleOpen(ws),
        message: (ws, message) => this.handleMessage(ws, message),
        close: (ws) => this.handleClose(ws)
      },
      fetch: (req, server) => {
        const url = new URL(req.url);
        if (server.upgrade(req, { data: undefined })) {
          return;
        }
        if (url.pathname === "/health") {
          return new Response("OK", { status: 200 });
        }
        if (url.pathname === "/stats") {
          const stats = this.sessionManager?.getStats() ?? {
            activeSessions: 0,
            pendingSessions: 0,
            totalConnections: 0
          };
          return new Response(JSON.stringify(stats), {
            headers: { "Content-Type": "application/json" }
          });
        }
        return new Response("Hypen Remote Server", { status: 200 });
      }
    });
    console.log(`\uD83D\uDE80 Hypen app streaming on ws://${hostname}:${finalPort}`);
    return this;
  }
  stop() {
    if (this.server) {
      this.server.stop();
      this.server = null;
    }
    if (this.sessionManager) {
      this.sessionManager.destroy();
      this.sessionManager = null;
    }
  }
  get url() {
    if (!this.server)
      return null;
    const hostname = this._config.hostname ?? "localhost";
    const port = this._config.port ?? 3000;
    return `ws://${hostname}:${port}`;
  }
  async handleOpen(ws) {
    try {
      const clientId = `client_${this.nextClientId++}`;
      const connectedAt = new Date;
      const engine = new Engine;
      await engine.init();
      const moduleInstance = new HypenModuleInstance(engine, this._module);
      const clientData = {
        id: clientId,
        engine,
        moduleInstance,
        revision: 0,
        connectedAt,
        sessionId: "",
        helloReceived: false
      };
      this.clients.set(ws, clientData);
      clientData.helloTimeout = setTimeout(() => {
        if (!clientData.helloReceived) {
          this.initializeSession(ws, clientData, undefined, undefined);
        }
      }, 1000);
    } catch (error) {
      console.error("Error handling WebSocket open:", error);
      ws.close(1011, "Internal server error");
    }
  }
  async initializeSession(ws, clientData, requestedSessionId, props) {
    if (clientData.helloReceived)
      return;
    clientData.helloReceived = true;
    if (clientData.helloTimeout) {
      clearTimeout(clientData.helloTimeout);
      clientData.helloTimeout = undefined;
    }
    let session;
    let isNew = true;
    let isRestored = false;
    let restoredState = null;
    if (requestedSessionId && this.sessionManager) {
      const resumed = this.sessionManager.resumeSession(requestedSessionId);
      if (resumed) {
        session = resumed.session;
        restoredState = resumed.savedState;
        isNew = false;
        isRestored = true;
        await this.triggerReconnect(clientData, session, restoredState);
      } else {
        const activeSession = this.sessionManager.getActiveSession(requestedSessionId);
        if (activeSession) {
          const handled = await this.handleConcurrentConnection(ws, clientData, activeSession, props);
          if (!handled)
            return;
          session = activeSession;
          isNew = false;
        } else {
          session = this.sessionManager.createSession(props);
        }
      }
    } else if (this.sessionManager) {
      session = this.sessionManager.createSession(props);
    } else {
      session = {
        id: crypto.randomUUID(),
        ttl: 3600,
        createdAt: new Date,
        lastConnectedAt: new Date,
        props
      };
    }
    clientData.sessionId = session.id;
    this.sessionManager?.trackConnection(session.id, ws);
    const sessionAck = {
      type: "sessionAck",
      sessionId: session.id,
      isNew,
      isRestored
    };
    ws.send(JSON.stringify(sessionAck));
    this.setupRenderCallback(ws, clientData);
    const initialPatches = [];
    clientData.engine.setRenderCallback((patches) => {
      initialPatches.push(...patches);
    });
    clientData.engine.renderSource(this._ui);
    this.setupRenderCallback(ws, clientData);
    const initialMessage = {
      type: "initialTree",
      module: this._moduleName,
      state: clientData.moduleInstance.getState(),
      patches: initialPatches,
      revision: 0
    };
    ws.send(JSON.stringify(initialMessage));
    const client = {
      id: clientData.id,
      socket: ws,
      connectedAt: clientData.connectedAt
    };
    this._onConnectionCallbacks.forEach((cb) => cb(client));
  }
  setupRenderCallback(ws, clientData) {
    clientData.engine.setRenderCallback((patches) => {
      const data = this.clients.get(ws);
      if (!data)
        return;
      data.revision++;
      const message = {
        type: "patch",
        module: this._moduleName,
        patches,
        revision: data.revision
      };
      ws.send(JSON.stringify(message));
      if (this.sessionManager?.getConcurrentPolicy() === "allow-multiple" && data.sessionId) {
        const connections = this.sessionManager.getConnections(data.sessionId);
        if (connections) {
          for (const conn of connections) {
            if (conn !== ws) {
              conn.send(JSON.stringify(message));
            }
          }
        }
      }
    });
  }
  async handleConcurrentConnection(ws, clientData, existingSession, props) {
    const policy = this.sessionManager?.getConcurrentPolicy() ?? "kick-old";
    switch (policy) {
      case "kick-old": {
        const existingConnections = this.sessionManager?.getConnections(existingSession.id);
        if (existingConnections) {
          for (const conn of existingConnections) {
            const oldWs = conn;
            const expiredMsg = {
              type: "sessionExpired",
              sessionId: existingSession.id,
              reason: "kicked"
            };
            oldWs.send(JSON.stringify(expiredMsg));
            oldWs.close(1000, "Session taken by new connection");
          }
        }
        return true;
      }
      case "reject-new": {
        const expiredMsg = {
          type: "sessionExpired",
          sessionId: existingSession.id,
          reason: "kicked"
        };
        ws.send(JSON.stringify(expiredMsg));
        ws.close(1000, "Session already active");
        return false;
      }
      case "allow-multiple": {
        return true;
      }
      default:
        return true;
    }
  }
  async triggerReconnect(clientData, session, savedState) {
    const handler = this._module?.handlers.onReconnect;
    if (!handler)
      return;
    let restored = false;
    const restore = (state2) => {
      restored = true;
      clientData.moduleInstance.updateState(state2);
    };
    await handler({ session, restore });
  }
  handleMessage(ws, message) {
    try {
      const clientData = this.clients.get(ws);
      if (!clientData)
        return;
      const msg = JSON.parse(message.toString());
      switch (msg.type) {
        case "hello": {
          const helloMsg = msg;
          this.initializeSession(ws, clientData, helloMsg.sessionId, helloMsg.props);
          break;
        }
        case "dispatchAction": {
          const actionMsg = msg;
          clientData.engine.dispatchAction(actionMsg.action, actionMsg.payload);
          break;
        }
        default:
          break;
      }
    } catch (error) {
      console.error("Error handling WebSocket message:", error);
    }
  }
  async handleClose(ws) {
    const clientData = this.clients.get(ws);
    if (!clientData)
      return;
    if (clientData.helloTimeout) {
      clearTimeout(clientData.helloTimeout);
    }
    const currentState = clientData.moduleInstance.getState();
    if (clientData.sessionId && this._module?.handlers.onDisconnect) {
      const session = this.sessionManager?.getActiveSession(clientData.sessionId);
      if (session) {
        await this._module.handlers.onDisconnect({
          state: currentState,
          session
        });
      }
    }
    if (clientData.sessionId && this.sessionManager) {
      this.sessionManager.untrackConnection(clientData.sessionId, ws);
      if (this.sessionManager.getConnectionCount(clientData.sessionId) === 0) {
        const session = this.sessionManager.getActiveSession(clientData.sessionId);
        if (session) {
          this.sessionManager.suspendSession(clientData.sessionId, currentState, async (expiredSession) => {
            if (this._module?.handlers.onExpire) {
              await this._module.handlers.onExpire({ session: expiredSession });
            }
          });
        }
      }
    }
    await clientData.moduleInstance.destroy();
    this.clients.delete(ws);
    const client = {
      id: clientData.id,
      socket: ws,
      connectedAt: clientData.connectedAt
    };
    this._onDisconnectionCallbacks.forEach((cb) => cb(client));
  }
  getClientCount() {
    return this.clients.size;
  }
  getSessionStats() {
    return this.sessionManager?.getStats() ?? {
      activeSessions: 0,
      pendingSessions: 0,
      totalConnections: 0
    };
  }
  broadcast(message) {
    const json = JSON.stringify(message);
    this.clients.forEach((_, ws) => {
      ws.send(json);
    });
  }
}
function serve(options) {
  const server = new RemoteServer().module(options.moduleName ?? "App", options.module).ui(options.ui);
  if (options.port || options.hostname) {
    server.config({
      port: options.port,
      hostname: options.hostname
    });
  }
  if (options.session) {
    server.session(options.session);
  }
  if (options.onConnection) {
    server.onConnection(options.onConnection);
  }
  if (options.onDisconnection) {
    server.onDisconnection(options.onDisconnection);
  }
  return server.listen(options.port);
}

// src/index.ts
init_result();
init_logger();
export {
  withRetry,
  watchComponents,
  usingSync,
  using,
  unwrapProxy,
  unwrapOrElse,
  unwrapOr,
  unwrap,
  state,
  setLogLevel,
  serve,
  retryResult,
  retry,
  registerHypenPlugin,
  match,
  mapErr,
  map,
  logger,
  log,
  loadDiscoveredComponents,
  item,
  isStateProxy,
  isOk,
  isErr,
  isDisposable,
  index,
  hypenPlugin,
  hypen,
  hasElementDisposables,
  getStateSnapshot,
  getLogLevel,
  getElementDisposables,
  generateComponentsCode,
  fromTry,
  fromPromise,
  frameworkLoggers,
  flatMap,
  enableLogging,
  disposeElement,
  disposableWebSocket,
  disposableTimeout,
  disposableSubscription,
  disposableListener,
  disposableInterval,
  disposableAbortController,
  discoverComponents,
  disableLogging,
  defaultHypenPlugin,
  createObservableState,
  createLogger,
  createEventEmitter,
  configureLogger,
  compositeDisposable,
  componentLoader,
  batchStateUpdates,
  app,
  all,
  TypedEventEmitter,
  StateError,
  SessionManager,
  Router,
  Route,
  RetryPresets,
  RetryConditions,
  RemoteServer,
  RemoteEngine,
  Ok,
  Logger,
  Link,
  HypenRouter,
  HypenModuleInstance,
  HypenGlobalContext,
  HypenError,
  HypenAppBuilder,
  HypenApp,
  Err,
  Engine,
  DisposableStack,
  DisposableMixin,
  ConsoleRenderer,
  ConnectionError,
  ComponentResolver,
  ComponentLoader,
  Engine2 as BrowserEngine,
  BaseRenderer,
  ActionError
};

//# debugId=C14CD722218CC69564756E2164756E21
