import {
  ActorDefinition,
  createActorRouter,
  createFileSystemOrMemoryDriver,
  ensureDirectoryExists,
  getStoragePath,
  lookupInRegistry
} from "./chunk-T4PQK4WA.js";
import {
  ClientConfigSchemaBase,
  RemoteManagerDriver,
  convertRegistryConfigToClientConfig,
  createClient,
  createClientWithDriver,
  getDatacenters,
  getEndpoint,
  transformClientConfig,
  updateRunnerConfig
} from "./chunk-AWJU3AZB.js";
import {
  CONN_STATE_MANAGER_SYMBOL,
  ENGINE_ENDPOINT,
  ENGINE_PORT,
  InspectorConfigSchema,
  PATH_CONNECT,
  PATH_INSPECTOR_CONNECT,
  PATH_WEBSOCKET_BASE,
  PATH_WEBSOCKET_PREFIX,
  RegistryConfigSchema,
  buildActorNames,
  buildManagerRouter,
  createRouter,
  deserializeActorKey,
  getInitialActorKvState,
  getNodeChildProcess,
  getNodeCrypto,
  getNodeFs,
  getNodeFsSync,
  getNodePath,
  getNodeStream,
  getValueLength,
  handleHealthRequest,
  handleMetadataRequest,
  importNodeDependencies,
  parseWebSocketProtocols,
  routeWebSocket,
  truncateRawWebSocketPathPrefix
} from "./chunk-MICYEINX.js";
import {
  ActorConfigSchema,
  KEYS,
  event,
  loggerWithoutContext,
  queue
} from "./chunk-XQWBEBLD.js";
import {
  LogLevelSchema,
  VERSION,
  configureBaseLogger,
  configureDefaultLogger,
  detectRuntime,
  getLogger,
  getNodeEnv,
  getRivetRunEngine,
  getRivetRunEngineVersion,
  getRivetRunnerKey,
  getRivetRunnerKind,
  getRivetToken,
  isDev,
  package_default,
  promiseWithResolvers,
  setLongTimeout,
  stringifyError
} from "./chunk-IZ4M5TJU.js";
import {
  EndpointMismatch,
  InvalidRequest,
  NamespaceMismatch,
  UserError
} from "./chunk-7HTNH26M.js";

// src/actor/mod.ts
function actor(input) {
  const config = ActorConfigSchema.parse(input);
  return new ActorDefinition(config);
}
var event2 = event;
var queue2 = queue;

// src/drivers/engine/actor-driver.ts
import { idToStr, Runner } from "@rivetkit/engine-runner";
import * as cbor from "cbor-x";
import { streamSSE } from "hono/streaming";
import { WSContext } from "hono/ws";
import invariant from "invariant";

// src/drivers/engine/log.ts
function logger() {
  return getLogger("driver-engine");
}

