Sign in

    admiral

    Async-first declarative CLI builder for MoonBit, inspired by gunshi

    cli
    argparse
    moonbit
    Download zip
    Author
    Version
    0.6.5
    License
    MIT
    Last updated
    last month
    Downloads
    694

    #admiral

    Admiral is an async-first declarative CLI module for MoonBit applications that need typed arguments, configuration, interactive input, schema output, and shell completion.

    #Usage

    Build a greeter that passes a required name to its callback. The checked typed-option example verifies that --name Alice produces the captured "Alice" value.

    Use a nested command when it owns an option such as a server port. The checked nested-command example verifies that MYAPP_PORT=8080 reaches the serve callback as an Int.

    For filesystem discovery, the checked target-file-discovery Usage example creates a temporary project.toml and verifies that the helper returns its exact path.

    #Key features

    • Typed declarative CLI definitions, async command execution, configuration, interactive input, schema rendering, and completion.
    • Cross-target asynchronous discovery of named files with inherited .gitignore rules.
    • JavaScript, native, and Wasm module targets, with JavaScript as the preferred target.

    #Prerequisites

    • MoonBit: Install the MoonBit toolchain.
    • Supported targets: Use JavaScript, native, or Wasm according to the consuming application.

    #Setup

    moon add totto2727/admiral moon add moonbitlang/async

    Import the CLI package in a consuming package's moon.pkg. This package supports JavaScript and native targets:

    supported_targets = "js+native" import { "totto2727/admiral" @admiral, "moonbitlang/async", }

    Import the target-file discovery package from the same module instead when the application needs its JavaScript, native, and Wasm-compatible filesystem helpers:

    supported_targets = "js+native+wasm" import { "totto2727/admiral/util/target-file-discovery" @target-file-discovery, "moonbitlang/async", }

    For the distinct filesystem helper API, see target-file-discovery.

    #API

    The Mooncakes Admiral API reference is the canonical generated API index for the published CLI package. For the distinct filesystem helper API, see the target-file-discovery guide.

    #Defining options and positions

    Option and position constructors return typed definitions. The scalar helpers are string, bool, int, int64, uint, uint64, and double; each has a repeated form such as strings or doubles. Position helpers provide the same scalar and repeated numeric families.

    Pass the same definition to CommandDef::CommandDef or CliApp::CliApp and to the matching Context getter so the option name remains one type-checked source of truth.

    The checked typed-definition example verifies option metadata and a required positional definition.

    #Reading values

    Context exposes scalar getters such as get_bool, get_string, get_int, get_int64, get_uint, get_uint64, and get_double, plus raising _required variants. Repeated values use get_strings, get_ints, get_int64s, get_uints, get_uint64s, and get_doubles; their _required variants return NonEmptyArray and raise when no value is available.

    #Environment and configuration

    CliApp::run reads process arguments and environment by default, while tests and embedders can inject both sources. Values resolve in the order argv > env > config > default.

    Environment-backed booleans accept 1, 0, true, false, yes, no, on, and off; the parsing and precedence rules come from moonbitlang/core/argparse.

    Provide load_config when configuration must come from a file or another source. It returns a Map[String, Json], and its keys are the independent config names declared on definitions.

    #Nested commands and positions

    Commands can contain subcommands and each command owns its options, positions, examples, and callback. Context::get_subcommand() returns the selected subcommand name and its nested context when a child command was selected.

    #Interactive input

    Set interactive=true on definitions and provide one callback on the owning command or application. Admiral invokes it only when an opted-in definition is present and a TTY is available. InteractiveContext::to_context() exposes initial values, and typed setters replace selected values before the command callback runs.

    #Schema and completion

    ToJson::to_json(app) returns the structured schema, while render_schema() returns its string form. render_bash_completion(), render_zsh_completion(), and render_fish_completion() generate shell-specific completion scripts.

    The checked schema-rendering example verifies that an option name appears in the generated schema.

    #Development

    For development guidance, see AGENTS.md.

    #License

    MIT. See LICENSE.

    This README was generated from the share-artifact skill and README template.

    OptionDef

    type OptionDef[T] = ArgDef[T, OptionMetadata]

    Typed definition for an option declaration.

    PositionDef

    type PositionDef[T] = ArgDef[T, PositionMetadata]

    Typed definition for a positional argument declaration.

    ToStoredOption

    trait ToStoredOption

    ToStoredPosition

    trait ToStoredPosition

    ConfigLoadFailure

    pub(all) suberror ConfigLoadFailure {
    ConfigLoadFailure(String)
    } derive(Eq,
    Debug
    )

    Failure raised by a configuration loader when external configuration cannot be read or decoded into a map.

    ArgDef

    pub struct ArgDef[_, Metadata] {
    name : String
    config : String?
    metadata : Metadata
    } derive(
    Debug
    )

    Typed, read-only argument definition shared by a parser declaration and its matching Context getter.

    CliApp

    pub struct CliApp {
    name : String
    version : String
    description : String
    root_options : Array[ArgDef[Unit, OptionMetadata]]
    root_positionals : Array[ArgDef[Unit, PositionMetadata]]
    commands : Array[CommandDef]
    interactive : async (InteractiveContext) -> Unit?
    run : async (Context) -> Unit?
    load_config : () -> Map[String, Json] raise ConfigLoadFailure?
    }

    Root application definition containing global arguments and command routing.
    impl ToJson for CliApp

    CliApp::CliApp

    fn CliApp::CliApp(name~ : String, version? : String, description? : String, options? : Array[&ToStoredOption], positionals? : Array[&ToStoredPosition], commands? : Array[CommandDef], interactive? : async (InteractiveContext) -> Unit?, run? : async (Context) -> Unit?, load_config? : () -> Map[String, Json] raise ConfigLoadFailure?) -> CliApp

    Constructs the root CLI application. run handles root arguments, while commands dispatches nested command paths; load_config supplies a map of independent configuration keys.

    CliApp::render_bash_completion

    fn CliApp::render_bash_completion(self : CliApp) -> String

    Renders a Bash completion function for the application's commands and options. Install the returned script in a Bash completion file.

    CliApp::render_fish_completion

    fn CliApp::render_fish_completion(self : CliApp) -> String

    Renders Fish completion declarations for the application's commands and options. Save the returned text under Fish's completions directory.

    CliApp::render_schema

    fn CliApp::render_schema(self : CliApp) -> String

    Renders the complete CLI definition as a JSON string for documentation and tooling integrations.

    CliApp::render_zsh_completion

    fn CliApp::render_zsh_completion(self : CliApp) -> String

    Renders a Zsh completion function for the application's commands and options. Install the returned script in a Zsh completion file.

    CliApp::run

    async fn CliApp::run(self : CliApp, argv? : Array[String]?, env? : Map[String, String]) -> Unit

    Parses inputs and runs the selected callback, invoking interactive input only when at least one selected definition opts in and an input TTY is available.

    CommandDef

    pub struct CommandDef {
    name : String
    description : String
    options : Array[ArgDef[Unit, OptionMetadata]]
    positionals : Array[ArgDef[Unit, PositionMetadata]]
    examples : Array[String]
    subcommands : Array[CommandDef]
    interactive : async (InteractiveContext) -> Unit?
    run : async (Context) -> Unit?
    }

    A declarative command definition with options, positions, nested commands, optional interactive input, and an async execution callback.

    CommandDef::CommandDef

    fn CommandDef::CommandDef(name~ : String, description? : String, options? : Array[&ToStoredOption], positionals? : Array[&ToStoredPosition], examples? : Array[String], subcommands? : Array[CommandDef], interactive? : async (InteractiveContext) -> Unit?, run? : async (Context) -> Unit?) -> CommandDef

    Constructs a command definition. The same typed option or position value can be passed to the declaration and later to its matching context getter.

    Context

    pub struct Context {
    flags :
    HashMap
    [String, Bool]
    values :
    HashMap
    [String, ReadOnlyArray[String]]
    sources :
    HashMap
    [String,
    ValueSource
    ]
    config :
    HashMap
    [String, Json]
    interactive_flags :
    HashMap
    [String, Bool]
    interactive_values :
    HashMap
    [String, ReadOnlyArray[String]]
    subcommand : (String, Context)?
    }

    Parsed values and independently loaded configuration for a command callback.

    Context::Context

    fn Context::Context(flags? : Map[String, Bool], values? : Map[String, Array[String]], sources? : Map[String,
    ValueSource
    ], config? : Map[String, Json], subcommand? : (String, Context)?) -> Context

    Creates a context from parser maps and an optional configuration map. The constructor is useful for embedding or tests that provide explicit matches.

    Context::get_bool

    Boolean options are flags stored separately from positional values. Their absence means false, so this getter intentionally accepts only OptionDef.

    Context::get_double

    fn[Metadata] Context::get_double(self : Context, argument : ArgDef[Double, Metadata]) -> Double? raise

    Parses and returns the first double value, or None when absent.

    Context::get_double_required

    fn[Metadata] Context::get_double_required(self : Context, argument : ArgDef[Double, Metadata]) -> Double raise

    Returns the required double value.

    Context::get_doubles

    fn[Metadata] Context::get_doubles(self : Context, argument : ArgDef[Array[Double], Metadata]) -> ReadOnlyArray[Double] raise

    Parses and returns all double values.

    Context::get_doubles_required

    fn[Metadata] Context::get_doubles_required(self : Context, argument : ArgDef[Array[Double], Metadata]) -> NonEmptyArray[Double] raise

    Returns all required double values as a NonEmptyArray.

    Context::get_int

    fn[Metadata] Context::get_int(self : Context, argument : ArgDef[Int, Metadata]) -> Int? raise

    Parses and returns the first signed integer value, or None when absent.

    Context::get_int64

    fn[Metadata] Context::get_int64(self : Context, argument : ArgDef[Int64, Metadata]) -> Int64? raise

    Parses and returns the first 64-bit signed integer value, or None when absent.

    Context::get_int64_required

    fn[Metadata] Context::get_int64_required(self : Context, argument : ArgDef[Int64, Metadata]) -> Int64 raise

    Returns the required 64-bit signed integer value.

    Context::get_int64s

    fn[Metadata] Context::get_int64s(self : Context, argument : ArgDef[Array[Int64], Metadata]) -> ReadOnlyArray[Int64] raise

    Parses and returns all 64-bit signed integer values.

    Context::get_int64s_required

    fn[Metadata] Context::get_int64s_required(self : Context, argument : ArgDef[Array[Int64], Metadata]) -> NonEmptyArray[Int64] raise

    Returns all required 64-bit signed integer values as a NonEmptyArray.

    Context::get_int_required

    fn[Metadata] Context::get_int_required(self : Context, argument : ArgDef[Int, Metadata]) -> Int raise

    Returns the required signed integer value, raising when it is absent.

    Context::get_ints

    fn[Metadata] Context::get_ints(self : Context, argument : ArgDef[Array[Int], Metadata]) -> ReadOnlyArray[Int] raise

    Parses and returns all signed integer values, or an empty array when absent.

    Context::get_ints_required

    fn[Metadata] Context::get_ints_required(self : Context, argument : ArgDef[Array[Int], Metadata]) -> NonEmptyArray[Int] raise

    Returns all required signed integer values as a NonEmptyArray.

    Context::get_string

    fn[Metadata] Context::get_string(self : Context, argument : ArgDef[String, Metadata]) -> String? raise
    JsonDecodeError

    Returns the first string value from an option or positional definition, or None when the argument is absent. Configuration JSON is decoded when the parser source is a default or is missing.

    Context::get_string_required

    fn[Metadata] Context::get_string_required(self : Context, argument : ArgDef[String, Metadata]) -> String raise

    Returns the required string value, raising when argument is absent.

    Context::get_strings

    fn[Metadata] Context::get_strings(self : Context, argument : ArgDef[Array[String], Metadata]) -> ReadOnlyArray[String] raise
    JsonDecodeError

    Returns all string values, or an empty read-only array when absent.

    Context::get_strings_required

    fn[Metadata] Context::get_strings_required(self : Context, argument : ArgDef[Array[String], Metadata]) -> NonEmptyArray[String] raise

    Returns all required string values as a NonEmptyArray.

    Context::get_subcommand

    fn Context::get_subcommand(self : Context) -> (String, Context)?

    Returns the selected subcommand name and its nested context, when present.

    Context::get_uint

    fn[Metadata] Context::get_uint(self : Context, argument : ArgDef[UInt, Metadata]) -> UInt? raise

    Parses and returns the first unsigned integer value, or None when absent.

    Context::get_uint64

    fn[Metadata] Context::get_uint64(self : Context, argument : ArgDef[UInt64, Metadata]) -> UInt64? raise

    Parses and returns the first 64-bit unsigned integer value, or None when absent.

    Context::get_uint64_required

    fn[Metadata] Context::get_uint64_required(self : Context, argument : ArgDef[UInt64, Metadata]) -> UInt64 raise

    Returns the required 64-bit unsigned integer value.

    Context::get_uint64s

    fn[Metadata] Context::get_uint64s(self : Context, argument : ArgDef[Array[UInt64], Metadata]) -> ReadOnlyArray[UInt64] raise

    Parses and returns all 64-bit unsigned integer values.

    Context::get_uint64s_required

    fn[Metadata] Context::get_uint64s_required(self : Context, argument : ArgDef[Array[UInt64], Metadata]) -> NonEmptyArray[UInt64] raise

    Returns all required 64-bit unsigned integer values as a NonEmptyArray.

    Context::get_uint_required

    fn[Metadata] Context::get_uint_required(self : Context, argument : ArgDef[UInt, Metadata]) -> UInt raise

    Returns the required unsigned integer value.

    Context::get_uints

    fn[Metadata] Context::get_uints(self : Context, argument : ArgDef[Array[UInt], Metadata]) -> ReadOnlyArray[UInt] raise

    Parses and returns all unsigned integer values.

    Context::get_uints_required

    fn[Metadata] Context::get_uints_required(self : Context, argument : ArgDef[Array[UInt], Metadata]) -> NonEmptyArray[UInt] raise

    Returns all required unsigned integer values as a NonEmptyArray.

    InteractiveContext

    pub struct InteractiveContext {
    initial : Context
    flags : Map[String, Bool]
    values : Map[String, Array[String]]
    }

    Mutable input passed to an interactive callback. Values set here override parser, environment, configuration, and default values before execution. Accumulates values selected by a command's interactive input callback. Read to_context() before setting values to obtain the resolved argv, environment, configuration, positional, and default values as initial input.

    InteractiveContext::set_bool

    fn InteractiveContext::set_bool(self : InteractiveContext, option : ArgDef[Bool, OptionMetadata], value : Bool) -> Unit

    Replaces a boolean option with a value selected interactively.

    InteractiveContext::set_double

    fn[Metadata] InteractiveContext::set_double(self : InteractiveContext, argument : ArgDef[Double, Metadata], value : Double) -> Unit

    Replaces a floating-point input with a value selected interactively.

    InteractiveContext::set_doubles

    fn[Metadata] InteractiveContext::set_doubles(self : InteractiveContext, argument : ArgDef[Array[Double], Metadata], values : Array[Double]) -> Unit

    Replaces repeated floating-point input with values selected interactively.

    InteractiveContext::set_int

    fn[Metadata] InteractiveContext::set_int(self : InteractiveContext, argument : ArgDef[Int, Metadata], value : Int) -> Unit

    Replaces an integer option or positional with a value selected interactively.

    InteractiveContext::set_int64

    fn[Metadata] InteractiveContext::set_int64(self : InteractiveContext, argument : ArgDef[Int64, Metadata], value : Int64) -> Unit

    Replaces a 64-bit integer input with a value selected interactively.

    InteractiveContext::set_int64s

    fn[Metadata] InteractiveContext::set_int64s(self : InteractiveContext, argument : ArgDef[Array[Int64], Metadata], values : Array[Int64]) -> Unit

    Replaces repeated 64-bit integer input with values selected interactively.

    InteractiveContext::set_ints

    fn[Metadata] InteractiveContext::set_ints(self : InteractiveContext, argument : ArgDef[Array[Int], Metadata], values : Array[Int]) -> Unit

    Replaces repeated integer input with values selected interactively.

    InteractiveContext::set_string

    fn[Metadata] InteractiveContext::set_string(self : InteractiveContext, argument : ArgDef[String, Metadata], value : String) -> Unit

    Replaces a string option or positional with a value selected interactively.

    InteractiveContext::set_strings

    fn[Metadata] InteractiveContext::set_strings(self : InteractiveContext, argument : ArgDef[Array[String], Metadata], values : Array[String]) -> Unit

    Replaces a repeated string option or positional with values selected interactively.

    InteractiveContext::set_uint

    fn[Metadata] InteractiveContext::set_uint(self : InteractiveContext, argument : ArgDef[UInt, Metadata], value : UInt) -> Unit

    Replaces an unsigned integer input with a value selected interactively.

    InteractiveContext::set_uint64

    fn[Metadata] InteractiveContext::set_uint64(self : InteractiveContext, argument : ArgDef[UInt64, Metadata], value : UInt64) -> Unit

    Replaces a 64-bit unsigned integer input with a value selected interactively.

    InteractiveContext::set_uint64s

    fn[Metadata] InteractiveContext::set_uint64s(self : InteractiveContext, argument : ArgDef[Array[UInt64], Metadata], values : Array[UInt64]) -> Unit

    Replaces repeated 64-bit unsigned integer input with values selected interactively.

    InteractiveContext::set_uints

    fn[Metadata] InteractiveContext::set_uints(self : InteractiveContext, argument : ArgDef[Array[UInt], Metadata], values : Array[UInt]) -> Unit

    Replaces repeated unsigned integer input with values selected interactively.

    InteractiveContext::to_context

    Returns the current context, including values already selected by this callback.

    NonEmptyArray

    pub struct NonEmptyArray[T] {
    first : T
    rest : ArrayView[T]
    all : ReadOnlyArray[T]
    } derive(
    Debug
    )

    Non-empty view returned by required plural getters. first is the first value, rest is the remaining view, and all preserves the full sequence.

    OptionMetadata

    pub struct OptionMetadata {
    type_ : OptionType
    short : Char
    description : String
    env : String?
    required : Bool
    default_value : String?
    multiple : Bool
    interactive : Bool
    } derive(
    Debug
    )

    Metadata used to build and resolve a named option, including its short flag, environment key, configuration key, required state, and multiplicity.

    OptionType

    pub enum OptionType {
    StringOpt
    BoolOpt
    IntOpt
    Int64Opt
    UIntOpt
    UInt64Opt
    DoubleOpt
    } derive(Eq,
    Debug
    )

    The scalar value kinds supported by options and positional arguments.
    impl Show for OptionType

    PositionMetadata

    pub struct PositionMetadata {
    type_ : OptionType
    description : String
    required : Bool
    multiple : Bool
    interactive : Bool
    } derive(
    Debug
    )

    Metadata used to build and resolve a positional argument.

    bool

    fn bool(name : String, short? : Char, description? : String, env? : String, config? : String, interactive? : Bool) -> ArgDef[Bool, OptionMetadata]

    Defines a boolean flag. An omitted flag resolves to false; environment values use the boolean literals accepted by core/argparse.

    build_command

    Builds a core/argparse command from a declarative command definition. This is useful when an embedding application needs to parse the command itself instead of running a CliApp.

    double

    fn double(name : String, short? : Char, description? : String, env? : String, config? : String, required? : Bool, default? : Double?, interactive? : Bool) -> ArgDef[Double, OptionMetadata]

    Defines a scalar double-precision floating-point option.

    doubles

    fn doubles(name : String, short? : Char, description? : String, env? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Array[Double], OptionMetadata]

    Defines a repeated double-precision floating-point option.

    int

    fn int(name : String, short? : Char, description? : String, env? : String, config? : String, required? : Bool, default? : Int?, interactive? : Bool) -> ArgDef[Int, OptionMetadata]

    Defines a scalar signed integer option with optional defaults and required validation.

    int64

    fn int64(name : String, short? : Char, description? : String, env? : String, config? : String, required? : Bool, default? : Int64?, interactive? : Bool) -> ArgDef[Int64, OptionMetadata]

    Defines a scalar 64-bit signed integer option.

    int64s

    fn int64s(name : String, short? : Char, description? : String, env? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Array[Int64], OptionMetadata]

    Defines a repeated 64-bit signed integer option.

    ints

    fn ints(name : String, short? : Char, description? : String, env? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Array[Int], OptionMetadata]

    Defines a repeated signed integer option. Use Context::get_ints to read all values after parsing.

    position_double

    fn position_double(name : String, description? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Double, PositionMetadata]

    Defines a scalar double-precision floating-point positional argument.

    position_doubles

    fn position_doubles(name : String, description? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Array[Double], PositionMetadata]

    Defines a variadic double-precision floating-point positional argument.

    position_int

    fn position_int(name : String, description? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Int, PositionMetadata]

    Defines a scalar signed integer positional argument.

    position_int64

    fn position_int64(name : String, description? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Int64, PositionMetadata]

    Defines a scalar 64-bit signed integer positional argument.

    position_int64s

    fn position_int64s(name : String, description? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Array[Int64], PositionMetadata]

    Defines a variadic 64-bit signed integer positional argument.

    position_ints

    fn position_ints(name : String, description? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Array[Int], PositionMetadata]

    Defines a variadic signed integer positional argument.

    position_string

    fn position_string(name : String, description? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[String, PositionMetadata]

    Defines a scalar string positional argument. A required position is checked by the parser unless an interactive callback or configured value supplies it.

    position_strings

    fn position_strings(name : String, description? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Array[String], PositionMetadata]

    Defines a variadic string positional argument. Read all values with Context::get_strings.

    position_uint

    fn position_uint(name : String, description? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[UInt, PositionMetadata]

    Defines a scalar unsigned integer positional argument.

    position_uint64

    fn position_uint64(name : String, description? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[UInt64, PositionMetadata]

    Defines a scalar 64-bit unsigned integer positional argument.

    position_uint64s

    fn position_uint64s(name : String, description? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Array[UInt64], PositionMetadata]

    Defines a variadic 64-bit unsigned integer positional argument.

    position_uints

    fn position_uints(name : String, description? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Array[UInt], PositionMetadata]

    Defines a variadic unsigned integer positional argument.

    string

    fn string(name : String, short? : Char, description? : String, env? : String, config? : String, required? : Bool, default? : String?, interactive? : Bool) -> ArgDef[String, OptionMetadata]

    Defines a scalar string option. Set required when the command must receive a value and default when a fallback should be used. env and config select independent environment and configuration keys.

    strings

    fn strings(name : String, short? : Char, description? : String, env? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Array[String], OptionMetadata]

    Defines a repeated string option. Values are returned by Context::get_strings; configuration values must be JSON arrays.

    uint

    fn uint(name : String, short? : Char, description? : String, env? : String, config? : String, required? : Bool, default? : UInt?, interactive? : Bool) -> ArgDef[UInt, OptionMetadata]

    Defines a scalar unsigned integer option.

    uint64

    fn uint64(name : String, short? : Char, description? : String, env? : String, config? : String, required? : Bool, default? : UInt64?, interactive? : Bool) -> ArgDef[UInt64, OptionMetadata]

    Defines a scalar 64-bit unsigned integer option.

    uint64s

    fn uint64s(name : String, short? : Char, description? : String, env? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Array[UInt64], OptionMetadata]

    Defines a repeated 64-bit unsigned integer option.

    uints

    fn uints(name : String, short? : Char, description? : String, env? : String, config? : String, required? : Bool, interactive? : Bool) -> ArgDef[Array[UInt], OptionMetadata]

    Defines a repeated unsigned integer option.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io