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

// src/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();
          } else {}
        });
      }
    } else {
      pendingChange = pendingChange || { paths: [], newValues: {} };
    }
  }
  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) {
        const existed = Object.prototype.hasOwnProperty.call(obj, prop);
        const result = delete obj[prop];
        if (existed) {
          scheduleBatch();
        }
        return result;
      }
    });
    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/logger.ts
function isProduction() {
  if (typeof process !== "undefined" && process.env) {
    return false;
  }
  return false;
}
function setLogLevel(level) {
  config.level = level;
}
function getLogLevel() {
  return config.level;
}
function configureLogger(options) {
  config = { ...config, ...options };
}
function enableLogging() {
  config.level = "debug";
}
function disableLogging() {
  config.level = "none";
}
function setDebugMode(enabled) {
  config.level = enabled ? "debug" : "error";
}
function isDebugMode() {
  return config.level === "debug";
}
function shouldLog(level) {
  return LOG_LEVEL_ORDER[level] >= LOG_LEVEL_ORDER[config.level];
}
function formatTag(tag, level) {
  const timestamp = config.timestamps ? `${new Date().toISOString()} ` : "";
  if (config.colors && level !== "none") {
    const color = LOG_LEVEL_COLORS[level];
    return `${timestamp}${color}[${tag}]${RESET_COLOR}`;
  }
  return `${timestamp}[${tag}]`;
}

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

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

// src/datasource.ts
class DataSourceManager {
  plugins = new Map;
  configs = new Map;
  state = new Map;
  engine;
  constructor(engine) {
    this.engine = engine;
  }
  async use(plugin, config2) {
    const { name } = plugin;
    this.state.set(name, {});
    this.plugins.set(name, plugin);
    this.configs.set(name, config2);
    await plugin.connect(config2, (change) => {
      const current = this.state.get(name) ?? {};
      for (const path of change.paths) {
        if (path in change.values) {
          current[path] = change.values[path];
        }
      }
      this.state.set(name, current);
      this.engine.setContext(name, current);
    });
  }
  get(name) {
    return this.plugins.get(name);
  }
  has(name) {
    return this.plugins.has(name);
  }
  getNames() {
    return Array.from(this.plugins.keys());
  }
  getStatus(name) {
    return this.plugins.get(name)?.status;
  }
  async remove(name) {
    const plugin = this.plugins.get(name);
    if (plugin) {
      await plugin.disconnect();
      this.plugins.delete(name);
      this.configs.delete(name);
      this.state.delete(name);
      this.engine.removeContext(name);
    }
  }
  async disconnectAll() {
    const names = Array.from(this.plugins.keys());
    await Promise.all(names.map((name) => this.remove(name)));
  }
}

// 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;
  _registry;
  dataSourceEntries = [];
  constructor(initialState, options, registry) {
    this.initialState = initialState;
    this.options = options || {};
    this._registry = registry;
  }
  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;
  }
  useDataSource(plugin, config2) {
    this.dataSourceEntries.push({ plugin, config: config2 });
    return this;
  }
  ui(template) {
    this.template = template;
    return this.build();
  }
  uiFile(path) {
    const fs = (() => ({}));
    this.template = fs.readFileSync(path, "utf-8").trim();
    return this.build();
  }
  build() {
    const stateKeys = this.initialState !== null && typeof this.initialState === "object" ? Object.keys(this.initialState) : [];
    const definition = {
      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,
      dataSources: this.dataSourceEntries.length > 0 ? this.dataSourceEntries : undefined,
      handlers: {
        onCreated: this.createdHandler,
        onAction: this.actionHandlers,
        onDestroyed: this.destroyedHandler,
        onDisconnect: this.disconnectHandler,
        onReconnect: this.reconnectHandler,
        onExpire: this.expireHandler,
        onError: this.errorHandler
      }
    };
    if (this.options.name && this._registry) {
      this._registry.set(this.options.name, definition);
    }
    return definition;
  }
}