// src/drivers/engine/actor-driver.ts
var RUNNER_SSE_PING_INTERVAL = 1e3;
var CONN_MESSAGE_ACK_DEADLINE = 5e3;
var CONN_BUFFERED_MESSAGE_SIZE_THRESHOLD = 5e5;
var EngineActorDriver = class {
  #config;
  #managerDriver;
  #inlineClient;
  #runner;
  #actors = /* @__PURE__ */ new Map();
  #actorRouter;
  #runnerStarted = promiseWithResolvers((reason) => logger().warn({ msg: "unhandled runner started promise rejection", reason }));
  #runnerStopped = promiseWithResolvers((reason) => logger().warn({ msg: "unhandled runner stopped promise rejection", reason }));
  #isRunnerStopped = false;
  // HACK: Track actor stop intent locally since the runner protocol doesn't
  // pass the stop reason to onActorStop. This will be fixed when the runner
  // protocol is updated to send the intent directly (see RVT-5284)
  #actorStopIntent = /* @__PURE__ */ new Map();
  // Map of conn IDs to message index waiting to be persisted before sending
  // an ack
  //
  // serverMessageIndex is updated and pendingAck is flagged in needed in
  // onBeforePersistConnect, then the HWS ack message is sent in
  // onAfterPersistConn. This allows us to track what's about to be written
  // to storage to prevent race conditions with the serverMessageIndex being
  // updated while writing the existing state.
  //
  // bufferedMessageSize tracks the total bytes received since last persist
  // to force a saveState when threshold is reached. This is the amount of
  // data currently buffered on the gateway.
  #hwsMessageIndex = /* @__PURE__ */ new Map();
  constructor(config, managerDriver, inlineClient) {
    this.#config = config;
    this.#managerDriver = managerDriver;
    this.#inlineClient = inlineClient;
    const token = config.token;
    this.#actorRouter = createActorRouter(
      config,
      this,
      void 0,
      config.test.enabled
    );
    const engineRunnerConfig = {
      version: config.runner.version,
      endpoint: getEndpoint(config),
      token,
      namespace: config.namespace,
      totalSlots: config.runner.totalSlots,
      runnerName: config.runner.runnerName,
      runnerKey: config.runner.runnerKey ?? crypto.randomUUID(),
      metadata: {
        rivetkit: { version: VERSION }
      },
      prepopulateActorNames: buildActorNames(config),
      onConnected: () => {
        this.#runnerStarted.resolve(void 0);
      },
      onDisconnected: (_code, _reason) => {
      },
      onShutdown: () => {
        this.#runnerStopped.resolve(void 0);
        this.#isRunnerStopped = true;
      },
      fetch: this.#runnerFetch.bind(this),
      websocket: this.#runnerWebSocket.bind(this),
      hibernatableWebSocket: {
        canHibernate: this.#hwsCanHibernate.bind(this)
      },
      onActorStart: this.#runnerOnActorStart.bind(this),
      onActorStop: this.#runnerOnActorStop.bind(this),
      logger: getLogger("engine-runner")
    };
    this.#runner = new Runner(engineRunnerConfig);
    this.#runner.start();
    logger().debug({
      msg: "engine runner started",
      endpoint: config.endpoint,
      namespace: config.namespace,
      runnerName: config.runner.runnerName
    });
  }
  getExtraActorLogParams() {
    return { runnerId: this.#runner.runnerId ?? "-" };
  }
  async #loadActorHandler(actorId) {
    const handler = this.#actors.get(actorId);
    if (!handler)
      throw new Error(`Actor handler does not exist ${actorId}`);
    if (handler.actorStartPromise) await handler.actorStartPromise.promise;
    if (!handler.actor) throw new Error("Actor should be loaded");
    return handler;
  }
  getContext(actorId) {
    return {};
  }
  async setAlarm(actor2, timestamp) {
    const handler = this.#actors.get(actor2.id);
    if (!handler) {
      logger().warn({
        msg: "no handler for actor to set alarm"
      });
      return;
    }
    if (handler.alarmTimeout) {
      handler.alarmTimeout.abort();
      handler.alarmTimeout = void 0;
    }
    const delay = Math.max(0, timestamp - Date.now());
    handler.alarmTimeout = setLongTimeout(() => {
      actor2.onAlarm();
      handler.alarmTimeout = void 0;
    }, delay);
    this.#runner.setAlarm(actor2.id, timestamp);
  }
  // No database overrides - will use KV-backed implementation from rivetkit/db
  // MARK: - Batch KV operations
  async kvBatchPut(actorId, entries) {
    await this.#runner.kvPut(actorId, entries);
  }
  async kvBatchGet(actorId, keys) {
    return await this.#runner.kvGet(actorId, keys);
  }
  async kvBatchDelete(actorId, keys) {
    await this.#runner.kvDelete(actorId, keys);
  }
  async kvList(actorId) {
    const entries = await this.#runner.kvListPrefix(
      actorId,
      new Uint8Array()
    );
    const keys = entries.map(([key]) => key);
    logger().info({
      msg: "kvList called",
      actorId,
      keysCount: keys.length,
      keys: keys.map((k) => new TextDecoder().decode(k))
    });
    return keys;
  }
  async kvListPrefix(actorId, prefix) {
    const result = await this.#runner.kvListPrefix(actorId, prefix);
    logger().info({
      msg: "kvListPrefix called",
      actorId,
      prefixStr: new TextDecoder().decode(prefix),
      entriesCount: result.length,
      keys: result.map(([key]) => new TextDecoder().decode(key))
    });
    return result;
  }
  // MARK: - Actor Lifecycle
  async loadActor(actorId) {
    const handler = await this.#loadActorHandler(actorId);
    if (!handler.actor) throw new Error(`Actor ${actorId} failed to load`);
    return handler.actor;
  }
  startSleep(actorId) {
    this.#actorStopIntent.set(actorId, "sleep");
    this.#runner.sleepActor(actorId);
  }
  startDestroy(actorId) {
    this.#actorStopIntent.set(actorId, "destroy");
    this.#runner.stopActor(actorId);
  }
  async shutdownRunner(immediate) {
    logger().info({ msg: "stopping engine actor driver", immediate });
    logger().debug({
      msg: "stopping all actors before shutdown",
      actorCount: this.#actors.size
    });
    const stopPromises = [];
    for (const [_actorId, handler] of this.#actors.entries()) {
      if (handler.actor) {
        stopPromises.push(
          handler.actor.onStop("sleep").catch((err) => {
            var _a;
            (_a = handler.actor) == null ? void 0 : _a.rLog.error({
              msg: "onStop errored",
              error: stringifyError(err)
            });
          })
        );
      }
    }
    await Promise.all(stopPromises);
    logger().debug({ msg: "all actors stopped" });
    await this.#runner.shutdown(immediate);
  }
  async serverlessHandleStart(c) {
    return streamSSE(c, async (stream) => {
      stream.onAbort(() => {
      });
      c.req.raw.signal.addEventListener("abort", () => {
        logger().debug("SSE aborted, shutting down runner");
        this.shutdownRunner(false);
      });
      await this.#runnerStarted.promise;
      const payload = this.#runner.getServerlessInitPacket();
      invariant(payload, "runnerId not set");
      await stream.writeSSE({ data: payload });
      while (true) {
        if (this.#isRunnerStopped) {
          logger().debug({
            msg: "runner is stopped"
          });
          break;
        }
        if (stream.closed || stream.aborted) {
          logger().debug({
            msg: "runner sse stream closed",
            closed: stream.closed,
            aborted: stream.aborted
          });
          break;
        }
        await stream.writeSSE({ event: "ping", data: "" });
        await stream.sleep(RUNNER_SSE_PING_INTERVAL);
      }
      await this.#runnerStopped.promise;
    });
  }
  async #runnerOnActorStart(actorId, generation, actorConfig) {
    var _a;
    logger().debug({
      msg: "runner actor starting",
      actorId,
      name: actorConfig.name,
      key: actorConfig.key,
      generation
    });
    let input;
    if (actorConfig.input) {
      input = cbor.decode(actorConfig.input);
    }
    let handler = this.#actors.get(actorId);
    if (!handler) {
      handler = {
        actorStartPromise: promiseWithResolvers((reason) => logger().warn({ msg: "unhandled actor start promise rejection", reason }))
      };
      this.#actors.set(actorId, handler);
    }
    const name = actorConfig.name;
    invariant(actorConfig.key, "actor should have a key");
    const key = deserializeActorKey(actorConfig.key);
    try {
      const [persistDataBuffer] = await this.#runner.kvGet(actorId, [
        KEYS.PERSIST_DATA
      ]);
      if (persistDataBuffer === null) {
        const initialKvState = getInitialActorKvState(input);
        await this.#runner.kvPut(actorId, initialKvState);
        logger().debug({
          msg: "initialized persist data for new actor",
          actorId
        });
      } else {
        logger().debug({
          msg: "found existing persist data for actor",
          actorId,
          dataSize: persistDataBuffer.byteLength
        });
      }
      const definition = lookupInRegistry(this.#config, actorConfig.name);
      handler.actor = await definition.instantiate();
      await handler.actor.start(
        this,
        this.#inlineClient,
        actorId,
        name,
        key,
        "unknown"
        // TODO: Add regions
      );
      logger().debug({ msg: "runner actor started", actorId, name, key });
    } catch (innerError) {
      const error = new Error(
        `Failed to start actor ${actorId}: ${innerError}`,
        { cause: innerError }
      );
      (_a = handler.actorStartPromise) == null ? void 0 : _a.reject(error);
      handler.actorStartPromise = void 0;
      throw error;
    }
  }
  async #runnerOnActorStop(actorId, generation) {
    logger().debug({ msg: "runner actor stopping", actorId, generation });
    const reason = this.#actorStopIntent.get(actorId) ?? "sleep";
    this.#actorStopIntent.delete(actorId);
    const handler = this.#actors.get(actorId);
    if (handler == null ? void 0 : handler.actor) {
      try {
        await handler.actor.onStop(reason);
      } catch (err) {
        logger().error({
          msg: "error in onStop, proceeding with removing actor",
          err: stringifyError(err)
        });
      }
      this.#actors.delete(actorId);
    }
    logger().debug({ msg: "runner actor stopped", actorId, reason });
  }
  // MARK: - Runner Networking
  async #runnerFetch(_runner, actorId, _gatewayIdBuf, _requestIdBuf, request) {
    logger().debug({
      msg: "runner fetch",
      actorId,
      url: request.url,
      method: request.method
    });
    return await this.#actorRouter.fetch(request, { actorId });
  }
  async #runnerWebSocket(_runner, actorId, websocketRaw, gatewayIdBuf, requestIdBuf, request, requestPath, requestHeaders, isHibernatable, isRestoringHibernatable) {
    var _a, _b, _c, _d;
    const websocket = websocketRaw;
    const wsUniqueId = `ws_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    websocket.__rivet_ws_id = wsUniqueId;
    logger().debug({
      msg: "runner websocket",
      actorId,
      url: request.url,
      isRestoringHibernatable,
      websocketObjectId: websocketRaw ? Object.prototype.toString.call(websocketRaw) : "null",
      websocketType: (_a = websocketRaw == null ? void 0 : websocketRaw.constructor) == null ? void 0 : _a.name,
      wsUniqueId,
      websocketProps: websocketRaw ? Object.keys(websocketRaw).join(", ") : "null"
    });
    const protocols = request.headers.get("sec-websocket-protocol");
    const { encoding, connParams } = parseWebSocketProtocols(protocols);
    let wsHandler;
    try {
      wsHandler = await routeWebSocket(
        request,
        requestPath,
        requestHeaders,
        this.#config,
        this,
        actorId,
        encoding,
        connParams,
        gatewayIdBuf,
        requestIdBuf,
        isHibernatable,
        isRestoringHibernatable
      );
    } catch (err) {
      logger().error({ msg: "building websocket handlers errored", err });
      websocketRaw.close(1011, "ws.route_error");
      return;
    }
    websocket.raw = websocket;
    const wsContext = new WSContext(websocket);
    const conn = wsHandler.conn;
    const actor2 = wsHandler.actor;
    const connStateManager = conn == null ? void 0 : conn[CONN_STATE_MANAGER_SYMBOL];
    logger().debug({
      msg: "attaching websocket event listeners",
      actorId,
      connId: conn == null ? void 0 : conn.id,
      wsUniqueId: websocket.__rivet_ws_id,
      isRestoringHibernatable,
      websocketType: (_b = websocket == null ? void 0 : websocket.constructor) == null ? void 0 : _b.name
    });
    if (isRestoringHibernatable) {
      (_c = wsHandler.onRestore) == null ? void 0 : _c.call(wsHandler, wsContext);
    }
    websocket.addEventListener("open", (event3) => {
      wsHandler.onOpen(event3, wsContext);
    });
    websocket.addEventListener("message", (event3) => {
      var _a2, _b2;
      logger().debug({
        msg: "websocket message event listener triggered",
        connId: conn == null ? void 0 : conn.id,
        actorId: actor2 == null ? void 0 : actor2.id,
        messageIndex: event3.rivetMessageIndex,
        hasWsHandler: !!wsHandler,
        hasOnMessage: !!(wsHandler == null ? void 0 : wsHandler.onMessage),
        actorIsStopping: actor2 == null ? void 0 : actor2.isStopping,
        websocketType: (_a2 = websocket == null ? void 0 : websocket.constructor) == null ? void 0 : _a2.name,
        wsUniqueId: websocket.__rivet_ws_id,
        eventTargetWsId: (_b2 = event3.target) == null ? void 0 : _b2.__rivet_ws_id
      });
      if (actor2 == null ? void 0 : actor2.isStopping) {
        logger().debug({
          msg: "ignoring ws message, actor is stopping",
          connId: conn == null ? void 0 : conn.id,
          actorId: actor2 == null ? void 0 : actor2.id,
          messageIndex: event3.rivetMessageIndex
        });
        return;
      }
      logger().debug({
        msg: "calling wsHandler.onMessage",
        connId: conn == null ? void 0 : conn.id,
        messageIndex: event3.rivetMessageIndex
      });
      wsHandler.onMessage(event3, wsContext);
      const hibernate = connStateManager == null ? void 0 : connStateManager.hibernatableData;
      if (hibernate && conn && actor2) {
        invariant(
          typeof event3.rivetMessageIndex === "number",
          "missing event.rivetMessageIndex"
        );
        const previousMsgIndex = hibernate.serverMessageIndex;
        hibernate.serverMessageIndex = event3.rivetMessageIndex;
        logger().info({
          msg: "persisting message index",
          connId: conn.id,
          previousMsgIndex,
          newMsgIndex: event3.rivetMessageIndex
        });
        const entry = this.#hwsMessageIndex.get(conn.id);
        if (entry) {
          const messageLength = getValueLength(event3.data);
          entry.bufferedMessageSize += messageLength;
          if (entry.bufferedMessageSize >= CONN_BUFFERED_MESSAGE_SIZE_THRESHOLD) {
            entry.bufferedMessageSize = 0;
            entry.pendingAckFromBufferSize = true;
            actor2.stateManager.saveState({
              immediate: true
            });
          } else {
            actor2.stateManager.saveState({
              maxWait: CONN_MESSAGE_ACK_DEADLINE
            });
          }
        } else {
          actor2.stateManager.saveState({
            maxWait: CONN_MESSAGE_ACK_DEADLINE
          });
        }
      }
    });
    websocket.addEventListener("close", (event3) => {
      wsHandler.onClose(event3, wsContext);
    });
    websocket.addEventListener("error", (event3) => {
      wsHandler.onError(event3, wsContext);
    });
    if (isRestoringHibernatable) {
      logger().info({
        msg: "event listeners attached to restored websocket",
        actorId,
        connId: conn == null ? void 0 : conn.id,
        gatewayId: idToStr(gatewayIdBuf),
        requestId: idToStr(requestIdBuf),
        websocketType: (_d = websocket == null ? void 0 : websocket.constructor) == null ? void 0 : _d.name,
        hasMessageListener: !!websocket.addEventListener
      });
    }
  }
  // MARK: - Hibernating WebSockets
  #hwsCanHibernate(actorId, gatewayId, requestId, request) {
    var _a;
    const url = new URL(request.url);
    const path = url.pathname;
    const actorInstance = this.#runner.getActor(actorId);
    if (!actorInstance) {
      logger().warn({
        msg: "actor not found in #hwsCanHibernate",
        actorId
      });
      return false;
    }
    const handler = this.#actors.get(actorId);
    if (!handler) {
      logger().warn({
        msg: "actor handler not found in #hwsCanHibernate",
        actorId
      });
      return false;
    }
    if (!handler.actor) {
      logger().warn({
        msg: "actor not found in #hwsCanHibernate",
        actorId
      });
      return false;
    }
    logger().debug({
      msg: "no existing hibernatable websocket found",
      gatewayId: idToStr(gatewayId),
      requestId: idToStr(requestId)
    });
    if (path === PATH_CONNECT) {
      return true;
    } else if (path === PATH_WEBSOCKET_BASE || path.startsWith(PATH_WEBSOCKET_PREFIX)) {
      const definition = lookupInRegistry(
        this.#config,
        actorInstance.config.name
      );
      const canHibernateWebSocket = (_a = definition.config.options) == null ? void 0 : _a.canHibernateWebSocket;
      if (canHibernateWebSocket === true) {
        return true;
      } else if (typeof canHibernateWebSocket === "function") {
        try {
          const newPath = truncateRawWebSocketPathPrefix(
            url.pathname
          );
          const truncatedRequest = new Request(
            `http://actor${newPath}`,
            request
          );
          const canHibernate = canHibernateWebSocket(truncatedRequest);
          return canHibernate;
        } catch (error) {
          logger().error({
            msg: "error calling canHibernateWebSocket",
            error
          });
          return false;
        }
      } else {
        return false;
      }
    } else if (path === PATH_INSPECTOR_CONNECT) {
      return false;
    } else {
      logger().warn({
        msg: "unexpected path for getActorHibernationConfig",
        path
      });
      return false;
    }
  }
  async #hwsLoadAll(actorId) {
    const actor2 = await this.loadActor(actorId);
    return actor2.conns.values().map((conn) => {
      const connStateManager = conn[CONN_STATE_MANAGER_SYMBOL];
      const hibernatable = connStateManager.hibernatableData;
      if (!hibernatable) return void 0;
      return {
        gatewayId: hibernatable.gatewayId,
        requestId: hibernatable.requestId,
        serverMessageIndex: hibernatable.serverMessageIndex,
        clientMessageIndex: hibernatable.clientMessageIndex,
        path: hibernatable.requestPath,
        headers: hibernatable.requestHeaders
      };
    }).filter((x) => x !== void 0).toArray();
  }
  async onBeforeActorStart(actor2) {
    var _a;
    const handler = this.#actors.get(actor2.id);
    invariant(handler, "missing actor handler in onBeforeActorReady");
    (_a = handler.actorStartPromise) == null ? void 0 : _a.resolve();
    handler.actorStartPromise = void 0;
    const metaEntries = await this.#hwsLoadAll(actor2.id);
    await this.#runner.restoreHibernatingRequests(actor2.id, metaEntries);
  }
  onCreateConn(conn) {
    const hibernatable = conn[CONN_STATE_MANAGER_SYMBOL].hibernatableData;
    if (!hibernatable) return;
    this.#hwsMessageIndex.set(conn.id, {
      serverMessageIndex: hibernatable.serverMessageIndex,
      bufferedMessageSize: 0,
      pendingAckFromMessageIndex: false,
      pendingAckFromBufferSize: false
    });
    logger().debug({
      msg: "created #hwsMessageIndex entry",
      connId: conn.id,
      serverMessageIndex: hibernatable.serverMessageIndex
    });
  }
  onDestroyConn(conn) {
    this.#hwsMessageIndex.delete(conn.id);
    logger().debug({
      msg: "removed #hwsMessageIndex entry",
      connId: conn.id
    });
  }
  onBeforePersistConn(conn) {
    const stateManager = conn[CONN_STATE_MANAGER_SYMBOL];
    const hibernatable = stateManager.hibernatableDataOrError();
    const entry = this.#hwsMessageIndex.get(conn.id);
    if (!entry) {
      logger().warn({
        msg: "missing EngineActorDriver.#hwsMessageIndex entry for conn",
        connId: conn.id
      });
      return;
    }
    entry.pendingAckFromMessageIndex = hibernatable.serverMessageIndex > entry.serverMessageIndex;
    entry.serverMessageIndex = hibernatable.serverMessageIndex;
  }
  onAfterPersistConn(conn) {
    const stateManager = conn[CONN_STATE_MANAGER_SYMBOL];
    const hibernatable = stateManager.hibernatableDataOrError();
    const entry = this.#hwsMessageIndex.get(conn.id);
    if (!entry) {
      logger().warn({
        msg: "missing EngineActorDriver.#hwsMessageIndex entry for conn",
        connId: conn.id
      });
      return;
    }
    if (entry.pendingAckFromMessageIndex || entry.pendingAckFromBufferSize) {
      this.#runner.sendHibernatableWebSocketMessageAck(
        hibernatable.gatewayId,
        hibernatable.requestId,
        entry.serverMessageIndex
      );
      entry.pendingAckFromMessageIndex = false;
      entry.pendingAckFromBufferSize = false;
      entry.bufferedMessageSize = 0;
    }
  }
};

