@alneas/eaos-server 0.0.0-stage → 0.2.0

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 (360) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/LICENSE +21 -0
  3. package/README.ja.md +49 -0
  4. package/README.md +46 -2
  5. package/dist/agentic/agent-jobs.d.ts +333 -0
  6. package/dist/agentic/agent-jobs.js +15 -0
  7. package/dist/agentic/agent-jobs.js.map +1 -0
  8. package/dist/agentic/agent-node.d.ts +40 -0
  9. package/dist/agentic/agent-node.js +7 -0
  10. package/dist/agentic/agent-node.js.map +1 -0
  11. package/dist/agentic/agent-runner.d.ts +33 -0
  12. package/dist/agentic/agent-runner.js +7 -0
  13. package/dist/agentic/agent-runner.js.map +1 -0
  14. package/dist/agentic/ai-broker.d.ts +169 -0
  15. package/dist/agentic/ai-broker.js +8 -0
  16. package/dist/agentic/ai-broker.js.map +1 -0
  17. package/dist/agentic/atomic-checkpointer.d.ts +137 -0
  18. package/dist/agentic/atomic-checkpointer.js +9 -0
  19. package/dist/agentic/atomic-checkpointer.js.map +1 -0
  20. package/dist/agentic/autonomous-worker.d.ts +122 -0
  21. package/dist/agentic/autonomous-worker.js +10 -0
  22. package/dist/agentic/autonomous-worker.js.map +1 -0
  23. package/dist/agentic/event-bridge.d.ts +350 -0
  24. package/dist/agentic/event-bridge.js +9 -0
  25. package/dist/agentic/event-bridge.js.map +1 -0
  26. package/dist/agentic/executor.d.ts +91 -0
  27. package/dist/agentic/executor.js +12 -0
  28. package/dist/agentic/executor.js.map +1 -0
  29. package/dist/agentic/graph-projection.d.ts +119 -0
  30. package/dist/agentic/graph-projection.js +7 -0
  31. package/dist/agentic/graph-projection.js.map +1 -0
  32. package/dist/agentic/graph.d.ts +524 -0
  33. package/dist/agentic/graph.js +7 -0
  34. package/dist/agentic/graph.js.map +1 -0
  35. package/dist/agentic/index.d.ts +28 -0
  36. package/dist/agentic/index.js +87 -0
  37. package/dist/agentic/index.js.map +1 -0
  38. package/dist/agentic/local-runner.d.ts +106 -0
  39. package/dist/agentic/local-runner.js +7 -0
  40. package/dist/agentic/local-runner.js.map +1 -0
  41. package/dist/agentic/remote-executor.d.ts +67 -0
  42. package/dist/agentic/remote-executor.js +8 -0
  43. package/dist/agentic/remote-executor.js.map +1 -0
  44. package/dist/agentic/types.d.ts +157 -0
  45. package/dist/agentic/types.js +7 -0
  46. package/dist/agentic/types.js.map +1 -0
  47. package/dist/agentic/workflow-catalog.d.ts +152 -0
  48. package/dist/agentic/workflow-catalog.js +7 -0
  49. package/dist/agentic/workflow-catalog.js.map +1 -0
  50. package/dist/agentic/workflow-compiler.d.ts +44 -0
  51. package/dist/agentic/workflow-compiler.js +8 -0
  52. package/dist/agentic/workflow-compiler.js.map +1 -0
  53. package/dist/agentic/workflow-coordinator.d.ts +170 -0
  54. package/dist/agentic/workflow-coordinator.js +8 -0
  55. package/dist/agentic/workflow-coordinator.js.map +1 -0
  56. package/dist/agentic/workflow-manager.d.ts +91 -0
  57. package/dist/agentic/workflow-manager.js +7 -0
  58. package/dist/agentic/workflow-manager.js.map +1 -0
  59. package/dist/agentic/workflow-registry.d.ts +334 -0
  60. package/dist/agentic/workflow-registry.js +7 -0
  61. package/dist/agentic/workflow-registry.js.map +1 -0
  62. package/dist/artifacts-CteHmCMI.d.ts +614 -0
  63. package/dist/auth-hooks/api-key.d.ts +75 -0
  64. package/dist/auth-hooks/api-key.js +7 -0
  65. package/dist/auth-hooks/api-key.js.map +1 -0
  66. package/dist/auth-hooks/index.d.ts +80 -0
  67. package/dist/auth-hooks/index.js +28 -0
  68. package/dist/auth-hooks/index.js.map +1 -0
  69. package/dist/browser-auth/account-memberships.d.ts +213 -0
  70. package/dist/browser-auth/account-memberships.js +13 -0
  71. package/dist/browser-auth/account-memberships.js.map +1 -0
  72. package/dist/browser-auth/index.d.ts +8 -0
  73. package/dist/browser-auth/index.js +59 -0
  74. package/dist/browser-auth/index.js.map +1 -0
  75. package/dist/browser-auth/kratos-session.d.ts +37 -0
  76. package/dist/browser-auth/kratos-session.js +7 -0
  77. package/dist/browser-auth/kratos-session.js.map +1 -0
  78. package/dist/browser-auth/platform-session.d.ts +120 -0
  79. package/dist/browser-auth/platform-session.js +7 -0
  80. package/dist/browser-auth/platform-session.js.map +1 -0
  81. package/dist/browser-auth/public-memberships.d.ts +22 -0
  82. package/dist/browser-auth/public-memberships.js +7 -0
  83. package/dist/browser-auth/public-memberships.js.map +1 -0
  84. package/dist/cache/index.d.ts +181 -0
  85. package/dist/cache/index.js +197 -0
  86. package/dist/cache/index.js.map +1 -0
  87. package/dist/chunk-32746UQZ.js +131 -0
  88. package/dist/chunk-32746UQZ.js.map +1 -0
  89. package/dist/chunk-3NLSWP2Y.js +223 -0
  90. package/dist/chunk-3NLSWP2Y.js.map +1 -0
  91. package/dist/chunk-3P4XTTQH.js +346 -0
  92. package/dist/chunk-3P4XTTQH.js.map +1 -0
  93. package/dist/chunk-3VITSHN3.js +15 -0
  94. package/dist/chunk-3VITSHN3.js.map +1 -0
  95. package/dist/chunk-46UWPEW2.js +226 -0
  96. package/dist/chunk-46UWPEW2.js.map +1 -0
  97. package/dist/chunk-4BARRLJR.js +180 -0
  98. package/dist/chunk-4BARRLJR.js.map +1 -0
  99. package/dist/chunk-4VORUFKR.js +45 -0
  100. package/dist/chunk-4VORUFKR.js.map +1 -0
  101. package/dist/chunk-55D4ERWO.js +1 -0
  102. package/dist/chunk-55D4ERWO.js.map +1 -0
  103. package/dist/chunk-5TBBZIND.js +18 -0
  104. package/dist/chunk-5TBBZIND.js.map +1 -0
  105. package/dist/chunk-5WOZWPZA.js +211 -0
  106. package/dist/chunk-5WOZWPZA.js.map +1 -0
  107. package/dist/chunk-6CBVZ6CT.js +168 -0
  108. package/dist/chunk-6CBVZ6CT.js.map +1 -0
  109. package/dist/chunk-6XZXYNJR.js +74 -0
  110. package/dist/chunk-6XZXYNJR.js.map +1 -0
  111. package/dist/chunk-753HWDSC.js +222 -0
  112. package/dist/chunk-753HWDSC.js.map +1 -0
  113. package/dist/chunk-7AHSS7SO.js +81 -0
  114. package/dist/chunk-7AHSS7SO.js.map +1 -0
  115. package/dist/chunk-7FTJN4SA.js +1 -0
  116. package/dist/chunk-7FTJN4SA.js.map +1 -0
  117. package/dist/chunk-AVDMLTPF.js +24 -0
  118. package/dist/chunk-AVDMLTPF.js.map +1 -0
  119. package/dist/chunk-BKAHUEIT.js +95 -0
  120. package/dist/chunk-BKAHUEIT.js.map +1 -0
  121. package/dist/chunk-CJ7OFEF7.js +134 -0
  122. package/dist/chunk-CJ7OFEF7.js.map +1 -0
  123. package/dist/chunk-EJKWDZIU.js +111 -0
  124. package/dist/chunk-EJKWDZIU.js.map +1 -0
  125. package/dist/chunk-F2J6IQ6I.js +247 -0
  126. package/dist/chunk-F2J6IQ6I.js.map +1 -0
  127. package/dist/chunk-F3O7CELI.js +23 -0
  128. package/dist/chunk-F3O7CELI.js.map +1 -0
  129. package/dist/chunk-F4IITPGX.js +221 -0
  130. package/dist/chunk-F4IITPGX.js.map +1 -0
  131. package/dist/chunk-FD6VC2NA.js +66 -0
  132. package/dist/chunk-FD6VC2NA.js.map +1 -0
  133. package/dist/chunk-FRRRWAHO.js +14 -0
  134. package/dist/chunk-FRRRWAHO.js.map +1 -0
  135. package/dist/chunk-HCKAGLLB.js +17 -0
  136. package/dist/chunk-HCKAGLLB.js.map +1 -0
  137. package/dist/chunk-HVC7FG6B.js +191 -0
  138. package/dist/chunk-HVC7FG6B.js.map +1 -0
  139. package/dist/chunk-HXFVOR7Z.js +496 -0
  140. package/dist/chunk-HXFVOR7Z.js.map +1 -0
  141. package/dist/chunk-IJWYS2PF.js +122 -0
  142. package/dist/chunk-IJWYS2PF.js.map +1 -0
  143. package/dist/chunk-INCHXAFK.js +1395 -0
  144. package/dist/chunk-INCHXAFK.js.map +1 -0
  145. package/dist/chunk-JT6BRGQM.js +1 -0
  146. package/dist/chunk-JT6BRGQM.js.map +1 -0
  147. package/dist/chunk-L6PMAEMB.js +115 -0
  148. package/dist/chunk-L6PMAEMB.js.map +1 -0
  149. package/dist/chunk-LOUGW6MU.js +126 -0
  150. package/dist/chunk-LOUGW6MU.js.map +1 -0
  151. package/dist/chunk-LYPZPE53.js +129 -0
  152. package/dist/chunk-LYPZPE53.js.map +1 -0
  153. package/dist/chunk-NNCCN7JP.js +186 -0
  154. package/dist/chunk-NNCCN7JP.js.map +1 -0
  155. package/dist/chunk-PBC3MZUL.js +212 -0
  156. package/dist/chunk-PBC3MZUL.js.map +1 -0
  157. package/dist/chunk-R4ADP2DC.js +20 -0
  158. package/dist/chunk-R4ADP2DC.js.map +1 -0
  159. package/dist/chunk-RHN4Y6VS.js +44 -0
  160. package/dist/chunk-RHN4Y6VS.js.map +1 -0
  161. package/dist/chunk-SDJNYPYK.js +770 -0
  162. package/dist/chunk-SDJNYPYK.js.map +1 -0
  163. package/dist/chunk-UGNTCOVR.js +28 -0
  164. package/dist/chunk-UGNTCOVR.js.map +1 -0
  165. package/dist/chunk-VC2PCK2S.js +31 -0
  166. package/dist/chunk-VC2PCK2S.js.map +1 -0
  167. package/dist/chunk-VF5YDZJC.js +305 -0
  168. package/dist/chunk-VF5YDZJC.js.map +1 -0
  169. package/dist/chunk-VISRDVTK.js +675 -0
  170. package/dist/chunk-VISRDVTK.js.map +1 -0
  171. package/dist/chunk-W6O3LVBY.js +102 -0
  172. package/dist/chunk-W6O3LVBY.js.map +1 -0
  173. package/dist/chunk-WFEAKJLR.js +47 -0
  174. package/dist/chunk-WFEAKJLR.js.map +1 -0
  175. package/dist/chunk-XIEY2OF4.js +67 -0
  176. package/dist/chunk-XIEY2OF4.js.map +1 -0
  177. package/dist/chunk-XITLHDG3.js +37 -0
  178. package/dist/chunk-XITLHDG3.js.map +1 -0
  179. package/dist/chunk-XMWPYZWZ.js +61 -0
  180. package/dist/chunk-XMWPYZWZ.js.map +1 -0
  181. package/dist/chunk-XUP6CNOM.js +34 -0
  182. package/dist/chunk-XUP6CNOM.js.map +1 -0
  183. package/dist/chunk-YABTQZDJ.js +25 -0
  184. package/dist/chunk-YABTQZDJ.js.map +1 -0
  185. package/dist/chunk-YMHVRBUU.js +199 -0
  186. package/dist/chunk-YMHVRBUU.js.map +1 -0
  187. package/dist/chunk-YMJ7BRKU.js +510 -0
  188. package/dist/chunk-YMJ7BRKU.js.map +1 -0
  189. package/dist/chunk-ZC3XWU7B.js +28 -0
  190. package/dist/chunk-ZC3XWU7B.js.map +1 -0
  191. package/dist/chunk-ZHMTD5KV.js +90 -0
  192. package/dist/chunk-ZHMTD5KV.js.map +1 -0
  193. package/dist/chunk-ZWZDLOE7.js +160 -0
  194. package/dist/chunk-ZWZDLOE7.js.map +1 -0
  195. package/dist/db-package/db/local-first-sqlite-adapter.d.ts +432 -0
  196. package/dist/db-package/db/local-first-sqlite-adapter.js +7 -0
  197. package/dist/db-package/db/local-first-sqlite-adapter.js.map +1 -0
  198. package/dist/db-package/index.d.ts +7 -0
  199. package/dist/db-package/index.js +16 -0
  200. package/dist/db-package/index.js.map +1 -0
  201. package/dist/db-package/migration/aggregator.d.ts +86 -0
  202. package/dist/db-package/migration/aggregator.js +7 -0
  203. package/dist/db-package/migration/aggregator.js.map +1 -0
  204. package/dist/db-package/migration/index.d.ts +3 -0
  205. package/dist/db-package/migration/index.js +8 -0
  206. package/dist/db-package/migration/index.js.map +1 -0
  207. package/dist/engine.d.ts +205 -0
  208. package/dist/engine.js +20 -0
  209. package/dist/engine.js.map +1 -0
  210. package/dist/entitlements/index.d.ts +313 -0
  211. package/dist/entitlements/index.js +23 -0
  212. package/dist/entitlements/index.js.map +1 -0
  213. package/dist/execution/chat.d.ts +7 -0
  214. package/dist/execution/chat.js +7 -0
  215. package/dist/execution/chat.js.map +1 -0
  216. package/dist/execution/index.d.ts +342 -0
  217. package/dist/execution/index.js +66 -0
  218. package/dist/execution/index.js.map +1 -0
  219. package/dist/execution/tool-agent.d.ts +7 -0
  220. package/dist/execution/tool-agent.js +8 -0
  221. package/dist/execution/tool-agent.js.map +1 -0
  222. package/dist/extensions/index.d.ts +172 -0
  223. package/dist/extensions/index.js +95 -0
  224. package/dist/extensions/index.js.map +1 -0
  225. package/dist/health/index.d.ts +35 -0
  226. package/dist/health/index.js +7 -0
  227. package/dist/health/index.js.map +1 -0
  228. package/dist/index.d.ts +43 -0
  229. package/dist/index.js +161 -0
  230. package/dist/index.js.map +1 -0
  231. package/dist/lifecycle/index.d.ts +133 -0
  232. package/dist/lifecycle/index.js +13 -0
  233. package/dist/lifecycle/index.js.map +1 -0
  234. package/dist/lifecycle-engine/backend-migration.d.ts +2 -0
  235. package/dist/lifecycle-engine/backend-migration.js +11 -0
  236. package/dist/lifecycle-engine/backend-migration.js.map +1 -0
  237. package/dist/lifecycle-engine/baseline.d.ts +2 -0
  238. package/dist/lifecycle-engine/baseline.js +9 -0
  239. package/dist/lifecycle-engine/baseline.js.map +1 -0
  240. package/dist/lifecycle-engine/index.d.ts +1497 -0
  241. package/dist/lifecycle-engine/index.js +41 -0
  242. package/dist/lifecycle-engine/index.js.map +1 -0
  243. package/dist/lifecycle-engine/platform/artifacts.d.ts +4 -0
  244. package/dist/lifecycle-engine/platform/artifacts.js +7 -0
  245. package/dist/lifecycle-engine/platform/artifacts.js.map +1 -0
  246. package/dist/lifecycle-engine/platform/control-plane.d.ts +12 -0
  247. package/dist/lifecycle-engine/platform/control-plane.js +2 -0
  248. package/dist/lifecycle-engine/platform/control-plane.js.map +1 -0
  249. package/dist/lifecycle-engine/platform/database-router.d.ts +118 -0
  250. package/dist/lifecycle-engine/platform/database-router.js +7 -0
  251. package/dist/lifecycle-engine/platform/database-router.js.map +1 -0
  252. package/dist/lifecycle-engine/platform/deployment-placements.d.ts +464 -0
  253. package/dist/lifecycle-engine/platform/deployment-placements.js +16 -0
  254. package/dist/lifecycle-engine/platform/deployment-placements.js.map +1 -0
  255. package/dist/lifecycle-engine/platform/deployment.d.ts +4 -0
  256. package/dist/lifecycle-engine/platform/deployment.js +17 -0
  257. package/dist/lifecycle-engine/platform/deployment.js.map +1 -0
  258. package/dist/lifecycle-engine/platform/index.d.ts +737 -0
  259. package/dist/lifecycle-engine/platform/index.js +60 -0
  260. package/dist/lifecycle-engine/platform/index.js.map +1 -0
  261. package/dist/lifecycle-engine/platform/runtime.d.ts +80 -0
  262. package/dist/lifecycle-engine/platform/runtime.js +11 -0
  263. package/dist/lifecycle-engine/platform/runtime.js.map +1 -0
  264. package/dist/lifecycle-engine/platform/site-publication.d.ts +102 -0
  265. package/dist/lifecycle-engine/platform/site-publication.js +7 -0
  266. package/dist/lifecycle-engine/platform/site-publication.js.map +1 -0
  267. package/dist/lifecycle-engine/platform/user-directory.d.ts +139 -0
  268. package/dist/lifecycle-engine/platform/user-directory.js +13 -0
  269. package/dist/lifecycle-engine/platform/user-directory.js.map +1 -0
  270. package/dist/mcp.d.ts +92 -0
  271. package/dist/mcp.js +10 -0
  272. package/dist/mcp.js.map +1 -0
  273. package/dist/middleware/auth.d.ts +118 -0
  274. package/dist/middleware/auth.js +7 -0
  275. package/dist/middleware/auth.js.map +1 -0
  276. package/dist/middleware/error-handler.d.ts +35 -0
  277. package/dist/middleware/error-handler.js +7 -0
  278. package/dist/middleware/error-handler.js.map +1 -0
  279. package/dist/middleware/module-guard.d.ts +33 -0
  280. package/dist/middleware/module-guard.js +7 -0
  281. package/dist/middleware/module-guard.js.map +1 -0
  282. package/dist/platform-admin/index.d.ts +37 -0
  283. package/dist/platform-admin/index.js +11 -0
  284. package/dist/platform-admin/index.js.map +1 -0
  285. package/dist/platform-admin/store.d.ts +346 -0
  286. package/dist/platform-admin/store.js +7 -0
  287. package/dist/platform-admin/store.js.map +1 -0
  288. package/dist/plugin-data/annotations.d.ts +6 -0
  289. package/dist/plugin-data/annotations.js +12 -0
  290. package/dist/plugin-data/annotations.js.map +1 -0
  291. package/dist/plugin-data/atomic-context.d.ts +41 -0
  292. package/dist/plugin-data/atomic-context.js +9 -0
  293. package/dist/plugin-data/atomic-context.js.map +1 -0
  294. package/dist/plugin-data/atomic.d.ts +6 -0
  295. package/dist/plugin-data/atomic.js +26 -0
  296. package/dist/plugin-data/atomic.js.map +1 -0
  297. package/dist/plugin-data/audit.d.ts +6 -0
  298. package/dist/plugin-data/audit.js +12 -0
  299. package/dist/plugin-data/audit.js.map +1 -0
  300. package/dist/plugin-data/commands.d.ts +6 -0
  301. package/dist/plugin-data/commands.js +8 -0
  302. package/dist/plugin-data/commands.js.map +1 -0
  303. package/dist/plugin-data/composite.d.ts +6 -0
  304. package/dist/plugin-data/composite.js +8 -0
  305. package/dist/plugin-data/composite.js.map +1 -0
  306. package/dist/plugin-data/delivery.d.ts +6 -0
  307. package/dist/plugin-data/delivery.js +16 -0
  308. package/dist/plugin-data/delivery.js.map +1 -0
  309. package/dist/plugin-data/index.d.ts +2798 -0
  310. package/dist/plugin-data/index.js +102 -0
  311. package/dist/plugin-data/index.js.map +1 -0
  312. package/dist/plugin-data/json-documents.d.ts +6 -0
  313. package/dist/plugin-data/json-documents.js +16 -0
  314. package/dist/plugin-data/json-documents.js.map +1 -0
  315. package/dist/plugin-data/outbox.d.ts +6 -0
  316. package/dist/plugin-data/outbox.js +16 -0
  317. package/dist/plugin-data/outbox.js.map +1 -0
  318. package/dist/plugin-data/scoped-records.d.ts +6 -0
  319. package/dist/plugin-data/scoped-records.js +10 -0
  320. package/dist/plugin-data/scoped-records.js.map +1 -0
  321. package/dist/plugin-sdk.d.ts +41 -0
  322. package/dist/plugin-sdk.js +10 -0
  323. package/dist/plugin-sdk.js.map +1 -0
  324. package/dist/sql-lifecycle/index.d.ts +138 -0
  325. package/dist/sql-lifecycle/index.js +167 -0
  326. package/dist/sql-lifecycle/index.js.map +1 -0
  327. package/dist/storage/index.d.ts +2 -0
  328. package/dist/storage/index.js +13 -0
  329. package/dist/storage/index.js.map +1 -0
  330. package/dist/storage/s3-compatible.d.ts +139 -0
  331. package/dist/storage/s3-compatible.js +11 -0
  332. package/dist/storage/s3-compatible.js.map +1 -0
  333. package/dist/tenant.d.ts +108 -0
  334. package/dist/tenant.js +9 -0
  335. package/dist/tenant.js.map +1 -0
  336. package/dist/workspace-context.d.ts +119 -0
  337. package/dist/workspace-context.js +9 -0
  338. package/dist/workspace-context.js.map +1 -0
  339. package/docs/api.md +30560 -0
  340. package/docs/db/README.md +49 -0
  341. package/docs/db/docs/api.md +1613 -0
  342. package/docs/db/docs/guide.md +47 -0
  343. package/docs/entitlements.md +60 -0
  344. package/docs/guide.md +92 -0
  345. package/docs/index.md +57 -0
  346. package/docs/ja/db/README.md +46 -0
  347. package/docs/ja/db/docs/guide.md +53 -0
  348. package/docs/ja/guide.md +46 -0
  349. package/docs/ja/lifecycle/README.md +46 -0
  350. package/docs/ja/lifecycle/docs/guide.md +45 -0
  351. package/docs/ja/storage/README.md +46 -0
  352. package/docs/ja/storage/docs/guide.md +43 -0
  353. package/docs/lifecycle/README.md +49 -0
  354. package/docs/lifecycle/docs/api.md +3770 -0
  355. package/docs/lifecycle/docs/baseline.md +50 -0
  356. package/docs/lifecycle/docs/guide.md +47 -0
  357. package/docs/storage/README.md +49 -0
  358. package/docs/storage/docs/api.md +470 -0
  359. package/docs/storage/docs/guide.md +47 -0
  360. package/package.json +354 -4