class HypenApp {
  _registry = new Map;
  defineState(initial, options) {
    return new HypenAppBuilder(initial, options, this._registry);
  }
  module(name) {
    const registry = this._registry;
    return {
      defineState: (initial, options) => {
        return new HypenAppBuilder(initial, { ...options, name }, registry);
      }
    };
  }
  get(name) {
    return this._registry.get(name);
  }
  has(name) {
    return this._registry.has(name);
  }
  get components() {
    return this._registry;
  }
  getNames() {
    return Array.from(this._registry.keys());
  }
  get size() {
    return this._registry.size;
  }
  unregister(name) {
    this._registry.delete(name);
  }
  clear() {
    this._registry.clear();
  }
}

class HypenModuleInstance {
  engine;
  definition;
  state;
  isDestroyed = false;
  router;
  globalContext;
  stateChangeCallbacks = [];
  dataSourceManager;
  dataSourceAccessor = {};
  constructor(engine, definition, router, globalContext) {
    this.engine = engine;
    this.definition = definition;
    this.router = router ?? null;
    this.globalContext = globalContext;
    const statePrefix = (definition.name || "").toLowerCase();
    this.state = createObservableState(definition.initialState, {
      onChange: (change) => {
        this.engine.notifyStateChange(change.paths, change.newValues);
        this.stateChangeCallbacks.forEach((cb) => cb());
      },
      pathPrefix: statePrefix || undefined
    });
    const snapshot = getStateSnapshot(this.state);
    const prefixedState = statePrefix ? { [statePrefix]: snapshot } : snapshot;
    this.engine.setModule(definition.name || "AnonymousModule", definition.actions, definition.stateKeys, prefixedState);
    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 context = this.globalContext ? this.createGlobalContextAPI() : undefined;
        const result = await this.executeAction(actionName, handler, {
          action: actionCtx,
          state: this.state,
          context,
          dataSources: this.dataSourceAccessor
        });
        if (!result.ok) {
          const shouldRethrow = await this.handleError(result.error, { actionName });
          if (shouldRethrow) {
            throw result.error;
          }
        } else {
          log2.debug(`Action handler completed: ${actionName}`);
        }
      });
    }
    this.engine.onAction("__hypen_bind", (action) => {
      const payload = action.payload;
      if (!payload?.path)
        return;
      const segments = payload.path.split(".");
      let target = this.state;
      for (let i = 0;i < segments.length - 1; i++) {
        const seg = segments[i];
        target = target?.[seg];
        if (target == null)
          return;
      }
      const lastSeg = segments[segments.length - 1];
      target[lastSeg] = payload.value;
    });
    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),
      router: this.router
    };
    const ctxRecord = ctx;
    if (ctxRecord.__hypenEngine) {
      api.__hypenEngine = ctxRecord.__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.dataSources?.length) {
      const dsEngine = this.engine;
      this.dataSourceManager = new DataSourceManager(dsEngine);
      for (const { plugin, config: config2 } of this.definition.dataSources) {
        try {
          await this.dataSourceManager.use(plugin, config2);
          this.dataSourceAccessor[plugin.name] = new Proxy(plugin, {
            get(target, prop) {
              if (typeof prop === "string" && !(prop in target)) {
                return (...args) => target.call(prop, ...args);
              }
              return target[prop];
            }
          });
        } catch (e) {
          log2.error(`Failed to connect data source "${plugin.name}":`, e);
        }
      }
    }
    if (this.definition.handlers.onCreated) {
      const context = this.globalContext ? this.createGlobalContextAPI() : undefined;
      try {
        await this.definition.handlers.onCreated(this.state, context);
      } catch (e) {
        const error = e instanceof HypenError ? e : new ActionError("onCreated", e);
        const shouldRethrow = await this.handleError(error, { lifecycle: "created" });
        if (shouldRethrow) {
          throw error;
        }
      }
    }
  }
  onStateChange(callback) {
    this.stateChangeCallbacks.push(callback);
  }
  async destroy() {
    if (this.isDestroyed)
      return;
    if (this.dataSourceManager) {
      try {
        await this.dataSourceManager.disconnectAll();
      } catch (e) {
        log2.error("Error disconnecting data sources:", e);
      }
      this.dataSourceManager = undefined;
      this.dataSourceAccessor = {};
    }
    if (this.definition.handlers.onDestroyed) {
      try {
        await this.definition.handlers.onDestroyed(this.state);
      } catch (e) {
        const error = e instanceof HypenError ? e : new ActionError("onDestroyed", e);
        const shouldRethrow = await this.handleError(error, { lifecycle: "destroyed" });
        if (shouldRethrow) {
          throw error;
        }
      }
    }
    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;
});

