create-windy 0.2.28 → 0.2.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +385 -23
- package/package.json +1 -1
- package/template/.windy-template.json +2 -2
- package/template/README.md +3 -0
- package/template/apps/server/src/work-order/drizzle-repository.ts +1 -2
- package/template/apps/web/src/router/router-test-fixtures.ts +32 -0
- package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +36 -4
- package/template/docs/architecture/migration-bundle.md +151 -0
- package/template/module-schema.lineages.json +8 -0
- package/template/packages/database/drizzle/0033_rich_george_stacy.sql +2 -0
- package/template/packages/database/drizzle/meta/0033_snapshot.json +7239 -0
- package/template/packages/database/drizzle/meta/_journal.json +7 -0
- package/template/packages/database/src/schema/index.ts +0 -3
- package/template/packages/example-work-order/drizzle/0000_bitter_the_santerians.sql +2 -0
- package/template/packages/example-work-order/drizzle/meta/0000_snapshot.json +179 -0
- package/template/packages/example-work-order/drizzle/meta/_journal.json +13 -0
- package/template/packages/example-work-order/drizzle.config.ts +8 -0
- package/template/packages/example-work-order/index.ts +1 -0
- package/template/packages/example-work-order/package.json +4 -0
- package/template/packages/{database/src/schema/work-orders.ts → example-work-order/src/drizzle-schema.ts} +16 -2
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Migration Bundle 契约
|
|
2
|
+
|
|
3
|
+
更新时间:2026-07-25。
|
|
4
|
+
|
|
5
|
+
## 目标与边界
|
|
6
|
+
|
|
7
|
+
Migration Bundle 是模块级的数据库迁移机制:业务模块把迁移作为代码资产自持,
|
|
8
|
+
平台在启动时对所有声明了 Bundle 的模块做 fail-fast 结构校验,并由
|
|
9
|
+
`PlatformMigrationRunner` 在监听端口前按拓扑顺序应用、记录历史。
|
|
10
|
+
|
|
11
|
+
它与 drizzle 规范迁移是两条互补链路:
|
|
12
|
+
|
|
13
|
+
- 平台核心表结构走 drizzle 迁移,由 `bun run db:migrate` CLI 应用,Manifest 只在
|
|
14
|
+
`schemaExports.migrationIds` 保留历史 migration 证据;
|
|
15
|
+
- 业务模块的可插拔表结构走 Migration Bundle,随模块安装、校验和执行,不要求平台
|
|
16
|
+
核心预知业务表。
|
|
17
|
+
|
|
18
|
+
## 字段契约
|
|
19
|
+
|
|
20
|
+
`MigrationBundle`(`packages/shared/src/migration.ts`):
|
|
21
|
+
|
|
22
|
+
- `formatVersion`:固定为 `"1"`,其它版本直接拒绝;
|
|
23
|
+
- `module` / `version`:必须与 Manifest 的 `name` / `version` 完全一致,
|
|
24
|
+
`version` 必须是 semver(`x.y.z`,允许预发布后缀);
|
|
25
|
+
- `dependencies`:可选,声明本模块依赖的其它模块,必须与 Manifest `dependencies`
|
|
26
|
+
一致;被依赖模块当前也必须携带 Bundle,否则按“依赖未安装模块”拒绝;
|
|
27
|
+
- `migrations`:`BundleMigrationDefinition` 列表,字段为 `id`、`module`、
|
|
28
|
+
`description`、`direction`、`checksum`、可选 `dependsOn` 和 `run(context)`。
|
|
29
|
+
|
|
30
|
+
## checksum 规则
|
|
31
|
+
|
|
32
|
+
- Bundle 内每条 Migration 必须携带非空 `checksum`(推荐 `sha256:<hex>`),
|
|
33
|
+
缺失即启动校验失败;
|
|
34
|
+
- `PlatformMigrationRunner` 持久化已应用 Migration 的 checksum,重启后发现
|
|
35
|
+
checksum 漂移或模块归属变更会拒绝执行,防止已发布迁移被静默改写;
|
|
36
|
+
- 未声明 Bundle 的旧式 Manifest 由 `normalizeManifestBundles()` 合成
|
|
37
|
+
`legacy:<module>:<version>:<id>` 形式的 checksum,仅用于 runner 兼容路径,
|
|
38
|
+
不参与启动校验。
|
|
39
|
+
|
|
40
|
+
## 校验与排序
|
|
41
|
+
|
|
42
|
+
校验函数已下沉到 `packages/shared/src/migration-bundle-validation.ts`
|
|
43
|
+
(不依赖 drizzle),`@southwind-ai/database` 的 runner 与
|
|
44
|
+
`@southwind-ai/modules` 的启动校验共用同一份实现:
|
|
45
|
+
|
|
46
|
+
- `normalizeManifestBundles(modules)`:校验 Bundle 与 Manifest 的
|
|
47
|
+
module/version/dependencies/migrations 完全一致,且 `schemaExports` 只引用
|
|
48
|
+
Bundle 中存在的 Migration;
|
|
49
|
+
- `validateAndOrderBundles(bundles)`:拒绝格式版本不支持、非 semver 版本、
|
|
50
|
+
重复模块、重复 Migration ID、未声明的跨模块 `dependsOn`,并对模块依赖做
|
|
51
|
+
拓扑排序,循环依赖即抛错;
|
|
52
|
+
- `orderBundleMigrations(bundle)`:按 `dependsOn` 对单个 Bundle 内的
|
|
53
|
+
Migration 排序,循环即抛错。
|
|
54
|
+
|
|
55
|
+
## 启动校验行为
|
|
56
|
+
|
|
57
|
+
`validateCapabilityCatalogs()`(`packages/modules/src/catalog-validation.ts`)
|
|
58
|
+
在 Server 启动装配 Foundation 快照时调用 `validateMigrationBundleCatalog()`:
|
|
59
|
+
|
|
60
|
+
1. 只校验显式声明了 `migrationBundle` 的模块;无 Bundle 的旧式模块(含全部
|
|
61
|
+
平台系统模块)保持兼容,不受 `normalizeManifestBundles` 的一致性约束;
|
|
62
|
+
2. 对筛选出的 Bundle 依次执行一致性与拓扑校验,任一失败即抛错,启动中止;
|
|
63
|
+
3. 校验不做任何数据库 I/O,纯粹是结构检查;全部 Manifest 校验通过后,Server
|
|
64
|
+
才把显式 Bundle 交给 PostgreSQL runner。
|
|
65
|
+
|
|
66
|
+
示例:`packages/example-work-order` 的 `workOrderMigrationBundle` 是唯一随
|
|
67
|
+
仓库分发的业务 Bundle,其合法性由启动链路的回归测试覆盖。
|
|
68
|
+
|
|
69
|
+
## PostgreSQL 启动执行
|
|
70
|
+
|
|
71
|
+
配置 `DATABASE_URL` 后,Server 使用 `createServerPersistence()` 已建立的同一个
|
|
72
|
+
PostgreSQL Pool,在 Module Host、Repository、Worker 和 HTTP listener 初始化前执行:
|
|
73
|
+
|
|
74
|
+
1. 获取单一 PostgreSQL client,并开启 transaction;
|
|
75
|
+
2. 获取 `windy.module-migrations.v1` 的 transaction advisory lock,使多个 Server
|
|
76
|
+
实例并发启动时只有一个实例计算和应用计划;
|
|
77
|
+
3. 读取 `windy_migration_history`,再次校验已应用 migration 的 module 与 checksum;
|
|
78
|
+
4. 在同一 transaction 内按 Bundle 拓扑和 migration 依赖顺序执行 statement,并写入
|
|
79
|
+
`id`、`module`、`module_version`、`migration_order`、`checksum` 和完成时间;
|
|
80
|
+
5. 全部成功后 commit;任一 statement 或 history 写入失败则 rollback,并以非零启动
|
|
81
|
+
结果拒绝 Server 监听端口。
|
|
82
|
+
|
|
83
|
+
相同 checksum 的已应用 migration 会跳过。已发布 migration 的 checksum 漂移会在任何
|
|
84
|
+
新 statement 执行前拒绝启动。无 `DATABASE_URL` 的开发内存模式仍执行纯结构校验,但
|
|
85
|
+
不会假装已经持久化业务 schema。
|
|
86
|
+
|
|
87
|
+
平台 Drizzle journal 只负责平台 schema,包括为 history 增加治理字段的
|
|
88
|
+
`0032_marvelous_pepper_potts`;project-owned Module 只维护自己的 Bundle,不得修改
|
|
89
|
+
`.windy` 标记为 Windy-managed 的 journal、SQL 或 snapshot。
|
|
90
|
+
|
|
91
|
+
## Module-owned Drizzle authoring lineage
|
|
92
|
+
|
|
93
|
+
Migration Bundle 是唯一生产 DDL 执行者;Drizzle lineage 只帮助模块作者从 TypeScript
|
|
94
|
+
schema 生成、审阅下一条 Bundle migration。两者不能同时执行同一份 SQL。
|
|
95
|
+
|
|
96
|
+
Drizzle Kit 的 `generate` 命令没有按表排除的选项;`tablesFilter` 和 `schemaFilter`
|
|
97
|
+
只适用于 `push` / `pull`。因此业务 schema 不能继续作为平台 `drizzle.config.ts` 的
|
|
98
|
+
输入。每个业务 Module 应维护独立、project-owned 的 snapshot lineage:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"schemaVersion": 1,
|
|
103
|
+
"platform": {
|
|
104
|
+
"schema": ["packages/database/src/schema/index.ts"],
|
|
105
|
+
"out": "packages/database/drizzle"
|
|
106
|
+
},
|
|
107
|
+
"modules": [
|
|
108
|
+
{
|
|
109
|
+
"key": "customer.records",
|
|
110
|
+
"schema": ["apps/server/src/domain-schema/records.ts"],
|
|
111
|
+
"out": "module-schema/customer-records",
|
|
112
|
+
"legacyPlatformTables": ["records", "record_attachments"]
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
配置文件 `module-schema.lineages.json` 与 `module-schema/` 均为 project-owned。平台包
|
|
119
|
+
不导出业务 schema,Server 直接从项目自己的 `domain-schema` 或业务 package 引用。
|
|
120
|
+
|
|
121
|
+
### 从被污染的平台 snapshot 交接
|
|
122
|
+
|
|
123
|
+
已经把业务表写入平台 snapshot 的项目必须在改表名前执行一次受控交接:
|
|
124
|
+
|
|
125
|
+
1. 先升级 Windy,使根 `drizzle.config.ts` 只读取平台 schema;
|
|
126
|
+
2. 把业务 schema 移到 project-owned 路径,但保持当前表名不变;
|
|
127
|
+
3. 在 `module-schema.lineages.json` 声明全部 Module schema、独立输出目录,以及仍存在于
|
|
128
|
+
平台 snapshot 的旧表;
|
|
129
|
+
4. 保持 Git 工作区干净,运行
|
|
130
|
+
`bun run windy schema handoff --config module-schema.lineages.json`;
|
|
131
|
+
5. 审阅并提交平台的新 snapshot/journal、metadata-only SQL,以及每个 Module 的
|
|
132
|
+
baseline snapshot/journal。
|
|
133
|
+
|
|
134
|
+
交接命令在临时目录真实运行 Drizzle:
|
|
135
|
+
|
|
136
|
+
- Module baseline 从当前业务 schema 生成,生成 SQL 被替换为不执行 DDL 的
|
|
137
|
+
metadata-only marker;
|
|
138
|
+
- 平台 lineage 从历史 snapshot 生成下一版差异,只允许出现配置中声明表的
|
|
139
|
+
`DROP TABLE`;出现其它 DDL、未知表或漏表立即中止;
|
|
140
|
+
- 校验通过后才整体替换 lineage 目录,失败不触碰原目录;
|
|
141
|
+
- marker 只前移 Drizzle 的 authoring metadata,绝不删除生产表。生产数据库仍由
|
|
142
|
+
Migration Bundle 的 checksum/history/transaction 链路迁移。
|
|
143
|
+
|
|
144
|
+
交接完成后,模块作者可在自己的 lineage 上运行 `drizzle-kit generate`,审阅 rename
|
|
145
|
+
SQL,再把等价、方言兼容的前向变更实现到新的 Bundle migration。Module lineage 中的
|
|
146
|
+
SQL 永远不得交给 `db:migrate` 或生产启动链执行。
|
|
147
|
+
|
|
148
|
+
官方 Drizzle 参考:
|
|
149
|
+
|
|
150
|
+
- [配置文件](https://orm.drizzle.team/docs/drizzle-config-file)
|
|
151
|
+
- [`drizzle-kit generate`](https://orm.drizzle.team/docs/drizzle-kit-generate)
|