@@ -0,0 +1,770 @@
1
+ // src/lifecycle-engine/baseline.ts
2
+ import { lt as lt2, valid as valid2, satisfies as satisfies2 } from "semver";
3
+
4
+ // src/lifecycle-engine/index.ts
5
+ import { dataContractTables, resolveDataContracts } from "@alneas/eaos-core/contracts/data-contracts";
6
+ import { createHash } from "node:crypto";
7
+ import { satisfies, valid, validRange, lt } from "semver";
8
+
9
+ // package.json
10
+ var version = "0.2.0";
11
+
12
+ // src/lifecycle-engine/backend-migration.ts
13
+ function text(value, name, max = 256) {
14
+ if (typeof value !== "string" || !value.trim() || value.length > max) throw new Error(`Invalid migration ${name}`);
15
+ return value;
16
+ }
17
+ function assertBackend(backend, name) {
18
+ if (!backend || typeof backend !== "object") throw new Error(`Invalid migration ${name}`);
19
+ text(backend?.id, `${name} id`);
20
+ text(backend?.databaseId, `${name} databaseId`);
21
+ if (!["sqlite", "postgres", "oracle"].includes(backend.dialect) || !["shared", "dedicated"].includes(backend.mode)) throw new Error(`Invalid migration ${name}`);
22
+ if (backend.mode === "dedicated" && !backend.tenantId) throw new Error(`Dedicated ${name} requires tenantId`);
23
+ if (backend.mode === "shared" && backend.tenantId !== void 0) throw new Error(`Shared ${name} cannot pin tenantId`);
24
+ }
25
+ function planBackendMigration(request) {
26
+ text(request?.migrationId, "migrationId");
27
+ text(request?.reason, "reason", 1024);
28
+ if (!Number.isFinite(Date.parse(request.requestedAt))) throw new Error("Invalid migration requestedAt");
29
+ assertBackend(request.source, "source backend");
30
+ assertBackend(request.target, "target backend");
31
+ if (request.source.id === request.target.id || request.source.databaseId === request.target.databaseId) throw new Error("Backend migration requires a different physical database");
32
+ if (!Array.isArray(request.pluginIds) || request.pluginIds.length === 0 || new Set(request.pluginIds).size !== request.pluginIds.length || request.pluginIds.some((id2) => !/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(id2))) throw new Error("Invalid migration pluginIds");
33
+ if (!request.scope || typeof request.scope !== "object" || !["all-tenants", "tenants"].includes(request.scope.kind)) throw new Error("Invalid migration scope");
34
+ if (request.scope.kind === "tenants" && (!Array.isArray(request.scope.tenantIds) || request.scope.tenantIds.length === 0 || new Set(request.scope.tenantIds).size !== request.scope.tenantIds.length || request.scope.tenantIds.some((id2) => !/^\S{1,256}$/.test(id2)))) throw new Error("Invalid migration tenant scope");
35
+ const scope = request.scope.kind === "all-tenants" ? request.scope : { kind: "tenants", tenantIds: [...request.scope.tenantIds].sort() };
36
+ const scopedTenants = scope.kind === "tenants" ? scope.tenantIds : null;
37
+ if (request.source.mode === "dedicated" && (!scopedTenants || !scopedTenants.includes(request.source.tenantId))) throw new Error("Migration scope does not include the dedicated source tenant");
38
+ if (request.target.mode === "dedicated" && (!scopedTenants || !scopedTenants.includes(request.target.tenantId))) throw new Error("Migration scope does not include the dedicated target tenant");
39
+ const normalized = structuredClone({ ...request, pluginIds: [...request.pluginIds].sort(), scope });
40
+ const body = { format: 1, request: normalized, phases: ["prepare", "copy", "verify", "cutover", "retain-source"], requiresQuiesce: true, retainSourceData: true, destructiveOperations: false };
41
+ return Object.freeze({ ...body, id: lifecycleDigest(body) });
42
+ }
43
+ function assertBackendMigrationVerification(plan, report) {
44
+ if (!report || report.migrationId !== plan.request.migrationId || report.sourceBackendId !== plan.request.source.id || report.targetBackendId !== plan.request.target.id || !Number.isFinite(Date.parse(report.checkedAt)) || report.passed !== true || !Array.isArray(report.records) || report.records.length === 0) throw new Error("Backend migration verification is incomplete");
45
+ const tenants = plan.request.scope.kind === "tenants" ? new Set(plan.request.scope.tenantIds) : null;
46
+ for (const record of report.records) {
47
+ if (!record || typeof record.tenantId !== "string" || tenants && !tenants.has(record.tenantId) || !plan.request.pluginIds.includes(record.pluginId) || !Number.isSafeInteger(record.sourceRevision) || record.sourceRevision < 0 || !Number.isSafeInteger(record.targetRevision) || record.targetRevision < 0 || record.matched !== true) throw new Error("Backend migration verification is incomplete");
48
+ }
49
+ return report;
50
+ }
51
+ function assertBackendMigrationCutover(plan, report, cutover) {
52
+ assertBackendMigrationVerification(plan, report);
53
+ if (!cutover || cutover.migrationId !== plan.request.migrationId || cutover.targetBackendId !== plan.request.target.id || cutover.sourceRetained !== true || !Number.isSafeInteger(cutover.routingRevision) || cutover.routingRevision < 0 || !Number.isFinite(Date.parse(cutover.cutoverAt))) throw new Error("Backend migration cutover is invalid");
54
+ return cutover;
55
+ }
56
+
57
+ // src/lifecycle-engine/index.ts
58
+ export * from "@alneas/eaos-core/contracts/data-contracts";
59
+ import { dataContractTables as dataContractTables2, resolveDataContracts as resolveDataContracts2, assertDataContractBinding, collectSchemaMigrations, createJsonDocumentAssertion, assertJsonDocumentContracts } from "@alneas/eaos-core/contracts/data-contracts";
60
+ var FRAMEWORK_VERSION = version;
61
+ var emptyLifecyclePlatformState = () => ({ tenants: {}, placements: {}, backendMetadata: {}, migrationPlans: {}, migrationRecords: {} });
62
+ var emptyLifecycleState = () => ({ format: 1, revision: 0, backends: {}, installations: {}, activations: {}, migrations: {}, infrastructure: {}, platform: emptyLifecyclePlatformState() });
63
+ function normalizeLifecycleState(input) {
64
+ if (!input || typeof input !== "object") throw new Error("Invalid lifecycle state");
65
+ const state = structuredClone(input);
66
+ if (state.platform === void 0) state.platform = emptyLifecyclePlatformState();
67
+ if (!state.platform || typeof state.platform !== "object" || Array.isArray(state.platform)) throw new Error("Invalid lifecycle platform state");
68
+ for (const field of ["tenants", "placements", "backendMetadata", "migrationPlans", "migrationRecords"]) {
69
+ if (state.platform[field] === void 0) state.platform[field] = {};
70
+ if (!state.platform[field] || typeof state.platform[field] !== "object" || Array.isArray(state.platform[field])) throw new Error(`Invalid lifecycle platform ${field}`);
71
+ }
72
+ return state;
73
+ }
74
+ function canonical(value) {
75
+ if (value === null || typeof value !== "object") {
76
+ const result = JSON.stringify(value);
77
+ if (result === void 0) throw new Error("Lifecycle configuration must be JSON serializable");
78
+ return result;
79
+ }
80
+ if (Array.isArray(value)) return "[" + value.map(canonical).join(",") + "]";
81
+ if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new Error("Lifecycle configuration must contain plain objects");
82
+ return "{" + Object.keys(value).sort().map((k) => JSON.stringify(k) + ":" + canonical(value[k])).join(",") + "}";
83
+ }
84
+ var lifecycleDigest = (value) => createHash("sha256").update(canonical(value)).digest("hex");
85
+ var key = (...parts) => JSON.stringify(parts);
86
+ function id(value) {
87
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(value)) throw new Error("Invalid lifecycle identifier");
88
+ }
89
+ function assertState(s) {
90
+ if (s?.format !== 1 || !Number.isSafeInteger(s.revision) || s.revision < 0 || !s.backends || !s.installations || !s.activations || !s.migrations || !s.infrastructure) throw new Error("Invalid lifecycle state");
91
+ if (s.dataContracts !== void 0) {
92
+ if (!s.dataContracts || typeof s.dataContracts !== "object" || Array.isArray(s.dataContracts)) throw new Error("Invalid data contract state");
93
+ for (const [recordKey, record] of Object.entries(s.dataContracts)) {
94
+ const parts = JSON.parse(recordKey);
95
+ if (!Array.isArray(parts) || parts.length !== 2 || parts[1] !== record.contract?.id || record.digest !== lifecycleDigest(record.contract) || !["installing", "installed", "failed"].includes(record.status)) throw new Error("Invalid data contract state record");
96
+ }
97
+ }
98
+ }
99
+ var PluginLifecycle = class {
100
+ /**
101
+ * Validates and snapshots the registered plugin, backend, contract and bundle catalogs.
102
+ * @param project - Host lifecycle storage, trusted catalogs and execution adapters.
103
+ * @param frameworkVersion - Valid semver framework version; defaults to the installed package version.
104
+ * @throws Error for invalid/duplicate IDs, versions, migrations, conflicting schemas, unsupported backend configuration or unsatisfied contract dependencies.
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * import { PluginLifecycle, type LifecycleProject } from '@alneas/eaos-server/lifecycle-engine';
109
+ * export function connectLifecycle(project: LifecycleProject) { return new PluginLifecycle(project); }
110
+ * ```
111
+ */
112
+ constructor(project, frameworkVersion = FRAMEWORK_VERSION) {
113
+ this.project = project;
114
+ this.frameworkVersion = frameworkVersion;
115
+ if (!valid(frameworkVersion)) throw new Error("Invalid framework version");
116
+ const plugins = JSON.parse(canonical(project.plugins));
117
+ const backends = JSON.parse(canonical(project.backends));
118
+ this.plugins = /* @__PURE__ */ new Map();
119
+ this.backends = /* @__PURE__ */ new Map();
120
+ for (const p of plugins) {
121
+ id(p.id);
122
+ if (this.plugins.has(p.id) || !valid(p.version) || !validRange(p.framework)) throw new Error("Invalid or duplicate plugin release");
123
+ if ((project.dataContracts !== void 0 || Object.keys(p.schemaDependencies ?? {}).length) && p.migrations.length) throw new Error(`Contract-backed plugin must put DDL in data contracts: ${p.id}`);
124
+ const migrations = /* @__PURE__ */ new Set();
125
+ for (const m of p.migrations) {
126
+ id(m.id);
127
+ if (migrations.has(m.id) || !["database", "tenant"].includes(m.scope)) throw new Error("Invalid or duplicate migration");
128
+ for (const sql of Object.values(m.sql)) if (!Array.isArray(sql) || !sql.length || sql.some((s) => typeof s !== "string" || !s.trim())) throw new Error("Invalid migration SQL");
129
+ migrations.add(m.id);
130
+ }
131
+ const release = { ...p };
132
+ delete release.schemaContracts;
133
+ this.plugins.set(p.id, release);
134
+ }
135
+ const contracts = /* @__PURE__ */ new Map();
136
+ for (const contract of [...project.dataContracts ?? [], ...plugins.flatMap((p) => p.schemaContracts ?? [])]) {
137
+ const previous = contracts.get(contract.id);
138
+ if (previous && canonical(previous) !== canonical(contract)) throw new Error(`Conflicting data contract definitions: ${contract.id}`);
139
+ contracts.set(contract.id, JSON.parse(canonical(contract)));
140
+ }
141
+ this.contracts = [...contracts.values()];
142
+ resolveDataContracts(this.contracts, Object.fromEntries(this.contracts.map((c) => [c.id, c.version])));
143
+ for (const p of plugins) this.catalog([p]);
144
+ for (const [bundleId, pluginIds] of Object.entries(project.bundles ?? {})) {
145
+ id(bundleId);
146
+ if (!Array.isArray(pluginIds) || !pluginIds.length || new Set(pluginIds).size !== pluginIds.length || pluginIds.some((pluginId) => !this.plugins.has(pluginId))) throw new Error(`Invalid plugin bundle: ${bundleId}`);
147
+ }
148
+ for (const b of backends) {
149
+ id(b.id);
150
+ id(b.databaseId);
151
+ if (this.backends.has(b.id) || !["sqlite", "oracle", "postgres"].includes(b.dialect) || !["shared", "dedicated"].includes(b.mode)) throw new Error("Invalid backend target");
152
+ if (b.mode === "dedicated") {
153
+ if (!b.tenantId) throw new Error("Dedicated backend requires tenant");
154
+ id(b.tenantId);
155
+ } else if (b.tenantId !== void 0) throw new Error("Shared backend cannot bind a tenant");
156
+ for (const other of backends) if (other.databaseId === b.databaseId && other.dialect !== b.dialect) throw new Error("Database dialect conflict");
157
+ this.backends.set(b.id, b);
158
+ }
159
+ }
160
+ project;
161
+ frameworkVersion;
162
+ plugins;
163
+ backends;
164
+ contracts;
165
+ bundle(bundleId) {
166
+ id(bundleId);
167
+ const pluginIds = this.project.bundles?.[bundleId];
168
+ if (!pluginIds) throw new Error(`Plugin bundle not registered: ${bundleId}`);
169
+ return pluginIds;
170
+ }
171
+ /**
172
+ * Plans registered bundle members sequentially against a projected ledger state.
173
+ * @param request - Lifecycle request whose pluginId names a registered bundle.
174
+ * @returns A checksum-bound ordered bundle plan with each member's projected revision.
175
+ * @throws Rejects unknown bundle, invalid member request/state or propagated store failures.
176
+ *
177
+ * @remarks
178
+ * Disable/uninstall reverse bundle order. Planning executes no migrations or writes; projected revisions mirror member apply ordering.
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * import { PluginLifecycle, type LifecycleRequest } from '@alneas/eaos-server/lifecycle-engine';
183
+ * export function reviewBundle(lifecycle: PluginLifecycle, request: LifecycleRequest) { return lifecycle.planBundle(request); }
184
+ * ```
185
+ */
186
+ async planBundle(request) {
187
+ const pluginIds = [...this.bundle(request.pluginId)];
188
+ if (request.operation === "disable" || request.operation === "uninstall") pluginIds.reverse();
189
+ let projected = await this.project.store.read();
190
+ const plans = [];
191
+ for (const pluginId of pluginIds) {
192
+ const plan = this.makePlan({ ...request, pluginId }, projected);
193
+ plans.push(plan);
194
+ projected = this.projectBundleState(projected, plan);
195
+ }
196
+ const body = { format: 1, bundleId: request.pluginId, operation: request.operation, plans };
197
+ return JSON.parse(canonical({ ...body, id: lifecycleDigest(body) }));
198
+ }
199
+ /**
200
+ * Advance the read-only state used while constructing sequential bundle plans.
201
+ * apply() performs these writes in the same order; mirroring them here keeps
202
+ * each plan's revision and dependency closure valid when applyBundle() runs.
203
+ */
204
+ projectBundleState(source, plan) {
205
+ const state = structuredClone(source);
206
+ state.backends[plan.backend.id] = plan.backend;
207
+ for (const k of plan.deactivate) if (state.activations[k]) state.activations[k].enabled = false;
208
+ if (plan.request.operation === "install" || plan.request.operation === "update") {
209
+ for (const plugin of plan.plugins) state.installations[key(plan.backend.id, plugin.id)] = { release: plugin, digest: lifecycleDigest(plugin), status: "installed" };
210
+ if (plan.dataCatalog) {
211
+ state.dataContracts ??= {};
212
+ for (const contract of plan.dataCatalog.contracts) state.dataContracts[key(plan.backend.databaseId, contract.id)] = { contract, digest: lifecycleDigest(contract), status: "installed" };
213
+ }
214
+ for (const step of plan.migrations) state.migrations[step.key] = { ...step.record, status: "applied" };
215
+ for (const step of plan.infrastructure) state.infrastructure[step.key] = { digest: step.digest, status: "applied" };
216
+ state.revision += 2 + plan.migrations.length * 2 + plan.infrastructure.length * 2;
217
+ } else if (plan.request.operation === "enable") {
218
+ for (const plugin of plan.plugins) state.activations[key(plan.backend.id, plan.request.tenantId, plugin.id)] = { backendId: plan.backend.id, tenantId: plan.request.tenantId, pluginId: plugin.id, digest: lifecycleDigest(plugin), enabled: true };
219
+ state.revision += 1;
220
+ } else if (plan.request.operation === "uninstall") {
221
+ delete state.installations[key(plan.backend.id, plan.request.pluginId)];
222
+ state.revision += 1;
223
+ } else {
224
+ state.revision += 1;
225
+ }
226
+ return state;
227
+ }
228
+ /**
229
+ * Validates a reviewed bundle plan and applies its member plans in sequence.
230
+ * @param bundlePlan - Unmodified checksum-bound bundle plan from planBundle.
231
+ * @returns Resolves without a value after every member apply completes.
232
+ * @throws Rejects modified/stale/misordered bundle plans, member apply failures and propagated host errors.
233
+ *
234
+ * @remarks
235
+ * Members acquire their own ledger locks. There is no all-or-nothing bundle transaction; earlier members remain applied if a later member fails. Replan/reconcile before retrying uncertain outcomes.
236
+ *
237
+ * @example
238
+ * ```typescript
239
+ * import { PluginLifecycle, type LifecycleBundlePlan } from '@alneas/eaos-server/lifecycle-engine';
240
+ * export function applyReviewedBundle(lifecycle: PluginLifecycle, plan: LifecycleBundlePlan) { return lifecycle.applyBundle(plan); }
241
+ * ```
242
+ */
243
+ async applyBundle(bundlePlan) {
244
+ const normalized = JSON.parse(canonical(bundlePlan));
245
+ const pluginIds = this.bundle(normalized.bundleId);
246
+ const expectedIds = normalized.operation === "disable" || normalized.operation === "uninstall" ? [...pluginIds].reverse() : [...pluginIds];
247
+ if (normalized.format !== 1 || !["install", "update", "enable", "disable", "uninstall"].includes(normalized.operation) || normalized.plans.length !== expectedIds.length || normalized.plans.some((plan, index) => plan.request.pluginId !== expectedIds[index] || plan.request.operation !== normalized.operation)) throw new Error("Invalid plugin bundle plan");
248
+ const body = { format: 1, bundleId: normalized.bundleId, operation: normalized.operation, plans: normalized.plans };
249
+ if (lifecycleDigest(body) !== normalized.id) throw new Error("Stale or modified plugin bundle plan");
250
+ for (const plan of normalized.plans) await this.apply(plan);
251
+ }
252
+ release(id2) {
253
+ const p = this.plugins.get(id2);
254
+ if (!p) throw new Error(`Plugin code not registered: ${id2}`);
255
+ if (!satisfies(this.frameworkVersion, p.framework, { includePrerelease: true })) throw new Error(`Incompatible framework for ${id2}`);
256
+ return p;
257
+ }
258
+ order(pluginId) {
259
+ const ordered = [], visited = /* @__PURE__ */ new Set(), visiting = /* @__PURE__ */ new Set();
260
+ const visit = (id2) => {
261
+ if (visiting.has(id2)) throw new Error("Plugin dependency cycle");
262
+ if (visited.has(id2)) return;
263
+ visiting.add(id2);
264
+ const p = this.release(id2);
265
+ for (const [dependency, range] of Object.entries(p.dependencies ?? {})) {
266
+ if (!validRange(range) || !satisfies(this.release(dependency).version, range, { includePrerelease: true })) throw new Error(`Unsatisfied dependency: ${dependency}`);
267
+ visit(dependency);
268
+ }
269
+ visiting.delete(id2);
270
+ visited.add(id2);
271
+ ordered.push(p);
272
+ };
273
+ visit(pluginId);
274
+ return ordered;
275
+ }
276
+ /**
277
+ * Resolves the canonical schema dependency catalog for registered feature plugins.
278
+ *
279
+ * Read-only complete schema inventory for the selected trusted feature catalog.
280
+ * @param pluginIds - Optional feature IDs; omitted includes all registered plugins, while an empty array selects none.
281
+ * @returns A JSON-cloned dependency-resolved DataContractCatalog.
282
+ * @throws Error for unknown/incompatible feature dependencies or invalid/conflicting contract requirements.
283
+ *
284
+ * @remarks
285
+ * Read-only; schema dependencies never install or enable feature plugins.
286
+ *
287
+ * @example
288
+ * ```typescript
289
+ * import { PluginLifecycle } from '@alneas/eaos-server/lifecycle-engine';
290
+ * export function schemas(lifecycle: PluginLifecycle) { return lifecycle.schemaCatalog(); }
291
+ * ```
292
+ */
293
+ schemaCatalog(pluginIds) {
294
+ const plugins = pluginIds ? pluginIds.flatMap((id2) => this.order(id2)) : [...this.plugins.values()];
295
+ return JSON.parse(canonical(this.catalog(plugins)));
296
+ }
297
+ catalog(plugins) {
298
+ const requirements = {};
299
+ for (const plugin of plugins) {
300
+ resolveDataContracts(this.contracts, plugin.schemaDependencies ?? {});
301
+ Object.assign(requirements, plugin.schemaDependencies ?? {});
302
+ }
303
+ return resolveDataContracts(this.contracts, requirements);
304
+ }
305
+ contractReady(state, backend, tenantId, contract) {
306
+ const installed = state.dataContracts?.[key(backend.databaseId, contract.id)];
307
+ if (installed?.status !== "installed" || installed.digest !== lifecycleDigest(contract)) return false;
308
+ const identity = contract.migrationIdentity ?? `data-${contract.id}`;
309
+ return contract.migrations.every((m) => {
310
+ const record = state.migrations[key(backend.databaseId, identity, m.id, m.scope === "tenant" ? tenantId : "*")];
311
+ return record?.status === "applied" && record.checksum === lifecycleDigest(m);
312
+ });
313
+ }
314
+ /** Contract ledger identities may retain a feature's historical name even
315
+ * when that feature no longer consumes the contract. Match the actual
316
+ * immutable migration, rather than only the current feature's catalog. */
317
+ isContractMigration(record) {
318
+ return this.contracts.some((contract) => {
319
+ if ((contract.migrationIdentity ?? `data-${contract.id}`) !== record.pluginId) return false;
320
+ const migration = contract.migrations[record.position];
321
+ return !!migration && migration.id === record.migrationId && lifecycleDigest(migration) === record.checksum && record.status === "applied";
322
+ });
323
+ }
324
+ target(request) {
325
+ id(request.pluginId);
326
+ id(request.backendId);
327
+ id(request.tenantId);
328
+ if (!["install", "update", "enable", "disable", "uninstall"].includes(request.operation)) throw new Error("Invalid lifecycle operation");
329
+ const b = this.backends.get(request.backendId);
330
+ if (!b || b.mode === "dedicated" && b.tenantId !== request.tenantId) throw new Error("Backend tenant mismatch or unknown backend");
331
+ return b;
332
+ }
333
+ /**
334
+ * Creates a reviewable lifecycle plan from one current ledger snapshot without executing side effects.
335
+ * @param request - Concrete operation, registered plugin/backend and verified tenant; see LifecycleRequest.
336
+ * @returns A deterministic checksum-bound plan carrying the snapshot revision, dependency closure, migrations and retained-data policy.
337
+ * @throws Rejects invalid request/state, incompatible or missing dependencies, changed applied migrations, dirty ledger, unsupported dialect, tenant mismatch or propagated store failures.
338
+ *
339
+ * @remarks
340
+ * Planning is read-only but may become stale immediately. Backend physical identity is immutable. Updates require appended immutable migrations and compatible schema versions; installation and enablement are separate operations.
341
+ *
342
+ * @example
343
+ * ```typescript
344
+ * import { PluginLifecycle, type LifecycleRequest } from '@alneas/eaos-server/lifecycle-engine';
345
+ * export function review(lifecycle: PluginLifecycle, request: LifecycleRequest) { return lifecycle.plan(request); }
346
+ * ```
347
+ */
348
+ async plan(request) {
349
+ return this.makePlan(request, await this.project.store.read());
350
+ }
351
+ makePlan(request, state) {
352
+ assertState(state);
353
+ const backend = this.target(request), operation = request.operation;
354
+ if (state.backends[backend.id] && canonical(state.backends[backend.id]) !== canonical(backend)) throw new Error("Backend placement is immutable; use a new backend ID and migrate data explicitly");
355
+ for (const b of Object.values(state.backends)) if (b.databaseId === backend.databaseId && b.dialect !== backend.dialect) throw new Error("Database dialect conflict");
356
+ const changing = operation === "install" || operation === "update";
357
+ const installed = state.installations[key(backend.id, request.pluginId)];
358
+ const plugins = changing || operation === "enable" ? this.order(request.pluginId) : [installed?.release ?? this.plugins.get(request.pluginId)].filter(Boolean);
359
+ if (!plugins.length) throw new Error("Plugin is not installed");
360
+ if (operation === "update" && !installed) throw new Error("Install plugin before updating");
361
+ const dataCatalog = this.catalog(plugins);
362
+ for (const table of dataCatalog.tables) {
363
+ for (const [contractKey, installation] of Object.entries(state.dataContracts ?? {})) {
364
+ if (JSON.parse(contractKey)[0] !== backend.databaseId) continue;
365
+ if (installation.contract.id !== table.contractId && dataContractTables(installation.contract.schema).has(table.name)) throw new Error(`Installed data contract already owns table: ${table.name}`);
366
+ }
367
+ }
368
+ const migrations = [], infrastructure = [], deactivate = /* @__PURE__ */ new Set();
369
+ const markDependents = (backendId, pluginId) => {
370
+ const affected = /* @__PURE__ */ new Set([pluginId]);
371
+ let changed = true;
372
+ while (changed) {
373
+ changed = false;
374
+ for (const [k, entry] of Object.entries(state.installations)) {
375
+ if (JSON.parse(k)[0] !== backendId || affected.has(entry.release.id)) continue;
376
+ if (Object.keys(entry.release.dependencies ?? {}).some((d) => affected.has(d))) {
377
+ affected.add(entry.release.id);
378
+ changed = true;
379
+ }
380
+ }
381
+ }
382
+ for (const [k, a] of Object.entries(state.activations)) if (a.backendId === backendId && affected.has(a.pluginId)) deactivate.add(k);
383
+ };
384
+ if (changing) for (const contract of dataCatalog.contracts) {
385
+ const identity = contract.migrationIdentity ?? `data-${contract.id}`;
386
+ const old = state.dataContracts?.[key(backend.databaseId, contract.id)];
387
+ if (old && old.status !== "installed") throw new Error(`Data contract requires reconciliation: ${contract.id}`);
388
+ if (old && old.digest !== lifecycleDigest(contract)) {
389
+ if (old.contract.version === contract.version) throw new Error(`Data contract changed without version bump: ${contract.id}`);
390
+ if (lt(contract.version, old.contract.version)) throw new Error("Data contract downgrade requires explicit migration");
391
+ if (canonical(contract.schema) !== canonical(old.contract.schema) && contract.migrations.length <= old.contract.migrations.length) throw new Error(`Schema changed without an appended migration: ${contract.id}`);
392
+ }
393
+ for (const [installationKey, installation] of Object.entries(state.installations)) {
394
+ if (state.backends[JSON.parse(installationKey)[0]]?.databaseId !== backend.databaseId) continue;
395
+ const range = installation.release.schemaDependencies?.[contract.id];
396
+ if (range && !satisfies(contract.version, range)) throw new Error(`Installed plugin requires incompatible data contract: ${installation.release.id}`);
397
+ }
398
+ for (const record of Object.values(state.migrations).filter((m) => m.databaseId === backend.databaseId && m.pluginId === identity)) {
399
+ const m = contract.migrations[record.position];
400
+ if (!m || m.id !== record.migrationId || lifecycleDigest(m) !== record.checksum) throw new Error(`Applied contract migration changed or removed: ${record.migrationId}`);
401
+ if (record.status !== "applied") throw new Error(`Data contract migration requires reconciliation: ${record.migrationId}`);
402
+ }
403
+ for (const [position, m] of contract.migrations.entries()) {
404
+ const statements = m.sql[backend.dialect];
405
+ if (!statements?.length) throw new Error(`Data contract migration ${m.id} does not support ${backend.dialect}`);
406
+ const tenantId = m.scope === "tenant" ? request.tenantId : null;
407
+ const k = key(backend.databaseId, identity, m.id, tenantId ?? "*");
408
+ if (!state.migrations[k]) migrations.push({ key: k, record: { databaseId: backend.databaseId, pluginId: identity, migrationId: m.id, position, tenantId, checksum: lifecycleDigest(m), status: "running" }, statements });
409
+ }
410
+ if (!old || old.digest !== lifecycleDigest(contract) || contract.migrations.some((m) => !state.migrations[key(backend.databaseId, identity, m.id, m.scope === "tenant" ? request.tenantId : "*")])) {
411
+ for (const activation of Object.values(state.activations)) {
412
+ if (state.backends[activation.backendId]?.databaseId !== backend.databaseId) continue;
413
+ const release = state.installations[key(activation.backendId, activation.pluginId)]?.release;
414
+ if (release && this.catalog([release]).contracts.some((c) => c.id === contract.id)) markDependents(activation.backendId, activation.pluginId);
415
+ }
416
+ }
417
+ }
418
+ if (changing) for (const p of plugins) {
419
+ const digest = lifecycleDigest(p), old = state.installations[key(backend.id, p.id)];
420
+ if (old && old.status !== "installed") throw new Error(`Installation requires reconciliation: ${p.id}`);
421
+ if (old && old.digest !== digest) {
422
+ if (old.release.version === p.version) throw new Error(`Release changed without version bump: ${p.id}`);
423
+ if (lt(p.version, old.release.version)) throw new Error("Plugin downgrade requires an explicit data migration");
424
+ if (operation === "install") throw new Error("Use update for an existing release");
425
+ markDependents(backend.id, p.id);
426
+ }
427
+ for (const record of Object.values(state.migrations).filter((m) => m.databaseId === backend.databaseId && m.pluginId === p.id && !this.isContractMigration(m))) {
428
+ const m = p.migrations.find((m2) => m2.id === record.migrationId);
429
+ if (!m || p.migrations.indexOf(m) !== record.position || lifecycleDigest(m) !== record.checksum) throw new Error(`Applied migration changed or removed: ${record.migrationId}`);
430
+ if (record.status !== "applied") throw new Error(`Migration requires reconciliation: ${record.migrationId}`);
431
+ }
432
+ for (const m of p.migrations) {
433
+ const statements = m.sql[backend.dialect];
434
+ if (!statements?.length) throw new Error(`Migration ${m.id} does not support ${backend.dialect}`);
435
+ const tenantId = m.scope === "tenant" ? request.tenantId : null;
436
+ const k = key(backend.databaseId, p.id, m.id, tenantId ?? "*");
437
+ if (!state.migrations[k]) {
438
+ migrations.push({ key: k, record: { databaseId: backend.databaseId, pluginId: p.id, migrationId: m.id, position: p.migrations.indexOf(m), tenantId, checksum: lifecycleDigest(m), status: "running" }, statements });
439
+ if (m.scope === "database") {
440
+ for (const b of Object.values(state.backends)) if (b.databaseId === backend.databaseId) markDependents(b.id, p.id);
441
+ }
442
+ }
443
+ }
444
+ if (p.infrastructure) {
445
+ const k = key(backend.id, p.id), digest2 = lifecycleDigest({ backend, plugin: p });
446
+ if (state.infrastructure[k] && state.infrastructure[k].status !== "applied") throw new Error("Infrastructure requires reconciliation");
447
+ if (state.infrastructure[k]?.digest !== digest2) infrastructure.push({ key: k, digest: digest2, plugin: p, backend });
448
+ }
449
+ }
450
+ if (operation === "disable" || operation === "uninstall") {
451
+ const dependents = Object.values(state.activations).filter((a) => a.backendId === backend.id && a.tenantId === request.tenantId && a.enabled && a.pluginId !== request.pluginId && this.dependsOn(state, backend.id, a.pluginId, request.pluginId));
452
+ if (dependents.length) throw new Error("Disable dependent plugins first");
453
+ const activation = key(backend.id, request.tenantId, request.pluginId);
454
+ if (operation === "disable") deactivate.add(activation);
455
+ else {
456
+ if (Object.values(state.activations).some((a) => a.backendId === backend.id && a.pluginId === request.pluginId && a.enabled)) throw new Error("Disable plugin for every tenant on this backend before uninstalling");
457
+ if (Object.entries(state.installations).some(([k, i]) => JSON.parse(k)[0] === backend.id && i.release.id !== request.pluginId && this.dependsOn(state, backend.id, i.release.id, request.pluginId))) throw new Error("Uninstall dependent plugins first");
458
+ }
459
+ }
460
+ if (operation === "enable") {
461
+ for (const p of plugins) if (!this.ready(state, backend, request.tenantId, p)) throw new Error(`Install/migrate compatible release before enabling: ${p.id}`);
462
+ }
463
+ const body = { format: 1, revision: state.revision, frameworkVersion: this.frameworkVersion, request: { ...request }, backend, ...dataCatalog.contracts.length ? { dataCatalog } : {}, plugins, migrations, infrastructure, deactivate: [...deactivate].sort(), infrastructureProvider: this.project.infrastructure?.identity ?? null, retainData: true };
464
+ return JSON.parse(canonical({ ...body, id: lifecycleDigest(body) }));
465
+ }
466
+ dependsOn(state, backendId, pluginId, target, seen = /* @__PURE__ */ new Set()) {
467
+ if (seen.has(pluginId)) return false;
468
+ seen.add(pluginId);
469
+ return Object.keys(state.installations[key(backendId, pluginId)]?.release.dependencies ?? {}).some((d) => d === target || this.dependsOn(state, backendId, d, target, seen));
470
+ }
471
+ ready(state, backend, tenantId, p) {
472
+ const installed = state.installations[key(backend.id, p.id)];
473
+ if (installed?.status !== "installed" || installed.digest !== lifecycleDigest(p)) return false;
474
+ if (Object.values(state.migrations).some((m) => m.databaseId === backend.databaseId && m.pluginId === p.id && !this.isContractMigration(m) && (m.status !== "applied" || !p.migrations.some((definition) => definition.id === m.migrationId && p.migrations.indexOf(definition) === m.position && lifecycleDigest(definition) === m.checksum)))) return false;
475
+ return this.catalog([p]).contracts.every((c) => this.contractReady(state, backend, tenantId, c)) && p.migrations.every((m) => {
476
+ const record = state.migrations[key(backend.databaseId, p.id, m.id, m.scope === "tenant" ? tenantId : "*")];
477
+ return record?.status === "applied" && record.checksum === lifecycleDigest(m);
478
+ }) && (!p.infrastructure || state.infrastructure[key(backend.id, p.id)]?.digest === lifecycleDigest({ backend, plugin: p }) && state.infrastructure[key(backend.id, p.id)]?.status === "applied");
479
+ }
480
+ /**
481
+ * Checks that a tenant activation and its entire registered dependency closure match ready ledger releases.
482
+ * @param backendId - Registered execution backend ID.
483
+ * @param tenantId - Verified concrete tenant ID.
484
+ * @param pluginId - Registered feature ID to check.
485
+ * @returns True only when matching installation, migrations, infrastructure and activation digests are all ready; false for missing/incompatible readiness.
486
+ * @throws Rejects malformed IDs/state, unknown or mismatched backend/tenant and store failures.
487
+ *
488
+ * @remarks
489
+ * Read-only admission check; a later state change can revoke access. Host must still enforce current tenant/resource authorization.
490
+ *
491
+ * @example
492
+ * ```typescript
493
+ * import { PluginLifecycle } from '@alneas/eaos-server/lifecycle-engine';
494
+ * export function admitted(lifecycle: PluginLifecycle, backendId: string, tenantId: string, pluginId: string) { return lifecycle.isEnabled(backendId, tenantId, pluginId); }
495
+ * ```
496
+ */
497
+ async isEnabled(backendId, tenantId, pluginId) {
498
+ const state = await this.project.store.read();
499
+ assertState(state);
500
+ return this.enabledInState(state, backendId, tenantId, pluginId);
501
+ }
502
+ enabledInState(state, backendId, tenantId, pluginId) {
503
+ const backend = this.target({ operation: "enable", backendId, tenantId, pluginId });
504
+ if (!state.backends[backend.id] || canonical(state.backends[backend.id]) !== canonical(backend)) return false;
505
+ let plugins;
506
+ try {
507
+ plugins = this.order(pluginId);
508
+ } catch {
509
+ return false;
510
+ }
511
+ return plugins.every((p) => this.ready(state, backend, tenantId, p) && state.activations[key(backendId, tenantId, p.id)]?.enabled === true && state.activations[key(backendId, tenantId, p.id)]?.digest === lifecycleDigest(p));
512
+ }
513
+ /**
514
+ * Reads inventory including registered uninstalled code and orphaned historical installations.
515
+ *
516
+ * One read-only control-state snapshot; includes orphaned installation records and registered, uninstalled code.
517
+ * @param options - Optional backendId and tenantId filters; omitted returns all known inventory.
518
+ * @returns A revision-bound inventory with effective enabled state distinct from requested activation.
519
+ * @throws Rejects invalid filter IDs, unknown backend, corrupt installation/activation records, dedicated tenant mismatch or store failure.
520
+ *
521
+ * @remarks
522
+ * Performs one ledger read without writes. Dedicated backends for another tenant are omitted unless explicitly requested, in which case the request rejects.
523
+ *
524
+ * @example
525
+ * ```typescript
526
+ * import { PluginLifecycle } from '@alneas/eaos-server/lifecycle-engine';
527
+ * export function inventory(lifecycle: PluginLifecycle) { return lifecycle.list(); }
528
+ * ```
529
+ */
530
+ async list(options = {}) {
531
+ if (options.backendId !== void 0) id(options.backendId);
532
+ if (options.tenantId !== void 0) id(options.tenantId);
533
+ const state = await this.project.store.read();
534
+ assertState(state);
535
+ const installations = Object.entries(state.installations).map(([entryKey, installation]) => {
536
+ const parts = JSON.parse(entryKey);
537
+ if (!Array.isArray(parts) || parts.length !== 2 || parts.some((p) => typeof p !== "string")) throw new Error("Invalid installation key");
538
+ const [backendId, pluginId] = parts;
539
+ id(backendId);
540
+ id(pluginId);
541
+ if (installation.release?.id !== pluginId || !valid(installation.release.version) || !["installing", "installed", "failed"].includes(installation.status)) throw new Error("Invalid installation record");
542
+ return { backendId, pluginId };
543
+ });
544
+ const activations = Object.values(state.activations);
545
+ const backendIds = /* @__PURE__ */ new Set([...this.backends.keys(), ...Object.keys(state.backends), ...installations.map((p) => p.backendId), ...activations.map((p) => p.backendId)]);
546
+ for (const backendId of backendIds) id(backendId);
547
+ for (const activation of activations) {
548
+ id(activation.tenantId);
549
+ id(activation.pluginId);
550
+ if (typeof activation.enabled !== "boolean") throw new Error("Invalid activation record");
551
+ }
552
+ if (options.backendId && !backendIds.has(options.backendId)) throw new Error(`Unknown backend: ${options.backendId}`);
553
+ const targets = options.backendId ? [options.backendId] : backendIds.size ? [...backendIds].sort() : [null];
554
+ const plugins = [];
555
+ for (const backendId of targets) {
556
+ const backend = backendId ? this.backends.get(backendId) : void 0;
557
+ if (options.tenantId && backend?.mode === "dedicated" && backend.tenantId !== options.tenantId) {
558
+ if (options.backendId) throw new Error("Tenant does not belong to dedicated backend");
559
+ continue;
560
+ }
561
+ const pluginIds = /* @__PURE__ */ new Set([...this.plugins.keys(), ...installations.filter((p) => p.backendId === backendId).map((p) => p.pluginId), ...activations.filter((p) => p.backendId === backendId).map((p) => p.pluginId)]);
562
+ for (const pluginId of [...pluginIds].sort()) {
563
+ const registered = this.plugins.get(pluginId);
564
+ const installed = backendId ? state.installations[key(backendId, pluginId)] : void 0;
565
+ const tenantIds = options.tenantId ? [options.tenantId] : [...new Set(activations.filter((p) => p.backendId === backendId && p.pluginId === pluginId).map((p) => p.tenantId))].sort();
566
+ plugins.push({
567
+ pluginId,
568
+ backendId,
569
+ backendConfigured: !!backend,
570
+ registeredVersion: registered?.version ?? null,
571
+ installedVersion: installed?.release.version ?? null,
572
+ installationStatus: installed?.status ?? "not-installed",
573
+ releaseMatches: installed ? !!registered && installed.digest === lifecycleDigest(registered) : null,
574
+ tenants: tenantIds.map((tenantId) => ({
575
+ tenantId,
576
+ requestedEnabled: backendId ? state.activations[key(backendId, tenantId, pluginId)]?.enabled === true : false,
577
+ enabled: !!backend && !!backendId && (backend.mode !== "dedicated" || backend.tenantId === tenantId) && this.enabledInState(state, backendId, tenantId, pluginId)
578
+ }))
579
+ });
580
+ }
581
+ }
582
+ return { format: 1, revision: state.revision, frameworkVersion: this.frameworkVersion, plugins };
583
+ }
584
+ /**
585
+ * Runs a host callback only after the current plugin dependency closure passes lifecycle admission.
586
+ * @typeParam T - Value resolved by the admitted callback.
587
+ * @param backendId - Registered backend ID.
588
+ * @param tenantId - Verified concrete tenant ID.
589
+ * @param pluginId - Feature plugin ID.
590
+ * @param callback - Async work invoked once after admission; must enforce tenant/resource authorization itself.
591
+ * @returns The callback result without cloning.
592
+ * @throws Rejects inactive plugin, lifecycle/store validation failure or callback rejection.
593
+ *
594
+ * @remarks
595
+ * The admission check does not hold a lock or pin activation throughout callback execution. Host quiesce/drain and live authorization remain required.
596
+ *
597
+ * @example
598
+ * ```typescript
599
+ * import { PluginLifecycle } from '@alneas/eaos-server/lifecycle-engine';
600
+ * export function runAdmitted<T>(lifecycle: PluginLifecycle, backendId: string, tenantId: string, pluginId: string, work: () => Promise<T>) { return lifecycle.run(backendId, tenantId, pluginId, work); }
601
+ * ```
602
+ */
603
+ async run(backendId, tenantId, pluginId, callback) {
604
+ if (!await this.isEnabled(backendId, tenantId, pluginId)) throw new Error("Plugin is inactive");
605
+ return callback();
606
+ }
607
+ /**
608
+ * Checks plan freshness and invokes host infrastructure previews without applying ledger changes.
609
+ * @param plan - Unmodified lifecycle plan to preview.
610
+ * @returns Preview results in infrastructure-step order, or an empty array when no infrastructure is needed.
611
+ * @throws Rejects stale/modified plan, missing infrastructure adapter or propagated plan/preview errors.
612
+ *
613
+ * @remarks
614
+ * Infrastructure previews run concurrently through Promise.all. Host preview adapters own any external read/preview effects; this method executes no migrations.
615
+ *
616
+ * @example
617
+ * ```typescript
618
+ * import { PluginLifecycle, type LifecyclePlan } from '@alneas/eaos-server/lifecycle-engine';
619
+ * export function preview(lifecycle: PluginLifecycle, plan: LifecyclePlan) { return lifecycle.preview(plan); }
620
+ * ```
621
+ */
622
+ async preview(plan) {
623
+ const fresh = await this.plan(plan.request);
624
+ if (canonical(fresh) !== canonical(plan)) throw new Error("Stale or modified lifecycle plan");
625
+ if (plan.infrastructure.length && !this.project.infrastructure) throw new Error("Infrastructure executor is required");
626
+ return Promise.all(plan.infrastructure.map((s) => this.project.infrastructure.preview(s)));
627
+ }
628
+ /**
629
+ * Revalidates and executes a reviewed lifecycle plan while holding the host's exclusive ledger lock.
630
+ * @param plan - Unmodified plan bound to the current ledger revision and registered configuration.
631
+ * @returns Resolves without a value after ledger updates and requested side effects complete.
632
+ * @throws Rejects stale/modified plan, missing executors/quiesce, reconciliation conflicts or host store, migration, infrastructure or drain failures.
633
+ *
634
+ * @remarks
635
+ * Before upgrade side effects, affected activations are disabled durably and all-instance quiesce is required. Infrastructure/migration running/applied/failed receipts are persisted separately. Errors can follow partial external success; no automatic rollback/retry occurs. Uninstall retains business data and migration history.
636
+ *
637
+ * @example
638
+ * ```typescript
639
+ * import { PluginLifecycle, type LifecyclePlan } from '@alneas/eaos-server/lifecycle-engine';
640
+ * export function applyReviewed(lifecycle: PluginLifecycle, plan: LifecyclePlan) { return lifecycle.apply(plan); }
641
+ * ```
642
+ */
643
+ async apply(plan) {
644
+ plan = JSON.parse(canonical(plan));
645
+ await this.project.store.withLock(async (writer) => {
646
+ const state = await writer.read(), fresh = this.makePlan(plan.request, state);
647
+ if (canonical(fresh) !== canonical(plan)) throw new Error("Stale or modified lifecycle plan");
648
+ if (plan.infrastructure.length && !this.project.infrastructure) throw new Error("Infrastructure executor is required");
649
+ if (["install", "update"].includes(plan.request.operation) && plan.deactivate.length && !this.project.quiesce) throw new Error("A quiesce executor is required for active deployments");
650
+ const save = async () => {
651
+ state.revision++;
652
+ await writer.write(state);
653
+ };
654
+ const { request, backend } = plan;
655
+ state.backends[backend.id] = backend;
656
+ for (const k of plan.deactivate) if (state.activations[k]) state.activations[k].enabled = false;
657
+ if (request.operation === "install" || request.operation === "update") {
658
+ for (const p of plan.plugins) state.installations[key(backend.id, p.id)] = { release: p, digest: lifecycleDigest(p), status: "installing" };
659
+ state.dataContracts ??= {};
660
+ for (const contract of plan.dataCatalog?.contracts ?? []) state.dataContracts[key(backend.databaseId, contract.id)] = { contract, digest: lifecycleDigest(contract), status: "installing" };
661
+ await save();
662
+ try {
663
+ if (plan.deactivate.length) await this.project.quiesce(plan.deactivate.map((k) => state.activations[k]));
664
+ for (const step of plan.infrastructure) {
665
+ state.infrastructure[step.key] = { digest: step.digest, status: "running" };
666
+ await save();
667
+ try {
668
+ await this.project.infrastructure.apply(step);
669
+ } catch (error) {
670
+ state.infrastructure[step.key].status = "failed";
671
+ await save();
672
+ throw error;
673
+ }
674
+ state.infrastructure[step.key].status = "applied";
675
+ await save();
676
+ }
677
+ for (const step of plan.migrations) {
678
+ state.migrations[step.key] = { ...step.record };
679
+ await save();
680
+ try {
681
+ await this.project.migrate(step, backend);
682
+ } catch (error) {
683
+ state.migrations[step.key].status = "failed";
684
+ await save();
685
+ throw error;
686
+ }
687
+ state.migrations[step.key].status = "applied";
688
+ await save();
689
+ }
690
+ for (const contract of plan.dataCatalog?.contracts ?? []) state.dataContracts[key(backend.databaseId, contract.id)].status = "installed";
691
+ for (const p of plan.plugins) state.installations[key(backend.id, p.id)].status = "installed";
692
+ } catch (error) {
693
+ for (const contract of plan.dataCatalog?.contracts ?? []) state.dataContracts[key(backend.databaseId, contract.id)].status = "failed";
694
+ for (const p of plan.plugins) state.installations[key(backend.id, p.id)].status = "failed";
695
+ await save();
696
+ throw error;
697
+ }
698
+ } else if (request.operation === "enable") {
699
+ for (const p of plan.plugins) state.activations[key(backend.id, request.tenantId, p.id)] = { backendId: backend.id, tenantId: request.tenantId, pluginId: p.id, digest: lifecycleDigest(p), enabled: true };
700
+ } else if (request.operation === "uninstall") delete state.installations[key(backend.id, request.pluginId)];
701
+ await save();
702
+ });
703
+ }
704
+ };
705
+
706
+ // src/lifecycle-engine/baseline.ts
707
+ function createDataContractBaseline(source, version2, sql) {
708
+ if (!valid2(version2) || !lt2(source.version, version2) || !source.migrations.length || source.migrations.some((m) => m.scope !== "database")) throw Error("Baseline requires a newer contract version and database-scoped migrations");
709
+ const dialects = [...new Set(source.migrations.flatMap((m) => Object.keys(m.sql)))].sort();
710
+ if (JSON.stringify(dialects) !== JSON.stringify(Object.keys(sql).sort())) throw Error("Baseline must preserve every declared dialect");
711
+ const target = { ...structuredClone(source), version: version2, migrations: [{ id: `001_baseline_v${version2.replaceAll(".", "_").replaceAll("-", "_")}`, scope: "database", sql: structuredClone(sql) }] };
712
+ resolveDataContracts2([{ ...source, dependencies: void 0 }], { [source.id]: source.version });
713
+ resolveDataContracts2([{ ...target, dependencies: void 0 }], { [target.id]: version2 });
714
+ const body = { format: 1, source: structuredClone(source), target, replaces: source.migrations.map((m, position) => ({ id: m.id, position, checksum: lifecycleDigest(m) })) };
715
+ return { ...body, hash: lifecycleDigest(body) };
716
+ }
717
+ async function rebaseDataContractBaseline(store, databaseId, baseline, options) {
718
+ const { hash, ...body } = baseline;
719
+ if (hash !== lifecycleDigest(body) || hash !== createDataContractBaseline(baseline.source, baseline.target.version, baseline.target.migrations[0]?.sql ?? {}).hash) throw Error("Baseline integrity differs");
720
+ return store.withLock(async (writer) => {
721
+ const state = await writer.read();
722
+ if (state.revision !== options.expectedRevision) throw Error("Baseline plan is stale");
723
+ const key2 = JSON.stringify([databaseId, baseline.source.id]), installed = state.dataContracts?.[key2];
724
+ if (!installed || installed.status !== "installed" || installed.digest !== lifecycleDigest(baseline.source)) throw Error("Baseline source contract is not completely installed");
725
+ const identity = baseline.source.migrationIdentity ?? `data-${baseline.source.id}`;
726
+ const history = Object.entries(state.migrations).filter(([, r]) => r.databaseId === databaseId && r.pluginId === identity);
727
+ if (history.length !== baseline.replaces.length || baseline.replaces.some((replaced) => !history.some(([, r]) => r.migrationId === replaced.id && r.position === replaced.position && r.checksum === replaced.checksum && r.tenantId === null && r.status === "applied"))) throw Error("Baseline history is partial, changed or dirty");
728
+ for (const [key3, installed2] of Object.entries(state.installations)) {
729
+ const range = installed2.release.schemaDependencies?.[baseline.source.id];
730
+ if (state.backends[JSON.parse(key3)[0]]?.databaseId === databaseId && range && !satisfies2(baseline.target.version, range)) throw Error("Baseline violates an installed plugin contract range");
731
+ }
732
+ const live = Object.values(state.activations).filter((a) => a.enabled && state.backends[a.backendId]?.databaseId === databaseId && state.installations[JSON.stringify([a.backendId, a.pluginId])]?.release.schemaDependencies?.[baseline.source.id]);
733
+ if (live.length && !options.quiesce) throw Error("Baseline requires all-instance drain");
734
+ if (live.length) await options.quiesce({ databaseId, pluginIds: [...new Set(live.map((a) => a.pluginId))] });
735
+ for (const a of live) a.enabled = false;
736
+ state.baselineHistory ??= {};
737
+ const archiveKey = JSON.stringify([databaseId, identity, hash]);
738
+ if (state.baselineHistory[archiveKey]) throw Error("Baseline history already exists");
739
+ state.baselineHistory[archiveKey] = { hash, source: baseline.source, migrations: Object.fromEntries(history) };
740
+ for (const [key3] of history) delete state.migrations[key3];
741
+ const m = baseline.target.migrations[0];
742
+ state.migrations[JSON.stringify([databaseId, identity, m.id, "*"])] = { databaseId, pluginId: identity, migrationId: m.id, position: 0, tenantId: null, checksum: lifecycleDigest(m), status: "applied" };
743
+ state.dataContracts[key2] = { contract: structuredClone(baseline.target), digest: lifecycleDigest(baseline.target), status: "installed" };
744
+ state.revision++;
745
+ await writer.write(state);
746
+ return { baselineHash: hash, revision: state.revision, rebased: history.length, ddlExecuted: false };
747
+ });
748
+ }
749
+
750
+ export {
751
+ createDataContractBaseline,
752
+ rebaseDataContractBaseline,
753
+ FRAMEWORK_VERSION,
754
+ emptyLifecyclePlatformState,
755
+ emptyLifecycleState,
756
+ normalizeLifecycleState,
757
+ canonical,
758
+ lifecycleDigest,
759
+ PluginLifecycle,
760
+ dataContractTables2 as dataContractTables,
761
+ resolveDataContracts2 as resolveDataContracts,
762
+ assertDataContractBinding,
763
+ collectSchemaMigrations,
764
+ createJsonDocumentAssertion,
765
+ assertJsonDocumentContracts,
766
+ planBackendMigration,
767
+ assertBackendMigrationVerification,
768
+ assertBackendMigrationCutover
769
+ };
770
+ //# sourceMappingURL=chunk-SDJNYPYK.js.map