// src/drivers/engine/config.ts
import { z } from "zod/v4";
var EngineConfigSchemaBase = ClientConfigSchemaBase.extend({
  /** Unique key for this runner. Runners connecting a given key will replace any other runner connected with the same key. */
  runnerKey: z.string().optional().transform((val) => val ?? getRivetRunnerKey()),
  /** How many actors this runner can run. */
  totalSlots: z.number().default(1e5)
});
var EngineConfigSchemaTransformed = EngineConfigSchemaBase.transform(
  (config, ctx) => transformEngineConfig(config, ctx)
);
var EngineConfigSchema = EngineConfigSchemaTransformed.default(
  () => EngineConfigSchemaTransformed.parse({})
);
function transformEngineConfig(config, ctx) {
  return {
    ...transformClientConfig(config, ctx),
    runnerKey: config.runnerKey
  };
}

// src/drivers/engine/mod.ts
function createEngineDriver() {
  return {
    name: "engine",
    displayName: "Engine",
    manager: (config) => {
      const clientConfig = convertRegistryConfigToClientConfig(config);
      return new RemoteManagerDriver(clientConfig);
    },
    actor: (config, managerDriver, inlineClient) => {
      return new EngineActorDriver(
        config,
        managerDriver,
        inlineClient
      );
    },
    autoStartActorDriver: true
  };
}

