架构

mooncake_impact_factor 是一个更大应用中的 MoonBit 模块。MoonBit 包定义评分规则和少量 JavaScript 辅助函数;Python 构建数据;Next.js 应用提供服务,既可以基于 SQLite 动态运行,也可以作为静态文件发布。本指南沿着数据从注册表到浏览器的路径,指出每一步由哪个文件负责。

组件

组件路径语言职责
scoresrc/scoreMoonBit分数、等级和势头规则(API)。
clisrc/cliMoonBit (JS)为其他语言计算分数快照的命令(API)。
static_searchsrc/static_searchMoonBit (JS)静态模式的版本标记和文本规范化(API)。
索引构建器scripts/build_index.pyPython读取注册表,写入 SQLite 数据库。
静态导出器scripts/export_static_json.pyPython从数据库写出 public/data/**。
查询层lib/query.ts, lib/data.tsTypeScript查询 AST、表达式解析器、SQL 编译。
Web 应用app, frontend/srcTypeScript页面、路由处理器和静态搜索 worker。

从注册表到分数

scripts/build_index.py 每次运行都从零重建数据库。

  1. 读取注册表。 ~/.moon/registry/index/user 下每个 *.index 文件的每一行都是一个已发布的版本:名称、版本、创建时间、元数据和依赖。moon update 会刷新这份本地副本;排名恰好覆盖其中的包。

  2. 选出最新版本: 每个包先按创建时间、再按语义化版本选出最新版本。该版本的描述、关键词、仓库和许可证用于描述这个包。

  3. 收集下载量。 除非指定了 --skip-mooncakes-downloads,构建器会用八个线程向 https://mooncakes.io/api/v0/manifest/<package> 查询每个尚未在 data/download_cache.json 中的包,并把结果存入缓存。--downloads-json 可以覆盖单个包的计数。未知的计数为 0。

  4. 构建边。 对于每个依赖快照中另一个包的版本,构建器记录一条从依赖方指向被依赖包的包级边。自依赖会被忽略。first_seen_at 是依赖方中最早使用该依赖的版本的创建时间。

  5. 统计信号。 设 τ\tau 为构建时间,一个包的信号为

    信号定义
    dependents指向该包的边数。
    recent_dependentsfirst_seen_at ≥τ−180\ge \tau - 180 天的边。
    downloads收集到的下载量。
    days_since_release从最新发布到 τ\tau 的整天数;日期未知时为 3650。
    historical_dependentsfirst_seen_at ≤τ−30\le \tau - 30 天的边。
    historical_recent_dependentsfirst_seen_at 位于 [τ−210,τ−30][\tau - 210, \tau - 30] 天内的边。
    historical_downloads始终为 0;注册表没有下载历史。
    historical_days_since_release最新发布至少已有 30 天时为 days_since_release −30- 30,否则为 0。

    历史窗口就是把近期窗口向前平移 30 天,因此两个快照以同样的方式计算。

  6. 评分。 构建器对每个包用这八个信号运行 cli 命令,并把返回的快照存入 package_scores 表。因此评分规则只存在于 src/score 中。

  7. 索引文本。 一个 SQLite FTS5 表保存完整名称、所有者、包名、描述和关键词,用于全文搜索。

数据库表包括 packages、versions、dependencies、package_edges、package_scores 和 search_index。

服务

动态模式

设置 MOONCAKE_DB_PATH 后运行 npm run dev 或 npm run start,即可提供基于数据库的页面和 JSON 路由处理器:

路由返回
GET /api/feeds/top?limit=<n>按分数降序、再按名称排列的包。
GET /api/feeds/hot?limit=<n>按 30 天增长量、再按分数、再按名称排列的 Hot 包。
GET /api/feeds/rising?limit=<n>Rising 包,排序方式与 hot 相同。
GET /api/search?...{ "items": [...] },最多 100 条包摘要。
GET /api/packages/<owner>/<package>/analysis{ "detail": ..., "dependents": [...] }.

入门指南列出了搜索参数。ast 或 expr 参数中的查询会被编译成 SQL WHERE 子句;文本项使用 FTS5。

静态模式

npm run build:static-data 先运行索引构建器,再运行 scripts/export_static_json.py,后者把各个 feed、一个搜索索引、每个包一个文件以及一个清单写入 public/data。npm run build:static 编译 static_search,并以 NEXT_PUBLIC_APP_MODE=static 运行 next build,把站点导出到不含路由处理器的 out/。在浏览器中,feed 和包页面只是普通的文件请求,搜索则在 Web Worker 中运行;static_search 设计对此有详细说明。deploy-static 工作流每天重建并发布这个站点。

规则归属

每条规则都只有一个归属者,其他部分调用它:

规则归属
分数、等级和势头src/score(通过 src/cli 调用)
信号定义和时间窗口scripts/build_index.py
查询语言和 ASTlib/query.ts
SQL 编译和动态排序lib/data.ts
静态求值和排序frontend/src/static-search.worker.ts

scripts/build_index.py 中仍有与 MoonBit 规则对应的 Python 函数 compute_score 和 compute_momentum_label;构建过程不会调用它们。