RFC-31 Phase 1: 核心模型 + Store CRUD + GC 适配 #32

Closed
opened 2026-05-30 10:11:50 +00:00 by scottwei · 0 comments
Owner

Parent: #31

目标

重构 Variable 模型:去掉 ULID id 和 scope,用 (name, schema) 复合主键。

设计决策(来自 #31 评审)

  1. Name 格式[a-zA-Z0-9._-] 每段,/ 做分隔符,禁空段(a//b)、首尾 /
  2. PK = (name, schema):同名不同 schema 是不同变量
  3. PUT 语义set() 方法 — 存在则更新 value + updated,不存在则创建
  4. get 无 schema 时:单 schema 直接返回,多 schema 返回数组
  5. 保留 created/updated
  6. 不可 rename

变更范围

1. variable.ts — 类型重构

// 删除
export type VariableId = string;

// 修改
export type Variable = {
  name: string;          // qualified name, e.g. "workflow/config/agent"
  schema: Hash;          // schema hash, part of PK
  value: Hash;           // CAS node hash
  created: number;       // epoch ms
  updated: number;       // epoch ms
  tags: Record<string, string>;
  labels: string[];
};

2. variable-store.ts — DB schema + CRUD 重写

DB schema:

CREATE TABLE variables (
  name TEXT NOT NULL,
  schema TEXT NOT NULL,
  value TEXT NOT NULL,
  created INTEGER NOT NULL,
  updated INTEGER NOT NULL,
  PRIMARY KEY (name, schema)
);
CREATE INDEX idx_var_name_prefix ON variables(name);

Tags/Labels 表外键改为 (var_name, var_schema)

CREATE TABLE variable_tags (
  var_name TEXT NOT NULL,
  var_schema TEXT NOT NULL,
  key TEXT NOT NULL,
  value TEXT NOT NULL,
  PRIMARY KEY (var_name, var_schema, key),
  FOREIGN KEY (var_name, var_schema) REFERENCES variables(name, schema) ON DELETE CASCADE
);

CREATE TABLE variable_labels (
  var_name TEXT NOT NULL,
  var_schema TEXT NOT NULL,
  label TEXT NOT NULL,
  PRIMARY KEY (var_name, var_schema, label),
  FOREIGN KEY (var_name, var_schema) REFERENCES variables(name, schema) ON DELETE CASCADE
);

API 方法签名:

set(name: string, value: Hash, options?: { tags?, labels? }): Variable;  // upsert
get(name: string, schema?: Hash): Variable | Variable[] | null;          // 单/多 schema
remove(name: string, schema?: Hash): Variable[];                         // 删除
list(prefix?: string, options?: { tags?, labels? }): Variable[];         // 前缀查询
tag(name: string, schema: Hash, ops: TagOps): Variable;                  // tag 操作

Name 校验函数:

  • 每段匹配 [a-zA-Z0-9._-]+
  • / 分隔
  • 禁止空段、首尾 /

错误类型更新:

  • VariableNotFoundError(name, schema?) 替代 VariableNotFoundError(id)
  • InvalidScopeErrorInvalidNameError
  • 其他保持不变

3. gc.ts — 适配

gc() 只依赖 varStore.list() 返回的 .value,结构兼容,改动极小。确认 .value 字段仍然存在即可。

4. 测试全部重写

  • variable-store.test.ts — 所有用 id 的地方改用 (name, schema)
  • gc.test.ts — 创建变量方式从 create(scope, value) 改为 set(name, value)

验证步骤

  • bun test 全部通过
  • bun run build 构建成功
  • bun run check lint 通过
  • set() upsert 语义:首次创建,再次更新 value
  • get(name) 单 schema 返回 Variable,多 schema 返回 Variable[]
  • get(name, schema) 精确匹配
  • remove(name) 删除所有 schema 变体
  • remove(name, schema) 精确删除
  • list(prefix) 前缀匹配
  • name 校验拒绝非法格式
  • GC 仍然正常工作
  • schema mismatch 错误(set 时 value 的 schema 与已有不同 schema 变体共存,不报错)

Ref: #31

— 小橘 🍊(NEKO Team)

Parent: #31 ## 目标 重构 Variable 模型:去掉 ULID id 和 scope,用 `(name, schema)` 复合主键。 ## 设计决策(来自 #31 评审) 1. **Name 格式**:`[a-zA-Z0-9._-]` 每段,`/` 做分隔符,禁空段(`a//b`)、首尾 `/` 2. **PK = (name, schema)**:同名不同 schema 是不同变量 3. **PUT 语义**:`set()` 方法 — 存在则更新 value + updated,不存在则创建 4. **get 无 schema 时**:单 schema 直接返回,多 schema 返回数组 5. **保留 created/updated** 6. **不可 rename** ## 变更范围 ### 1. `variable.ts` — 类型重构 ```typescript // 删除 export type VariableId = string; // 修改 export type Variable = { name: string; // qualified name, e.g. "workflow/config/agent" schema: Hash; // schema hash, part of PK value: Hash; // CAS node hash created: number; // epoch ms updated: number; // epoch ms tags: Record<string, string>; labels: string[]; }; ``` ### 2. `variable-store.ts` — DB schema + CRUD 重写 DB schema: ```sql CREATE TABLE variables ( name TEXT NOT NULL, schema TEXT NOT NULL, value TEXT NOT NULL, created INTEGER NOT NULL, updated INTEGER NOT NULL, PRIMARY KEY (name, schema) ); CREATE INDEX idx_var_name_prefix ON variables(name); ``` Tags/Labels 表外键改为 `(var_name, var_schema)`: ```sql CREATE TABLE variable_tags ( var_name TEXT NOT NULL, var_schema TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, PRIMARY KEY (var_name, var_schema, key), FOREIGN KEY (var_name, var_schema) REFERENCES variables(name, schema) ON DELETE CASCADE ); CREATE TABLE variable_labels ( var_name TEXT NOT NULL, var_schema TEXT NOT NULL, label TEXT NOT NULL, PRIMARY KEY (var_name, var_schema, label), FOREIGN KEY (var_name, var_schema) REFERENCES variables(name, schema) ON DELETE CASCADE ); ``` API 方法签名: ```typescript set(name: string, value: Hash, options?: { tags?, labels? }): Variable; // upsert get(name: string, schema?: Hash): Variable | Variable[] | null; // 单/多 schema remove(name: string, schema?: Hash): Variable[]; // 删除 list(prefix?: string, options?: { tags?, labels? }): Variable[]; // 前缀查询 tag(name: string, schema: Hash, ops: TagOps): Variable; // tag 操作 ``` Name 校验函数: - 每段匹配 `[a-zA-Z0-9._-]+` - `/` 分隔 - 禁止空段、首尾 `/` 错误类型更新: - `VariableNotFoundError(name, schema?)` 替代 `VariableNotFoundError(id)` - `InvalidScopeError` → `InvalidNameError` - 其他保持不变 ### 3. `gc.ts` — 适配 `gc()` 只依赖 `varStore.list()` 返回的 `.value`,结构兼容,改动极小。确认 `.value` 字段仍然存在即可。 ### 4. 测试全部重写 - `variable-store.test.ts` — 所有用 id 的地方改用 (name, schema) - `gc.test.ts` — 创建变量方式从 `create(scope, value)` 改为 `set(name, value)` ## 验证步骤 - [ ] `bun test` 全部通过 - [ ] `bun run build` 构建成功 - [ ] `bun run check` lint 通过 - [ ] set() upsert 语义:首次创建,再次更新 value - [ ] get(name) 单 schema 返回 Variable,多 schema 返回 Variable[] - [ ] get(name, schema) 精确匹配 - [ ] remove(name) 删除所有 schema 变体 - [ ] remove(name, schema) 精确删除 - [ ] list(prefix) 前缀匹配 - [ ] name 校验拒绝非法格式 - [ ] GC 仍然正常工作 - [ ] schema mismatch 错误(set 时 value 的 schema 与已有不同 schema 变体共存,不报错) Ref: #31 — 小橘 🍊(NEKO Team)
This repo is archived. You cannot comment on issues.
No Label
1 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/json-cas#32