// src/registry/config/legacy-runner.ts
import { z as z2 } from "zod/v4";
var LegacyDriverConfigSchema = z2.object({
  /** Machine-readable name to identify this driver by. */
  name: z2.string(),
  manager: z2.custom(),
  actor: z2.custom()
});
var LegacyRunnerConfigSchemaUnmerged = z2.object({
  driver: LegacyDriverConfigSchema.optional(),
  /** @experimental */
  maxIncomingMessageSize: z2.number().optional().default(65536),
  /** @experimental */
  maxOutgoingMessageSize: z2.number().optional().default(1048576),
  /** @experimental */
  inspector: InspectorConfigSchema,
  /** @experimental */
  disableDefaultServer: z2.boolean().optional().default(false),
  /** @experimental */
  defaultServerPort: z2.number().default(6420),
  /** @experimental */
  runEngine: z2.boolean().optional().default(() => getRivetRunEngine()),
  /** @experimental */
  runEngineVersion: z2.string().optional().default(() => getRivetRunEngineVersion() ?? VERSION),
  /** @experimental */
  overrideServerAddress: z2.string().optional(),
  /** @experimental */
  disableActorDriver: z2.boolean().optional().default(false),
  /**
   * @experimental
   *
   * Whether to run runners normally or have them managed
   * serverlessly (by the Rivet Engine for example).
   */
  runnerKind: z2.enum(["serverless", "normal"]).optional().default(
    () => getRivetRunnerKind() === "serverless" ? "serverless" : "normal"
  ),
  totalSlots: z2.number().optional(),
  /**
   * @experimental
   *
   * Base path for the router. This is used to prefix all routes.
   * For example, if the base path is `/api`, then the route `/actors` will be
   * available at `/api/actors`.
   */
  basePath: z2.string().optional().default("/"),
  /**
   * @experimental
   *
   * Disable welcome message.
   * */
  noWelcome: z2.boolean().optional().default(false),
  /**
   * @experimental
   * */
  logging: z2.object({
    baseLogger: z2.custom().optional(),
    level: LogLevelSchema.optional()
  }).optional().default(() => ({})),
  /**
   * @experimental
   *
   * Automatically configure serverless runners in the engine.
   * Can only be used when runnerKind is "serverless".
   * If true, uses default configuration. Can also provide custom configuration.
   */
  autoConfigureServerless: z2.union([
    z2.boolean(),
    z2.object({
      url: z2.string().optional(),
      headers: z2.record(z2.string(), z2.string()).optional(),
      maxRunners: z2.number().optional(),
      minRunners: z2.number().optional(),
      requestLifespan: z2.number().optional(),
      runnersMargin: z2.number().optional(),
      slotsPerRunner: z2.number().optional(),
      metadata: z2.record(z2.string(), z2.unknown()).optional()
    })
  ]).optional(),
  // This is a function to allow for lazy configuration of upgradeWebSocket on the
  // fly. This is required since the dependencies that upgradeWebSocket
  // (specifically Node.js) can sometimes only be specified after the router is
  // created or must be imported async using `await import(...)`
  getUpgradeWebSocket: z2.custom().optional(),
  /** @experimental */
  token: z2.string().optional().transform((v) => v || getRivetToken())
}).merge(EngineConfigSchemaBase);
var LegacyRunnerConfigSchemaTransformed = LegacyRunnerConfigSchemaUnmerged.transform((config, ctx) => ({
  ...config,
  ...transformEngineConfig(config, ctx)
}));
var LegacyRunnerConfigSchema = LegacyRunnerConfigSchemaTransformed.default(
  () => LegacyRunnerConfigSchemaTransformed.parse({})
);

// runtime/index.ts
import invariant4 from "invariant";

// src/drivers/default.ts
function chooseDefaultDriver(config) {
  if (config.endpoint && config.driver) {
    throw new UserError(
      "Cannot specify both 'endpoint' and 'driver' in configuration"
    );
  }
  if (config.driver) {
    return config.driver;
  }
  if (config.endpoint || config.token) {
    loggerWithoutContext().debug({
      msg: "using rivet engine driver",
      endpoint: config.endpoint
    });
    return createEngineDriver();
  }
  loggerWithoutContext().debug({
    msg: "using default file system driver",
    storagePath: config.storagePath
  });
  return createFileSystemOrMemoryDriver(true, {
    path: config.storagePath
  });
}

// src/engine-process/log.ts
function logger2() {
  return getLogger("engine-process");
}

