create-windy 0.2.6 → 0.2.8

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.
Files changed (361) hide show
  1. package/README.md +14 -3
  2. package/dist/cli.js +191 -87
  3. package/package.json +1 -1
  4. package/template/.windy-template.json +2 -2
  5. package/template/README.md +8 -0
  6. package/template/apps/server/Dockerfile +2 -0
  7. package/template/apps/server/package.json +10 -8
  8. package/template/apps/server/src/access-guards.ts +1 -1
  9. package/template/apps/server/src/admin/routes.test.ts +10 -0
  10. package/template/apps/server/src/admin/routes.ts +4 -2
  11. package/template/apps/server/src/agent/routes.test.ts +1 -1
  12. package/template/apps/server/src/agent/routes.ts +2 -2
  13. package/template/apps/server/src/application-services.ts +3 -0
  14. package/template/apps/server/src/audit/drizzle-log-store.ts +2 -2
  15. package/template/apps/server/src/audit/policy.ts +1 -1
  16. package/template/apps/server/src/audit/recovery-queue.test.ts +1 -1
  17. package/template/apps/server/src/audit/recovery-queue.ts +1 -1
  18. package/template/apps/server/src/audit/redaction.ts +1 -1
  19. package/template/apps/server/src/audit/reliable-writer.test.ts +1 -1
  20. package/template/apps/server/src/audit/reliable-writer.ts +1 -1
  21. package/template/apps/server/src/audit/search-summary.ts +1 -1
  22. package/template/apps/server/src/auth/authentication-provider.test.ts +1 -1
  23. package/template/apps/server/src/auth/credential-service.ts +1 -1
  24. package/template/apps/server/src/auth/drizzle-repository.ts +2 -2
  25. package/template/apps/server/src/auth/drizzle-session-operations.ts +1 -1
  26. package/template/apps/server/src/auth/own-session-routes.test.ts +1 -1
  27. package/template/apps/server/src/auth/password.ts +2 -4
  28. package/template/apps/server/src/auth/repository.ts +1 -1
  29. package/template/apps/server/src/auth/routes.test.ts +1 -1
  30. package/template/apps/server/src/auth/service.ts +1 -1
  31. package/template/apps/server/src/bulk-data/artifact-store.ts +1 -1
  32. package/template/apps/server/src/bulk-data/dictionary-handler.ts +1 -1
  33. package/template/apps/server/src/bulk-data/drizzle-repository.ts +3 -3
  34. package/template/apps/server/src/bulk-data/repository.ts +1 -1
  35. package/template/apps/server/src/bulk-data/routes.test.ts +1 -1
  36. package/template/apps/server/src/bulk-data/routes.ts +1 -1
  37. package/template/apps/server/src/bulk-data/service.test.ts +1 -1
  38. package/template/apps/server/src/bulk-data/service.ts +1 -1
  39. package/template/apps/server/src/bulk-data/types.ts +1 -1
  40. package/template/apps/server/src/configuration/bootstrap.ts +2 -2
  41. package/template/apps/server/src/configuration/definition.ts +2 -2
  42. package/template/apps/server/src/configuration/drizzle-repository.ts +2 -2
  43. package/template/apps/server/src/configuration/repository.ts +1 -1
  44. package/template/apps/server/src/configuration/routes.ts +1 -1
  45. package/template/apps/server/src/configuration/service.ts +1 -1
  46. package/template/apps/server/src/data-access/context.ts +2 -2
  47. package/template/apps/server/src/data-access/query-specification.test.ts +1 -1
  48. package/template/apps/server/src/data-access/scoped-repository.integration.test.ts +1 -1
  49. package/template/apps/server/src/data-access/scoped-repository.ts +1 -1
  50. package/template/apps/server/src/data-access/scoped-resource.test.ts +1 -1
  51. package/template/apps/server/src/data-access/scoped-resource.ts +1 -1
  52. package/template/apps/server/src/data-governance/export/approval-port.ts +1 -1
  53. package/template/apps/server/src/data-governance/export/drizzle-repository.ts +1 -1
  54. package/template/apps/server/src/data-governance/export/job-runtime.test.ts +2 -2
  55. package/template/apps/server/src/data-governance/export/job-runtime.ts +2 -3
  56. package/template/apps/server/src/data-governance/export/postgres-api-fixture.ts +2 -2
  57. package/template/apps/server/src/data-governance/export/postgres-api.integration.test.ts +1 -1
  58. package/template/apps/server/src/data-governance/export/routes-test-fixture.ts +3 -3
  59. package/template/apps/server/src/data-governance/export/routes.ts +1 -1
  60. package/template/apps/server/src/data-governance/export/scope-snapshot.ts +1 -1
  61. package/template/apps/server/src/data-governance/export/service.ts +2 -2
  62. package/template/apps/server/src/data-governance/export/source.ts +1 -1
  63. package/template/apps/server/src/data-governance/export/types.ts +1 -1
  64. package/template/apps/server/src/data-governance/plaintext-access.test.ts +1 -1
  65. package/template/apps/server/src/data-governance/plaintext-access.ts +1 -1
  66. package/template/apps/server/src/data-governance/plaintext-postgres.integration.test.ts +1 -1
  67. package/template/apps/server/src/data-governance/user-plaintext-routes.ts +1 -1
  68. package/template/apps/server/src/data-governance/user-projection.ts +2 -2
  69. package/template/apps/server/src/example-modules.ts +25 -1
  70. package/template/apps/server/src/feature/manifest-feature-repository.test.ts +1 -1
  71. package/template/apps/server/src/feature/manifest-feature-repository.ts +1 -1
  72. package/template/apps/server/src/feature/routes.ts +3 -6
  73. package/template/apps/server/src/foundation-access.test.ts +1 -1
  74. package/template/apps/server/src/foundation-access.ts +1 -1
  75. package/template/apps/server/src/foundation.ts +21 -7
  76. package/template/apps/server/src/guards.test.ts +1 -1
  77. package/template/apps/server/src/guards.ts +3 -3
  78. package/template/apps/server/src/index.ts +27 -3
  79. package/template/{packages/modules → apps/server}/src/installed-business-modules.ts +2 -2
  80. package/template/apps/server/src/jobs/artifact-service.test.ts +1 -1
  81. package/template/apps/server/src/jobs/artifact-service.ts +2 -2
  82. package/template/apps/server/src/jobs/drizzle-artifact.ts +2 -2
  83. package/template/apps/server/src/jobs/drizzle-claim.ts +1 -1
  84. package/template/apps/server/src/jobs/drizzle-execution-repository.ts +2 -2
  85. package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +1 -1
  86. package/template/apps/server/src/jobs/drizzle-lease.ts +2 -2
  87. package/template/apps/server/src/jobs/drizzle-mappers.ts +3 -3
  88. package/template/apps/server/src/jobs/drizzle-recovery-repository.ts +1 -1
  89. package/template/apps/server/src/jobs/drizzle-repository.integration.test.ts +1 -1
  90. package/template/apps/server/src/jobs/drizzle-repository.ts +3 -3
  91. package/template/apps/server/src/jobs/execution-repository.ts +1 -1
  92. package/template/apps/server/src/jobs/execution.test.ts +2 -2
  93. package/template/apps/server/src/jobs/in-memory-claim.ts +1 -1
  94. package/template/apps/server/src/jobs/in-memory-execution.ts +1 -1
  95. package/template/apps/server/src/jobs/in-memory-lease.ts +1 -1
  96. package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +1 -1
  97. package/template/apps/server/src/jobs/legacy-adapter.test.ts +1 -1
  98. package/template/apps/server/src/jobs/legacy-adapter.ts +1 -1
  99. package/template/apps/server/src/jobs/legacy-service-runtime.ts +4 -2
  100. package/template/apps/server/src/jobs/repository.ts +2 -2
  101. package/template/apps/server/src/jobs/service.ts +2 -2
  102. package/template/apps/server/src/jobs/worker.ts +3 -2
  103. package/template/apps/server/src/license/legacy-license-resolver.ts +1 -1
  104. package/template/apps/server/src/license/license-resolver.test.ts +1 -1
  105. package/template/apps/server/src/license/license-resolver.ts +3 -3
  106. package/template/apps/server/src/license/routes-offline-code.test.ts +2 -2
  107. package/template/apps/server/src/license/routes.ts +1 -1
  108. package/template/apps/server/src/license/runtime-audit-reliability.test.ts +2 -2
  109. package/template/apps/server/src/license/runtime-reminder.test.ts +2 -2
  110. package/template/apps/server/src/license/runtime-service.test.ts +2 -2
  111. package/template/apps/server/src/license/runtime-service.ts +2 -2
  112. package/template/apps/server/src/module-composition.test.ts +1 -1
  113. package/template/apps/server/src/module-composition.ts +1 -1
  114. package/template/apps/server/src/module-host.test.ts +145 -0
  115. package/template/apps/server/src/module-host.ts +110 -0
  116. package/template/apps/server/src/notification/drizzle-repository.ts +2 -2
  117. package/template/apps/server/src/notification/repository.ts +1 -1
  118. package/template/apps/server/src/notification/routes.ts +1 -1
  119. package/template/apps/server/src/pagination.ts +1 -1
  120. package/template/apps/server/src/persistence.integration.test.ts +1 -1
  121. package/template/apps/server/src/persistence.ts +1 -1
  122. package/template/apps/server/src/profile/routes.ts +1 -1
  123. package/template/apps/server/src/route-guards.test.ts +1 -1
  124. package/template/apps/server/src/route-guards.ts +1 -1
  125. package/template/apps/server/src/runtime-feature.ts +125 -0
  126. package/template/apps/server/src/runtime-license.ts +1 -1
  127. package/template/apps/server/src/runtime.test.ts +48 -1
  128. package/template/apps/server/src/runtime.ts +17 -64
  129. package/template/apps/server/src/scheduler/audit.ts +1 -1
  130. package/template/apps/server/src/scheduler/definitions.test.ts +1 -1
  131. package/template/apps/server/src/scheduler/definitions.ts +2 -2
  132. package/template/apps/server/src/scheduler/drizzle-durable-links.ts +1 -1
  133. package/template/apps/server/src/scheduler/drizzle-mappers.ts +1 -1
  134. package/template/apps/server/src/scheduler/drizzle-repository.ts +1 -1
  135. package/template/apps/server/src/scheduler/execution-recorder.ts +1 -1
  136. package/template/apps/server/src/scheduler/history.ts +1 -1
  137. package/template/apps/server/src/scheduler/overview.ts +1 -1
  138. package/template/apps/server/src/scheduler/recovery-service.test.ts +1 -1
  139. package/template/apps/server/src/scheduler/recovery-service.ts +1 -1
  140. package/template/apps/server/src/scheduler/repository.ts +1 -1
  141. package/template/apps/server/src/scheduler/routes.ts +1 -1
  142. package/template/apps/server/src/scheduler/service-idle.test.ts +1 -1
  143. package/template/apps/server/src/scheduler/service.test.ts +1 -1
  144. package/template/apps/server/src/scheduler/service.ts +1 -1
  145. package/template/apps/server/src/scheduler/types.ts +1 -1
  146. package/template/apps/server/src/search/contracts.ts +2 -2
  147. package/template/apps/server/src/search/host.ts +1 -1
  148. package/template/apps/server/src/search/registry.ts +1 -1
  149. package/template/apps/server/src/search/routes.test.ts +1 -1
  150. package/template/apps/server/src/search/routes.ts +1 -1
  151. package/template/apps/server/src/search/service.ts +2 -2
  152. package/template/apps/server/src/search/test-support.ts +2 -2
  153. package/template/apps/server/src/session/routes.ts +1 -1
  154. package/template/apps/server/src/settings/drizzle-repository.ts +2 -2
  155. package/template/apps/server/src/settings/repository.ts +1 -1
  156. package/template/apps/server/src/settings/routes.test.ts +1 -1
  157. package/template/apps/server/src/settings/routes.ts +1 -1
  158. package/template/apps/server/src/settings/runtime.ts +1 -1
  159. package/template/apps/server/src/storage/blob-reference-registry.test.ts +1 -1
  160. package/template/apps/server/src/storage/blob-reference-registry.ts +1 -1
  161. package/template/apps/server/src/storage/drizzle-upload-repository.integration.test.ts +1 -1
  162. package/template/apps/server/src/storage/drizzle-upload-repository.ts +2 -2
  163. package/template/apps/server/src/storage/route-support.ts +1 -1
  164. package/template/apps/server/src/storage/routes.test.ts +1 -1
  165. package/template/apps/server/src/storage/routes.ts +2 -2
  166. package/template/apps/server/src/storage/runtime.ts +6 -4
  167. package/template/apps/server/src/system/audit-routes.ts +1 -1
  168. package/template/apps/server/src/system/department-routes.ts +1 -1
  169. package/template/apps/server/src/system/drizzle-repository.ts +1 -1
  170. package/template/apps/server/src/system/drizzle-scoped-repository.ts +1 -1
  171. package/template/apps/server/src/system/drizzle-system.ts +1 -1
  172. package/template/apps/server/src/system/entities.ts +1 -1
  173. package/template/apps/server/src/system/identity-route-config.ts +1 -1
  174. package/template/apps/server/src/system/interface-catalog.test.ts +3 -3
  175. package/template/apps/server/src/system/interface-catalog.ts +2 -2
  176. package/template/apps/server/src/system/menu-registry.ts +1 -1
  177. package/template/apps/server/src/system/menu-routes.ts +1 -1
  178. package/template/apps/server/src/system/operations.test.ts +1 -1
  179. package/template/apps/server/src/system/operations.ts +1 -1
  180. package/template/apps/server/src/system/redis-health.ts +1 -1
  181. package/template/apps/server/src/system/repository.ts +1 -1
  182. package/template/apps/server/src/system/resource-route-support.ts +1 -1
  183. package/template/apps/server/src/system/route-helpers.ts +1 -1
  184. package/template/apps/server/src/system/route-types.ts +1 -1
  185. package/template/apps/server/src/system/routes.test.ts +1 -1
  186. package/template/apps/server/src/system/routes.ts +1 -1
  187. package/template/apps/server/src/system/user-projection.ts +1 -1
  188. package/template/apps/server/src/work-order/data-scope.integration.test.ts +1 -1
  189. package/template/apps/server/src/work-order/drizzle-repository.integration.test.ts +1 -1
  190. package/template/apps/server/src/work-order/drizzle-repository.ts +2 -2
  191. package/template/apps/server/src/work-order/foundation-modules.test.ts +15 -5
  192. package/template/apps/server/src/work-order/repository.ts +1 -1
  193. package/template/apps/server/src/work-order/routes.test.ts +2 -2
  194. package/template/apps/server/src/work-order/routes.ts +2 -2
  195. package/template/apps/server/src/work-order/search-api.test.ts +3 -3
  196. package/template/apps/server/src/work-order/search-provider.test.ts +3 -3
  197. package/template/apps/server/src/work-order/service.ts +1 -1
  198. package/template/apps/server/src/work-order/task.test.ts +1 -1
  199. package/template/apps/server/src/work-order/task.ts +2 -2
  200. package/template/apps/server/src/workflow/drizzle-repository.integration.test.ts +2 -2
  201. package/template/apps/server/src/workflow/drizzle-repository.ts +3 -3
  202. package/template/apps/server/src/workflow/effects.test.ts +1 -1
  203. package/template/apps/server/src/workflow/effects.ts +1 -1
  204. package/template/apps/server/src/workflow/repository.ts +2 -2
  205. package/template/apps/server/src/workflow/routes.ts +3 -3
  206. package/template/apps/server/src/workflow/service.ts +1 -1
  207. package/template/apps/server/src/workflow/timeout-runtime.test.ts +1 -1
  208. package/template/apps/server/src/workflow/timeout-runtime.ts +2 -2
  209. package/template/apps/web/Dockerfile +2 -0
  210. package/template/apps/web/package.json +4 -4
  211. package/template/apps/web/runtime-server.ts +1 -1
  212. package/template/apps/web/src/app/layouts/AppLayout.vue +6 -0
  213. package/template/apps/web/src/components/auth/PasswordChangeForm.vue +52 -7
  214. package/template/apps/web/src/components/auth/PasswordChangeForm.webtest.ts +43 -0
  215. package/template/apps/web/src/components/common/DataPagination.vue +1 -1
  216. package/template/apps/web/src/components/license/LicenseActivationCodeForm.vue +1 -1
  217. package/template/apps/web/src/components/license/LicenseFlow.webtest.ts +2 -2
  218. package/template/apps/web/src/composables/useModuleConfigurations.ts +1 -1
  219. package/template/apps/web/src/composables/useWatermarkSettings.ts +1 -0
  220. package/template/apps/web/src/composables/watermark-settings.ts +6 -0
  221. package/template/apps/web/src/composables/watermark-settings.webtest.ts +27 -1
  222. package/template/apps/web/src/layout/AppSidebar.vue +12 -3
  223. package/template/apps/web/src/layout/AppSidebar.webtest.ts +11 -0
  224. package/template/apps/web/src/layout/GlobalWatermark.vue +25 -8
  225. package/template/apps/web/src/layout/GlobalWatermark.webtest.ts +109 -0
  226. package/template/apps/web/src/layout/NavigationSearchDialog.vue +1 -1
  227. package/template/apps/web/src/layout/NotificationMenu.vue +1 -1
  228. package/template/apps/web/src/layout/navigation.ts +5 -1
  229. package/template/apps/web/src/layout/navigation.webtest.ts +1 -1
  230. package/template/apps/web/src/pages/configuration/components/ModuleConfigRollbackDialog.vue +1 -1
  231. package/template/apps/web/src/pages/configuration/components/ModuleConfigVersionList.vue +1 -1
  232. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.vue +1 -1
  233. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.webtest.ts +1 -1
  234. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationWorkbench.vue +1 -1
  235. package/template/apps/web/src/pages/dashboard/dashboard-presenter.webtest.ts +1 -1
  236. package/template/apps/web/src/pages/settings/WatermarkSettingsSection.vue +21 -0
  237. package/template/apps/web/src/pages/system/SystemNotificationsPage.vue +2 -2
  238. package/template/apps/web/src/pages/system/components/AuditActionCell.vue +7 -2
  239. package/template/apps/web/src/pages/system/components/AuditLogFilters.vue +7 -2
  240. package/template/apps/web/src/pages/system/components/NotificationPublishForm.vue +1 -1
  241. package/template/apps/web/src/pages/system/components/SchedulerOperationsPanel.vue +1 -1
  242. package/template/apps/web/src/pages/system/components/SchedulerOperationsPanel.webtest.ts +1 -1
  243. package/template/apps/web/src/pages/system/components/SchedulerRunTable.vue +1 -1
  244. package/template/apps/web/src/pages/system/components/SchedulerRunTable.webtest.ts +1 -1
  245. package/template/apps/web/src/pages/system/components/SchedulerStaleRecoveryForm.vue +1 -1
  246. package/template/apps/web/src/pages/system/components/SchedulerStaleRecoveryForm.webtest.ts +1 -1
  247. package/template/apps/web/src/pages/system/components/SystemResourcePage.vue +1 -1
  248. package/template/apps/web/src/pages/system/components/SystemResourceTable.vue +10 -3
  249. package/template/apps/web/src/pages/system/components/role-policy/RolePermissionTree.vue +1 -1
  250. package/template/apps/web/src/pages/system/components/role-policy/RolePermissionTree.webtest.ts +1 -1
  251. package/template/apps/web/src/pages/system/components/role-policy/RolePolicyForm.vue +1 -1
  252. package/template/apps/web/src/pages/system/components/role-policy/permission-tree.ts +1 -1
  253. package/template/apps/web/src/pages/system/components/role-policy/permission-tree.webtest.ts +1 -1
  254. package/template/apps/web/src/pages/system/composables/useSchedulerOperations.ts +2 -2
  255. package/template/apps/web/src/pages/system/composables/useSchedulerOperations.webtest.ts +1 -1
  256. package/template/apps/web/src/pages/system/composables/useSystemResource.ts +1 -1
  257. package/template/apps/web/src/router/manifest-routes.ts +1 -1
  258. package/template/apps/web/src/router/manifest-routes.webtest.ts +1 -1
  259. package/template/apps/web/src/services/access-api.ts +10 -1
  260. package/template/apps/web/src/services/configuration-api.ts +1 -1
  261. package/template/apps/web/src/services/license-api.ts +1 -1
  262. package/template/apps/web/src/services/notification-api.ts +2 -2
  263. package/template/apps/web/src/services/operations-api.ts +1 -1
  264. package/template/apps/web/src/services/platform-settings-api.ts +2 -2
  265. package/template/apps/web/src/services/profile-api.ts +2 -2
  266. package/template/docs/architecture/durable-jobs.md +5 -0
  267. package/template/docs/architecture/module-manifest-composition.md +14 -7
  268. package/template/docs/architecture/object-storage.md +1 -1
  269. package/template/docs/development/crud-generator.md +4 -1
  270. package/template/docs/platform/audit-reliability.md +1 -1
  271. package/template/package.json +2 -1
  272. package/template/packages/config/package.json +15 -4
  273. package/template/packages/config/src/platform.ts +1 -1
  274. package/template/packages/crud-generator/package.json +1 -1
  275. package/template/packages/crud-generator/src/generator.test.ts +16 -0
  276. package/template/packages/crud-generator/src/templates/domain.ts +1 -1
  277. package/template/packages/crud-generator/src/templates/manifest.ts +2 -2
  278. package/template/packages/crud-generator/src/templates/project.ts +3 -3
  279. package/template/packages/crud-generator/src/templates/repository.ts +1 -1
  280. package/template/packages/crud-generator/src/templates/routes.ts +2 -2
  281. package/template/packages/crud-generator/src/validation.ts +22 -1
  282. package/template/packages/database/package.json +15 -5
  283. package/template/packages/database/src/bundle-validation.ts +218 -0
  284. package/template/packages/database/src/dialects.ts +1 -1
  285. package/template/packages/database/src/runner.test.ts +160 -13
  286. package/template/packages/database/src/runner.ts +95 -77
  287. package/template/packages/database/src/schema/governed-exports.ts +1 -1
  288. package/template/packages/database/src/schema/identity.ts +1 -1
  289. package/template/packages/database/src/schema/license-v1.ts +1 -1
  290. package/template/packages/database/src/schema/notifications.ts +1 -1
  291. package/template/packages/database/src/schema/platform-settings.ts +1 -1
  292. package/template/packages/database/src/schema/workflow.ts +1 -1
  293. package/template/packages/database/src/sql-store.ts +1 -1
  294. package/template/packages/database/src/store.ts +1 -1
  295. package/template/packages/example-work-order/index.ts +1 -0
  296. package/template/packages/example-work-order/package.json +4 -3
  297. package/template/packages/example-work-order/src/contract.ts +1 -1
  298. package/template/packages/example-work-order/src/manifest.ts +8 -3
  299. package/template/packages/example-work-order/src/migration-bundle.test.ts +43 -0
  300. package/template/packages/example-work-order/src/migration-bundle.ts +64 -0
  301. package/template/packages/jobs/index.ts +2 -0
  302. package/template/packages/jobs/package.json +29 -0
  303. package/template/packages/jobs/src/handlers.test.ts +72 -0
  304. package/template/{apps/server/src/jobs/handler-registry.ts → packages/jobs/src/handlers.ts} +4 -4
  305. package/template/{apps/server/src/jobs → packages/jobs/src}/types.ts +7 -1
  306. package/template/packages/{license-sdk → jobs}/tsconfig.json +0 -1
  307. package/template/packages/modules/index.ts +1 -1
  308. package/template/packages/modules/package.json +15 -5
  309. package/template/packages/modules/src/catalog-validation.ts +123 -0
  310. package/template/packages/modules/src/composition.test.ts +75 -2
  311. package/template/packages/modules/src/composition.ts +4 -2
  312. package/template/packages/modules/src/manifest.ts +7 -12
  313. package/template/packages/modules/src/menu-composition.ts +1 -1
  314. package/template/packages/modules/src/system-api-binding.ts +1 -1
  315. package/template/packages/modules/src/system-api-data-governance.ts +1 -1
  316. package/template/packages/modules/src/system-api-permissions.ts +1 -1
  317. package/template/packages/modules/src/system-data-governance.ts +1 -1
  318. package/template/packages/modules/src/system-features.ts +1 -1
  319. package/template/packages/modules/src/system-governed-export-bindings.ts +1 -1
  320. package/template/packages/modules/src/system-module-catalog.ts +1 -1
  321. package/template/packages/server-sdk/index.ts +5 -0
  322. package/template/packages/server-sdk/package.json +25 -0
  323. package/template/packages/server-sdk/src/module-host.ts +29 -0
  324. package/template/packages/server-sdk/tsconfig.json +16 -0
  325. package/template/packages/shared/index.ts +3 -0
  326. package/template/packages/shared/package.json +14 -3
  327. package/template/packages/shared/src/audit.ts +22 -1
  328. package/template/packages/shared/src/license-catalog.test.ts +17 -0
  329. package/template/packages/shared/src/license-catalog.ts +33 -0
  330. package/template/packages/shared/src/migration.ts +39 -0
  331. package/template/packages/shared/src/password.ts +4 -0
  332. package/template/packages/storage/package.json +14 -3
  333. package/template/packages/license-sdk/index.ts +0 -13
  334. package/template/packages/license-sdk/package.json +0 -18
  335. package/template/packages/license-sdk/src/bounded-json-body.test.ts +0 -65
  336. package/template/packages/license-sdk/src/bounded-json-body.ts +0 -135
  337. package/template/packages/license-sdk/src/canonical-json.test.ts +0 -28
  338. package/template/packages/license-sdk/src/canonical-json.ts +0 -104
  339. package/template/packages/license-sdk/src/client.test.ts +0 -82
  340. package/template/packages/license-sdk/src/client.ts +0 -86
  341. package/template/packages/license-sdk/src/device.ts +0 -46
  342. package/template/packages/license-sdk/src/file-storage.test.ts +0 -80
  343. package/template/packages/license-sdk/src/file-storage.ts +0 -77
  344. package/template/packages/license-sdk/src/installation.test.ts +0 -241
  345. package/template/packages/license-sdk/src/installation.ts +0 -150
  346. package/template/packages/license-sdk/src/license-parser.ts +0 -56
  347. package/template/packages/license-sdk/src/offline-code-base32.ts +0 -57
  348. package/template/packages/license-sdk/src/offline-code-decompression.ts +0 -55
  349. package/template/packages/license-sdk/src/offline-code-limits.ts +0 -10
  350. package/template/packages/license-sdk/src/offline-code-wire.ts +0 -72
  351. package/template/packages/license-sdk/src/offline-code.test.ts +0 -172
  352. package/template/packages/license-sdk/src/offline-code.ts +0 -319
  353. package/template/packages/license-sdk/src/payload.test.ts +0 -72
  354. package/template/packages/license-sdk/src/payload.ts +0 -201
  355. package/template/packages/license-sdk/src/storage.ts +0 -64
  356. package/template/packages/license-sdk/src/test-fixtures.ts +0 -59
  357. package/template/packages/license-sdk/src/v1-test-vector.ts +0 -49
  358. package/template/packages/license-sdk/src/validation.test.ts +0 -62
  359. package/template/packages/license-sdk/src/validation.ts +0 -143
  360. package/template/packages/license-sdk/src/verification.test.ts +0 -97
  361. package/template/packages/license-sdk/src/verification.ts +0 -199