// node:path
var exports_path = {};
__export(exports_path, {
  sep: () => sep,
  resolve: () => resolve,
  relative: () => relative,
  posix: () => posix,
  parse: () => parse,
  normalize: () => normalize,
  join: () => join,
  isAbsolute: () => isAbsolute,
  format: () => format,
  extname: () => extname,
  dirname: () => dirname,
  delimiter: () => delimiter,
  default: () => path_default,
  basename: () => basename,
  _makeLong: () => _makeLong
});
function assertPath(path) {
  if (typeof path !== "string")
    throw TypeError("Path must be a string. Received " + JSON.stringify(path));
}
function normalizeStringPosix(path, allowAboveRoot) {
  var res = "", lastSegmentLength = 0, lastSlash = -1, dots = 0, code;
  for (var i = 0;i <= path.length; ++i) {
    if (i < path.length)
      code = path.charCodeAt(i);
    else if (code === 47)
      break;
    else
      code = 47;
    if (code === 47) {
      if (lastSlash === i - 1 || dots === 1)
        ;
      else if (lastSlash !== i - 1 && dots === 2) {
        if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 || res.charCodeAt(res.length - 2) !== 46) {
          if (res.length > 2) {
            var lastSlashIndex = res.lastIndexOf("/");
            if (lastSlashIndex !== res.length - 1) {
              if (lastSlashIndex === -1)
                res = "", lastSegmentLength = 0;
              else
                res = res.slice(0, lastSlashIndex), lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
              lastSlash = i, dots = 0;
              continue;
            }
          } else if (res.length === 2 || res.length === 1) {
            res = "", lastSegmentLength = 0, lastSlash = i, dots = 0;
            continue;
          }
        }
        if (allowAboveRoot) {
          if (res.length > 0)
            res += "/..";
          else
            res = "..";
          lastSegmentLength = 2;
        }
      } else {
        if (res.length > 0)
          res += "/" + path.slice(lastSlash + 1, i);
        else
          res = path.slice(lastSlash + 1, i);
        lastSegmentLength = i - lastSlash - 1;
      }
      lastSlash = i, dots = 0;
    } else if (code === 46 && dots !== -1)
      ++dots;
    else
      dots = -1;
  }
  return res;
}
function _format(sep, pathObject) {
  var dir = pathObject.dir || pathObject.root, base = pathObject.base || (pathObject.name || "") + (pathObject.ext || "");
  if (!dir)
    return base;
  if (dir === pathObject.root)
    return dir + base;
  return dir + sep + base;
}
function resolve() {
  var resolvedPath = "", resolvedAbsolute = false, cwd;
  for (var i = arguments.length - 1;i >= -1 && !resolvedAbsolute; i--) {
    var path;
    if (i >= 0)
      path = arguments[i];
    else {
      if (cwd === undefined)
        cwd = process.cwd();
      path = cwd;
    }
    if (assertPath(path), path.length === 0)
      continue;
    resolvedPath = path + "/" + resolvedPath, resolvedAbsolute = path.charCodeAt(0) === 47;
  }
  if (resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute), resolvedAbsolute)
    if (resolvedPath.length > 0)
      return "/" + resolvedPath;
    else
      return "/";
  else if (resolvedPath.length > 0)
    return resolvedPath;
  else
    return ".";
}
function normalize(path) {
  if (assertPath(path), path.length === 0)
    return ".";
  var isAbsolute = path.charCodeAt(0) === 47, trailingSeparator = path.charCodeAt(path.length - 1) === 47;
  if (path = normalizeStringPosix(path, !isAbsolute), path.length === 0 && !isAbsolute)
    path = ".";
  if (path.length > 0 && trailingSeparator)
    path += "/";
  if (isAbsolute)
    return "/" + path;
  return path;
}
function isAbsolute(path) {
  return assertPath(path), path.length > 0 && path.charCodeAt(0) === 47;
}
function join() {
  if (arguments.length === 0)
    return ".";
  var joined;
  for (var i = 0;i < arguments.length; ++i) {
    var arg = arguments[i];
    if (assertPath(arg), arg.length > 0)
      if (joined === undefined)
        joined = arg;
      else
        joined += "/" + arg;
  }
  if (joined === undefined)
    return ".";
  return normalize(joined);
}
function relative(from, to) {
  if (assertPath(from), assertPath(to), from === to)
    return "";
  if (from = resolve(from), to = resolve(to), from === to)
    return "";
  var fromStart = 1;
  for (;fromStart < from.length; ++fromStart)
    if (from.charCodeAt(fromStart) !== 47)
      break;
  var fromEnd = from.length, fromLen = fromEnd - fromStart, toStart = 1;
  for (;toStart < to.length; ++toStart)
    if (to.charCodeAt(toStart) !== 47)
      break;
  var toEnd = to.length, toLen = toEnd - toStart, length = fromLen < toLen ? fromLen : toLen, lastCommonSep = -1, i = 0;
  for (;i <= length; ++i) {
    if (i === length) {
      if (toLen > length) {
        if (to.charCodeAt(toStart + i) === 47)
          return to.slice(toStart + i + 1);
        else if (i === 0)
          return to.slice(toStart + i);
      } else if (fromLen > length) {
        if (from.charCodeAt(fromStart + i) === 47)
          lastCommonSep = i;
        else if (i === 0)
          lastCommonSep = 0;
      }
      break;
    }
    var fromCode = from.charCodeAt(fromStart + i), toCode = to.charCodeAt(toStart + i);
    if (fromCode !== toCode)
      break;
    else if (fromCode === 47)
      lastCommonSep = i;
  }
  var out = "";
  for (i = fromStart + lastCommonSep + 1;i <= fromEnd; ++i)
    if (i === fromEnd || from.charCodeAt(i) === 47)
      if (out.length === 0)
        out += "..";
      else
        out += "/..";
  if (out.length > 0)
    return out + to.slice(toStart + lastCommonSep);
  else {
    if (toStart += lastCommonSep, to.charCodeAt(toStart) === 47)
      ++toStart;
    return to.slice(toStart);
  }
}
function _makeLong(path) {
  return path;
}
function dirname(path) {
  if (assertPath(path), path.length === 0)
    return ".";
  var code = path.charCodeAt(0), hasRoot = code === 47, end = -1, matchedSlash = true;
  for (var i = path.length - 1;i >= 1; --i)
    if (code = path.charCodeAt(i), code === 47) {
      if (!matchedSlash) {
        end = i;
        break;
      }
    } else
      matchedSlash = false;
  if (end === -1)
    return hasRoot ? "/" : ".";
  if (hasRoot && end === 1)
    return "//";
  return path.slice(0, end);
}
function basename(path, ext) {
  if (ext !== undefined && typeof ext !== "string")
    throw TypeError('"ext" argument must be a string');
  assertPath(path);
  var start = 0, end = -1, matchedSlash = true, i;
  if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
    if (ext.length === path.length && ext === path)
      return "";
    var extIdx = ext.length - 1, firstNonSlashEnd = -1;
    for (i = path.length - 1;i >= 0; --i) {
      var code = path.charCodeAt(i);
      if (code === 47) {
        if (!matchedSlash) {
          start = i + 1;
          break;
        }
      } else {
        if (firstNonSlashEnd === -1)
          matchedSlash = false, firstNonSlashEnd = i + 1;
        if (extIdx >= 0)
          if (code === ext.charCodeAt(extIdx)) {
            if (--extIdx === -1)
              end = i;
          } else
            extIdx = -1, end = firstNonSlashEnd;
      }
    }
    if (start === end)
      end = firstNonSlashEnd;
    else if (end === -1)
      end = path.length;
    return path.slice(start, end);
  } else {
    for (i = path.length - 1;i >= 0; --i)
      if (path.charCodeAt(i) === 47) {
        if (!matchedSlash) {
          start = i + 1;
          break;
        }
      } else if (end === -1)
        matchedSlash = false, end = i + 1;
    if (end === -1)
      return "";
    return path.slice(start, end);
  }
}
function extname(path) {
  assertPath(path);
  var startDot = -1, startPart = 0, end = -1, matchedSlash = true, preDotState = 0;
  for (var i = path.length - 1;i >= 0; --i) {
    var code = path.charCodeAt(i);
    if (code === 47) {
      if (!matchedSlash) {
        startPart = i + 1;
        break;
      }
      continue;
    }
    if (end === -1)
      matchedSlash = false, end = i + 1;
    if (code === 46) {
      if (startDot === -1)
        startDot = i;
      else if (preDotState !== 1)
        preDotState = 1;
    } else if (startDot !== -1)
      preDotState = -1;
  }
  if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
    return "";
  return path.slice(startDot, end);
}
function format(pathObject) {
  if (pathObject === null || typeof pathObject !== "object")
    throw TypeError('The "pathObject" argument must be of type Object. Received type ' + typeof pathObject);
  return _format("/", pathObject);
}
function parse(path) {
  assertPath(path);
  var ret = { root: "", dir: "", base: "", ext: "", name: "" };
  if (path.length === 0)
    return ret;
  var code = path.charCodeAt(0), isAbsolute2 = code === 47, start;
  if (isAbsolute2)
    ret.root = "/", start = 1;
  else
    start = 0;
  var startDot = -1, startPart = 0, end = -1, matchedSlash = true, i = path.length - 1, preDotState = 0;
  for (;i >= start; --i) {
    if (code = path.charCodeAt(i), code === 47) {
      if (!matchedSlash) {
        startPart = i + 1;
        break;
      }
      continue;
    }
    if (end === -1)
      matchedSlash = false, end = i + 1;
    if (code === 46) {
      if (startDot === -1)
        startDot = i;
      else if (preDotState !== 1)
        preDotState = 1;
    } else if (startDot !== -1)
      preDotState = -1;
  }
  if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
    if (end !== -1)
      if (startPart === 0 && isAbsolute2)
        ret.base = ret.name = path.slice(1, end);
      else
        ret.base = ret.name = path.slice(startPart, end);
  } else {
    if (startPart === 0 && isAbsolute2)
      ret.name = path.slice(1, startDot), ret.base = path.slice(1, end);
    else
      ret.name = path.slice(startPart, startDot), ret.base = path.slice(startPart, end);
    ret.ext = path.slice(startDot, end);
  }
  if (startPart > 0)
    ret.dir = path.slice(0, startPart - 1);
  else if (isAbsolute2)
    ret.dir = "/";
  return ret;
}
var sep = "/", delimiter = ":", posix, path_default;
var init_path = __esm(() => {
  posix = ((p) => (p.posix = p, p))({ resolve, normalize, isAbsolute, join, relative, _makeLong, dirname, basename, extname, format, parse, sep, delimiter, win32: null, posix: null });
  path_default = posix;
});

