#@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

    #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.

    ToAny

    pub trait ToAny {
    to_any(Self) -> Any
    }

    Trait for converting values to JavaScript Any type.

    Provides a type-safe alternative to the @core.any() function. All implementations use zero-cost %identity conversion.

    Usage:
    fn[T : ToAny] set_value(obj : Any, key : String, value : T) -> Unit {
    obj._set(key, ToAny::to_any(value))
    }

    Trade-off: Using fn[T : ToAny] causes monomorphization (code duplication per type). For smaller bundle size, either:
    • Use |> @core.any directly (no trait overhead)
    • Use &ToAny trait objects (dynamic dispatch, single function)
    impl ToAny for Unit
    impl ToAny for Bool
    impl ToAny for Int
    impl ToAny for Int64
    impl ToAny for UInt
    impl ToAny for UInt64
    impl ToAny for Float
    impl ToAny for Double
    impl ToAny for String
    impl ToAny for Bytes
    impl ToAny for Array[T]

    JsError

    pub suberror JsError {
    JsError(String)
    }

    Generic JS error type

    ThrowError

    pub suberror ThrowError {
    ThrowError(Any)
    }

    impl Show for ThrowError

    Any

    #external
    pub type Any

    Opaque type for JavaScript values
    impl ToAny for Any
    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

    Get property: obj[key]

    Any::_get_by_index

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

    Get by index: 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

    Any::cast

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

    Any::to_string

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

    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([
    from_async(async fn() { fetch_user() }),
    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([
    from_async(async fn() { 1 }),
    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([
    from_async(async fn() { fetch_from_mirror1() }),
    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::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([
    from_async(async fn() {
    sleep(100)
    "slow"
    }),
    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

    TemplateStringsArray

    #external
    pub type TemplateStringsArray

    TemplateStringsArray - corresponds to TypeScript's TemplateStringsArray.

    In JavaScript tagged templates, the first argument is an array of string literals with a raw property containing the raw (unescaped) versions.

    TypeScript definition:
    interface TemplateStringsArray extends ReadonlyArray<string> { readonly raw: ReadonlyArray<string>; }

    See: https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html

    TemplateStringsArray::length

    fn TemplateStringsArray::length(self : TemplateStringsArray) -> Int

    Get the length of the template strings array

    TemplateStringsArray::op_get

    fn TemplateStringsArray::op_get(self : TemplateStringsArray, index : Int) -> String

    Get the string at the given index

    TemplateStringsArray::raw

    Get the raw array

    TemplateStringsArray::raw_get

    fn TemplateStringsArray::raw_get(self : TemplateStringsArray, index : Int) -> String

    Get the raw (unescaped) string at the given index

    TemplateStringsArray::to_any

    TypeOf

    #external
    pub type TypeOf[T]

    Represents a JavaScript constructor/class type. Similar to TypeScript's typeof ClassName.

    TypeOf::as_any

    fn[T] TypeOf::as_any(self : TypeOf[T]) -> Any

    Convert TypeOf[T] to Any for use with generic functions

    TypeOf::is_instanceof

    fn[T] TypeOf::is_instanceof(self : TypeOf[T], v : Any) -> Bool

    Type-safe instanceof check: TypeOf[T].is_instanceof(value)

    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]

    as_any

    fn as_any(opt : Any?) -> Any

    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

    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_async

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

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

    from_entries

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

    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.

    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).

    instanceof_

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

    is_array

    fn is_array(v : Any) -> Bool

    is_null

    fn is_null(v : Any) -> Bool

    is_nullish

    fn is_nullish(v : Any) -> Bool

    is_object

    fn is_object(v : Any) -> Bool

    is_undefined

    fn is_undefined(v : Any) -> Bool

    json_parse

    fn json_parse(text : String) -> Any

    json_stringify

    fn json_stringify(value : Any) -> String

    json_stringify_pretty

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

    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_instance

    fn[T] new_instance(cls : TypeOf[T], args : Array[Any]) -> T

    Type-safe constructor call: new cls(...args) -> T

    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

    object_assign

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

    object_has_own

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

    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

    tag

    fn[T] tag(tag_fn : (TemplateStringsArray, Array[Any]) -> T, template : String, args : Array[Any]) -> T

    Call a JavaScript tagged template function with type safety.

    This function enables MoonBit code to call JavaScript tagged template literals like css\color: ${color}`` or html\${content}``.

    Template Format

    Use ${@0}, ${@1}, etc. as placeholders (0-indexed):
    • "color: ${@0}; font-size: ${@1}px" with args ["red", "16"]

    Tag Function Type

    The tag function should have the signature:
    fn(TemplateStringsArray, Array[Any]) -> T

    TypeScript equivalent:
    type TagFn<T> = (strings: TemplateStringsArray, ...values: any[]) => T

    Example

    // Define a typed tag function
    fn my_css(
    strings : @core.TemplateStringsArray,
    values : Array[@core.Any],
    ) -> String {
    let mut result = strings[0]
    for i, v in values {
    result = result + v.to_string() + strings[i + 1]
    }
    result
    }

    // Call it

    let result : String = tag(my_css, "color: ${@0}", [@core.any("red")])

    How It Works

    JavaScript tagged templates receive:
    1. An array of string parts (with a raw property)
    2. The interpolation values as separate arguments

    For template "a${@0}b${@1}c" with args [1, 2]:
    • strings: ["a", "b", "c"] (with strings.raw = ["a", "b", "c"])
    • values: [1, 2]
    • Calls: tagFn(strings, 1, 2)

    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

    undefined

    fn undefined() -> Any