@@ -0,0 +1,218 @@
1
+ import {
2
+ MIGRATION_BUNDLE_FORMAT_VERSION,
3
+ type BundleMigrationDefinition,
4
+ type MigrationBundle,
5
+ type MigrationDefinition,
6
+ type MigrationManifestSource,
7
+ } from "@southwind-ai/shared";
8
+
9
+ export function normalizeManifestBundles(
10
+ modules: readonly MigrationManifestSource[],
11
+ ): MigrationBundle[] {
12
+ return modules.map((manifest) => {
13
+ const bundle =
14
+ manifest.migrationBundle ||
15
+ legacyManifestBundle(manifest, manifest.migrations);
16
+ if (bundle.module !== manifest.name) {
17
+ throw new Error(
18
+ `Migration Bundle ${bundle.module} 与 Manifest ${manifest.name} 不一致`,
19
+ );
20
+ }
21
+ if (bundle.version !== manifest.version) {
22
+ throw new Error(
23
+ `Migration Bundle ${bundle.module} 版本 ${bundle.version} 与 Manifest ${manifest.version} 不一致`,
24
+ );
25
+ }
26
+ assertSameDependencies(manifest, bundle);
27
+ assertSameMigrations(manifest, bundle);
28
+ assertSchemaMigrationReferences(manifest, bundle);
29
+ return bundle;
30
+ });
31
+ }
32
+
33
+ function assertSameDependencies(
34
+ manifest: MigrationManifestSource,
35
+ bundle: MigrationBundle,
36
+ ): void {
37
+ const declared = manifest.dependencies || [];
38
+ const bundled = bundle.dependencies || [];
39
+ if (
40
+ declared.length !== bundled.length ||
41
+ declared.some((dependency) => !bundled.includes(dependency))
42
+ ) {
43
+ throw new Error(
44
+ `Manifest ${manifest.name} 的 dependencies 必须与 Migration Bundle 一致`,
45
+ );
46
+ }
47
+ }
48
+
49
+ function legacyManifestBundle(
50
+ manifest: MigrationManifestSource,
51
+ migrations: readonly MigrationDefinition[],
52
+ ): MigrationBundle {
53
+ return {
54
+ formatVersion: MIGRATION_BUNDLE_FORMAT_VERSION,
55
+ module: manifest.name,
56
+ version: manifest.version,
57
+ dependencies: manifest.dependencies,
58
+ migrations: migrations.map((migration) => ({
59
+ ...migration,
60
+ checksum:
61
+ migration.checksum ||
62
+ `legacy:${manifest.name}:${manifest.version}:${migration.id}`,
63
+ })),
64
+ };
65
+ }
66
+
67
+ function assertSameMigrations(
68
+ manifest: MigrationManifestSource,
69
+ bundle: MigrationBundle,
70
+ ): void {
71
+ const declared = manifest.migrations.map(({ id }) => id);
72
+ const bundled = bundle.migrations.map(({ id }) => id);
73
+ if (
74
+ declared.length !== bundled.length ||
75
+ declared.some((id, index) => id !== bundled[index])
76
+ ) {
77
+ throw new Error(
78
+ `Manifest ${manifest.name} 的 migrations 必须与 Migration Bundle 完全一致`,
79
+ );
80
+ }
81
+ }
82
+
83
+ function assertSchemaMigrationReferences(
84
+ manifest: MigrationManifestSource,
85
+ bundle: MigrationBundle,
86
+ ): void {
87
+ const ids = new Set(bundle.migrations.map(({ id }) => id));
88
+ for (const schema of manifest.schemaExports || []) {
89
+ for (const migrationId of schema.migrationIds) {
90
+ if (!ids.has(migrationId)) {
91
+ throw new Error(
92
+ `Schema Export ${schema.key} 引用了 Bundle 中不存在的 Migration:${migrationId}`,
93
+ );
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ export function validateAndOrderBundles(
100
+ bundles: readonly MigrationBundle[],
101
+ ): MigrationBundle[] {
102
+ const byModule = new Map<string, MigrationBundle>();
103
+ const migrationOwner = new Map<string, string>();
104
+ for (const bundle of bundles) {
105
+ validateBundle(bundle);
106
+ if (byModule.has(bundle.module)) {
107
+ throw new Error(`Migration Bundle 模块重复:${bundle.module}`);
108
+ }
109
+ byModule.set(bundle.module, bundle);
110
+ for (const migration of bundle.migrations) {
111
+ const owner = migrationOwner.get(migration.id);
112
+ if (owner) {
113
+ throw new Error(
114
+ `Migration ID 重复:${migration.id}(${owner} / ${bundle.module})`,
115
+ );
116
+ }
117
+ migrationOwner.set(migration.id, bundle.module);
118
+ }
119
+ }
120
+ for (const bundle of bundles) {
121
+ for (const dependency of bundle.dependencies || []) {
122
+ if (!byModule.has(dependency)) {
123
+ throw new Error(
124
+ `Migration Bundle ${bundle.module} 依赖未安装模块:${dependency}`,
125
+ );
126
+ }
127
+ }
128
+ validateMigrationDependencies(bundle, migrationOwner);
129
+ }
130
+ return topologicalBundles(byModule);
131
+ }
132
+
133
+ function validateBundle(bundle: MigrationBundle): void {
134
+ if (bundle.formatVersion !== MIGRATION_BUNDLE_FORMAT_VERSION) {
135
+ throw new Error(
136
+ `Migration Bundle ${bundle.module} 格式版本不受支持:${bundle.formatVersion}`,
137
+ );
138
+ }
139
+ if (!bundle.module.trim()) throw new Error("Migration Bundle 缺少 module");
140
+ if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(bundle.version)) {
141
+ throw new Error(
142
+ `Migration Bundle ${bundle.module} 版本无效:${bundle.version}`,
143
+ );
144
+ }
145
+ for (const migration of bundle.migrations) {
146
+ if (migration.module !== bundle.module) {
147
+ throw new Error(
148
+ `Migration ${migration.id} 的模块归属应为 ${bundle.module}`,
149
+ );
150
+ }
151
+ if (!migration.id.trim()) throw new Error("Migration ID 不能为空");
152
+ if (!migration.checksum.trim()) {
153
+ throw new Error(`Migration ${migration.id} 缺少 checksum`);
154
+ }
155
+ }
156
+ }
157
+
158
+ function validateMigrationDependencies(
159
+ bundle: MigrationBundle,
160
+ owners: ReadonlyMap<string, string>,
161
+ ): void {
162
+ for (const migration of bundle.migrations) {
163
+ for (const dependency of migration.dependsOn || []) {
164
+ const owner = owners.get(dependency);
165
+ if (!owner) {
166
+ throw new Error(`Migration ${migration.id} 依赖不存在:${dependency}`);
167
+ }
168
+ if (
169
+ owner !== bundle.module &&
170
+ !(bundle.dependencies || []).includes(owner)
171
+ ) {
172
+ throw new Error(
173
+ `Migration ${migration.id} 跨模块依赖 ${owner},但 Bundle 未声明该模块依赖`,
174
+ );
175
+ }
176
+ }
177
+ }
178
+ }
179
+
180
+ function topologicalBundles(
181
+ pendingSource: ReadonlyMap<string, MigrationBundle>,
182
+ ): MigrationBundle[] {
183
+ const pending = new Map(pendingSource);
184
+ const sorted: MigrationBundle[] = [];
185
+ while (pending.size > 0) {
186
+ const ready = Array.from(pending.values()).find((bundle) =>
187
+ (bundle.dependencies || []).every(
188
+ (dependency) => !pending.has(dependency),
189
+ ),
190
+ );
191
+ if (!ready) throw new Error("Migration Bundle 模块依赖存在循环");
192
+ sorted.push(ready);
193
+ pending.delete(ready.module);
194
+ }
195
+ return sorted;
196
+ }
197
+
198
+ export function orderBundleMigrations(
199
+ bundle: MigrationBundle,
200
+ ): BundleMigrationDefinition[] {
201
+ const pending = new Map(
202
+ bundle.migrations.map((migration) => [migration.id, migration]),
203
+ );
204
+ const sorted: BundleMigrationDefinition[] = [];
205
+ while (pending.size > 0) {
206
+ const ready = Array.from(pending.values()).find((migration) =>
207
+ (migration.dependsOn || []).every(
208
+ (dependency) => !pending.has(dependency),
209
+ ),
210
+ );
211
+ if (!ready) {
212
+ throw new Error(`Migration Bundle ${bundle.module} 的迁移依赖存在循环`);
213
+ }
214
+ sorted.push(ready);
215
+ pending.delete(ready.id);
216
+ }
217
+ return sorted;
218
+ }
@@ -5,7 +5,7 @@ import type {
5
5
  DialectCapabilities,
6
6
  PageRequest,
7
7
  PaginationSqlParts,
8
- } from "@windy/shared";
8
+ } from "@southwind-ai/shared";
9
9
 
10
10
  const reservedWords: Record<DatabaseDialect, string[]> = {
11
11
  postgresql: ["user", "role", "order"],
@@ -1,23 +1,20 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import type { ModuleManifest } from "@windy/modules";
2
+ import {
3
+ MIGRATION_BUNDLE_FORMAT_VERSION,
4
+ type MigrationBundle,
5
+ type MigrationManifestSource,
6
+ } from "@southwind-ai/shared";
3
7
  import { PlatformMigrationRunner } from "./runner.js";
4
8
  import { InMemoryMigrationHistoryStore } from "./store.js";
5
9
 
6
10
  function moduleFixture(
7
11
  name: string,
8
12
  dependencies: string[] = [],
9
- ): ModuleManifest {
13
+ ): MigrationManifestSource {
10
14
  return {
11
15
  name,
12
16
  version: "0.1.0",
13
- label: name,
14
- description: `${name} module`,
15
17
  dependencies,
16
- menus: [],
17
- adminRoutes: [],
18
- permissions: [],
19
- apiPermissions: [],
20
- features: [],
21
18
  migrations: [
22
19
  {
23
20
  id: `${name}.001`,
@@ -32,10 +29,30 @@ function moduleFixture(
32
29
  },
33
30
  ],
34
31
  schemaExports: [],
35
- tasks: [],
36
- tools: [],
37
- providers: [],
38
- auditActions: [],
32
+ };
33
+ }
34
+
35
+ function bundleFixture(
36
+ module: string,
37
+ ids: readonly string[] = [`${module}.001`],
38
+ dependencies: readonly string[] = [],
39
+ ): MigrationBundle {
40
+ return {
41
+ formatVersion: MIGRATION_BUNDLE_FORMAT_VERSION,
42
+ module,
43
+ version: "1.0.0",
44
+ dependencies,
45
+ migrations: ids.map((id, index) => ({
46
+ id,
47
+ module,
48
+ description: `migration ${index + 1}`,
49
+ direction: "up",
50
+ checksum: `sha256:${module}-${index + 1}`,
51
+ dependsOn: index === 0 ? [] : [ids[index - 1]],
52
+ async run(context) {
53
+ await context.execute({ sql: `select '${id}'` });
54
+ },
55
+ })),
39
56
  };
40
57
  }
41
58
 
@@ -93,4 +110,134 @@ describe("PlatformMigrationRunner", () => {
93
110
  expect(report.applied).toEqual(["audit.001"]);
94
111
  expect(executed).toEqual(["create table audit (id varchar(36))"]);
95
112
  });
113
+
114
+ test("fresh bundle 按模块与迁移依赖顺序执行并记录 checksum", async () => {
115
+ const store = new InMemoryMigrationHistoryStore();
116
+ const executed: string[] = [];
117
+ const runner = new PlatformMigrationRunner({
118
+ dialect: "postgresql",
119
+ store,
120
+ execute: async ({ sql }) => {
121
+ executed.push(sql);
122
+ },
123
+ });
124
+ const feature = bundleFixture(
125
+ "feature",
126
+ ["feature.002", "feature.001"],
127
+ ["system"],
128
+ );
129
+ feature.migrations[0].dependsOn = ["feature.001"];
130
+ feature.migrations[1].dependsOn = [];
131
+
132
+ const report = await runner.runBundles([feature, bundleFixture("system")]);
133
+
134
+ expect(report.applied).toEqual([
135
+ "system.001",
136
+ "feature.001",
137
+ "feature.002",
138
+ ]);
139
+ expect(executed).toEqual([
140
+ "select 'system.001'",
141
+ "select 'feature.001'",
142
+ "select 'feature.002'",
143
+ ]);
144
+ expect((await store.listApplied())[0]?.checksum).toBe("sha256:system-1");
145
+ });
146
+
147
+ test("repeat bundle 跳过 checksum 相同的已应用迁移", async () => {
148
+ const store = new InMemoryMigrationHistoryStore();
149
+ const runner = new PlatformMigrationRunner({
150
+ dialect: "postgresql",
151
+ store,
152
+ });
153
+ const bundle = bundleFixture("orders");
154
+
155
+ await runner.runBundles([bundle]);
156
+ const repeated = await runner.runBundles([bundle]);
157
+
158
+ expect(repeated.applied).toEqual([]);
159
+ expect(repeated.planned[0]).toMatchObject({
160
+ id: "orders.001",
161
+ checksum: "sha256:orders-1",
162
+ skipped: true,
163
+ skipReason: "already-applied",
164
+ });
165
+ });
166
+
167
+ test("执行失败后停止后续 migration", async () => {
168
+ const executed: string[] = [];
169
+ const bundle = bundleFixture("orders", [
170
+ "orders.001",
171
+ "orders.002",
172
+ "orders.003",
173
+ ]);
174
+ const runner = new PlatformMigrationRunner({
175
+ dialect: "postgresql",
176
+ store: new InMemoryMigrationHistoryStore(),
177
+ execute: async ({ sql }) => {
178
+ executed.push(sql);
179
+ if (sql.includes("002")) throw new Error("database unavailable");
180
+ },
181
+ });
182
+
183
+ const report = await runner.runBundles([bundle]);
184
+
185
+ expect(report.applied).toEqual(["orders.001"]);
186
+ expect(report.failed).toEqual([
187
+ { id: "orders.002", error: "database unavailable" },
188
+ ]);
189
+ expect(executed).toEqual(["select 'orders.001'", "select 'orders.002'"]);
190
+ });
191
+
192
+ test("拒绝错误模块归属和重复 migration id", async () => {
193
+ const runner = new PlatformMigrationRunner({
194
+ dialect: "postgresql",
195
+ store: new InMemoryMigrationHistoryStore(),
196
+ });
197
+ const wrongOwner = bundleFixture("orders");
198
+ wrongOwner.migrations[0].module = "billing";
199
+
200
+ await expect(runner.planBundles([wrongOwner])).rejects.toThrow(
201
+ "模块归属应为 orders",
202
+ );
203
+
204
+ const first = bundleFixture("orders", ["shared.001"]);
205
+ const second = bundleFixture("billing", ["shared.001"]);
206
+ await expect(runner.planBundles([first, second])).rejects.toThrow(
207
+ "Migration ID 重复:shared.001",
208
+ );
209
+ });
210
+
211
+ test("拒绝已应用 migration 的 checksum 漂移", async () => {
212
+ const store = new InMemoryMigrationHistoryStore();
213
+ const runner = new PlatformMigrationRunner({
214
+ dialect: "postgresql",
215
+ store,
216
+ });
217
+ const bundle = bundleFixture("orders");
218
+ await runner.runBundles([bundle]);
219
+ bundle.migrations[0].checksum = "sha256:changed";
220
+
221
+ await expect(runner.planBundles([bundle])).rejects.toThrow(
222
+ "checksum 与当前 Bundle 不一致",
223
+ );
224
+ });
225
+
226
+ test("Manifest schemaExports 只能引用所属 bundle migration", async () => {
227
+ const manifest = moduleFixture("orders");
228
+ manifest.schemaExports = [
229
+ {
230
+ key: "orders.schema",
231
+ migrationIds: ["orders.missing"],
232
+ },
233
+ ];
234
+ const runner = new PlatformMigrationRunner({
235
+ dialect: "postgresql",
236
+ store: new InMemoryMigrationHistoryStore(),
237
+ });
238
+
239
+ await expect(runner.plan([manifest])).rejects.toThrow(
240
+ "Bundle 中不存在的 Migration:orders.missing",
241
+ );
242
+ });
96
243
  });
@@ -1,11 +1,18 @@
1
- import type { MigrationDefinition, ModuleManifest } from "@windy/modules";
2
1
  import type {
2
+ BundleMigrationDefinition,
3
3
  DatabaseCompatibilityAdapter,
4
4
  DatabaseDialect,
5
+ MigrationBundle,
6
+ MigrationManifestSource,
5
7
  MigrationStatement,
6
- } from "@windy/shared";
8
+ } from "@southwind-ai/shared";
9
+ import {
10
+ normalizeManifestBundles,
11
+ orderBundleMigrations,
12
+ validateAndOrderBundles,
13
+ } from "./bundle-validation.js";
7
14
  import { createDialectAdapter } from "./dialects.js";
8
- import type { MigrationHistoryStore } from "./store.js";
15
+ import type { MigrationHistoryEntry, MigrationHistoryStore } from "./store.js";
9
16
 
10
17
  export interface MigrationRunnerOptions {
11
18
  dialect: DatabaseDialect;
@@ -19,8 +26,9 @@ export interface MigrationPlanItem {
19
26
  module: string;
20
27
  description: string;
21
28
  direction: "up" | "down";
29
+ checksum: string;
22
30
  skipped: boolean;
23
- skipReason?: string;
31
+ skipReason?: "already-applied";
24
32
  }
25
33
 
26
34
  export interface MigrationRunReport {
@@ -47,45 +55,51 @@ export class PlatformMigrationRunner {
47
55
  this.now = options.now || (() => new Date());
48
56
  }
49
57
 
50
- async plan(modules: ModuleManifest[]): Promise<MigrationPlanItem[]> {
51
- const appliedIds = new Set(
52
- (await this.options.store.listApplied()).map((entry) => entry.id),
53
- );
58
+ async plan(modules: readonly MigrationManifestSource[]) {
59
+ return this.planBundles(normalizeManifestBundles(modules));
60
+ }
54
61
 
55
- return sortModulesByDependency(modules).flatMap((module) =>
56
- module.migrations.map((migration) => ({
57
- id: migration.id,
58
- module: migration.module,
59
- description: migration.description,
60
- direction: migration.direction,
61
- skipped: appliedIds.has(migration.id),
62
- skipReason: appliedIds.has(migration.id)
63
- ? "already-applied"
64
- : undefined,
65
- })),
62
+ async planBundles(
63
+ bundles: readonly MigrationBundle[],
64
+ ): Promise<MigrationPlanItem[]> {
65
+ const ordered = validateAndOrderBundles(bundles);
66
+ const applied = appliedById(await this.options.store.listApplied());
67
+ return ordered.flatMap((bundle) =>
68
+ orderBundleMigrations(bundle).map((migration) =>
69
+ planItem(migration, applied.get(migration.id)),
70
+ ),
66
71
  );
67
72
  }
68
73
 
69
74
  async run(
70
- modules: ModuleManifest[],
75
+ modules: readonly MigrationManifestSource[],
76
+ dryRun = false,
77
+ ): Promise<MigrationRunReport> {
78
+ return this.runBundles(normalizeManifestBundles(modules), dryRun);
79
+ }
80
+
81
+ async runBundles(
82
+ bundles: readonly MigrationBundle[],
71
83
  dryRun = false,
72
84
  ): Promise<MigrationRunReport> {
73
85
  const startedAt = this.now().toISOString();
74
- const planned = await this.plan(modules);
75
- const migrations = collectMigrations(modules);
86
+ const ordered = validateAndOrderBundles(bundles);
87
+ const planned = await this.planBundles(ordered);
88
+ const migrations = new Map(
89
+ ordered.flatMap((bundle) =>
90
+ bundle.migrations.map(
91
+ (migration) => [migration.id, migration] as const,
92
+ ),
93
+ ),
94
+ );
76
95
  const applied: string[] = [];
77
96
  const failed: Array<{ id: string; error: string }> = [];
78
97
  const statements: MigrationStatement[] = [];
79
98
 
80
99
  for (const item of planned) {
81
- if (item.skipped) {
82
- continue;
83
- }
84
-
100
+ if (item.skipped) continue;
85
101
  const migration = migrations.get(item.id);
86
- if (!migration) {
87
- continue;
88
- }
102
+ if (!migration) throw new Error(`Migration ${item.id} 不存在`);
89
103
 
90
104
  const stepStartedAt = this.now().toISOString();
91
105
  const stepStatements: MigrationStatement[] = [];
@@ -97,34 +111,19 @@ export class PlatformMigrationRunner {
97
111
  execute: async (statement) => {
98
112
  statements.push(statement);
99
113
  stepStatements.push(statement);
100
- if (!dryRun) {
101
- await this.executeStatement(statement);
102
- }
114
+ if (!dryRun) await this.executeStatement(statement);
103
115
  },
104
116
  });
105
117
  applied.push(item.id);
106
- await this.options.store.record({
107
- id: item.id,
108
- module: item.module,
109
- direction: item.direction,
110
- status: "applied",
111
- startedAt: stepStartedAt,
112
- finishedAt: this.now().toISOString(),
113
- statements: stepStatements.map((statement) => statement.sql),
114
- });
118
+ if (!dryRun) {
119
+ await this.recordResult(item, stepStartedAt, stepStatements);
120
+ }
115
121
  } catch (error) {
116
122
  const message = error instanceof Error ? error.message : String(error);
117
123
  failed.push({ id: item.id, error: message });
118
- await this.options.store.record({
119
- id: item.id,
120
- module: item.module,
121
- direction: item.direction,
122
- status: "failed",
123
- startedAt: stepStartedAt,
124
- finishedAt: this.now().toISOString(),
125
- statements: stepStatements.map((statement) => statement.sql),
126
- error: message,
127
- });
124
+ if (!dryRun) {
125
+ await this.recordResult(item, stepStartedAt, stepStatements, message);
126
+ }
128
127
  break;
129
128
  }
130
129
  }
@@ -140,36 +139,55 @@ export class PlatformMigrationRunner {
140
139
  statements,
141
140
  };
142
141
  }
143
- }
144
142
 
145
- function collectMigrations(
146
- modules: ModuleManifest[],
147
- ): Map<string, MigrationDefinition> {
148
- return new Map(
149
- modules
150
- .flatMap((module) => module.migrations)
151
- .map((migration) => [migration.id, migration]),
152
- );
143
+ private async recordResult(
144
+ item: MigrationPlanItem,
145
+ startedAt: string,
146
+ statements: readonly MigrationStatement[],
147
+ error?: string,
148
+ ): Promise<void> {
149
+ await this.options.store.record({
150
+ id: item.id,
151
+ module: item.module,
152
+ direction: item.direction,
153
+ status: error ? "failed" : "applied",
154
+ checksum: item.checksum,
155
+ startedAt,
156
+ finishedAt: this.now().toISOString(),
157
+ statements: statements.map(({ sql }) => sql),
158
+ error,
159
+ });
160
+ }
153
161
  }
154
162
 
155
- function sortModulesByDependency(modules: ModuleManifest[]): ModuleManifest[] {
156
- const pending = new Map(modules.map((module) => [module.name, module]));
157
- const sorted: ModuleManifest[] = [];
163
+ function appliedById(entries: readonly MigrationHistoryEntry[]) {
164
+ return new Map(entries.map((entry) => [entry.id, entry]));
165
+ }
158
166
 
159
- while (pending.size > 0) {
160
- const ready = Array.from(pending.values()).find((module) =>
161
- (module.dependencies || []).every(
162
- (dependency) => !pending.has(dependency),
163
- ),
167
+ function planItem(
168
+ migration: BundleMigrationDefinition,
169
+ applied: MigrationHistoryEntry | undefined,
170
+ ): MigrationPlanItem {
171
+ if (applied?.module !== undefined && applied.module !== migration.module) {
172
+ throw new Error(
173
+ `已应用 Migration ${migration.id} 归属 ${applied.module},当前归属 ${migration.module}`,
164
174
  );
165
-
166
- if (!ready) {
167
- throw new Error("模块依赖存在循环,无法生成迁移计划");
168
- }
169
-
170
- sorted.push(ready);
171
- pending.delete(ready.name);
172
175
  }
173
-
174
- return sorted;
176
+ if (
177
+ applied?.checksum !== undefined &&
178
+ applied.checksum !== migration.checksum
179
+ ) {
180
+ throw new Error(
181
+ `已应用 Migration ${migration.id} checksum 与当前 Bundle 不一致`,
182
+ );
183
+ }
184
+ return {
185
+ id: migration.id,
186
+ module: migration.module,
187
+ description: migration.description,
188
+ direction: migration.direction,
189
+ checksum: migration.checksum,
190
+ skipped: Boolean(applied),
191
+ skipReason: applied ? "already-applied" : undefined,
192
+ };
175
193
  }
@@ -1,5 +1,5 @@
1
1
  import { sql } from "drizzle-orm";
2
- import type { LifecycleStatus } from "@windy/shared";
2
+ import type { LifecycleStatus } from "@southwind-ai/shared";
3
3
  import {
4
4
  boolean,
5
5
  check,
@@ -12,7 +12,7 @@ import {
12
12
  } from "drizzle-orm/pg-core";
13
13
  import { sql } from "drizzle-orm";
14
14
  import { auditColumns, idColumn, statusColumn } from "./common.js";
15
- import type { CredentialState } from "@windy/shared";
15
+ import type { CredentialState } from "@southwind-ai/shared";
16
16
 
17
17
  export const departments = pgTable(
18
18
  "departments",
@@ -1,4 +1,4 @@
1
- import type { LicenseV1Claims, LicenseVersionKey } from "@windy/shared";
1
+ import type { LicenseV1Claims, LicenseVersionKey } from "@southwind-ai/shared";
2
2
  import { sql } from "drizzle-orm";
3
3
  import {
4
4
  check,
@@ -8,7 +8,7 @@ import {
8
8
  uniqueIndex,
9
9
  varchar,
10
10
  } from "drizzle-orm/pg-core";
11
- import type { TargetRef } from "@windy/shared";
11
+ import type { TargetRef } from "@southwind-ai/shared";
12
12
  import { auditColumns, idColumn, statusColumn } from "./common.js";
13
13
  import { users } from "./identity.js";
14
14