Sign in

    moonatlas

    Local multi-document knowledge maps with source evidence and explicit incremental changes.

    knowledge-map
    markdown
    provenance
    incremental
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    15 hours ago
    Downloads
    2

    #MoonAtlas

    A pure MoonBit library for local, multi-document knowledge maps with source evidence. It performs no filesystem or network I/O. Hosts provide UTF-8 Markdown/TXT snapshots. Core module: icyaaaww/moonatlas, library version 0.1.0. The browser application's 1.0.0 version is independent of the library version. The current publishing tool requires a 0.x.y module version; this is the first registry release of the core used by the 1.0 application.

    #Install and use

    In a MoonBit project, add the registry dependency:

    moon add icyaaaww/moonatlas

    Import the root package in your moon.pkg:

    ///|
    import {
    "icyaaaww/moonatlas",
    }

    Then call the API from a test or application. The following example is checked as part of the library's tests:

    ///|
    test {
    let docs : Array[@moonatlas.Document] = [
    {
    path: "team/release.md",
    content: "# Release\n## Rollback\nRestore the previous version.",
    },
    {
    path: "team/review.md",
    content: "# Review\n## Rollback\nRecord the decision.",
    },
    ]
    let atlas = @moonatlas.build_atlas(docs, [], [])
    assert_eq(atlas.issues.length(), 0)
    assert_eq(atlas.concepts[0].paths.length(), 2)
    assert_eq(atlas.concepts[0].sources[0].start_line, 2)
    assert_true(@moonatlas.export_index(atlas).contains("Rollback"))
    }

    Public entry points:

    • validate_documents(documents) checks document counts, paths and text sizes.
    • parse_document(document, preferences) extracts ATX headings and exact source lines.
    • build_atlas(documents, preferences, aliases) validates and creates a deterministic map.
    • compare_documents(before, after) reports additions, changes, removals and unique exact-content renames.
    • compare_sections(before, after) returns literal section additions/removals/edits with complete before/after text and line evidence; inspect issues and repeated-heading notes.
    • reconcile_preferences(before, after, preferences) transfers manual choices across those renames.
    • health_checks(atlas) creates stable structural findings with original paths and lines; validate Atlas.issues first.
    • update_impacts(before, after) returns direct incoming reference evidence from both snapshots, including deleted or renamed targets; inspect issues.
    • diff_lines(before, after) returns 1-based changed line ranges; a count of zero is an insertion/deletion boundary. precise: false explicitly indicates a bounded whole-region fallback. Browser transport limits each text to 200,000 UTF-16 units.
    • search_documents(documents, preferences, query) returns literal case-insensitive matches with snippets and 1-based source lines. Metadata hits use line 0. At most 200 hits are returned; total includes remaining matches. Inspect issues before use.
    • classify_documents(documents, preferences, paths, topic) applies one topic to 1–200 unique existing paths while preserving display titles and unselected preferences. Invalid selections fail atomically; inspect issues before using preferences.
    • export_index(atlas) creates an escaped Markdown inventory with source line ranges.
    • export_svg(atlas, title) creates a portable topic/document overview.

    Check Atlas.issues before using graph results. Invalid input produces an empty graph and explicit issues; invalid comparisons contain a kind: "invalid" entry. Low-level parse_document expects already validated documents; build_atlas is the recommended bounded API. Repeated headings are lexical clues, not inferred semantic equivalence. See docs/architecture.md for scope and limitations, and docs/provenance.md for sources and licenses. The archive includes the core, its tests, and examples/demo; the browser UI and npm build tools remain in the GitHub repository.

    #Validate from source

    moon check --target all moon test --target js moon test --target wasm-gc moon run examples/demo --target js

    Input limits: 200 documents, 200,000 UTF-16 units per document, 2,000,000 in total, 3,000 headings, 5,000 references, 200 preferences and 200 aliases. Inputs beyond the limits produce explicit issues. Markdown scanning is intentionally bounded, not a complete CommonMark/YAML implementation. There is no PDF/Word reader, network connector, semantic inference or source-file mutation in this library.

    Alias

    pub(all) struct Alias {
    name : String
    target : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    An explicit concept-name mapping, not a semantic equivalence inferred by AI.

    Alias::equal

    fn Alias::equal(Alias, Alias) -> Bool

    Alias::not_equal

    fn Alias::not_equal(x : Alias, y : Alias) -> Bool

    Alias::to_json

    fn Alias::to_json(Alias) -> Json

    Alias::to_repr

    Atlas

    pub(all) struct Atlas {
    schema : Int
    pages : Array[Page]
    concepts : Array[Concept]
    links : Array[Link]
    duplicates : Array[Duplicate]
    issues : Array[Issue]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Atlas::equal

    fn Atlas::equal(Atlas, Atlas) -> Bool

    Atlas::not_equal

    fn Atlas::not_equal(x : Atlas, y : Atlas) -> Bool

    Atlas::to_json

    fn Atlas::to_json(Atlas) -> Json

    Atlas::to_repr

    BatchClassification

    pub(all) struct BatchClassification {
    preferences : Array[Preference]
    issues : Array[Issue]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    BatchClassification::equal

    BatchClassification::not_equal

    BatchClassification::to_json

    Change

    pub(all) struct Change {
    kind : String
    path : String
    previous_path : String
    detail : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Change::equal

    fn Change::equal(Change, Change) -> Bool

    Change::not_equal

    fn Change::not_equal(x : Change, y : Change) -> Bool

    Change::to_json

    fn Change::to_json(Change) -> Json

    Change::to_repr

    Concept

    pub(all) struct Concept {
    id : String
    label : String
    sources : Array[Evidence]
    paths : Array[String]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Concept::equal

    fn Concept::equal(Concept, Concept) -> Bool

    Concept::not_equal

    fn Concept::not_equal(x : Concept, y : Concept) -> Bool

    Concept::to_json

    fn Concept::to_json(Concept) -> Json

    Concept::to_repr

    Document

    pub(all) struct Document {
    path : String
    content : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Input content is always literal text; this library performs no I/O.

    Document::equal

    fn Document::equal(Document, Document) -> Bool

    Document::not_equal

    fn Document::not_equal(x : Document, y : Document) -> Bool

    Document::to_json

    fn Document::to_json(Document) -> Json

    Document::to_repr

    Duplicate

    pub(all) struct Duplicate {
    paths : Array[String]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Duplicate::equal

    fn Duplicate::equal(Duplicate, Duplicate) -> Bool

    Duplicate::not_equal

    fn Duplicate::not_equal(x : Duplicate, y : Duplicate) -> Bool

    Duplicate::to_json

    fn Duplicate::to_json(Duplicate) -> Json

    Evidence

    pub(all) struct Evidence {
    path : String
    start_line : Int
    end_line : Int
    quote : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Evidence::equal

    fn Evidence::equal(Evidence, Evidence) -> Bool

    Evidence::not_equal

    fn Evidence::not_equal(x : Evidence, y : Evidence) -> Bool

    Evidence::to_json

    fn Evidence::to_json(Evidence) -> Json

    Evidence::to_repr

    HealthCheck

    pub(all) struct HealthCheck {
    id : String
    kind : String
    paths : Array[String]
    line : Int
    target : String
    status : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    HealthCheck::equal

    fn HealthCheck::equal(HealthCheck, HealthCheck) -> Bool

    HealthCheck::not_equal

    fn HealthCheck::not_equal(x : HealthCheck, y : HealthCheck) -> Bool

    HealthCheck::to_json

    fn HealthCheck::to_json(HealthCheck) -> Json

    Impact

    pub(all) struct Impact {
    changed_path : String
    previous_path : String
    kind : String
    source_path : String
    line : Int
    snapshot : String
    target : String
    status : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Impact::equal

    fn Impact::equal(Impact, Impact) -> Bool

    Impact::not_equal

    fn Impact::not_equal(x : Impact, y : Impact) -> Bool

    Impact::to_json

    fn Impact::to_json(Impact) -> Json

    Impact::to_repr

    ImpactReport

    pub(all) struct ImpactReport {
    impacts : Array[Impact]
    issues : Array[Issue]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    ImpactReport::equal

    ImpactReport::not_equal

    fn ImpactReport::not_equal(x : ImpactReport, y : ImpactReport) -> Bool

    ImpactReport::to_json

    Issue

    pub(all) struct Issue {
    code : String
    path : String
    message : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Issue::equal

    fn Issue::equal(Issue, Issue) -> Bool

    Issue::not_equal

    fn Issue::not_equal(x : Issue, y : Issue) -> Bool

    Issue::to_json

    fn Issue::to_json(Issue) -> Json

    Issue::to_repr

    LineDiff

    pub(all) struct LineDiff {
    edits : Array[LineEdit]
    precise : Bool
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    LineDiff::equal

    fn LineDiff::equal(LineDiff, LineDiff) -> Bool

    LineDiff::not_equal

    fn LineDiff::not_equal(x : LineDiff, y : LineDiff) -> Bool

    LineDiff::to_json

    fn LineDiff::to_json(LineDiff) -> Json

    LineDiff::to_repr

    LineEdit

    pub(all) struct LineEdit {
    before_start : Int
    before_count : Int
    after_start : Int
    after_count : Int
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    LineEdit::equal

    fn LineEdit::equal(LineEdit, LineEdit) -> Bool

    LineEdit::not_equal

    fn LineEdit::not_equal(x : LineEdit, y : LineEdit) -> Bool

    LineEdit::to_json

    fn LineEdit::to_json(LineEdit) -> Json

    LineEdit::to_repr

    pub(all) struct Link {
    from : String
    target : String
    line : Int
    status : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Link::equal

    fn Link::equal(Link, Link) -> Bool

    Link::not_equal

    fn Link::not_equal(x : Link, y : Link) -> Bool

    Link::to_json

    fn Link::to_json(Link) -> Json

    Link::to_repr

    Page

    pub(all) struct Page {
    id : String
    path : String
    title : String
    topic : String
    assignment : String
    sections : Array[Section]
    tags : Array[String]
    excerpt : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Page::equal

    fn Page::equal(Page, Page) -> Bool

    Page::not_equal

    fn Page::not_equal(x : Page, y : Page) -> Bool

    Page::to_json

    fn Page::to_json(Page) -> Json

    Page::to_repr

    Preference

    pub(all) struct Preference {
    path : String
    topic : String
    title : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Persisted user choices are keyed by document path, independent of line numbers.

    Preference::equal

    fn Preference::equal(Preference, Preference) -> Bool

    Preference::not_equal

    fn Preference::not_equal(x : Preference, y : Preference) -> Bool

    Preference::to_json

    fn Preference::to_json(Preference) -> Json

    SearchHit

    pub(all) struct SearchHit {
    path : String
    line : Int
    snippet : String
    kind : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    SearchHit::equal

    fn SearchHit::equal(SearchHit, SearchHit) -> Bool

    SearchHit::not_equal

    fn SearchHit::not_equal(x : SearchHit, y : SearchHit) -> Bool

    SearchHit::to_json

    fn SearchHit::to_json(SearchHit) -> Json

    SearchResult

    pub(all) struct SearchResult {
    hits : Array[SearchHit]
    total : Int
    issues : Array[Issue]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    SearchResult::equal

    SearchResult::not_equal

    fn SearchResult::not_equal(x : SearchResult, y : SearchResult) -> Bool

    SearchResult::to_json

    Section

    pub(all) struct Section {
    id : String
    title : String
    level : Int
    evidence : Evidence
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Section::equal

    fn Section::equal(Section, Section) -> Bool

    Section::not_equal

    fn Section::not_equal(x : Section, y : Section) -> Bool

    Section::to_json

    fn Section::to_json(Section) -> Json

    Section::to_repr

    SectionChange

    pub(all) struct SectionChange {
    id : String
    title : String
    kind : String
    before : Evidence?
    after : Evidence?
    before_text : String
    after_text : String
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    A literal section comparison; renamed titles are added/removed, not guessed.

    SectionChange::equal

    SectionChange::not_equal

    fn SectionChange::not_equal(x : SectionChange, y : SectionChange) -> Bool

    SectionChange::to_json

    SectionComparison

    pub(all) struct SectionComparison {
    changes : Array[SectionChange]
    issues : Array[Issue]
    notes : Array[String]
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    SectionComparison::equal

    SectionComparison::not_equal

    fn SectionComparison::not_equal(x : SectionComparison, y : SectionComparison) -> Bool

    SectionComparison::to_json

    build_atlas

    fn build_atlas(documents : Array[Document], preferences : Array[Preference], aliases : Array[Alias]) -> Atlas

    Construct a source-backed graph. Repeated headings indicate lexical evidence, not a claim that the sources express semantically equivalent concepts.

    classify_documents

    fn classify_documents(documents : Array[Document], preferences : Array[Preference], paths : Array[String], topic : String) -> BatchClassification

    One atomic classification result; preserve titles and reject stale selections.

    compare_documents

    fn compare_documents(before : Array[Document], after : Array[Document]) -> Array[Change]

    Compare literal document snapshots. Rename requires a unique exact-content match.

    compare_sections

    fn compare_sections(before : Document, after : Document) -> SectionComparison

    Compare complete section text, never the clipped evidence excerpt. Identical content moving to different line numbers is not an edit.

    diff_lines

    fn diff_lines(before : String, after : String) -> LineDiff

    Exact line equality; large changed regions use one explicit bounded fallback.

    export_index

    fn export_index(atlas : Atlas) -> String

    Portable inventory with original paths and line numbers; no guessed heading anchors.

    export_svg

    fn export_svg(atlas : Atlas, title : String) -> String

    Deterministic topic/document overview. Detailed headings remain in HTML/Markdown exports.

    health_checks

    fn health_checks(atlas : Atlas) -> Array[HealthCheck]

    Findings are structural clues in the imported dataset, never a quality score.

    normalize_name

    fn normalize_name(name : String) -> String

    parse_document

    fn parse_document(doc : Document, preferences : Array[Preference]) -> Page

    Supported metadata is deliberately limited to topic: and tags: [a, b].

    reconcile_preferences

    fn reconcile_preferences(before : Array[Document], after : Array[Document], preferences : Array[Preference]) -> Array[Preference]

    Carry manual choices across only unambiguous content-identical renames.

    search_documents

    fn search_documents(documents : Array[Document], preferences : Array[Preference], query : String) -> SearchResult

    Case-insensitive literal search; line zero denotes metadata rather than body evidence.

    update_impacts

    fn update_impacts(before : Array[Document], after : Array[Document]) -> ImpactReport

    Direct incoming references only; both snapshots retain their own evidence.

    validate_documents

    fn validate_documents(documents : Array[Document]) -> Array[Issue]