// Runtime app export and server launch helpers
///|
/// Global app reference for non-Node runtimes (Cloudflare, Deno, Bun)
/// This is set by run() and exported as __SOL_APP__
let sol_app_ref : Ref[App?] = { val: None }
///|
extern "js" fn ffi_export_sol_app(fetch_handler : MarsFetchHandler) -> Unit =
#| (fetchHandler) => { globalThis.__SOL_APP__ = { fetch: fetchHandler }; }
///|
/// Type alias for Mars fetch handler with js_async Promise
priv struct MarsFetchHandler((@mars.JsRequest) -> @js.Promise[@mars.JsResponse])
///|
/// Convert Mars Promise type to @js.Promise for FFI compatibility
/// Mars uses moonbitlang/async/js_async.Promise, Sol uses mizchi/js/core.Promise
/// They are the same JS Promise object at runtime
extern "js" fn ffi_convert_fetch_handler(handler : @js.Any) -> MarsFetchHandler =
#| (handler) => handler
///|
/// Check if running in Node.js environment
extern "js" fn ffi_is_node() -> Bool =
#| () => globalThis.__SOL_RUNTIME__ !== "cloudflare" &&
#| typeof process !== 'undefined' &&
#| process.versions?.node != null
///|
fn runtime_is_node() -> Bool {
ffi_is_node()
}
///|
fn export_runtime_app(app : App) -> Unit {
sol_app_ref.val = Some(app)
let fetch_handler = app.to_fetch_handler()
ffi_export_sol_app(ffi_convert_fetch_handler(@js.any(fetch_handler)))
}
///|
fn should_start_server() -> Bool {
runtime_is_node() && !is_adapter_mode()
}
///|
fn maybe_start_server(app : App, port : Int) -> Unit {
if should_start_server() {
println("Server running at http://localhost:" + port.to_string())
serve_runtime(app, port)
}
}
///|
fn serve_runtime(app : App, port : Int) -> Unit {
let fetch_handler = app.to_fetch_handler()
ffi_serve(ffi_convert_fetch_handler(@js.any(fetch_handler)), port)
}
///|
extern "js" fn ffi_serve(fetch_handler : MarsFetchHandler, port : Int) -> Unit =
#|(fetchHandler, port) => {
#| const importer = globalThis.__SOL_DYNAMIC_IMPORT__ ||
#| ((specifier) => import(specifier));
#| importer('@hono/node-server').then(({ serve }) => {
#| serve({ fetch: fetchHandler, port });
#| });
#|}