// src/engine-process/mod.ts
var ENGINE_BASE_URL = "https://releases.rivet.dev/rivet";
var ENGINE_BINARY_NAME = "rivet-engine";
async function ensureEngineProcess(options) {
  importNodeDependencies();
  logger2().debug({
    msg: "ensuring engine process",
    version: options.version
  });
  const path = getNodePath();
  const storageRoot = getStoragePath();
  const binDir = path.join(storageRoot, "bin");
  const varDir = path.join(storageRoot, "var");
  const logsDir = path.join(varDir, "logs", "rivet-engine");
  await ensureDirectoryExists(binDir);
  await ensureDirectoryExists(varDir);
  await ensureDirectoryExists(logsDir);
  if (await isEngineRunning()) {
    try {
      const health = await waitForEngineHealth();
      logger2().debug({
        msg: "engine already running and healthy",
        version: health.version
      });
      return;
    } catch (error) {
      logger2().warn({
        msg: "existing engine process not healthy, cannot restart automatically",
        error
      });
      throw new Error(
        "Engine process exists but is not healthy. Please manually stop the process on port 6420 and retry."
      );
    }
  }
  const executableName = process.platform === "win32" ? `${ENGINE_BINARY_NAME}-${options.version}.exe` : `${ENGINE_BINARY_NAME}-${options.version}`;
  const binaryPath = path.join(binDir, executableName);
  await downloadEngineBinaryIfNeeded(binaryPath, options.version, varDir);
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-").replace(/\./g, "-");
  const stdoutLogPath = path.join(logsDir, `engine-${timestamp}-stdout.log`);
  const stderrLogPath = path.join(logsDir, `engine-${timestamp}-stderr.log`);
  const fsSync = getNodeFsSync();
  const stdoutStream = fsSync.createWriteStream(stdoutLogPath, {
    flags: "a"
  });
  const stderrStream = fsSync.createWriteStream(stderrLogPath, {
    flags: "a"
  });
  logger2().debug({
    msg: "creating engine log files",
    stdout: stdoutLogPath,
    stderr: stderrLogPath
  });
  const childProcess = getNodeChildProcess();
  const child = childProcess.spawn(binaryPath, ["start"], {
    cwd: path.dirname(binaryPath),
    stdio: ["inherit", "pipe", "pipe"],
    env: {
      ...process.env,
      // Development environment overrides for Rivet Engine.
      //
      // NOTE: When modifying these env vars, also update scripts/run/dev-env.sh
      // to keep them in sync for manual engine runs.
      //
      // In development, runners can be terminated without a graceful
      // shutdown (i.e. SIGKILL instead of SIGTERM). This is treated as a
      // crash by Rivet Engine in production and implements a backoff for
      // rescheduling actors in case of a crash loop.
      //
      // This is problematic in development since this will cause actors
      // to become unresponsive if frequently killing your dev server.
      //
      // We reduce the timeouts for resetting a runner as healthy in
      // order to account for this.
      RIVET__PEGBOARD__RETRY_RESET_DURATION: "100",
      RIVET__PEGBOARD__BASE_RETRY_TIMEOUT: "100",
      // Set max exponent to 1 to have a maximum of base_retry_timeout
      RIVET__PEGBOARD__RESCHEDULE_BACKOFF_MAX_EXPONENT: "1",
      // Reduce thresholds for faster development iteration
      //
      // Default ping interval is 3s, this gives a 2s & 4s grace
      RIVET__PEGBOARD__RUNNER_ELIGIBLE_THRESHOLD: "5000",
      RIVET__PEGBOARD__RUNNER_LOST_THRESHOLD: "7000",
      // Allow faster metadata polling for hot-reload in development (in milliseconds)
      RIVET__PEGBOARD__MIN_METADATA_POLL_INTERVAL: "1000",
      // Reduce shutdown durations for faster development iteration (in seconds)
      RIVET__RUNTIME__WORKER_SHUTDOWN_DURATION: "1",
      RIVET__RUNTIME__GUARD_SHUTDOWN_DURATION: "1",
      // Force exit after this duration (must be > worker and guard shutdown durations)
      RIVET__RUNTIME__FORCE_SHUTDOWN_DURATION: "2"
    }
  });
  if (!child.pid) {
    throw new Error("failed to spawn rivet engine process");
  }
  if (child.stdout) {
    child.stdout.pipe(stdoutStream);
  }
  const stderrChunks = [];
  if (child.stderr) {
    child.stderr.on("data", (chunk) => {
      stderrChunks.push(chunk);
    });
    child.stderr.pipe(stderrStream);
  }
  logger2().debug({
    msg: "spawned engine process",
    pid: child.pid,
    cwd: path.dirname(binaryPath)
  });
  child.once("exit", (code, signal) => {
    const stderrOutput = Buffer.concat(stderrChunks).toString("utf-8");
    if (stderrOutput.includes("LOCK: Resource temporarily unavailable")) {
      logger2().error({
        msg: "another instance of rivet engine is unexpectedly running, this is an internal error",
        code,
        signal,
        stdoutLog: stdoutLogPath,
        stderrLog: stderrLogPath,
        issues: "https://github.com/rivet-dev/rivetkit/issues",
        support: "https://rivet.dev/discord"
      });
    } else if (stderrOutput.includes("Rivet Engine has been rolled back to a previous version")) {
      logger2().error({
        msg: "rivet engine version downgrade detected",
        hint: `You attempted to downgrade the RivetKit version in development. To fix this, nuke the database by running: '${binaryPath}' database nuke --yes`,
        code,
        signal,
        stdoutLog: stdoutLogPath,
        stderrLog: stderrLogPath
      });
    } else {
      logger2().warn({
        msg: "engine process exited, please report this error",
        code,
        signal,
        stdoutLog: stdoutLogPath,
        stderrLog: stderrLogPath,
        issues: "https://github.com/rivet-dev/rivetkit/issues",
        support: "https://rivet.dev/discord"
      });
    }
    stdoutStream.end();
    stderrStream.end();
  });
  child.once("error", (error) => {
    logger2().error({
      msg: "engine process failed",
      error
    });
    stdoutStream.end();
    stderrStream.end();
  });
  await waitForEngineHealth();
  logger2().info({
    msg: "engine process started",
    pid: child.pid,
    version: options.version,
    logs: {
      stdout: stdoutLogPath,
      stderr: stderrLogPath
    }
  });
}
async function downloadEngineBinaryIfNeeded(binaryPath, version, varDir) {
  const binaryExists = await fileExists(binaryPath);
  if (binaryExists) {
    logger2().debug({
      msg: "engine binary already cached",
      version,
      path: binaryPath
    });
    return;
  }
  const { targetTriplet, extension } = resolveTargetTriplet();
  const remoteFile = `${ENGINE_BINARY_NAME}-${targetTriplet}${extension}`;
  const downloadUrl = `${ENGINE_BASE_URL}/${version}/engine/${remoteFile}`;
  logger2().info({
    msg: "downloading engine binary",
    url: downloadUrl,
    path: binaryPath,
    version
  });
  const response = await fetch(downloadUrl);
  if (!response.ok || !response.body) {
    throw new Error(
      `failed to download rivet engine binary from ${downloadUrl}: ${response.status} ${response.statusText}`
    );
  }
  const crypto2 = getNodeCrypto();
  const tempPath = `${binaryPath}.${crypto2.randomUUID()}.tmp`;
  const startTime = Date.now();
  logger2().debug({
    msg: "starting binary download",
    tempPath,
    contentLength: response.headers.get("content-length")
  });
  const slowDownloadWarning = setTimeout(() => {
    logger2().warn({
      msg: "engine binary download is taking longer than expected, please be patient",
      version
    });
  }, 5e3);
  try {
    const stream = getNodeStream();
    const fsSync = getNodeFsSync();
    await stream.pipeline(
      response.body,
      fsSync.createWriteStream(tempPath)
    );
    clearTimeout(slowDownloadWarning);
    const fs = getNodeFs();
    const stats = await fs.stat(tempPath);
    const downloadDuration = Date.now() - startTime;
    if (process.platform !== "win32") {
      await fs.chmod(tempPath, 493);
    }
    await fs.rename(tempPath, binaryPath);
    logger2().debug({
      msg: "engine binary download complete",
      version,
      path: binaryPath,
      size: stats.size,
      durationMs: downloadDuration
    });
    logger2().info({
      msg: "engine binary downloaded",
      version,
      path: binaryPath
    });
  } catch (error) {
    clearTimeout(slowDownloadWarning);
    logger2().warn({
      msg: "engine download failed, please report this error",
      tempPath,
      error,
      issues: "https://github.com/rivet-dev/rivetkit/issues",
      support: "https://rivet.dev/discord"
    });
    try {
      const fs = getNodeFs();
      await fs.unlink(tempPath);
    } catch (unlinkError) {
    }
    throw error;
  }
}
function resolveTargetTriplet() {
  return resolveTargetTripletFor(process.platform, process.arch);
}
function resolveTargetTripletFor(platform, arch) {
  switch (platform) {
    case "darwin":
      if (arch === "arm64") {
        return { targetTriplet: "aarch64-apple-darwin", extension: "" };
      }
      if (arch === "x64") {
        return { targetTriplet: "x86_64-apple-darwin", extension: "" };
      }
      break;
    case "linux":
      if (arch === "x64") {
        return {
          targetTriplet: "x86_64-unknown-linux-musl",
          extension: ""
        };
      }
      break;
    case "win32":
      if (arch === "x64") {
        return {
          targetTriplet: "x86_64-pc-windows-gnu",
          extension: ".exe"
        };
      }
      break;
  }
  throw new Error(
    `unsupported platform for rivet engine binary: ${platform}/${arch}`
  );
}
async function isEngineRunning() {
  return await checkIfEngineAlreadyRunningOnPort(ENGINE_PORT);
}
async function checkIfEngineAlreadyRunningOnPort(port) {
  let response;
  try {
    response = await fetch(`http://127.0.0.1:${port}/health`);
  } catch (err) {
    return false;
  }
  if (response.ok) {
    const health = await response.json();
    if (health.runtime === "engine") {
      logger2().debug({
        msg: "rivet engine already running on port",
        port
      });
      return true;
    } else if (health.runtime === "rivetkit") {
      logger2().error({
        msg: "another rivetkit process is already running on port",
        port
      });
      throw new Error(
        "RivetKit process already running on port 6420, stop that process and restart this."
      );
    } else {
      throw new Error(
        "Unknown process running on port 6420, cannot identify what it is."
      );
    }
  }
  return false;
}
async function fileExists(filePath) {
  try {
    const fs = getNodeFs();
    await fs.access(filePath);
    return true;
  } catch {
    return false;
  }
}
var HEALTH_MAX_WAIT = 1e4;
var HEALTH_INTERVAL = 100;
async function waitForEngineHealth() {
  const maxRetries = Math.ceil(HEALTH_MAX_WAIT / HEALTH_INTERVAL);
  logger2().debug({ msg: "waiting for engine health check" });
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(`${ENGINE_ENDPOINT}/health`, {
        signal: AbortSignal.timeout(1e3)
      });
      if (response.ok) {
        const health = await response.json();
        logger2().debug({ msg: "engine health check passed" });
        return health;
      }
    } catch (error) {
      logger2().debug({ msg: "engine health check failed", error });
      if (i === maxRetries - 1) {
        throw new Error(
          `engine health check failed after ${maxRetries} retries: ${error}`
        );
      }
    }
    if (i < maxRetries - 1) {
      logger2().trace({
        msg: "engine not ready, retrying",
        attempt: i + 1,
        maxRetries
      });
      await new Promise(
        (resolve) => setTimeout(resolve, HEALTH_INTERVAL)
      );
    }
  }
  throw new Error(`engine health check failed after ${maxRetries} retries`);
}