// src/engine.ts
init_logger();
init_result();
import { WasmEngine } from "../wasm-node/hypen_engine.js";
var log3 = frameworkLoggers.engine;
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();
    try {
      engine.renderSource(source);
    } catch (err) {
      throw classifyEngineError(err);
    }
  }
  renderLazyComponent(source) {
    const engine = this.ensureInitialized();
    engine.renderLazyComponent(source);
  }
  renderInto(source, parentNodeId, state) {
    const engine = this.ensureInitialized();
    try {
      engine.renderInto(source, parentNodeId, unwrapForWasm(state));
    } catch (err) {
      throw classifyEngineError(err);
    }
  }
  notifyStateChange(paths, values) {
    const engine = this.ensureInitialized();
    if (paths.length === 0) {
      return;
    }
    try {
      engine.updateStateSparse(paths, unwrapForWasm(values));
    } catch (err) {
      throw classifyEngineError(err);
    }
    log3.debug("State changed (sparse):", paths);
  }
  notifyStateChangeFull(paths, currentState) {
    const engine = this.ensureInitialized();
    engine.updateState(unwrapForWasm(currentState));
    if (paths.length > 0) {
      log3.debug("State changed (full):", paths);
    }
  }
  updateState(statePatch) {
    const engine = this.ensureInitialized();
    engine.updateState(unwrapForWasm(statePatch));
  }
  dispatchAction(name, payload) {
    const engine = this.ensureInitialized();
    try {
      engine.dispatchAction(name, payload ?? null);
    } catch (err) {
      throw classifyEngineError(err);
    }
  }
  _onErrorHandler;
  setOnError(handler) {
    this._onErrorHandler = handler;
  }
  onAction(actionName, handler) {
    const engine = this.ensureInitialized();
    const handleError = (err) => {
      log3.error("Action handler error:", err);
      if (this._onErrorHandler) {
        this._onErrorHandler(err instanceof Error ? err : new Error(String(err)));
      }
    };
    engine.onAction(actionName, (action) => {
      try {
        const result = handler(action);
        if (result && typeof result.catch === "function") {
          result.catch(handleError);
        }
      } catch (err) {
        handleError(err);
      }
    });
  }
  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);
  }
  setContext(name, data) {
    const engine = this.ensureInitialized();
    try {
      engine.setContext(name, unwrapForWasm(data));
    } catch (err) {
      throw classifyEngineError(err);
    }
    log3.debug(`Data source "${name}" context set`);
  }
  removeContext(name) {
    const engine = this.ensureInitialized();
    engine.removeContext(name);
  }
}

