@caverar/ts-实体
用于TypeScript的类型安全域实体,具有schema验证,明确状态过渡,和序列化.
安装
npm install @caverar/ts-entity zod
类型脚本 ^6.0.3 是同级依赖。 软件包使用 标准Schema 界面,因此您可以修改另一个兼容的验证库,而不是Zod.
快开始
import * as z from 'zod'
import {
EntityFactory,
type EntityMetaStatusVariants,
type EntityFlatSnapshot,
type EntitySelf,
} from '@caverar/ts-entity'
const factory = new EntityFactory({
dateFormat: 'Date',
id: {
idSchema: z.uuid(),
idGenerator: () => crypto.randomUUID(),
},
fieldsSchemasGenerator: (schemas) =>
Object.assign({}, ...(schemas as any).map((schema: z.ZodObject) => schema.shape)),
mergeSchemaGenerator: (schemas) => z.union(schemas as any),
})
const UserBase = factory.createAbstractEntity({
draft: {
schema: z.object({
email: z.email(),
name: z.string().min(1),
}),
mutatesTo: ['draft', 'active'],
},
active: {
schema: z.object({
email: z.email(),
name: z.string().min(1),
activatedAt: z.date(),
}),
mutatesTo: [],
},
})
class User extends UserBase {
activate(this: EntitySelf<User, 'draft'>) {
return this.mutateEntity({ activatedAt: new Date() }, 'active')
}
}
const result = User.createEntity({ email: '[email protected]', name: 'Ada' }, 'draft')
if (!result.isOk) {
throw new Error('Invalid user data')
}
const user = result.value
console.log(user.id, user.data.email)
// Use EntityMetaStatusVariants for a value whose current domain state varies.
// `metaStatus` narrows the union to its state-specific data and methods.
function inspectUser(entity: EntityMetaStatusVariants<User>) {
if (entity.metaStatus === 'draft') {
console.log(entity.data.email)
} else {
console.log(entity.data.activatedAt)
}
}
inspectUser(user)
// A snapshot is also assignable to the merged shape accepted by restoreEntity().
const flatSnapshot: EntityFlatSnapshot<User> = user.toSnapshot()
const activation = user.activate()
if (activation.isOk) {
console.log(activation.value.metaStatus) // 'active'
}
toSnapshot() 保存当前状态的形状并返回 a { meta, data } 工会
歧视 metaStatus它接受 { target: 'json' } 将日期等值序列化
以及JSON存储的硬币 见 选择输出 何时使用
相对于其他系列化方法.
选择输出
实体揭露出三个集团的API. 选谁消耗结果.
运行时间序列化(情报方法)
| 方法 | 消费者 | 形状 | version | 品牌 |
|---|---|---|---|---|
toSnapshot() | 简单寄存器; 反向 restoreEntity() | { meta, data }歧视 metaStatus | 包含 | 删除 |
toPersistenceChanges() | 只写已改变内容的仓库 | { persistenceStatus, meta, set, unset } | 包含 | 删除 |
toDTO() | 使用大小写输入/输出、其他限定上下文、切入点 | 平面对象:元和一级数据 | 从未曝光 | 删除; keepBrands: true 保留它们(仅类型) |
三个都接受 { target: 'json' }。 。 。 . toDTO() 还接受 omit (任何密钥,但 metaStatus, (中文(简体) ),
并且不会泄露实体本身.
建筑和重建(静态方法)
| 方法 | 目的 |
|---|---|
createEntity(data, metaStatus, meta?) | 开始一个新的生命周期( E)persistenceStatus: 'new', (中文(简体) ), version: 1(中文(简体) ) |
restoreEntity({ meta, data }) | 重建一个 unchanged 储存快照的基线; data 使用合并形状 |
类型助手
| 类型 | 说明 |
|---|---|
EntitySnapshot<E, Target, States> | 返回类型 toSnapshot() |
EntityFlatSnapshot<E, Target, States> | 与每个州合并的快照( 特定州字段可选); 形状 restoreEntity() 投入和数据库预测 |
EntityPersistenceChanges<...> | 返回类型 toPersistenceChanges() |
EntityDTO<E, States, Omitted, Target> | 返回类型 toDTO(),无品牌 metaStatus |
EntityBrandedDTO<...> | 与 EntityDTO保留品牌toDTO({ keepBrands: true })(中文(简体) ) |
EntityRawDTO<...> | EntityDTO 平整成一个跨州对象,用于手动 DTO/类型构造 |
EntityPickDTO<E, States, Keys> | 选取平面 DTO 的密钥, 用于大小写输入 |
type AccountResponse = EntityDTO<Account, 'active' | 'frozen', 'createdAt', 'json'>
const response: AccountResponse = account.toDTO({ target: 'json', omit: ['createdAt'] })
持久性和乐观的货币
实体曝光 persistenceStatus (单位:千美元)new, (中文(简体) ), updated,或 unchanged和数字 version。 。 。 .
他们让一个寄存器一起选择一个插入,一个乐观的更新,或者没有写入. 这些价值
描述内存持久性生命周期;它们本身不执行数据库操作.
创建新实体
User.createEntity(data, state) 验证初始数据并返回新实体
persistenceStatus: 'new' 财务报告和审定财务报表 version: 1在第一个插入之前的进一步突变
new 在第一版中坚持 INSERT,包括版本1;不将其视为更新.
只有创造不能拯救它.
对于不属于单一数据库的虚拟实体,通过可选元数据作为第三个
参数 : User.createEntity(data, state, { id, createdAt, updatedAt })。每个密钥都是可选的.
一个不见了 id 生成; 提供 id 对id schema(无效的 ids) 进行验证
返回时 INVALID_DATA) (中文(简体) ). 失踪日期默认为现在,以及 updatedAt 默认为 createdAt。 。 。 .
改变一个实体
mutateEntity() 被域方法使用并返回一个新的不可变实体; 源实例
不变。 成功突变后:
- A 类
new实体留下来new在版本1中,无论插入前变异多少次. - 第一个变种a
unchanged实体标出结果updated并递增 一个版本. - 仍在持续期间的进一步突变
updated保留这个版本。 这样可以多次运行 更改构成一个编辑周期和一个数据库更新. - 计划验证失败返回
INVALID_DATA;不产生变异实体.
例如,如果加载实体为第4版,其第一次突变产生第5版. 再来 该周期的突变仍为第五版. 对于乐观的更新,请使用上一个版本作为 比较条件和实体的版本作为写入的值:
UPDATE users
SET name = :name, version = :newVersion
WHERE id = :id AND version = :expectedVersion;
对于一个实体,第5版,设置 newVersion = 5 财务报告和审定财务报表 expectedVersion = 4 (相当于,
newVersion = expectedVersion + 1) (中文(简体) ).
将影响零行的更新作为货币冲突处理: 另一位写入器已更改或
删除了行。 不要用静默替换实体的版本来标记实体清理或重试; 重新装入
最新的快照,并按照应用程序的规则解决冲突。 一个实体
persistenceStatus: 'unchanged' 可被仓库跳过.
描述持久性变化
toPersistenceChanges() 返回包含当前元数据的数据库不可知描述符,并
存储器可能选择写入的数据。 它不执行查询或要求寄存器到
使用特定的持久性战略:
new数字 :set包含所有当前实体数据,作为插入的值.updated数字 :set包含有最新值的已触动字段;unset包含已删除的字段 从当前数据中,例如当状态过渡去除特定状态的字段时.unchanged: 两者set财务报告和审定财务报表unset为空.
触摸过的田地累积 restoreEntity()。如果一个字段更改,然后返回,则
保留在 set 它的最新价值。 仓库可以使用此描述符来避免写入
没有修改的字段,或者忽略它,而是坚持一个完整的快照。 库没有映射
用于数据库列或决定数据库如何代表未设置的字段的实体字段。 更改为
按顶级实体字段报告; 提供嵌入对象作为其顶级值
字段。 和快照一样,方法接受 { target: 'json' } 以序列化日期和大小
基于JSON的存储.
就乐观货币而言,储存库可包括: meta.version 更新条件
将这个版本与修改一起写入。 例如,当描述符有版本 5,a
更新可以要求存储版本为4并设定为5. 无法覆盖一行
被另一个编剧所更改,但每个编剧都遵循相同的版本规则. 零行码
更新是一个冲突,而不是成功的保存.
持久性后恢复
User.restoreEntity(snapshot) 是静态的,重建一个实体 从持续的快照。 这个
校验 id, 正安全整数版本, 以及针对 secretma 所选择的 chema 的数据
meta.metaStatus编辑 metaStatus 当数据库查询不返回时,可省略字段;
在这种情况下,按照申报顺序和第一次匹配的状态计划核对数据
假设状态。 如果状态计划重叠, 此命令决定了推断状态, 所以使用
在省略状态时,有特殊要求/歧视性领域。 何时 metaStatus 提供,
如果省略,它与可能的国家结合。 A 类
成功的结果 persistenceStatus: 'unchanged' 并保存快照的版本;
恢复本身既不能节省,也不能增加.
仅在插入/更新成功后才能调用,最好用数据库返回的行. 恢复实例是下一个编辑周期的基线。 保存第五版后恢复 存储行生成一个不变的版本-5实体;其下一次成功的突变开始版本 6. 如果由于版本冲突而写作失败,保留冲突结果,而不是恢复 尝试的快照仿佛一直存在.
恢复投入不是故意歧视的结合: data 使用交叉合并的形状
所有各州。 每个州所需的字段在编译时需要;州特有字段是
可选的普通数据库结果类型仍然可用。 运行时间验证检查完成
明确的计划 meta.metaStatus,或推断一个州 并验证该州
省略状态时的计划 。 无论是哪种情况,选定州的所需字段必须是
现相. 这个 metaStatus 类型仅限于实体已声明状态;无效的运行时输入
返回时 INVALID_DATA 而不是扔东西.
持久性状态没有序列化:由操作决定(createEntity 制造
new· ; restoreEntity 制造 unchanged) (中文(简体) ).
抓图包括 meta.version,但不会 persistenceStatus。 。 。 . toSnapshot() 保留
状态特定数据形状,可指定为 EntityFlatSnapshot,合并的形状
restoreEntity() 接受这个建议。 JSON- 目标快照将日期和大写转换为字符串; 将这些值转换回配置的本地
在将快照传递到 restoreEntity(). 支持明确本地与JSON
恢复模式,对日期、时间值和重金进行解码,仍然是计划中的改进.
上面的Schema callback使Zod对象的Schemas适应了工厂. 如果您的状态使用不同的策略或者使用不同的验证库,请为它的标准Schema兼容策略提供等同的回调.
出版
该套件被配置为公开npm发布. GitHub Actions在初始版本之后发布 v* 标签匹配 version 内 package.json 被推进。 工作流程运行类型检查、涂料、测试、构建和 publint 在使用 npm 出处发布前.
一次性设置
-
确认您的 npm 账户拥有
@caverar范围及范围@caverar/ts-entity已提供. -
第一版,发布版本
1.0.0从此取出。 npm 在您可以配置其信任的出版商之前需要软件包存在 :bun install --frozen-lockfile npm login npm publish --access public -
在 npmjs.com 上打开
@caverar/ts-entity→ 设置 → 信任的出版商选择 GitHub 动作,并设定:- 拥有者 :
caverar - 仓库 :
ts-entity - 工作流量 :
publish.yml - 环境:留空
- 拥有者 :
-
按下一个未发布版本的匹配标签( 例如, 更新软件包到
1.0.1,推动v1.0.1) (中文(简体) ). 别推v1.0.0发布后标记1.0.0手动;npm不允许发布同版本两次.
Npm 令牌或 GitHub 密语在信任的发布启用后不需要发布 。 npm在首次成功发布时验证了新的可信任的出版商配置,因此在创建配置后两天内就推出发行标记.