// src/inspector/utils.ts
import { createMiddleware } from "hono/factory";
function getInspectorUrl(config, managerPort) {
  if (!config.inspector.enabled) return void 0;
  const base = config.inspector.defaultEndpoint ?? `http://127.0.0.1:${managerPort}`;
  return new URL("/ui/", base).href;
}

// src/serverless/log.ts
function logger3() {
  return getLogger("serverless");
}

// src/serverless/configure.ts
import invariant2 from "invariant";
async function configureServerlessRunner(config) {
  logger3().debug("configuring serverless runner");
  try {
    if (!config.namespace) {
      throw new Error(
        "namespace is required for serverless configuration"
      );
    }
    if (!config.endpoint) {
      throw new Error(
        "endpoint is required for serverless configuration"
      );
    }
    const customConfig = config.serverless.configureRunnerPool;
    invariant2(customConfig, "configureRunnerPool should exist");
    const clientConfig = convertRegistryConfigToClientConfig(config);
    logger3().debug({
      msg: "fetching datacenters",
      endpoint: config.endpoint
    });
    const dcsRes = await getDatacenters(clientConfig);
    const runnerName = customConfig.name ?? "default";
    logger3().debug({
      msg: "configuring serverless runner",
      runnerName,
      namespace: config.namespace
    });
    const serverlessConfig = {
      serverless: {
        url: customConfig.url,
        headers: customConfig.headers ?? {},
        max_runners: customConfig.maxRunners ?? 1e3,
        min_runners: customConfig.minRunners ?? 0,
        request_lifespan: customConfig.requestLifespan ?? 15 * 60,
        runners_margin: customConfig.runnersMargin ?? 0,
        slots_per_runner: customConfig.slotsPerRunner ?? 1,
        metadata_poll_interval: customConfig.metadataPollInterval ?? 1e3
      },
      metadata: customConfig.metadata ?? {},
      drain_on_version_upgrade: customConfig.drainOnVersionUpgrade ?? true,
      metadataPollInterval: customConfig.metadataPollInterval ?? 1e3
    };
    await updateRunnerConfig(clientConfig, runnerName, {
      datacenters: Object.fromEntries(
        dcsRes.datacenters.map((dc) => [dc.name, serverlessConfig])
      )
    });
    logger3().info({
      msg: "serverless runner configured successfully",
      runnerName,
      namespace: config.namespace
    });
  } catch (error) {
    logger3().error({
      msg: "failed to configure serverless runner, validate endpoint is configured correctly then restart this process",
      error
    });
  }
}

// src/registry/log.ts
function logger4() {
  return getLogger("registry");
}