// 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
init_logger();

// src/discovery.ts
init_path();
init_logger();
var {existsSync, readdirSync, readFileSync, watch} = (() => ({}));
async function discoverComponents(baseDir, options = {}) {
  const {
    patterns = ["single-file", "folder", "sibling", "index"],
    recursive = true,
    debug = false
  } = options;
  const log4 = debug ? (...args) => frameworkLoggers.discovery.debug(...args) : () => {};
  const resolvedDir = resolve(baseDir);
  const components = [];
  const seen = new Set;
  log4("Scanning directory:", resolvedDir);
  log4("Patterns:", patterns);
  const addTwoFileComponent = (name, hypenPath, modulePath) => {
    if (seen.has(name)) {
      log4(`Skipping duplicate: ${name}`);
      return;
    }
    seen.add(name);
    const templateRaw = readFileSync(hypenPath, "utf-8");
    const template = templateRaw.trim();
    components.push({
      name,
      hypenPath,
      modulePath,
      template,
      hasModule: modulePath !== null,
      isSingleFile: false
    });
    log4(`Found: ${name} (two-file, ${modulePath ? "with module" : "stateless"})`);
  };
  const addSingleFileComponent = (name, modulePath, template) => {
    if (seen.has(name)) {
      log4(`Skipping duplicate: ${name}`);
      return;
    }
    seen.add(name);
    components.push({
      name,
      hypenPath: null,
      modulePath,
      template,
      hasModule: true,
      isSingleFile: true
    });
    log4(`Found: ${name} (single-file with inline template)`);
  };
  const scanForFolderComponents = (dir) => {
    if (!existsSync(dir))
      return;
    const entries = readdirSync(dir, { withFileTypes: true });
    for (const entry of entries) {
      if (!entry.isDirectory())
        continue;
      const folderPath = join(dir, entry.name);
      const componentName = entry.name;
      if (patterns.includes("folder")) {
        const hypenPath = join(folderPath, "component.hypen");
        if (existsSync(hypenPath)) {
          const modulePath = join(folderPath, "component.ts");
          addTwoFileComponent(componentName, hypenPath, existsSync(modulePath) ? modulePath : null);
          continue;
        }
      }
      if (patterns.includes("index")) {
        const hypenPath = join(folderPath, "index.hypen");
        if (existsSync(hypenPath)) {
          const modulePath = join(folderPath, "index.ts");
          addTwoFileComponent(componentName, hypenPath, existsSync(modulePath) ? modulePath : null);
          continue;
        }
      }
      if (recursive) {
        scanForFolderComponents(folderPath);
      }
    }
  };
  const scanForSiblingComponents = (dir) => {
    if (!existsSync(dir))
      return;
    const entries = readdirSync(dir, { withFileTypes: true });
    for (const entry of entries) {
      if (entry.isDirectory()) {
        if (recursive) {
          scanForSiblingComponents(join(dir, entry.name));
        }
        continue;
      }
      if (!entry.name.endsWith(".hypen"))
        continue;
      const hypenPath = join(dir, entry.name);
      const baseName = basename(entry.name, ".hypen");
      if (baseName === "component" || baseName === "index")
        continue;
      const modulePath = join(dir, `${baseName}.ts`);
      addTwoFileComponent(baseName, hypenPath, existsSync(modulePath) ? modulePath : null);
    }
  };
  const scanForSingleFileComponents = async (dir) => {
    if (!existsSync(dir))
      return;
    const entries = readdirSync(dir, { withFileTypes: true });
    for (const entry of entries) {
      if (entry.isDirectory()) {
        if (recursive) {
          await scanForSingleFileComponents(join(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 = join(dir, `${baseName}.hypen`);
      if (existsSync(hypenPath))
        continue;
      const modulePath = join(dir, entry.name);
      const content = readFileSync(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);
          } else if (module && typeof module === "object") {
            frameworkLoggers.discovery.warn(`Skipping "${entry.name}": has .ui() pattern but module has no .template property. Did you forget to call .ui(hypen\`...\`)?`);
          }
        } catch (e) {
          log4(`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);
  }
  log4(`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;
}

// src/remote/server.ts
var log4 = frameworkLoggers.remote;

class RemoteServer {
  _module = null;
  _moduleName = "App";
  _ui = "";
  _sourceDir = null;
  _discoveredComponents = new Map;
  _config = {};
  _sessionConfig = {};
  _onConnectionCallbacks = [];
  _onDisconnectionCallbacks = [];
  clients = new Map;
  nextClientId = 1;
  server = null;
  sessionManager = null;
  _syncActions = false;
  module(name, module) {
    this._moduleName = name;
    this._module = module;
    return this;
  }
  ui(dsl) {
    this._ui = dsl;
    return this;
  }
  source(dir) {
    this._sourceDir = dir;
    return this;
  }
  config(config2) {
    this._config = { ...this._config, ...config2 };
    return this;
  }
  session(config2) {
    this._sessionConfig = config2;
    return this;
  }
  syncActions() {
    this._syncActions = true;
    return this;
  }
  onConnection(callback) {
    this._onConnectionCallbacks.push(callback);
    return this;
  }
  onDisconnection(callback) {
    this._onDisconnectionCallbacks.push(callback);
    return this;
  }
  async listen(port) {
    if (!this._module) {
      throw new Error("Module not set. Call .module() before .listen()");
    }
    if (this._sourceDir) {
      await this.discoverFromSource();
    }
    if (!this._ui) {
      throw new Error("UI not set. Call .ui() or .source() 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 });
      }
    });
    log4.info(`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 discoverFromSource() {
    const dir = this._sourceDir;
    log4.info(`Discovering components from ${dir}...`);
    const discovered = await discoverComponents(dir);
    const loaded = await loadDiscoveredComponents(discovered);
    for (const [name, { module, template }] of loaded) {
      this._discoveredComponents.set(name, { template, module });
    }
    log4.info(`Discovered ${this._discoveredComponents.size} components: ${Array.from(this._discoveredComponents.keys()).join(", ")}`);
    if (!this._ui) {
      const entry = this._discoveredComponents.get(this._moduleName);
      if (entry) {
        this._ui = entry.template;
      }
    }
  }
  setupComponentResolver(engine) {
    if (this._discoveredComponents.size === 0)
      return;
    engine.setComponentResolver((componentName, _contextPath) => {
      const comp = this._discoveredComponents.get(componentName);
      if (!comp) {
        log4.warn(`Component "${componentName}" not found. Available: ${Array.from(this._discoveredComponents.keys()).join(", ") || "(none)"}`);
        return null;
      }
      return {
        source: comp.template,
        path: componentName
      };
    });
  }
  handleOpen(ws) {
    try {
      const clientId = `client_${this.nextClientId++}`;
      const connectedAt = new Date;
      const engine = new Engine;
      engine.init().catch((err) => log4.error("Engine init failed:", err));
      this.setupComponentResolver(engine);
      const moduleInstance = new HypenModuleInstance(engine, this._module);
      log4.info(`Client ${clientId} connected, engine initialized`);
      const clientData = {
        id: clientId,
        engine,
        moduleInstance,
        revision: 0,
        connectedAt,
        sessionId: "",
        helloReceived: false,
        stateSubscribed: false
      };
      this.clients.set(ws, clientData);
      clientData.helloTimeout = setTimeout(() => {
        if (!clientData.helloReceived) {
          this.initializeSession(ws, clientData, undefined, undefined).catch((err) => log4.error("Error initializing legacy session:", err));
        }
      }, 1000);
    } catch (error) {
      log4.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;
    log4.info(`Initializing session for ${clientData.id} (sessionId: ${requestedSessionId ?? "new"})`);
    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));
    log4.info(`Sent sessionAck to ${clientData.id} (session: ${session.id})`);
    const initialPatches = [];
    clientData.engine.setRenderCallback((patches) => {
      initialPatches.push(...patches);
    });
    try {
      clientData.engine.renderSource(this._ui);
    } catch (renderError) {
      log4.error(`Failed to render UI for ${clientData.id}:`, renderError);
      ws.close(1011, "Render failed");
      return;
    }
    this.setupRenderCallback(ws, clientData);
    const initialMessage = {
      type: "initialTree",
      module: this._moduleName,
      state: clientData.moduleInstance.getState(),
      patches: initialPatches,
      revision: 0
    };
    try {
      ws.send(JSON.stringify(initialMessage));
    } catch (sendError) {
      log4.error(`Failed to send initialTree to ${clientData.id}:`, sendError);
      return;
    }
    log4.info(`Sent initialTree to ${clientData.id} (${initialPatches.length} patches)`);
    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 patchMessage = {
        type: "patch",
        module: this._moduleName,
        patches,
        revision: data.revision
      };
      ws.send(JSON.stringify(patchMessage));
      if (data.stateSubscribed) {
        const stateMessage = {
          type: "stateUpdate",
          module: this._moduleName,
          state: data.moduleInstance.getState(),
          revision: data.revision
        };
        ws.send(JSON.stringify(stateMessage));
      }
      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) {
              const otherWs = conn;
              const otherData = this.clients.get(otherWs);
              otherWs.send(JSON.stringify(patchMessage));
              if (otherData?.stateSubscribed) {
                const stateMessage = {
                  type: "stateUpdate",
                  module: this._moduleName,
                  state: data.moduleInstance.getState(),
                  revision: data.revision
                };
                otherWs.send(JSON.stringify(stateMessage));
              }
            }
          }
        }
      }
    });
  }
  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 = (state) => {
      if (state === null || typeof state !== "object") {
        log4.warn("restore() called with non-object state, ignoring:", typeof state);
        return;
      }
      restored = true;
      clientData.moduleInstance.updateState(state);
    };
    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).catch((err) => log4.error("Error initializing session from hello:", err));
          break;
        }
        case "dispatchAction": {
          const actionMsg = msg;
          clientData.engine.dispatchAction(actionMsg.action, actionMsg.payload);
          if (this._syncActions) {
            for (const [otherWs, otherClient] of this.clients) {
              if (otherWs === ws || !otherClient.helloReceived)
                continue;
              otherClient.engine.dispatchAction(actionMsg.action, actionMsg.payload);
            }
          }
          break;
        }
        case "updateState": {
          const stateMsg = msg;
          clientData.moduleInstance.updateState(stateMsg.state);
          if (this._syncActions) {
            for (const [otherWs, otherClient] of this.clients) {
              if (otherWs === ws || !otherClient.helloReceived)
                continue;
              otherClient.moduleInstance.updateState(stateMsg.state);
            }
          }
          break;
        }
        case "subscribeState": {
          clientData.stateSubscribed = true;
          log4.info(`Client ${clientData.id} subscribed to state updates`);
          break;
        }
        default:
          break;
      }
    } catch (error) {
      log4.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));
  }
  async reload() {
    if (this._sourceDir) {
      this._ui = "";
      await this.discoverFromSource();
    }
    if (!this._ui) {
      log4.warn("Reload skipped: no UI template available");
      return;
    }
    for (const [ws, clientData] of this.clients) {
      if (!clientData.helloReceived)
        continue;
      this.setupComponentResolver(clientData.engine);
      try {
        clientData.engine.renderSource(this._ui);
        log4.info(`Hot-reloaded client ${clientData.id}`);
      } catch (e) {
        log4.error(`Failed to hot-reload client ${clientData.id}:`, e);
      }
    }
  }
  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);
    });
  }
}
async function serve(options) {
  const server = new RemoteServer().module(options.moduleName ?? "App", options.module);
  if (options.source) {
    server.source(options.source);
  }
  if (options.ui) {
    server.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);
}
export {
  serve,
  RemoteServer
};

//# debugId=23B2AD6EC2DA1C3B64756E2164756E21
