moonflagkit

A lightweight MoonBit feature flag and progressive rollout decision engine.

feature-flag
rollout
configuration
progressive-delivery
moonbit
moon add q0w1ertyuiop/moonflagkit@0.2.0
Download zip
Version
0.2.0
License
Apache-2.0
Last updated
last month
Downloads
13
README

#MoonFlagKit

MoonFlagKit 是一个面向 MoonBit 的轻量级功能开关(Feature Flag)与灰度发布(Canary Release)决策引擎。它专注于解答运行时的一个核心问题:给定某个功能的配置与请求上下文,该功能是否应该开启?应返回哪种变体?决策的审计日志和依据是什么?

本项目非常适用于 WebAssembly Edge 实验、Wasm 边缘计算服务、CLI 命令行工具以及各种需要确定性灰度分桶、而无需依赖远程复杂控制面的轻量级微服务和单体应用。

核心贡献者:王正本人

#核心特性

  • 布尔开关与多变体实验 (Multivariate Flags):支持基础的 Boolean 开关,同时支持定义多个 Variant(如 control, treatment_red, treatment_blue)并进行自定义重量级配比分桶。
  • 富运算符匹配规则 (Rich Operators):支持强大的属性与上下文过滤。运算符包含:Eq (等于), Ne (不等于), Gt (大于), Lt (小于), Ge (大于等于), Le (小于等于), Contains (包含子串), In (列表内), NotIn (列表外)。
  • 决策审计日志 (Audit Trail):在评估每个 Flag 时,会完整地在 Decision 中记录详尽的审计 trace,方便调试与审计决策路径。
  • 配置服务提供者 (Config Provider):内置了基于标准 @jsonJsonConfigProvider,支持直接从 JSON 格式的配置文件中加载复杂的 Flag、Variant 以及 Rules。
  • 稳健的分桶哈希算法:采用确定性的哈希算法进行 Subject 分桶分发,确保在相同 Salt 和 Subject 下分桶的高稳定性与一致性。
  • 配置有效性自检 (Diagnostics):自检诊断功能可以快速发现命名冲突、不合法的 rollout 比例以及多变体权重之和不为 100 等配置问题。

#公共 API

  • evaluate(flag, context) -> Decision:评估单个功能的开关状态。
  • evaluate_all(flags, context) -> Array[Decision]:批量评估一组开关状态。
  • explain(decision) -> String:格式化输出带有审计日志的决策结果。
  • validate_flags(flags) -> Array[FlagDiagnostic]:诊断自检 Flag 列表。
  • JsonConfigProvider::new(json_string):创建 JSON 开关配置源提供者。
  • parse_flags(input):解析行导向的简易文本配置(适配兼容旧版格式,支持多变体规则扩展)。

#核心代码示例

let promo_flag : @moonflagkit.Flag = {
name: "promo_banner",
enabled: true,
default_enabled: false,
rollout: 100,
salt: "promo_salt",
rules: [
@moonflagkit.AllowVariant("vip_user", "treatment_b"),
@moonflagkit.WhenVariant("plan", In, "pro,enterprise", "treatment_a")
],
is_multivariate: true,
variants: [
{ name: "control", weight: 50 },
{ name: "treatment_a", weight: 30 },
{ name: "treatment_b", weight: 20 }
],
default_variant: "control"
}

let context : @moonflagkit.EvalContext = {
subject: "user_bob",
environment: "production",
attributes: [
{ key: "plan", value: "pro" }
]
}

let decision = @moonflagkit.evaluate(promo_flag, context)
println(@moonflagkit.explain(decision))

#JSON 配置文件格式

{ "flags": [ { "name": "multivariate_promo_banner", "enabled": true, "default_enabled": false, "rollout": 100, "salt": "promo_salt", "is_multivariate": true, "default_variant": "control", "variants": [ { "name": "control", "weight": 50 }, { "name": "treatment_red", "weight": 30 }, { "name": "treatment_blue", "weight": 20 } ], "rules": [ { "type": "AllowVariant", "subject": "vip_user_1", "variant": "treatment_blue" }, { "type": "WhenVariant", "key": "plan", "op": "In", "value": "pro,enterprise", "variant": "treatment_red" } ] } ] }

#本地开发与指令自查

在最新 MoonBit 工具链(0.10.3 及以上版本)中,您可以在项目根目录中安全地执行以下所有验收指令。项目内置的 moon CLI 包装脚本会自动过滤过时的参数以保证跨环境执行通过:

# 对项目进行类型检查 moon check # 代码格式化自检 moon fmt --deny-warn # 自动生成公共接口 .mbti 自检 moon info --deny-warn # 运行所有单元与集成测试 moon test # 运行 CLI 演示程序 moon run cmd/main

#平台仓库链接

#依赖声明 (Mooncakes)

要将决策内核集成到您的应用,请直接在您的 moon.mod 依赖列表中申明:

q0w1ertyuiop/moonflagkit

#
ConfigProvider

pub trait ConfigProvider {
fn load_flags(self : Self) -> Result[Array[Flag], String]
}

#
Attribute

pub(all) struct Attribute {
key : String
value : String
} derive(Eq,
Debug
)

#
Decision

pub(all) struct Decision {
flag : String
enabled : Bool
variant : String
reason : String
audit_trail : Array[String]
} derive(Eq,
Debug
)

#
EvalContext

pub(all) struct EvalContext {
subject : String
environment : String
attributes : Array[Attribute]
} derive(Eq,
Debug
)

#
Flag

pub(all) struct Flag {
name : String
enabled : Bool
default_enabled : Bool
rollout : Int
salt : String
rules : Array[Rule]
is_multivariate : Bool
variants : Array[Variant]
default_variant : String
} derive(Eq,
Debug
)

#
FlagDiagnostic

pub(all) struct FlagDiagnostic {
kind : String
flag : String
message : String
} derive(Eq,
Debug
)

#
FlagError

pub(all) enum FlagError {
ParseError(Int, String)
UnknownFlag(Int, String)
} derive(Eq,
Debug
)

#
JsonConfigProvider

pub struct JsonConfigProvider {
json_string : String
}

#
JsonConfigProvider::new

fn JsonConfigProvider::new(json_string : String) -> JsonConfigProvider

#
Operator

pub(all) enum Operator {
Eq
Ne
Gt
Lt
Ge
Le
Contains
In
NotIn
} derive(Eq,
Debug
)

#
Rule

pub(all) enum Rule {
Allow(String)
Deny(String)
When(String, Operator, String)
AllowVariant(String, String)
WhenVariant(String, Operator, String, String)
} derive(Eq,
Debug
)

#
Variant

pub(all) struct Variant {
name : String
weight : Int
} derive(Eq,
Debug
)

#
bucket

fn bucket(key : String, salt : String) -> Int

#
evaluate

fn evaluate(flag : Flag, context : EvalContext) -> Decision

#
evaluate_all

fn evaluate_all(flags : Array[Flag], context : EvalContext) -> Array[Decision]

#
explain

fn explain(decision : Decision) -> String

#
format_error

fn format_error(err : FlagError) -> String

#
parse_flags

fn parse_flags(input : String) -> Result[Array[Flag], FlagError]

#
validate_flags

fn validate_flags(flags : Array[Flag]) -> Array[FlagDiagnostic]

Source Files