Sign in

    #@core - MoonBit/JavaScript Interop Core

    @core is a minimal utility package for binding MoonBit objects to JavaScript objects. This package contains only the essential types and functions for JS interop, without JavaScript built-in APIs.

    #Package Responsibility

    Core responsibility: Provide minimal, zero-cost utilities for MoonBit ↔ JavaScript object binding.

    This package includes:
    • ✅ Type casting functions (identity, any, cast)
    • ✅ Core binding types (Any, Nullable, Nullish, Promise)
    • ✅ Basic type checking (typeof_, instanceof_)
    • ✅ Property access and method calls (_get, _set, _call)
    • ✅ Object and array creation (new_object, new_array)
    • ✅ Function conversion (from_fn0, from_fn1, etc.)

    This package does NOT include:
    • ❌ JavaScript built-in APIs (use @global, @math, @date, etc.)
    • ❌ Web APIs (use @web/* packages)
    • ❌ Runtime-specific APIs (use @node/*, @browser/*, etc.)

    #Core Types

    #Any - Universal JavaScript Value

    The fundamental type for all JavaScript values:

    pub type Any

    // Convert MoonBit value to JS
    let js_val : @core.Any = @core.any(42)
    let js_str : @core.Any = @core.any("hello")

    // Cast back to MoonBit type
    let num : Int = js_val.cast()
    let str : String = js_str.cast()

    #Nullable[T] - Nullable Type

    Represents values that can be null:

    pub type Nullable[T]

    // JavaScript value that might be null
    let maybe_null : @core.Nullable[Int] = get_nullable_value()

    match maybe_null {
    Null => @console.log("Got null")
    NotNull(value) => @console.log(value)
    }

    #Nullish[T] - Nullish Type

    Represents values that can be null or undefined:

    pub type Nullish[T]

    // JavaScript value that might be null or undefined
    let maybe_nullish : @core.Nullish[String] = get_nullish_value()

    match maybe_nullish {
    Nullish => @console.log("Got null or undefined")
    NotNullish(value) => @console.log(value)
    }

    #Promise[T] - Promise Type

    JavaScript Promise wrapper:

    pub type Promise[T]

    // Create promises
    let p = @core.Promise::resolve(42)
    let q = @core.Promise::reject("error")

    // Wait in async context
    async fn example() -> Int {
    let value = p.wait()
    value
    }

    #Type Casting Functions

    #identity[A, B](value: A) -> B

    Zero-cost type casting (unsafe, use with caution):

    // Cast between types without conversion
    let js_num : @core.Any = get_js_value()
    let num : Int = @core.identity(js_num)

    #any[T](value: T) -> Any

    Convert MoonBit value to JavaScript value:

    let js_val = @core.any(42)
    let js_str = @core.any("hello")
    let js_bool = @core.any(true)

    #cast[A, B](value: A) -> B

    Type-safe casting with .cast() method:

    let js_val : @core.Any = @core.any(42)
    let num : Int = js_val.cast() // Recommended

    #Type Checking

    // Check JavaScript type
    @core.typeof_(value) // "number", "string", "boolean", etc.

    // Check if value is instance of constructor
    @core.instanceof_(value, constructor)

    // Null/undefined checks
    @core.is_null(value)
    @core.is_undefined(value)
    @core.is_nullish(value)

    // Type checks
    @core.is_number(value)
    @core.is_string(value)
    @core.is_boolean(value)
    @core.is_object(value)
    @core.is_array(value)
    @core.is_function(value)

    #Property Access

    // Get property
    let name = obj._get("name")

    // Set property
    obj._set("name", @core.any("Alice"))

    // Index access (bracket notation)
    obj["key"] = @core.any(value)
    let val = obj["key"]

    #Method Calls

    // Call method
    let result = obj._call("toString", [])
    let result = obj._call("method", [@core.any(arg1), @core.any(arg2)])

    // Invoke function
    let fn = get_function()
    let result = fn._invoke([@core.any(1), @core.any(2)])

    #Object Creation

    // Create empty object
    let obj = @core.new_object()
    obj["name"] = @core.any("Alice")
    obj["age"] = @core.any(30)

    // Create empty array
    let arr = @core.new_array()

    // Create object with constructor
    let date = @core.new(date_constructor, [@core.any(2025), @core.any(11), @core.any(6)])

    #Function Conversion

    Convert MoonBit functions to JavaScript functions:

    // 0 arguments
    let js_fn = @core.from_fn0(fn() -> String { "hello" })

    // 1 argument
    let js_fn = @core.from_fn1(fn(x: Int) -> Int { x * 2 })

    // 2 arguments
    let js_fn = @core.from_fn2(fn(x: Int, y: Int) -> Int { x + y })

    // 3 arguments
    let js_fn = @core.from_fn3(fn(x: Int, y: Int, z: Int) -> Int { x + y + z })

    #Promise Utilities

    // Promisify async functions
    let promise_fn = @core.promisify0(async fn() -> Int { 42 })
    let promise_fn = @core.promisify1(async fn(x: Int) -> Int { x * 2 })

    // Run async code
    @core.run_async(async fn() {
    let value = some_promise.wait()
    @console.log(value)
    })

    // Suspend current async context
    @core.suspend(fn(resolve, reject) {
    // Async operation
    resolve(@core.any(42))
    })

    #Error Handling

    // Try-catch for JavaScript exceptions
    @core.throwable(fn() {
    potentially_throwing_code()
    }) catch {
    err => @console.log("Caught error")
    }

    #Logging

    // Console log (for debugging)
    @core.log("Debug message")
    @core.log(some_value)

    #Design Principles

    1. Minimal: Only essential interop utilities
    2. Zero-cost: Direct FFI bindings without overhead
    3. Type-safe: Leverage MoonBit's type system where possible
    4. Composable: Build higher-level abstractions on top

    #See Also

    • Built-in APIs: Use @global, @math, @date, etc. from mizchi/js
    • Web APIs: Use @web/* packages for fetch, WebSocket, etc.
    • Runtime APIs: Use @node/*, @browser/*, @deno, @cloudflare

    #Performance Note

    This package is designed for minimal overhead. Most operations compile to direct JavaScript operations without runtime checks or conversions. For detailed performance characteristics, see docs/runtime-cost.md.

    JsError

    pub suberror JsError String

    Generic JS error type

    ThrowError

    pub suberror ThrowError Any

    impl Show for ThrowError

    Any

    #external
    pub type Any

    Opaque type for JavaScript values
    impl Eq for Any
    impl Show for Any

    Any::_call

    fn Any::_call(self : Any, key : String, args : Array[Any]) -> Any

    Call method: obj._call("method", [arg1, arg2])

    Any::_get

    #alias("_[_]")
    fn Any::_get(self : Any, key : String) -> Any

    JS: value[key] Enables syntax: obj["key"]

    Any::_get_by_index

    fn Any::_get_by_index(self : Any, key : Int) -> Any

    JS: value[key] Enables syntax: obj[index]

    Any::_invoke

    fn Any::_invoke(self : Any, args : Array[Any]) -> Any

    Call function: func._invoke([arg1, arg2])

    Any::_set

    #alias("_[_]=_")
    fn Any::_set(self : Any, key : String, value : Any) -> Unit

    Set property: obj["key"] = value Enables syntax: obj["key"] = val

    Any::cast

    fn[T] Any::cast(self : Any) -> T

    Any::to_string

    fn Any::to_string(self : Any) -> String

    Convert Any to string representation

    Nullable

    #external
    pub type Nullable[T]

    null or T

    Nullable::is_null

    fn[T] Nullable::is_null(self : Nullable[T]) -> Bool

    Nullable::to_option

    fn[T] Nullable::to_option(self : Nullable[T]) -> T?

    Nullish

    #external
    pub type Nullish[T]

    null | undefined | T

    Nullish::to_option

    fn[T] Nullish::to_option(self : Nullish[T]) -> T?

    Promise

    #external
    pub type Promise[T]

    JavaScript Promise[T] https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise

    Promise::all

    fn[A] Promise::all(promises : Array[Promise[A]]) -> Promise[Array[A]]

    JS: Promise.all(promises) https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
    let results = Promise::all([
    Promise::from_async(async fn() { fetch_user() }),
    Promise::from_async(async fn() { fetch_posts() }),
    ]).wait()

    Promise::allSettled

    fn[A] Promise::allSettled(promises : Array[Promise[A]]) -> Promise[Array[SettledResult[A]]]

    JS: Promise.allSettled(promises) https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
    let results = Promise::allSettled([
    Promise::from_async(async fn() { 1 }),
    Promise::from_async(async fn() { fail("error") }),
    ]).wait()
    // results[0].status == "fulfilled", results[0].value == Some(1)
    // results[1].status == "rejected", results[1].reason != null

    Promise::any

    fn[T] Promise::any(promises : Array[Promise[T]]) -> Promise[T]

    JS: Promise.any(promises) https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/any
    let first_success = Promise::any([
    Promise::from_async(async fn() { fetch_from_mirror1() }),
    Promise::from_async(async fn() { fetch_from_mirror2() }),
    ]).wait()

    Promise::catch_

    fn[A, B] Promise::catch_(self : Promise[A], f : (Error) -> Promise[B]) -> Promise[B]

    Promise::finally_

    fn[A] Promise::finally_(self : Promise[A], f : () -> Unit) -> Promise[A]

    JS: promise.finally(() => { ... })

    Promise::from_async

    fn[A] Promise::from_async(f : async () -> A) -> Promise[A]

    Convert an async function to a Promise.
    let p : Promise[Int] = Promise::from_async(async fn() {
    let result = some_async_operation()
    result
    })

    Promise::new

    fn[A] Promise::new(f : async ((A) -> Unit, (Error) -> Unit) -> Unit) -> Promise[A]

    JS: new Promise((resolve, reject) => { ... }) Create a Promise from an async executor function with resolve/reject callbacks.
    let p : Promise[Int] = Promise::new(async fn(resolve, _reject) {
    let result = some_async_operation()
    resolve(result)
    })

    Promise::race

    fn[T] Promise::race(promises : Array[Promise[T]]) -> Promise[T]

    JS: Promise.race(promises) https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
    let first = Promise::race([
    Promise::from_async(async fn() {
    sleep(100)
    "slow"
    }),
    Promise::from_async(async fn() {
    sleep(10)
    "fast"
    }),
    ]).wait() // "fast"

    Promise::reject

    fn Promise::reject(x : Any) -> Promise[Any]

    js: Promise.reject(error) https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject

    Promise::resolve

    fn[A] Promise::resolve(x : A) -> Promise[A]

    js: Promise.resolve(value) https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve

    Promise::then

    fn[A, B] Promise::then(self : Promise[A], resolve : (A) -> Promise[B] raise) -> Promise[B]

    Promise::to_any

    fn[T] Promise::to_any(self : Promise[T]) -> Any

    Promise::wait

    async fn[T] Promise::wait(self : Promise[T]) -> T

    JS: await promise Waits for the Promise to be resolved or rejected.

    Promise::withResolvers

    fn[T] Promise::withResolvers() -> PromiseResolvers[T]

    JS: Promise.withResolvers() https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/withPromiseResolvers

    PromiseResolvers

    pub(all) struct PromiseResolvers[T] {
    promise : Promise[T]
    resolve : (T) -> Unit
    reject : (Error) -> Unit
    }

    JS: Promise.withResolvers() https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/withPromiseResolvers

    PromiseResolvers::reject

    fn[T] PromiseResolvers::reject(self : PromiseResolvers[T], error : Error) -> Unit

    JS: resolvers.reject(error)

    PromiseResolvers::resolve

    fn[T] PromiseResolvers::resolve(self : PromiseResolvers[T], value : T) -> Unit

    JS: resolvers.resolve(value)

    SettledResult

    pub(all) struct SettledResult[T] {
    status : String
    value : T?
    reason : Nullable[Any]
    }

    Result type for Promise.allSettled

    SettledResult::to_any

    fn[T] SettledResult::to_any(self : SettledResult[T]) -> Any

    Union2

    #external
    pub type Union2[A, B]

    TS: A | B

    Union2::from

    fn[A, B] Union2::from(x : Any) -> Union2[A, B]

    Union2::to0

    fn[A, B] Union2::to0(self : Union2[A, B]) -> A

    Union2::to1

    fn[A, B] Union2::to1(self : Union2[A, B]) -> B

    Union2::to_any

    fn[A, B] Union2::to_any(self : Union2[A, B]) -> Any

    Union3

    #external
    pub type Union3[A, B, C]

    TS: A | B | C

    Union3::from

    fn[A, B, C] Union3::from(x : Any) -> Union3[A, B, C]

    Union3::to0

    fn[A, B, C] Union3::to0(self : Union3[A, B, C]) -> A

    Union3::to1

    fn[A, B, C] Union3::to1(self : Union3[A, B, C]) -> B

    Union3::to2

    fn[A, B, C] Union3::to2(self : Union3[A, B, C]) -> C

    TS: A | B | C

    Union3::to_any

    fn[A, B, C] Union3::to_any(self : Union3[A, B, C]) -> Any

    Union4

    #external
    pub type Union4[A, B, C, D]

    TS: A | B | C | D

    Union4::from

    fn[A, B, C, D] Union4::from(x : Any) -> Union4[A, B, C, D]

    Union4::to0

    fn[A, B, C, D] Union4::to0(self : Union4[A, B, C, D]) -> A

    Union4::to1

    fn[A, B, C, D] Union4::to1(self : Union4[A, B, C, D]) -> B

    Union4::to2

    fn[A, B, C, D] Union4::to2(self : Union4[A, B, C, D]) -> C

    Union4::to3

    fn[A, B, C, D] Union4::to3(self : Union4[A, B, C, D]) -> D

    Union4::to_any

    fn[A, B, C, D] Union4::to_any(self : Union4[A, B, C, D]) -> Any

    Union5

    #external
    pub type Union5[A, B, C, D, E]

    TS: A | B | C | D | E

    Union5::from

    fn[A, B, C, D, E] Union5::from(x : Any) -> Union5[A, B, C, D, E]

    Union5::to0

    fn[A, B, C, D, E] Union5::to0(self : Union5[A, B, C, D, E]) -> A

    Union5::to1

    fn[A, B, C, D, E] Union5::to1(self : Union5[A, B, C, D, E]) -> B

    Union5::to2

    fn[A, B, C, D, E] Union5::to2(self : Union5[A, B, C, D, E]) -> C

    Union5::to3

    fn[A, B, C, D, E] Union5::to3(self : Union5[A, B, C, D, E]) -> D

    Union5::to4

    fn[A, B, C, D, E] Union5::to4(self : Union5[A, B, C, D, E]) -> E

    Union5::to_any

    fn[A, B, C, D, E] Union5::to_any(self : Union5[A, B, C, D, E]) -> Any

    any

    fn[T] any(value : T) -> Any

    array_from

    fn array_from(v : Any) -> Array[Any]

    Array.from(v)

    as_any

    fn as_any(opt : Any?) -> Any

    Convert Option[Any] to Any (None becomes undefined) Note: Any? is boxed, so we need FFI to extract the value

    assert_throws

    fn assert_throws(op : () -> Any) -> String raise JsError

    Test helper: assert that op throws a JS exception Returns the error message if thrown, fails if no exception

    assert_throws_with

    fn assert_throws_with(op : () -> Any, expected_msg : String) -> Unit

    Test helper: assert that op throws with specific message (contains check)

    dispose

    fn dispose(value : Any) -> Unit

    dispose_async

    async fn dispose_async(value : Any) -> Unit

    Call async dispose (Symbol.asyncDispose) Usage: defer value |> dispose_async

    dispose_async_ignore

    fn dispose_async_ignore(value : Any) -> Unit

    Call asyncDispose but ignore the result now moonbit can not async in defer Usage: defer browser |> dispose_async_ignore

    equal

    fn equal(a : Any, b : Any) -> Bool

    Check if value is null or undefined

    export_sync

    fn[E : Show + Error] export_sync(op : () -> Any raise E) -> Any

    Wrap a MoonBit operation that may raise, converting errors to JS exceptions Usage: export_sync(fn() { may_raise_error() })

    ffi_wrap_sync

    fn ffi_wrap_sync(op : () -> Any, on_ok : (Any) -> Unit, on_error : (Any) -> Unit) -> Unit

    JS operation wrapper with try-catch

    from_entries

    fn from_entries(entries : Array[(String, Any)]) -> Any

    Convert Array of tuples to JS Object using Object.fromEntries Tuples compile to {_0, _1} objects, which are converted to [key, value] in FFI

    from_fn0

    fn[A] from_fn0(f : () -> A) -> Any

    Wrap MoonBit function as JS function (0 args)

    from_fn1

    fn[A, B] from_fn1(f : (A) -> B) -> Any

    Wrap MoonBit function as JS function (1 arg)

    from_fn2

    fn[A, B, C] from_fn2(f : (A, B) -> C) -> Any

    Wrap MoonBit function as JS function (2 args)

    from_fn3

    fn[A, B, C, D] from_fn3(f : (A, B, C) -> D) -> Any

    Wrap MoonBit function as JS function (3 args)

    from_option

    fn[A] from_option(opt : A?) -> Any

    Convert an Option to Any, mapping None to null and Some(v) to v.

    This is the inverse of identity_option. Use this when you need to pass MoonBit Option values back to JavaScript APIs.

    Usage

    • Some(42) |> from_option returns Js(42)
    • None |> from_option returns Js(null)
    • Use with match to set optional properties on objects

    See Also

    • identity_option(Js) -> A? - Inverse conversion (Js to Option)

    global_this

    fn global_this() -> Any

    identity

    fn[A, B] identity(value : A) -> B

    identity_option

    fn[T] identity_option(v : Any) -> T?

    Safely convert a JavaScript value to an Option type.

    Converts JavaScript null or undefined to None, otherwise returns Some(value). This is essential for FFI because accessing properties on null/undefined will crash.

    Common Patterns

    • DOM API nullable returns: node._get("parentNode") |> identity_option()
    • Optional object properties: obj._get("name") |> identity_option()
    • Converting back to Js: use from_option(opt)

    Related Functions

    • from_option(A?) -> Js - Inverse conversion (Option to Js)
    • @core.Nullable[T] - For FFI struct fields that need explicit null handling

    instanceof_

    fn instanceof_(v : Any, cls : Any) -> Bool

    is_array

    fn is_array(v : Any) -> Bool

    Check if value is an array

    is_null

    fn is_null(v : Any) -> Bool

    Check if value is null

    is_nullish

    fn is_nullish(v : Any) -> Bool

    Check if value is null or undefined

    is_object

    fn is_object(v : Any) -> Bool

    is_undefined

    fn is_undefined(v : Any) -> Bool

    Check if value is null

    json_parse

    fn json_parse(text : String) -> Any

    JS: JSON.parse(text)

    json_stringify

    fn json_stringify(value : Any) -> String

    JS: JSON.stringify(value)

    json_stringify_pretty

    fn json_stringify_pretty(value : Any, space : Int) -> String

    JS: JSON.stringify(value, null, space)

    log

    fn log(message : Any) -> Unit

    new

    fn new(cls : Any, args : Array[Any]) -> Any

    JS: new cls(...args)

    new_array

    fn new_array() -> Any

    new_object

    fn new_object() -> Any

    null

    fn null() -> Any

    nullable

    fn[T] nullable(opt : T?) -> Any

    Convert Option[T] to Any (None becomes undefined) Zero-cost: concrete Option[T] compiles to T | undefined in JS Usage: from_entries([("key", nullable(maybe_value))])

    object_assign

    fn object_assign(target : Any, source : Any) -> Any

    JS: Object.assign(target, ...sources)

    object_has_own

    fn object_has_own(obj : Any, key : String) -> Bool

    JS: Object.hasOwn(obj, key) or obj.hasOwnProperty(key)

    object_keys

    fn object_keys(obj : Any) -> Array[String]

    object_values

    fn object_values(obj : Any) -> Array[Any]

    promisify0

    fn[R] promisify0(f : async () -> R) -> (() -> Promise[R])

    promisify1

    fn[A, R] promisify1(f : async (A) -> R) -> ((A) -> Promise[R])

    Moonbit Async Function to JS Promise Function

    promisify2

    fn[A, B, R] promisify2(f : async (A, B) -> R) -> ((A, B) -> Promise[R])

    Moonbit Async Function to JS Promise Function

    promisify3

    fn[A, B, C, R] promisify3(f : async (A, B, C) -> R) -> ((A, B, C) -> Promise[R])

    Moonbit Async Function to JS Promise Function

    run_async

    fn run_async(f : async () -> Unit noraise) -> Unit

    MoonBit builtin %async.run

    sleep

    async fn sleep(ms : Int) -> Unit noraise

    suspend

    async fn[T, E : Error] suspend(f : ((T) -> Unit, (E) -> Unit) -> Unit) -> T raise E

    MoonBit builtin %async.suspend

    throw_

    fn throw_(value : Any) -> Unit

    throw_error

    fn throw_error(msg : String) -> Unit

    Throw a JS Error with the given message

    throwable

    fn[T] throwable(f : () -> T raise?) -> T raise ThrowError

    Wraps a synchronous function call, converting any thrown JS errors into ThrowError
    let result = throwable(() => undefined()._invoke([]))

    try_sync

    fn try_sync(op : () -> Any) -> Any raise JsError

    Safe wrapper that converts JS exceptions to MoonBit errors

    typeof_

    fn typeof_(v : Any) -> String

    JS: typeof v

    undefined

    fn undefined() -> Any