// src/registry/serve.ts
import getPort from "get-port";
var DEFAULT_PORT = 6420;
async function findFreePort(startPort = DEFAULT_PORT) {
  function* portRange(start, count = 100) {
    for (let i = 0; i < count; i++) {
      yield start + i;
    }
  }
  return getPort({ port: portRange(startPort) });
}
async function crossPlatformServe(config, managerPort, app) {
  const runtime = detectRuntime();
  logger4().debug({ msg: "detected runtime for serve", runtime });
  switch (runtime) {
    case "deno":
      return serveDeno(config, managerPort, app);
    case "bun":
      return serveBun(config, managerPort, app);
    case "node":
      return serveNode(config, managerPort, app);
    default:
      return serveNode(config, managerPort, app);
  }
}
async function serveNode(config, managerPort, app) {
  const nodeServerModule = "@hono/node-server";
  let serve;
  try {
    const dep = await import(
      /* webpackIgnore: true */
      nodeServerModule
    );
    serve = dep.serve;
  } catch (err) {
    logger4().error({
      msg: "failed to import @hono/node-server. please run 'npm install @hono/node-server @hono/node-ws'",
      error: stringifyError(err)
    });
    process.exit(1);
  }
  const nodeWsModule = "@hono/node-ws";
  let createNodeWebSocket;
  try {
    const dep = await import(
      /* webpackIgnore: true */
      nodeWsModule
    );
    createNodeWebSocket = dep.createNodeWebSocket;
  } catch (err) {
    logger4().error({
      msg: "failed to import @hono/node-ws. please run 'npm install @hono/node-server @hono/node-ws'",
      error: stringifyError(err)
    });
    process.exit(1);
  }
  const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({
    app
  });
  const port = managerPort;
  const hostname = config.managerHost;
  const server = serve(
    { fetch: app.fetch, port, hostname },
    () => logger4().info({ msg: "server listening", port, hostname })
  );
  injectWebSocket(server);
  return { upgradeWebSocket };
}
async function serveDeno(config, managerPort, app) {
  const honoDenoModule = "hono/deno";
  let upgradeWebSocket;
  try {
    const dep = await import(
      /* webpackIgnore: true */
      honoDenoModule
    );
    upgradeWebSocket = dep.upgradeWebSocket;
  } catch (err) {
    logger4().error({
      msg: "failed to import hono/deno",
      error: stringifyError(err)
    });
    process.exit(1);
  }
  const port = config.managerPort;
  const hostname = config.managerHost;
  Deno.serve({ port, hostname }, app.fetch);
  logger4().info({ msg: "server listening", port, hostname });
  return { upgradeWebSocket };
}
async function serveBun(config, managerPort, app) {
  const honoBunModule = "hono/bun";
  let createBunWebSocket;
  try {
    const dep = await import(
      /* webpackIgnore: true */
      honoBunModule
    );
    createBunWebSocket = dep.createBunWebSocket;
  } catch (err) {
    logger4().error({
      msg: "failed to import hono/bun",
      error: stringifyError(err)
    });
    process.exit(1);
  }
  const { websocket, upgradeWebSocket } = createBunWebSocket();
  const port = config.managerPort;
  const hostname = config.managerHost;
  Bun.serve({
    fetch: app.fetch,
    port,
    hostname,
    websocket
  });
  logger4().info({ msg: "server listening", port, hostname });
  return { upgradeWebSocket };
}

// src/serverless/router.ts
import invariant3 from "invariant";

// src/manager/router-schema.ts
import { z as z3 } from "zod/v4";
var ServerlessStartHeadersSchema = z3.object({
  endpoint: z3.string({
    error: "x-rivet-endpoint header is required"
  }),
  token: z3.string({ error: "x-rivet-token header must be a string" }).optional(),
  totalSlots: z3.coerce.number({
    error: "x-rivet-total-slots header must be a number"
  }).int({ error: "x-rivet-total-slots header must be an integer" }).gte(1, { error: "x-rivet-total-slots header must be positive" }),
  runnerName: z3.string({
    error: "x-rivet-runner-name header is required"
  }),
  namespace: z3.string({
    error: "x-rivet-namespace-name header is required"
  })
});

// src/serverless/router.ts
function buildServerlessRouter(driverConfig, config) {
  return createRouter(config.serverless.basePath, (router) => {
    router.get("/", (c) => {
      return c.text(
        "This is a RivetKit server.\n\nLearn more at https://rivetkit.org"
      );
    });
    router.get("/start", async (c) => {
      var _a;
      const parseResult = ServerlessStartHeadersSchema.safeParse({
        endpoint: c.req.header("x-rivet-endpoint"),
        token: c.req.header("x-rivet-token") ?? void 0,
        totalSlots: c.req.header("x-rivet-total-slots"),
        runnerName: c.req.header("x-rivet-runner-name"),
        namespace: c.req.header("x-rivet-namespace-name")
      });
      if (!parseResult.success) {
        throw new InvalidRequest(
          ((_a = parseResult.error.issues[0]) == null ? void 0 : _a.message) ?? "invalid serverless start headers"
        );
      }
      const { endpoint, token, totalSlots, runnerName, namespace } = parseResult.data;
      logger3().debug({
        msg: "received serverless runner start request",
        endpoint,
        totalSlots,
        runnerName,
        namespace
      });
      if (config.endpoint) {
        if (!endpointsMatch(endpoint, config.endpoint)) {
          throw new EndpointMismatch(config.endpoint, endpoint);
        }
        if (namespace !== config.namespace) {
          throw new NamespaceMismatch(config.namespace, namespace);
        }
      }
      const newConfig = {
        ...config,
        endpoint,
        namespace,
        token,
        runner: {
          ...config.runner,
          totalSlots,
          runnerName,
          // Not supported on serverless
          runnerKey: void 0
        }
      };
      const managerDriver = new RemoteManagerDriver(
        convertRegistryConfigToClientConfig(newConfig)
      );
      const client = createClientWithDriver(managerDriver);
      const actorDriver = driverConfig.actor(
        newConfig,
        managerDriver,
        client
      );
      invariant3(
        actorDriver.serverlessHandleStart,
        "missing serverlessHandleStart on ActorDriver"
      );
      return await actorDriver.serverlessHandleStart(c);
    });
    router.get("/health", (c) => handleHealthRequest(c));
    router.get(
      "/metadata",
      (c) => handleMetadataRequest(
        c,
        config,
        { serverless: {} },
        config.publicEndpoint,
        config.publicNamespace,
        config.publicToken
      )
    );
  });
}
function normalizeEndpointUrl(url) {
  try {
    const parsed = new URL(url);
    const pathname = parsed.pathname === "/" ? "/" : parsed.pathname.replace(/\/+$/, "");
    let hostname = isLoopbackAddress(parsed.hostname) ? "localhost" : parsed.hostname;
    hostname = normalizeRegionalHostname(hostname);
    const host = parsed.port ? `${hostname}:${parsed.port}` : hostname;
    return `${parsed.protocol}//${host}${pathname}`;
  } catch {
    return null;
  }
}
function normalizeRegionalHostname(hostname) {
  if (!hostname.endsWith(".rivet.dev")) {
    return hostname;
  }
  if (!hostname.startsWith("api-")) {
    return hostname;
  }
  const withoutPrefix = hostname.slice(4);
  const firstDotIndex = withoutPrefix.indexOf(".");
  if (firstDotIndex === -1) {
    return hostname;
  }
  const domain = withoutPrefix.slice(firstDotIndex + 1);
  return `api.${domain}`;
}
function endpointsMatch(a, b) {
  const normalizedA = normalizeEndpointUrl(a);
  const normalizedB = normalizeEndpointUrl(b);
  if (normalizedA === null || normalizedB === null) {
    return a === b;
  }
  return normalizedA === normalizedB;
}
function isLoopbackAddress(hostname) {
  return hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1" || hostname === "[::1]";
}

