-
Notifications
You must be signed in to change notification settings - Fork 0
Closed
Description
背景
基于对 Airtable Interface Designer、Salesforce 2GP Package、Power Platform Solution、Retool/Appsmith 等平台的分析(参见 #823),我们确定:
发布粒度应该是 Package(解决方案),而不是单个元数据项。
为什么不是按单个元数据发布?
元数据之间有依赖关系:Interface → View → Object → Field → Flow。如果只发布了 Interface 但没发布它引用的新 Object 字段,终端用户看到的界面会报错。
Salesforce 和 Power Platform 的做法是:整个 Package/Solution 一起发布,要么全部上线,要么全部不上。
行业对标
| 平台 | 发布粒度 | 机制 |
|---|---|---|
| Salesforce 2GP | Package(整包) | sfdx force:package:version:create |
| Power Platform | Solution(整包) | Export Managed Solution → Import |
| Retool | 单个 App | Versioned release |
| Airtable | 单个 Interface | Publish button |
ObjectStack 定位企业级 → 采用 Salesforce/Power Platform 的 Package 级发布。
现有基础设施
已有的组件已经覆盖了 80%:
| 现有组件 | 作用 | 状态 |
|---|---|---|
MetadataRecordSchema (metadata-persistence.zod.ts) |
存储所有元数据,含 state: draft/active/archived |
✅ 已有 |
MetadataStateSchema |
生命周期状态枚举 | ✅ 已有 |
IMetadataService (metadata-service.ts) |
CRUD 合约 | ✅ 已有 |
MetadataManager |
实现类 | ✅ 已有 |
DeployBundleSchema (deploy-bundle.zod.ts) |
批量部署 | ✅ 已有 |
InstalledPackageSchema (package-registry.zod.ts) |
包安装状态 | ✅ 已有 |
package_id 字段 on sys_metadata |
元数据归属包 | ✅ 已有 |
需要做的事情
A. 协议层 (packages/spec)
A1. 扩展 MetadataRecordSchema — 加 3 个字段
// packages/spec/src/system/metadata-persistence.zod.ts
// 在现有 MetadataRecordSchema 上补充:
version: z.number().int().min(0).default(0)
.describe('Version counter, increments on each package publish'),
published_definition: z.unknown().optional()
.describe('Snapshot of the last published definition'),
published_at: z.string().datetime().optional()
.describe('When this metadata was last published'),
published_by: z.string().optional()
.describe('Who published this version'),A2. 扩展 IMetadataService 合约 — 加 3 个方法
// packages/spec/src/contracts/metadata-service.ts
/**
* Publish an entire package:
* 1. Validate all draft items (dependency check)
* 2. Snapshot all items in the package
* 3. Increment package version
* 4. Set all items state → active
*/
publishPackage(packageId: string, options?: {
changeNote?: string;
publishedBy?: string;
validate?: boolean;
}): Promise<PackagePublishResult>;
/**
* Revert entire package to last published state
*/
revertPackage(packageId: string): Promise<void>;
/**
* Get the published version of any metadata item (for runtime serving)
* Returns published_definition if exists, else current definition
*/
getPublished(type: string, name: string): Promise<unknown | undefined>;A3. 定义 PackagePublishResult 类型
// packages/spec/src/system/metadata-persistence.zod.ts 或新文件
export const PackagePublishResultSchema = z.object({
success: z.boolean(),
packageId: z.string(),
version: z.number().int(),
publishedAt: z.string().datetime(),
itemsPublished: z.number().int(),
validationErrors: z.array(z.object({
type: z.string(),
name: z.string(),
message: z.string(),
})).optional(),
});B. Runtime 层 (packages/metadata)
B1. MetadataManager 实现 publishPackage()
- 查询
package_id === packageId的所有元数据 - 依赖校验:检查新引用的 View/Object 是否在同一个包或已发布的包中
- Schema 校验:所有 draft 元数据格式合法
- 快照:
published_definition = clone(definition)for each item - 版本号递增
- 状态:所有
draft→active
B2. MetadataManager 实现 revertPackage()
- 查询包下所有元数据
definition = clone(published_definition)for each itemstate → active(丢弃未发布修改)
B3. MetadataManager 实现 getPublished()
- 返回
published_definitionif exists - Falls back to
definitionif never published
C. API 层 (packages/runtime)
C1. HttpDispatcher 加 publish 端点
POST /api/v1/packages/:packageId/publish → publishPackage()
POST /api/v1/packages/:packageId/revert → revertPackage()
GET /api/v1/metadata/:type/:name/published → getPublished()
D. 测试
-
publishPackage()单元测试:正常发布、依赖校验失败、空包 -
revertPackage()单元测试:正常回滚、从未发布过时报错 -
getPublished()单元测试:已发布返回快照、未发布返回 definition - 集成测试:编辑 → 发布 → 编辑 → 回滚 → 终端用户看到的版本正确
E. 文档 & ROADMAP
- 更新
packages/metadata/README.md加 Package Publish 章节 - 更新
ROADMAP.md - 更新
content/docs/guides/contracts/metadata-service.mdx加 publishPackage 文档
数据流
设计者日常编辑(自动保存到 draft)
├── 改了 Object "opportunity" 加了个字段
├── 改了 View "opp_list" 显示新字段
├── 改了 Interface "sales_dashboard" 布局
│
▼ 所有改动都在 draft,终端用户看不到
│
▼ [点击 Publish Package "crm"]
│
├── 1. 依赖校验:新 Interface 引用的 View/Object 都在这个包里吗?✓
├── 2. Schema 校验:所有 draft 元数据格式合法吗?✓
├── 3. 快照:crm 包下所有元数据 → published_definition = clone(definition)
├── 4. 版本号:crm package version++
├── 5. 状态:所有 draft → active
│
▼ 终端用户立刻看到所有变更,且保证一致性
工作量估计
| 改动 | 工作量 |
|---|---|
MetadataRecordSchema 加 4 个字段 |
1 天 |
IMetadataService 加 3 个方法 + 类型定义 |
1 天 |
MetadataManager 实现 3 个方法(含依赖校验) |
3 天 |
HttpDispatcher 加 3 个端点 |
1 天 |
| 测试 | 2 天 |
| 文档 | 1 天 |
| 总计 | ~1.5 周 |
关联
- 上游评估 Issue: Assessment: Airtable Interface Designer Pattern for ObjectUI #823
- 前端 UI 实现:
objectstack-ai/objectui(cross-repo) - 现有 Deploy Pipeline:
IDeployPipelineService— deploy = 初始化时批量 publish - 现有 Package Upgrade:
package-upgrade.zod.ts— upgrade = 外部包发布后 import
验收标准
-
pnpm test全部通过 - 协议变更都有
.describe(),published_*字段是.optional()保证兼容 -
MetadataManager.publishPackage()可正确快照+版本递增+状态转换 -
MetadataManager.getPublished()返回已发布快照,未发布返回当前 definition - REST 端点可用
- ROADMAP.md 更新
Reactions are currently unavailable