静态工作区API定义技能Skill static-workspace-api

静态工作区API是一种基于TypeScript的API,用于定义表格和键值存储的类型安全模式,支持版本化和迁移功能,以管理数据模式的演进和一致性。适用于软件开发中的数据层设计、模式定义和迁移场景。关键词:静态工作区API、表格定义、KV存储、版本迁移、TypeScript、数据模式、软件开发、后端开发、架构设计。

架构设计 0 次安装 0 次浏览 更新于 3/20/2026

name: 静态工作区-api description: 静态工作区API模式,用于defineTable、defineKv、版本化和迁移。在定义工作区模式、为现有表格/KV存储添加版本或编写迁移函数时使用。 metadata: author: epicenter version: ‘2.0’

静态工作区API

用于表格和KV存储的类型安全模式定义,支持版本化迁移。

何时应用此技能

  • 使用defineTable()defineKv()定义新表格或KV存储
  • 为现有定义添加新版本
  • 编写迁移函数
  • 从简写模式转换为构建器模式

表格

简写模式(单版本)

当表格只有一个版本时使用:

import { defineTable } from 'epicenter/static';
import { type } from 'arktype';

const users = defineTable(type({ id: 'string', email: 'string', _v: '1' }));

每个表格模式必须包含_v和数字字面量。类型系统强制执行此要求 — 将不包含_v的模式传递给defineTable()会导致编译错误。

构建器模式(多版本)

当您需要随时间演进模式时使用:

const posts = defineTable()
	.version(type({ id: 'string', title: 'string', _v: '1' }))
	.version(type({ id: 'string', title: 'string', views: 'number', _v: '2' }))
	.migrate((row) => {
		switch (row._v) {
			case 1:
				return { ...row, views: 0, _v: 2 };
			case 2:
				return row;
		}
	});

KV存储

KV存储灵活 — _v是可选的。两种模式都有效:

不带_v(字段存在性)

import { defineKv } from 'epicenter/static';

const sidebar = defineKv(type({ collapsed: 'boolean', width: 'number' }));

// 多版本,使用字段存在性
const theme = defineKv()
	.version(type({ mode: "'light' | 'dark'" }))
	.version(type({ mode: "'light' | 'dark' | 'system'", fontSize: 'number' }))
	.migrate((v) => {
		if (!('fontSize' in v)) return { ...v, fontSize: 14 };
		return v;
	});

_v(显式判别)

const theme = defineKv()
	.version(type({ mode: "'light' | 'dark'", _v: '1' }))
	.version(
		type({ mode: "'light' | 'dark' | 'system'", fontSize: 'number', _v: '2' }),
	)
	.migrate((v) => {
		switch (v._v) {
			case 1:
				return { ...v, fontSize: 14, _v: 2 };
			case 2:
				return v;
		}
	});

_v约定

  • _v是一个数字判别字段('1'在arktype中表示字面数字1
  • 对表格必填 — 通过类型级别CombinedStandardSchema<{ id: string; _v: number }>强制执行
  • 对KV存储可选 — KV保持完全灵活性
  • 在arktype模式中:_v: '1'_v: '2'_v: '3'(数字字面量)
  • 在迁移返回中:_v: 2(TypeScript自动缩小类型,as const不必要)
  • 约定:_v放在对象的最后({ id, ...fields, _v: '1' }

迁移函数规则

  1. 输入类型是所有版本输出的并集
  2. 返回类型是最新版本输出
  3. 使用switch (row._v)进行判别(表格总是有_v
  4. 最后一个case直接返回row(已是最新版本)
  5. 始终直接迁移到最新版本(不逐步通过每个版本)

反模式

逐步迁移(v1 -> v2 -> v3)

// 错误:通过每个版本链式迁移
.migrate((row) => {
  let current = row;
  if (current._v === 1) current = { ...current, views: 0, _v: 2 };
  if (current._v === 2) current = { ...current, tags: [], _v: 3 };
  return current;
})

// 正确:直接迁移到最新版本
.migrate((row) => {
  switch (row._v) {
    case 1: return { ...row, views: 0, tags: [], _v: 3 };
    case 2: return { ...row, tags: [], _v: 3 };
    case 3: return row;
  }
})

注意:as const不必要

TypeScript基于返回类型约束上下文性地缩小_v: 2到字面类型。这两种都有效:

return { ...row, views: 0, _v: 2 }; // 有效 — 上下文缩小
return { ...row, views: 0, _v: 2 as const }; // 也有效 — 冗余

参考

  • packages/epicenter/src/static/define-table.ts
  • packages/epicenter/src/static/define-kv.ts
  • packages/epicenter/src/static/index.ts
  • packages/epicenter/src/static/create-tables.ts
  • packages/epicenter/src/static/create-kv.ts