Sign in

    moonshim

    Conservative, compile-tested compatibility shims for evolving MoonBit public APIs.

    moonbit
    api-compatibility
    migration
    developer-tools
    codegen
    Download zip
    Version
    0.1.1
    License
    Apache-2.0
    Last updated
    4 days ago
    Downloads
    28

    #MoonShim

    CI

    Compile-tested compatibility shims for evolving MoonBit APIs.

    Published on Mooncakes.

    MoonShim compares two generated .mbti interfaces and turns explicitly approved, shape-compatible renames into a MoonBit compatibility layer. It also reports breaking changes that need human review instead of guessing at their semantics.

    The project is written primarily in MoonBit, runs without network access, and uses a deterministic, fail-closed migration model.

    #The problem

    MoonBit and its package ecosystem are evolving quickly. A library upgrade may:

    • rename a public function;
    • rename a public type;
    • change a parameter or return type;
    • change a struct or enum shape;
    • remove an API without an obvious replacement.

    API checkers can expose these changes, but maintainers still have to write the compatibility layer by hand. MoonShim prepares that layer and verifies it with an end-to-end compilation fixture.

    #What it does

    1. Parses the public declarations in two moon info interfaces.
    2. Normalizes function parameters, labels, defaults, results, and type bodies.
    3. Classifies added, removed, renamed, and changed symbols.
    4. Generates wrappers and type aliases only when an explicit rule maps two compatible public shapes.
    5. Emits Markdown and JSON migration reports.
    6. Fails closed when a change cannot be proved safe.

    MoonShim deliberately does not infer business equivalence from a similar signature. A rename must be declared by the maintainer.

    #Quick start

    Requirements: MoonBit 0.10.17 or newer and Node.js for the CLI target.

    Install the library:

    moon add 2227057182/moonshim@0.1.1

    Develop the CLI from source:

    git clone https://github.com/2227057182/moonshim.git cd moonshim moon update moon check --deny-warn moon test --deny-warn

    Create two interface snapshots from the versions you want to compare:

    git checkout <old-version> moon info cp pkg.generated.mbti ../old.mbti git checkout <new-version> moon info cp pkg.generated.mbti ../new.mbti

    Declare only the renames you have reviewed:

    { "version": 1, "renames": [ { "kind": "function", "from": "parse_amount", "to": "parse_money" }, { "kind": "type", "from": "Money", "to": "Cash" } ] }

    Generate a report and compatibility source:

    moon run cmd/main -- diff old.mbti new.mbti \ --rules renames.json \ --markdown migration-report.md \ --json migration-report.json \ --shim compat_generated.mbt \ --fail-on-breaking

    --fail-on-breaking exits with status 2 if any change still requires manual review. A partial shim may still be generated so that safe changes can proceed. Run moon fmt after generation and before committing the shim.

    #Example output

    MoonShim: 0 added, 5 shimmable, 0 blocked, 5 shim declaration(s)

    pub fn parse_amount(
    input : StringView,
    currency~ : String = "CNY",
    ) -> Int32 raise {
    parse_money(input, currency~)
    }

    pub type Money = Cash

    The repository's compat fixture keeps using the old names. Regenerating the shim and running moon test proves that the old consumer still compiles and runs.

    #Three real use cases

    Library maintainer: publish a major version while still allowing existing consumers to compile for one migration cycle.

    Application maintainer: inspect an upstream API change and obtain a reviewed adapter instead of a list of compile errors.

    CI maintainer: run MoonShim against release interfaces and require an explicit rule or a reviewed breaking-change decision before merging.

    #Safety model

    A function rename is shimmable only when both sides have:

    • the same number of parameters;
    • the same parameter modes and public types;
    • the same labels where labels affect callers;
    • the same default values;
    • the same result and raise clause.

    A type rename is shimmable only for identical alias, struct, or enum bodies. Traits, values, label renames, semantic changes, and body changes are reported for manual implementation.

    MoonShim never writes directly into a package unless the caller supplies --shim. It does not edit arbitrary source files.

    #Repository layout

    parser.mbt .mbti parser and interface model diff.mbt conservative compatibility classification rules.mbt JSON migration rules render.mbt Markdown and JSON reports cmd/main/ CLI compat_fixture/ compile-time proof that generated shims work fixtures/ old/new interfaces and reviewed rename rules docs/ architecture, rules, and submission material scripts/ local end-to-end verification

    See Architecture and Migration rules for the design boundaries.

    #Verification

    moon fmt --check moon check --deny-warn moon test --deny-warn ./scripts/e2e.sh

    On Windows PowerShell:

    ./scripts/e2e.ps1

    The end-to-end script regenerates compat_fixture/compat_generated.mbt, verifies that the generated file is stable under version control, and reruns the package tests.

    #Current limitations

    • The parser targets declarations emitted by the current moon info format.
    • Semantic equivalence is a maintainer decision, not an automated inference.
    • The first release supports explicit function and type renames.
    • Trait implementations, operators, and general-purpose codemods are roadmap items.

    #License

    Apache-2.0. See LICENSE.

    RuleError

    pub(all) suberror RuleError {
    RuleError(String)
    }

    Errors produced while loading a migration rule file.

    ApiChange

    pub(all) struct ApiChange {
    change_kind : ChangeKind
    symbol_kind : SymbolKind
    old_name : String?
    new_name : String?
    old_signature : String?
    new_signature : String?
    safely_shimmable : Bool
    reason : String
    }

    One actionable API change.

    ApiSnapshot

    pub(all) struct ApiSnapshot {
    package_name : String
    symbols : Array[ApiSymbol]
    }

    A parsed package interface.

    ApiSymbol

    pub(all) struct ApiSymbol {
    kind : SymbolKind
    name : String
    signature : String
    parameters : Array[Parameter]
    result_text : String
    }

    One public symbol extracted from a generated MoonBit interface.

    ChangeKind

    pub(all) enum ChangeKind {
    Added
    Removed
    Renamed
    SignatureChanged
    BodyChanged
    }

    A compatibility finding produced by comparing two snapshots.

    MigrationPlan

    pub(all) struct MigrationPlan {
    changes : Array[ApiChange]
    shim_sources : Array[String]
    diagnostics : Array[String]
    }

    Complete migration result consumed by both the CLI and library users.

    MigrationRule

    pub(all) struct MigrationRule {
    symbol_kind : SymbolKind
    old_name : String
    new_name : String
    }

    A user-authored mapping between an old and a new symbol name.

    Parameter

    pub(all) struct Parameter {
    name : String
    type_text : String
    mode : ParameterMode
    default_value : String?
    }

    A normalized function parameter extracted from a public interface.

    ParameterMode

    pub(all) enum ParameterMode {
    Positional
    Optional
    Labelled
    Receiver
    }

    How callers pass an argument to a MoonBit function.

    SymbolKind

    pub(all) enum SymbolKind {
    Function
    TypeAlias
    Struct
    Enum
    Trait
    Value
    }

    The kind of a public API declaration found in a generated .mbti file.

    change_kind_name

    fn change_kind_name(kind : ChangeKind) -> String

    find_symbol

    fn find_symbol(snapshot : ApiSnapshot, kind : SymbolKind, name : StringView) -> ApiSymbol?

    has_unresolved_breaking_changes

    fn has_unresolved_breaking_changes(plan : MigrationPlan) -> Bool

    parameter_mode_name

    fn parameter_mode_name(mode : ParameterMode) -> String

    parse_interface

    fn parse_interface(source : StringView) -> ApiSnapshot

    Parses the stable declaration subset emitted by moon info.

    Only public functions, type aliases, structs, enums, traits and values are retained. Documentation and implementation details are intentionally omitted.

    plan_migration

    fn plan_migration(old : ApiSnapshot, new : ApiSnapshot, rules : Array[MigrationRule]) -> MigrationPlan

    Compares two public interfaces and creates a conservative migration plan.

    render_json

    fn render_json(plan : MigrationPlan, old_name : StringView, new_name : StringView) -> String

    Renders a stable machine-readable report suitable for CI.

    render_markdown

    fn render_markdown(plan : MigrationPlan, old_name : StringView, new_name : StringView) -> String

    Renders a human-readable Markdown migration report.

    render_shim_source

    fn render_shim_source(plan : MigrationPlan) -> String

    Merges all generated declarations into one compilable source file.

    rules_from_json

    fn rules_from_json(source : StringView) -> Array[MigrationRule] raise RuleError

    Loads migration rules from the documented JSON format.

    Example:

    { "version": 1, "renames": [ { "kind": "function", "from": "parse_amount", "to": "parse_money" } ] }

    rules_to_json

    fn rules_to_json(rules : Array[MigrationRule]) -> String

    same_parameter_mode

    fn same_parameter_mode(left : ParameterMode, right : ParameterMode) -> Bool

    same_symbol_kind

    fn same_symbol_kind(left : SymbolKind, right : SymbolKind) -> Bool

    symbol_key

    fn symbol_key(kind : SymbolKind, name : StringView) -> String

    symbol_kind_name

    fn symbol_kind_name(kind : SymbolKind) -> String

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io