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;
  }
  const visited = new WeakMap;
  function cloneInternal(value) {
    if (value === null || typeof value !== "object") {
      return value;
    }
    if (visited.has(value)) {
      return visited.get(value);
    }
    if (value instanceof Date) {
      return new Date(value.getTime());
    }
    if (value instanceof RegExp) {
      return new RegExp(value.source, value.flags);
    }
    if (value instanceof Map) {
      const mapClone = new Map;
      visited.set(value, mapClone);
      for (const [k, v] of value.entries()) {
        mapClone.set(cloneInternal(k), cloneInternal(v));
      }
      return mapClone;
    }
    if (value instanceof Set) {
      const setClone = new Set;
      visited.set(value, setClone);
      for (const item of value.values()) {
        setClone.add(cloneInternal(item));
      }
      return setClone;
    }
    if (value instanceof WeakMap || value instanceof WeakSet) {
      return value;
    }
    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 (value.hasOwnProperty(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) {
    if (proxyCache.has(target)) {
      return proxyCache.get(target);
    }
    const proxy = new Proxy(target, {
      get(obj, prop) {
        const value = obj[prop];
        if (prop === "__beginBatch") {
          return () => {
            batchDepth++;
          };
        }
        if (prop === "__endBatch") {
          return () => {
            batchDepth--;
            if (batchDepth === 0) {
              notifyChange();
            }
          };
        }
        if (prop === "__getSnapshot") {
          return () => deepClone(obj);
        }
        if (value && typeof value === "object") {
          if (value instanceof Date || value instanceof RegExp || value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet) {
            return value;
          }
          return createProxy(value, basePath ? `${basePath}.${String(prop)}` : String(prop));
        }
        return value;
      },
      set(obj, prop, value) {
        const oldValue = obj[prop];
        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);
}

// 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;
  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;
  }
  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,
      handlers: {
        onCreated: this.createdHandler,
        onAction: this.actionHandlers,
        onDestroyed: this.destroyedHandler
      }
    };
  }
}

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) {
      console.log(`[ModuleInstance] Registering action handler: ${actionName} for module ${definition.name}`);
      this.engine.onAction(actionName, async (action) => {
        console.log(`[ModuleInstance] 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;
        try {
          await handler({
            action: actionCtx,
            state: this.state,
            next,
            context
          });
          console.log(`[ModuleInstance] Action handler completed: ${actionName}`);
        } catch (error) {
          console.error(`[ModuleInstance] Action handler error for ${actionName}:`, error);
          throw error;
        }
      });
    }
    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 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 app;
var init_app = __esm(() => {
  app = new HypenApp;
});

// 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 Engine {
  wasmEngine = null;
  initialized = false;
  async init(options = {}) {
    if (this.initialized)
      return;
    const cdnBase = "https://unpkg.com/@hypen-space/core@0.2.10/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
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 match = path.match(regex);
    if (!match)
      return null;
    const params = {};
    paramNames.forEach((name, i) => {
      const value = match[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
class RemoteEngine {
  ws = null;
  url;
  state = "disconnected";
  options;
  reconnectAttempts = 0;
  reconnectTimer = null;
  patchCallbacks = [];
  stateCallbacks = [];
  connectionCallbacks = [];
  disconnectionCallbacks = [];
  errorCallbacks = [];
  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
    };
  }
  async connect() {
    if (this.state === "connected" || this.state === "connecting") {
      return;
    }
    this.state = "connecting";
    return new Promise((resolve, reject) => {
      try {
        this.ws = new WebSocket(this.url);
        this.ws.onopen = () => {
          this.state = "connected";
          this.reconnectAttempts = 0;
          this.connectionCallbacks.forEach((cb) => cb());
          resolve();
        };
        this.ws.onmessage = (event) => {
          this.handleMessage(event.data);
        };
        this.ws.onerror = () => {
          this.state = "error";
          const error = new Error("WebSocket error");
          this.errorCallbacks.forEach((cb) => cb(error));
          reject(error);
        };
        this.ws.onclose = () => {
          this.state = "disconnected";
          this.disconnectionCallbacks.forEach((cb) => cb());
          this.attemptReconnect();
        };
      } catch (error) {
        this.state = "error";
        reject(error);
      }
    });
  }
  disconnect() {
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
      this.reconnectTimer = null;
    }
    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }
    this.state = "disconnected";
  }
  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;
  }
  getConnectionState() {
    return this.state;
  }
  getCurrentState() {
    return this.currentState;
  }
  getRevision() {
    return this.currentRevision;
  }
  handleMessage(data) {
    try {
      const message = JSON.parse(data);
      switch (message.type) {
        case "initialTree":
          this.handleInitialTree(message);
          break;
        case "patch":
          this.handlePatch(message);
          break;
        case "stateUpdate":
          this.currentState = message.state;
          this.stateCallbacks.forEach((cb) => cb(message.state));
          break;
      }
    } catch (error) {
      console.error("Error handling remote message:", error);
      this.errorCallbacks.forEach((cb) => cb(error instanceof Error ? error : new Error(String(error))));
    }
  }
  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;
    }
    if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
      console.error("Max reconnection attempts reached");
      return;
    }
    this.reconnectAttempts++;
    console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.options.maxReconnectAttempts})...`);
    this.reconnectTimer = setTimeout(() => {
      this.connect().catch((error) => {
        console.error("Reconnection failed:", error);
      });
    }, this.options.reconnectInterval);
  }
}

// src/index.browser.ts
init_app();
export {
  getStateSnapshot,
  createObservableState,
  createEventEmitter,
  batchStateUpdates,
  app,
  TypedEventEmitter,
  RemoteEngine,
  HypenRouter,
  HypenModuleInstance,
  HypenGlobalContext,
  HypenAppBuilder,
  HypenApp,
  Engine,
  ConsoleRenderer,
  BaseRenderer
};

//# debugId=5B60D8CD2B23375164756E2164756E21