// runtime/index.ts
function logLine(label, value) {
  const padding = " ".repeat(Math.max(0, 13 - label.length));
  console.log(`  - ${label}:${padding}${value}`);
}
var Runtime = class _Runtime {
  #registry;
  #config;
  #driver;
  #managerDriver;
  #startKind;
  managerPort;
  #serverlessRouter;
  get config() {
    return this.#config;
  }
  get driver() {
    return this.#driver;
  }
  get managerDriver() {
    return this.#managerDriver;
  }
  /** Use Runtime.create() instead */
  constructor(registry, config, driver, managerDriver, managerPort) {
    this.#registry = registry;
    this.#config = config;
    this.#driver = driver;
    this.#managerDriver = managerDriver;
    this.managerPort = managerPort;
  }
  static async create(registry) {
    var _a, _b, _c;
    logger4().info("rivetkit starting");
    const config = registry.parseConfig();
    if ((_a = config.logging) == null ? void 0 : _a.baseLogger) {
      configureBaseLogger(config.logging.baseLogger);
    } else {
      configureDefaultLogger((_b = config.logging) == null ? void 0 : _b.level);
    }
    invariant4(
      !(config.serverless.spawnEngine && config.serveManager),
      "cannot specify both spawnEngine and serveManager"
    );
    const driver = chooseDefaultDriver(config);
    const managerDriver = driver.manager(config);
    let managerPort;
    if (config.serverless.spawnEngine) {
      managerPort = ENGINE_PORT;
      logger4().debug({
        msg: "spawning engine",
        version: config.serverless.engineVersion
      });
      await ensureEngineProcess({
        version: config.serverless.engineVersion
      });
    } else if (config.serveManager) {
      const configuredManagerPort = config.managerPort;
      let upgradeWebSocket;
      const getUpgradeWebSocket = () => upgradeWebSocket;
      managerDriver.setGetUpgradeWebSocket(getUpgradeWebSocket);
      const { router: managerRouter } = buildManagerRouter(
        config,
        managerDriver,
        getUpgradeWebSocket
      );
      managerPort = await findFreePort(config.managerPort);
      logger4().debug({
        msg: "serving manager",
        port: managerPort
      });
      if (config.publicEndpoint === `http://127.0.0.1:${configuredManagerPort}`) {
        config.publicEndpoint = `http://127.0.0.1:${managerPort}`;
        config.serverless.publicEndpoint = config.publicEndpoint;
      }
      config.managerPort = managerPort;
      const out = await crossPlatformServe(
        config,
        managerPort,
        managerRouter
      );
      upgradeWebSocket = out.upgradeWebSocket;
    }
    const runtime = new _Runtime(
      registry,
      config,
      driver,
      managerDriver,
      managerPort
    );
    const driverLog = ((_c = managerDriver.extraStartupLog) == null ? void 0 : _c.call(managerDriver)) ?? {};
    logger4().info({
      msg: "rivetkit ready",
      driver: driver.name,
      definitions: Object.keys(config.use).length,
      ...driverLog
    });
    return runtime;
  }
  startServerless() {
    if (this.#startKind === "serverless") return;
    invariant4(!this.#startKind, "Runtime already started as runner");
    this.#startKind = "serverless";
    this.#serverlessRouter = buildServerlessRouter(
      this.#driver,
      this.#config
    ).router;
    this.#printWelcome();
    if (this.#config.serverless.configureRunnerPool) {
      configureServerlessRunner(this.#config);
    }
  }
  startRunner() {
    if (this.#startKind === "runner") return;
    invariant4(!this.#startKind, "Runtime already started as serverless");
    this.#startKind = "runner";
    if (this.#config.runner && this.#driver.autoStartActorDriver) {
      logger4().debug("starting actor driver");
      const inlineClient = createClientWithDriver(
        this.#managerDriver
      );
      this.#driver.actor(this.#config, this.#managerDriver, inlineClient);
    }
    this.#printWelcome();
  }
  #printWelcome() {
    if (this.#config.noWelcome) return;
    const inspectorUrl = this.managerPort ? getInspectorUrl(this.#config, this.managerPort) : void 0;
    console.log();
    console.log(
      `  RivetKit ${package_default.version} (${this.#driver.displayName} - ${this.#startKind === "serverless" ? "Serverless" : "Runner"})`
    );
    if (this.#config.namespace !== "default") {
      logLine("Namespace", this.#config.namespace);
    }
    if (this.#config.endpoint) {
      const endpointType = this.#config.serverless.spawnEngine ? "local native" : this.#config.serveManager ? "local manager" : "remote";
      logLine("Endpoint", `${this.#config.endpoint} (${endpointType})`);
    }
    if (this.#startKind === "serverless" && this.#config.publicEndpoint) {
      logLine("Client", this.#config.publicEndpoint);
    }
    if (inspectorUrl && this.#config.inspector.enabled) {
      logLine("Inspector", inspectorUrl);
    }
    const actorCount = Object.keys(this.#config.use).length;
    logLine("Actors", actorCount.toString());
    const displayInfo = this.#managerDriver.displayInformation();
    for (const [k, v] of Object.entries(displayInfo.properties)) {
      logLine(k, v);
    }
    console.log();
  }
  /** Handle serverless request */
  handleServerlessRequest(request) {
    invariant4(
      this.#startKind === "serverless",
      "not started as serverless"
    );
    invariant4(this.#serverlessRouter, "serverless router not initialized");
    return this.#serverlessRouter.fetch(request);
  }
};

// src/registry/index.ts
var Registry = class {
  #config;
  get config() {
    return this.#config;
  }
  parseConfig() {
    return RegistryConfigSchema.parse(this.#config);
  }
  // Shared runtime instance
  #runtime;
  #runtimePromise;
  constructor(config) {
    this.#config = config;
    if (getNodeEnv() !== "test") {
      setTimeout(() => {
        this.#ensureRuntime();
      }, 0);
    }
  }
  /** Creates runtime if not already created. Idempotent. */
  #ensureRuntime() {
    if (!this.#runtimePromise) {
      this.#runtimePromise = Runtime.create(this);
      this.#runtimePromise.then((rt) => {
        this.#runtime = rt;
      });
    }
    return this.#runtimePromise;
  }
  /**
   * Handle an incoming HTTP request for serverless deployments.
   *
   * @example
   * ```ts
   * const app = new Hono();
   * app.all("/api/rivet/*", (c) => registry.handler(c.req.raw));
   * export default app;
   * ```
   */
  async handler(request) {
    const runtime = await this.#ensureRuntime();
    runtime.startServerless();
    return await runtime.handleServerlessRequest(request);
  }
  /**
   * Returns a fetch handler for serverless deployments.
   *
   * @example
   * ```ts
   * export default registry.serve();
   * ```
   */
  serve() {
    return { fetch: this.handler.bind(this) };
  }
  /**
   * Starts an actor runner for standalone server deployments.
   */
  startRunner() {
    this.#ensureRuntime().then((runtime) => runtime.startRunner());
  }
  // MARK: Legacy
  /**
   * Runs the registry for a server.
   *
   * @deprecated Use {@link Registry.startRunner} for long-running servers or {@link Registry.handler} for serverless deployments.
   */
  start(inputConfig) {
    const config = LegacyRunnerConfigSchema.parse(inputConfig);
    if (config.autoConfigureServerless && config.runnerKind !== "serverless") {
      throw new Error(
        "autoConfigureServerless can only be configured when runnerKind is 'serverless'"
      );
    }
    const isDevEnv = isDev();
    if (isDevEnv && config.runnerKind === "serverless") {
      if ((inputConfig == null ? void 0 : inputConfig.runEngine) === void 0) config.runEngine = true;
      if ((inputConfig == null ? void 0 : inputConfig.autoConfigureServerless) === void 0)
        config.autoConfigureServerless = true;
    }
    if (config.runnerKind === "serverless") {
      return this.#legacyStartServerless(config, inputConfig);
    } else {
      return this.#legacyStartNormal(config);
    }
  }
  #legacyStartServerless(config, _inputConfig) {
    const client = createClient({
      endpoint: config.endpoint,
      token: config.token,
      namespace: config.namespace,
      headers: config.headers
    });
    return {
      client,
      fetch: this.handler.bind(this)
    };
  }
  #legacyStartNormal(config) {
    this.#ensureRuntime().then((runtime) => runtime.startRunner());
    const client = createClient({
      endpoint: config.endpoint,
      token: config.token,
      namespace: config.namespace,
      headers: config.headers
    });
    return {
      client,
      fetch: this.handler.bind(this)
    };
  }
};
function setup(input) {
  return new Registry(input);
}

export {
  actor,
  event2 as event,
  queue2 as queue,
  createEngineDriver,
  Registry,
  setup
};
//# sourceMappingURL=chunk-OONQLPOH.js.map