@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,1395 @@
1
+ import {
2
+ atomicSqlBindings,
3
+ inheritAtomicSqlSession
4
+ } from "./chunk-3VITSHN3.js";
5
+
6
+ // src/plugin-data/json-documents.ts
7
+ import { assertBusinessId } from "@alneas/eaos-core/plugin-sdk";
8
+ import { assertDataContractBinding, createJsonDocumentAssertion } from "@alneas/eaos-server/lifecycle-engine";
9
+
10
+ // src/plugin-data/index.ts
11
+ import { Hono } from "hono";
12
+ import { bodyLimit } from "hono/body-limit";
13
+ import { assertPayloadWithinPolicy as assertPayloadWithinPolicy2, resolvePluginStoragePolicy as resolvePluginStoragePolicy2 } from "@alneas/eaos-core/plugin-sdk";
14
+
15
+ // src/plugin-data/commands.ts
16
+ import Ajv from "ajv";
17
+ import addFormats from "ajv-formats";
18
+ var RecordCommandService = class {
19
+ /**
20
+ * Compiles the declared operation schemas and binds real host persistence/admission capabilities.
21
+ * @param host - Live authorization and durable store or transaction; transaction takes precedence when provided.
22
+ * @param definition - Plugin schemas, write-operation allowlist, record-ID resolver, initial-state factory, reducer and invariant validator.
23
+ * @throws Throws for absent durable store/transaction or live authorization and for invalid strict Ajv schemas.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * import { RecordCommandService, type CommandState, type CommandHost } from '@alneas/eaos-server/plugin-data';
28
+ * export function connectAggregate<T extends CommandState>(host: CommandHost<T>, definition: ConstructorParameters<typeof RecordCommandService<T>>[1]) { return new RecordCommandService(host, definition); }
29
+ * ```
30
+ */
31
+ constructor(host, definition) {
32
+ this.host = host;
33
+ this.definition = definition;
34
+ if (!host?.store && typeof host?.transaction !== "function" || typeof host.authorize !== "function") throw new Error("Durable store and live authorization required");
35
+ const ajv2 = new Ajv({ strict: true, coerceTypes: false });
36
+ addFormats(ajv2);
37
+ this.validators = new Map(Object.entries(definition.schemas).map(([key, schema]) => [key, ajv2.compile(schema)]));
38
+ }
39
+ host;
40
+ definition;
41
+ validators;
42
+ /**
43
+ * Validates and admits one aggregate operation, running a reducer against a private current snapshot.
44
+ * @param principal - Host-authenticated current tenant/user context; structural validation alone proves no membership.
45
+ * @param operation - Declared schema key; membership in definition.writes selects persistence.
46
+ * @param input - Operation-schema input; writes require a safe integer revision matching the current snapshot, including 0 for a new/unstored aggregate.
47
+ * @returns A tenant/record/revision envelope with cloned reducer data, or resulting state when the reducer returns undefined; read-only reducers do not persist mutations.
48
+ * @throws Rejects malformed principal (401), denied live admission (403), schema/scope errors (400), stale revision/capacity/CAS conflict (409), invalid foreign records/reducer scope, and propagated reducer/invariant/clock/store/transaction failures.
49
+ *
50
+ * @remarks
51
+ * Record ID defaults to state and must be nonblank/up to 256 characters. Writes append actor/UTC history and reject over 5000 entries or 8 MiB JSON state. Host transaction projections may start at revision 0. Rejections can follow partial plain-store/adapter effects; callers must reload before retrying uncertain outcomes.
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * import { RecordCommandService, type CommandState, type PluginPrincipal } from '@alneas/eaos-server/plugin-data';
56
+ * export function executeAggregate<T extends CommandState>(service: RecordCommandService<T>, principal: PluginPrincipal, operation: string, input: unknown) { return service.execute(principal, operation, input); }
57
+ * ```
58
+ */
59
+ async execute(principal, operation, input) {
60
+ assertPrincipal(principal);
61
+ const p = structuredClone(principal), args = structuredClone(input);
62
+ const allow = async (state) => {
63
+ if (await this.host.authorize(structuredClone(p), operation, structuredClone(args), state && structuredClone(state)) !== true) throw new PluginError(403, "Plugin access denied");
64
+ };
65
+ await allow();
66
+ if (!this.validators.get(operation)?.(args)) throw new PluginError(400, "Invalid command");
67
+ const id2 = this.definition.recordId?.(args) ?? "state";
68
+ if (typeof id2 !== "string" || !id2.trim() || id2.length > 256) throw new PluginError(400, "Invalid record scope");
69
+ const run = async (store) => {
70
+ const record = await store.read(p.tenantId, id2);
71
+ if (record && (record.id !== id2 || record.value.tenantId !== p.tenantId || !Number.isSafeInteger(record.revision) || record.revision < (this.host.transaction ? 0 : 1))) throw new Error("Foreign or invalid plugin record");
72
+ const state = structuredClone(record?.value ?? this.definition.initial(p.tenantId, id2)), current = structuredClone(state), revision = record?.revision ?? 0;
73
+ this.definition.validate(state);
74
+ await allow(current);
75
+ const write = this.definition.writes.includes(operation);
76
+ if (write && (!Number.isSafeInteger(args.revision) || args.revision !== revision)) throw new PluginError(409, "Plugin revision changed");
77
+ const at = (this.host.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
78
+ const result = await this.definition.reduce({ principal: structuredClone(p), operation, args, state, at, recordId: id2 });
79
+ if (write) {
80
+ if (state.tenantId !== p.tenantId) throw new Error("Reducer changed tenant");
81
+ state.history.push({ operation, actorId: p.userId, at });
82
+ this.definition.validate(state);
83
+ if (state.history.length > 5e3 || Buffer.byteLength(JSON.stringify(state)) > 8 * 1024 * 1024) throw new PluginError(409, "Plugin aggregate capacity reached");
84
+ await allow(current);
85
+ const next = { id: id2, revision: revision + 1, value: state };
86
+ if (!record || revision === 0) await store.create(p.tenantId, next);
87
+ else if (!await store.replace(p.tenantId, next, revision)) throw new PluginError(409, "Plugin revision changed");
88
+ } else await allow(current);
89
+ if (this.host.transaction) await allow(current);
90
+ return { tenantId: p.tenantId, recordId: id2, revision: revision + (write ? 1 : 0), data: structuredClone(result === void 0 ? state : result) };
91
+ };
92
+ return this.host.transaction ? this.host.transaction(p, run) : run(this.host.store);
93
+ }
94
+ };
95
+
96
+ // src/plugin-data/audit.ts
97
+ var eventKey = (sequence) => "audit/event/" + String(sequence).padStart(16, "0");
98
+ var jsonHash = (value) => atomicHash(value === void 0 ? null : JSON.parse(JSON.stringify(value)));
99
+ async function appendAtomicAudit(tx, p, operation, input, result, commandKey = null) {
100
+ assertPrincipal(p);
101
+ if (!operation || operation.length > 240 || commandKey !== null && !commandKey.startsWith("command/")) throw new PluginError(400, "Invalid trusted audit operation");
102
+ const head = await tx.get("audit/head");
103
+ if (head && (head.value.version !== 1 || head.value.tenantId !== p.tenantId || !Number.isSafeInteger(head.value.sequence) || head.value.sequence < 1 || !/^[a-f0-9]{64}$/.test(head.value.hash))) throw new PluginError(409, "Audit head differs");
104
+ if (head) {
105
+ const previous = await tx.get(eventKey(head.value.sequence));
106
+ if (!previous || previous.value.hash !== head.value.hash) throw new PluginError(409, "Previous audit event unavailable");
107
+ const { hash: hash2, ...v } = previous.value;
108
+ if (hash2 !== atomicHash(v)) throw new PluginError(409, "Previous audit integrity differs");
109
+ }
110
+ const sequence = (head?.value.sequence ?? 0) + 1;
111
+ if (!Number.isSafeInteger(sequence) || sequence > 1e12) throw new PluginError(409, "Audit sequence at capacity");
112
+ const value = { version: 1, tenantId: p.tenantId, sequence, operation, actorId: p.userId, at: (/* @__PURE__ */ new Date()).toISOString(), inputHash: jsonHash(input), resultHash: jsonHash(result), commandKey, previousHash: head?.value.hash ?? null }, event = { ...value, hash: atomicHash(value) };
113
+ await tx.put(eventKey(sequence), event, 0);
114
+ await tx.put("audit/head", { version: 1, tenantId: p.tenantId, sequence, hash: event.hash }, head?.revision ?? 0);
115
+ return event;
116
+ }
117
+ async function readAtomicAudit(tx, p, afterSequence = 0, limit = 50) {
118
+ assertPrincipal(p);
119
+ if (!Number.isSafeInteger(afterSequence) || afterSequence < 0 || !Number.isSafeInteger(limit) || limit < 1 || limit > 100) throw new PluginError(400, "Invalid audit page");
120
+ const rows = await tx.list("audit/event/", afterSequence ? eventKey(afterSequence) : "", limit + 1);
121
+ const events = [];
122
+ for (const row of rows.slice(0, limit)) {
123
+ const { hash: hash2, ...v } = row.value;
124
+ if (v.tenantId !== p.tenantId || v.version !== 1 || !Number.isSafeInteger(v.sequence) || v.sequence < 1 || row.id !== eventKey(v.sequence) || row.revision !== 1 || hash2 !== atomicHash(v)) throw new PluginError(409, "Audit event integrity differs");
125
+ events.push(row.value);
126
+ }
127
+ return { events, nextSequence: rows.length > limit ? events.at(-1).sequence : null };
128
+ }
129
+ async function verifyAtomicAudit(tx, p) {
130
+ assertPrincipal(p);
131
+ const head = await tx.get("audit/head");
132
+ let after = 0, count = 0, previousHash = null;
133
+ for (; ; ) {
134
+ const page = await readAtomicAudit(tx, p, after, 100);
135
+ for (const event of page.events) {
136
+ if (++count > 1e4) throw new PluginError(409, "Audit verification exceeds 10000 events; host archival verifier required");
137
+ if (event.sequence !== count || event.previousHash !== previousHash) throw new PluginError(409, "Audit sequence or previous hash differs");
138
+ if (event.commandKey) {
139
+ const receipt = await tx.get(event.commandKey);
140
+ if (!receipt || receipt.revision !== 1 || receipt.value.hash !== event.inputHash || jsonHash(receipt.value.result) !== event.resultHash) throw new PluginError(409, "Audit command receipt differs");
141
+ }
142
+ previousHash = event.hash;
143
+ after = event.sequence;
144
+ }
145
+ if (page.nextSequence === null) break;
146
+ }
147
+ if (count === 0 && head || count > 0 && (!head || head.value.version !== 1 || head.value.tenantId !== p.tenantId || head.value.sequence !== count || head.value.hash !== previousHash)) throw new PluginError(409, "Audit head or event tail differs");
148
+ return { tenantId: p.tenantId, status: "verified", count, headHash: previousHash, coverage: "atomic-journal-v1", externalTrustAnchor: false };
149
+ }
150
+
151
+ // src/plugin-data/atomic.ts
152
+ import { canonicalJsonHash } from "@alneas/eaos-core/serialization";
153
+ import { assertPayloadWithinPolicy, resolvePluginStoragePolicy } from "@alneas/eaos-core/plugin-sdk";
154
+ function storagePayload(policy, value) {
155
+ try {
156
+ return assertPayloadWithinPolicy(policy, value);
157
+ } catch (error) {
158
+ if (error instanceof Error && error.message === "Storage policy payload limit exceeded") throw new PluginError(409, error.message);
159
+ throw error;
160
+ }
161
+ }
162
+ function joinedAtomicSqlExecutor(session, principal) {
163
+ assertPrincipal(principal);
164
+ const binding = atomicSqlBindings.get(session);
165
+ if (!binding || binding.principal.tenantId !== principal.tenantId || binding.principal.userId !== principal.userId) throw new Error("Live joined SQL transaction required");
166
+ return {
167
+ dialect: binding.dialect,
168
+ /**
169
+ * Executes trusted SQL on this session's still-active joined connection.
170
+ * @param tenantId - Must match the verified tenant bound to the original session.
171
+ * @param sql - Trusted parameterized SQL with explicit tenant predicates.
172
+ * @param binds - Positional bind values in placeholder order.
173
+ * @returns Query rows and affected-row metadata from the existing transaction.
174
+ * @throws Rejects expired/mismatched bindings, foreign tenant or underlying SQL failure.
175
+ * @see joinedAtomicSqlExecutor for scoped usage that does not retain this capability.
176
+ */
177
+ async execute(tenantId, sql, binds) {
178
+ if (atomicSqlBindings.get(session) !== binding || tenantId !== binding.principal.tenantId) throw new Error("Joined SQL transaction expired or tenant differs");
179
+ return binding.connection.execute(sql, binds);
180
+ }
181
+ };
182
+ }
183
+ function atomicHash(value) {
184
+ return canonicalJsonHash(value);
185
+ }
186
+ async function atomicCommand(tx, p, operation, input, work) {
187
+ if (!/^[A-Za-z0-9_-]{1,80}$/.test(input.commandId)) throw new PluginError(400, "Command ID required");
188
+ const key = `command/${atomicHash([p.userId, operation, input.commandId])}`, hash2 = atomicHash(input);
189
+ const old = await tx.get(key);
190
+ if (old) {
191
+ if (old.value.hash !== hash2) throw new PluginError(409, "Command ID reused with different input");
192
+ return old.value.result;
193
+ }
194
+ const result = await work();
195
+ await tx.put(key, { hash: hash2, result }, 0);
196
+ await appendAtomicAudit(tx, p, operation, input, result, key);
197
+ return result;
198
+ }
199
+ function recordCursor(scope, after) {
200
+ return Buffer.from(JSON.stringify({ scope: atomicHash(scope), after })).toString("base64url");
201
+ }
202
+ function parseRecordCursor(scope, cursor) {
203
+ if (!cursor) return "";
204
+ try {
205
+ const value = JSON.parse(Buffer.from(cursor, "base64url").toString());
206
+ if (value.scope !== atomicHash(scope) || typeof value.after !== "string") throw new Error();
207
+ return idInput(value.after);
208
+ } catch {
209
+ throw new PluginError(400, "Invalid scoped cursor");
210
+ }
211
+ }
212
+ var idInput = (id2) => {
213
+ if (typeof id2 !== "string" || !/^[A-Za-z0-9_.:/-]{1,240}$/.test(id2)) throw new PluginError(400, "Invalid record key");
214
+ return id2;
215
+ };
216
+ function createAtomicRepository(table, driver, options = {}) {
217
+ if (!/^[a-z][a-z0-9_]{0,62}$/.test(table) || !driver?.transaction) throw new Error("Trusted table and transaction driver required");
218
+ const prefix = options.namespace ? `${options.namespace}:` : "";
219
+ const storagePolicy = resolvePluginStoragePolicy(options.storagePolicy);
220
+ const documents = options.documents && structuredClone(options.documents);
221
+ if (documents?.some((d) => d.table !== table || d.namespace !== options.namespace)) throw Error("JSON document storage binding differs");
222
+ const validateDocument = documents && createRecordDocumentValidator(documents);
223
+ if (prefix && !/^[a-z][a-z0-9_]{0,62}:$/.test(prefix)) throw new Error("Invalid plugin storage namespace");
224
+ const physicalId = (id2) => {
225
+ if (prefix && id2.length + prefix.length > 256) throw new PluginError(400, "Record id is too long for the shared plugin table");
226
+ return `${prefix}${id2}`;
227
+ };
228
+ const logicalId = (id2) => prefix ? id2.startsWith(prefix) ? id2.slice(prefix.length) : null : id2;
229
+ const bind = (n) => driver.dialect === "oracle" ? `:${n}` : driver.dialect === "postgres" ? `$${n}` : "?";
230
+ const decode = (tenant, row) => {
231
+ const id2 = logicalId(String(row.id ?? row.ID));
232
+ if (id2 === null || !id2) throw new Error("Record belongs to another plugin storage namespace");
233
+ const record = { id: id2, revision: Number(row.revision ?? row.REVISION), value: JSON.parse(String(row.payload ?? row.PAYLOAD)) };
234
+ validateDocument?.(tenant, record);
235
+ return record;
236
+ };
237
+ return { async run(principal, work) {
238
+ assertPrincipal(principal);
239
+ const p = structuredClone(principal);
240
+ return driver.transaction(p, async (conn) => {
241
+ let active = true;
242
+ const check = () => {
243
+ if (!active) throw new Error("Transaction session expired");
244
+ };
245
+ const session = {
246
+ async get(id2) {
247
+ check();
248
+ idInput(id2);
249
+ const r = await conn.execute(`SELECT id,revision,payload FROM ${table} WHERE tenant_id=${bind(1)} AND id=${bind(2)}`, [p.tenantId, physicalId(id2)]);
250
+ return r.rows?.[0] ? decode(p.tenantId, r.rows[0]) : null;
251
+ },
252
+ async getMany(ids) {
253
+ check();
254
+ ids.forEach(idInput);
255
+ const found = /* @__PURE__ */ new Map();
256
+ const unique = [...new Set(ids)];
257
+ for (let offset = 0; offset < unique.length; offset += 200) {
258
+ const batch = unique.slice(offset, offset + 200);
259
+ const r = await conn.execute(`SELECT id,revision,payload FROM ${table} WHERE tenant_id=${bind(1)} AND id IN (${batch.map((_, i) => bind(i + 2)).join(",")})`, [p.tenantId, ...batch.map(physicalId)]);
260
+ for (const raw of r.rows ?? []) {
261
+ const row = decode(p.tenantId, raw);
262
+ found.set(row.id, row);
263
+ }
264
+ }
265
+ return ids.map((id2) => found.get(id2) ?? null);
266
+ },
267
+ async putMany(changes) {
268
+ check();
269
+ if (new Set(changes.map((c) => c.id)).size !== changes.length) throw new PluginError(400, "Duplicate batch record key");
270
+ const prepared = changes.map((c) => {
271
+ idInput(c.id);
272
+ if (!Number.isSafeInteger(c.expected) || c.expected < 0 || c.expected >= Number.MAX_SAFE_INTEGER) throw new PluginError(400, "Invalid revision");
273
+ validateDocument?.(p.tenantId, { id: c.id, revision: c.expected + 1, value: c.value });
274
+ return { ...c, payload: storagePayload(storagePolicy, c.value) };
275
+ });
276
+ if (!conn.executeMany) {
277
+ const result = [];
278
+ for (const c of prepared) result.push(await session.put(c.id, c.value, c.expected));
279
+ return result;
280
+ }
281
+ try {
282
+ for (const inserting of [true, false]) {
283
+ const group = prepared.filter((c) => c.expected === 0 === inserting);
284
+ const sql = inserting ? `INSERT INTO ${table}(tenant_id,id,revision,payload) VALUES(${bind(1)},${bind(2)},1,${bind(3)})` : `UPDATE ${table} SET revision=${bind(1)},payload=${bind(2)} WHERE tenant_id=${bind(3)} AND id=${bind(4)} AND revision=${bind(5)}`;
285
+ for (let offset = 0; offset < group.length; offset += 100) {
286
+ const batch = group.slice(offset, offset + 100), result = await conn.executeMany(sql, batch.map((c) => inserting ? [p.tenantId, physicalId(c.id), c.payload] : [c.expected + 1, c.payload, p.tenantId, physicalId(c.id), c.expected]));
287
+ if (result.affectedRows.length !== batch.length || result.affectedRows.some((n) => n !== 1)) throw new PluginError(409, "Record revision changed");
288
+ }
289
+ }
290
+ } catch (e) {
291
+ if (["SQLITE_CONSTRAINT_PRIMARYKEY", "SQLITE_CONSTRAINT_UNIQUE", "23505", "ORA-00001"].includes(e?.code) || e?.errorNum === 1) throw new PluginError(409, "Record already exists");
292
+ throw e;
293
+ }
294
+ return prepared.map((c) => ({ id: c.id, revision: c.expected + 1, value: structuredClone(c.value) }));
295
+ },
296
+ async list(prefix2, after = "", limit = 100) {
297
+ check();
298
+ if (prefix2 !== "") idInput(prefix2);
299
+ if (after) idInput(after);
300
+ if (!Number.isInteger(limit) || limit < 1 || limit > 101) throw new PluginError(400, "Invalid page limit");
301
+ const tail = driver.dialect === "oracle" ? `FETCH FIRST ${limit} ROWS ONLY` : `LIMIT ${limit}`;
302
+ const physicalPrefix = physicalId(prefix2), physicalAfter = after ? physicalId(after) : `${physicalPrefix} `;
303
+ const r = await conn.execute(`SELECT id,revision,payload FROM ${table} WHERE tenant_id=${bind(1)} AND id>=${bind(2)} AND id<${bind(3)} AND id>${bind(4)} ORDER BY id ${tail}`, [p.tenantId, physicalPrefix, `${physicalPrefix}\uFFFF`, physicalAfter]);
304
+ return (r.rows ?? []).map((row) => decode(p.tenantId, row));
305
+ },
306
+ async put(id2, value, expected) {
307
+ check();
308
+ idInput(id2);
309
+ if (!Number.isSafeInteger(expected) || expected < 0 || expected >= Number.MAX_SAFE_INTEGER) throw new PluginError(400, "Invalid revision");
310
+ validateDocument?.(p.tenantId, { id: id2, revision: expected + 1, value });
311
+ const payload = storagePayload(storagePolicy, value);
312
+ try {
313
+ const r = expected === 0 ? await conn.execute(`INSERT INTO ${table}(tenant_id,id,revision,payload) VALUES(${bind(1)},${bind(2)},1,${bind(3)})`, [p.tenantId, physicalId(id2), payload]) : await conn.execute(`UPDATE ${table} SET revision=${bind(1)},payload=${bind(2)} WHERE tenant_id=${bind(3)} AND id=${bind(4)} AND revision=${bind(5)}`, [expected + 1, payload, p.tenantId, physicalId(id2), expected]);
314
+ if (r.affectedRows !== 1) throw new PluginError(409, "Record revision changed");
315
+ } catch (e) {
316
+ if (["SQLITE_CONSTRAINT_PRIMARYKEY", "SQLITE_CONSTRAINT_UNIQUE", "23505", "ORA-00001"].includes(e?.code) || e?.errorNum === 1) throw new PluginError(409, "Record already exists");
317
+ throw e;
318
+ }
319
+ return { id: id2, revision: expected + 1, value: structuredClone(value) };
320
+ }
321
+ };
322
+ atomicSqlBindings.set(session, { connection: conn, principal: p, dialect: driver.dialect });
323
+ try {
324
+ return await work(session);
325
+ } finally {
326
+ active = false;
327
+ atomicSqlBindings.delete(session);
328
+ }
329
+ });
330
+ } };
331
+ }
332
+ function createAtomicRecordStore(session, principal) {
333
+ assertPrincipal(principal);
334
+ const tenantId = principal.tenantId;
335
+ const scope = (requested) => {
336
+ if (requested !== tenantId) throw new PluginError(403, "Atomic record tenant differs");
337
+ };
338
+ return {
339
+ read: async (requested, id2) => {
340
+ scope(requested);
341
+ return session.get(id2);
342
+ },
343
+ list: async (requested, limit) => {
344
+ scope(requested);
345
+ return session.list("", "", limit);
346
+ },
347
+ listPage: async (requested, limit, after = "") => {
348
+ scope(requested);
349
+ return session.list("", after, limit);
350
+ },
351
+ create: async (requested, record) => {
352
+ scope(requested);
353
+ if (record.revision !== 1) throw new PluginError(400, "Invalid initial record revision");
354
+ await session.put(record.id, record.value, 0);
355
+ },
356
+ replace: async (requested, record, expected) => {
357
+ scope(requested);
358
+ if (record.revision !== expected + 1) throw new PluginError(400, "Invalid next record revision");
359
+ await session.put(record.id, record.value, expected);
360
+ return true;
361
+ }
362
+ };
363
+ }
364
+ function createPluginAtomicRepository(manifest, driver, catalog) {
365
+ const storage = manifest.schema.storage;
366
+ if (storage) {
367
+ if (!catalog) throw Error("Contract-backed record storage requires a trusted schema catalog");
368
+ const documents = recordDocuments(catalog, storage);
369
+ return createAtomicRepository(storage.table, driver, { namespace: storage.namespace, storagePolicy: manifest.schema.storagePolicy, ...documents?.length ? { documents } : {} });
370
+ }
371
+ if (manifest.schema.ownedTables.length !== 1) throw new Error("Plugin manifest must declare one storage table or shared storage metadata");
372
+ return createAtomicRepository(manifest.schema.ownedTables[0], driver, { storagePolicy: manifest.schema.storagePolicy });
373
+ }
374
+ function createSqliteAtomicDriver(db) {
375
+ let tail = Promise.resolve();
376
+ return { dialect: "sqlite", transaction(_p, work) {
377
+ const next = tail.then(async () => {
378
+ db.exec("BEGIN IMMEDIATE");
379
+ try {
380
+ const result = await work({ async execute(sql, binds) {
381
+ const q = db.query(sql);
382
+ return /^SELECT\b/i.test(sql) ? { rows: q.all(...binds), affectedRows: 0 } : { affectedRows: q.run(...binds).changes };
383
+ } });
384
+ db.exec("COMMIT");
385
+ return result;
386
+ } catch (e) {
387
+ db.exec("ROLLBACK");
388
+ throw e;
389
+ }
390
+ });
391
+ tail = next.catch(() => void 0);
392
+ return next;
393
+ } };
394
+ }
395
+ function createOracleAtomicDriver(host, types) {
396
+ return { dialect: "oracle", transaction: (p, work) => host.withTenantConnection("PLUGIN_RECORDS", async (conn) => {
397
+ try {
398
+ const execute = async (sql, binds) => {
399
+ const r = await conn.execute(sql, binds.map((value) => typeof value === "string" && value.length > 4e3 ? { val: value, type: types.clobType } : value), { autoCommit: false, outFormat: types.objectFormat, .../^SELECT\b/i.test(sql) ? { fetchInfo: { PAYLOAD: { type: types.stringType } } } : {} });
400
+ return { rows: r.rows, affectedRows: r.rowsAffected ?? 0 };
401
+ };
402
+ const result = await work({ execute, ...conn.executeMany ? { async executeMany(sql, binds) {
403
+ if (binds.some((row) => row.some((value) => typeof value === "string" && value.length > 4e3))) {
404
+ const affectedRows = [];
405
+ for (const row of binds) affectedRows.push((await execute(sql, row)).affectedRows);
406
+ return { affectedRows };
407
+ }
408
+ const result2 = await conn.executeMany(sql, binds, { autoCommit: false, dmlRowCounts: true });
409
+ if (!Array.isArray(result2.dmlRowCounts) || result2.dmlRowCounts.length !== binds.length || result2.dmlRowCounts.some((n) => !Number.isSafeInteger(n) || n < 0)) throw new Error("Oracle batch did not return valid row counts");
410
+ return { affectedRows: result2.dmlRowCounts };
411
+ } } : {} });
412
+ await conn.commit();
413
+ return result;
414
+ } catch (e) {
415
+ await conn.rollback();
416
+ throw e;
417
+ }
418
+ }, { tenantId: p.tenantId, userId: p.userId }) };
419
+ }
420
+
421
+ // src/plugin-data/outbox.ts
422
+ import { randomUUID } from "node:crypto";
423
+ import { OutboxDispatcher, createOutboxEvent } from "@alneas/eaos-core";
424
+ var outboxKey = (id2) => `outbox/${id2}`;
425
+ async function enqueueOutboxEvent(tx, input) {
426
+ const event = createOutboxEvent(input);
427
+ const key = outboxKey(event.id);
428
+ const existing = await tx.get(key);
429
+ if (existing) {
430
+ if (existing.value.payloadHash !== event.payloadHash || existing.value.topic !== event.topic || existing.value.idempotencyKey !== event.idempotencyKey) throw new PluginError(409, "Outbox idempotency key reused with different input");
431
+ return structuredClone(existing.value);
432
+ }
433
+ await tx.put(key, event, 0);
434
+ return structuredClone(event);
435
+ }
436
+ function createAtomicWorkflowEventSink(tx, topicPrefix = "workflow") {
437
+ if (!tx || typeof tx.put !== "function") throw new Error("Atomic transaction is required");
438
+ if (!/^[A-Za-z0-9_.:/-]+$/.test(topicPrefix)) throw new Error("Invalid workflow event topic prefix");
439
+ return {
440
+ async append(event) {
441
+ await enqueueOutboxEvent(tx, {
442
+ tenantId: event.tenantId,
443
+ producer: event.producer,
444
+ topic: `${topicPrefix}.${event.type}`,
445
+ idempotencyKey: event.idempotencyKey,
446
+ payload: structuredClone(event),
447
+ id: event.eventId,
448
+ occurredAt: event.occurredAt
449
+ });
450
+ }
451
+ };
452
+ }
453
+ function createAtomicOutboxStore(options) {
454
+ const p = structuredClone(options.principal);
455
+ const staleAfterMs = options.staleAfterMs ?? 5 * 6e4;
456
+ if (!Number.isFinite(staleAfterMs) || staleAfterMs < 1e3) throw new Error("Invalid outbox stale timeout");
457
+ return {
458
+ async claim(now, limit) {
459
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) throw new Error("Invalid outbox batch size");
460
+ return options.repository.run(p, async (tx) => {
461
+ const claimed = [];
462
+ let after = "", scanned = 0;
463
+ for (; ; ) {
464
+ const rows = await tx.list("outbox/", after, 100);
465
+ for (const row of rows) {
466
+ if (claimed.length >= limit) break;
467
+ if (++scanned > 3e4) throw new PluginError(409, "Outbox scan at capacity");
468
+ if (row.value.tenantId !== p.tenantId || row.id !== outboxKey(row.value.id)) throw new PluginError(409, "Outbox scope differs");
469
+ const due = row.value.status === "pending" && Date.parse(row.value.availableAt) <= now.getTime();
470
+ const stale = row.value.status === "processing" && Date.parse(row.value.claimedAt ?? "") <= now.getTime() - staleAfterMs;
471
+ if (!due && !stale) continue;
472
+ const next2 = { ...row.value, status: "processing", attempts: row.value.attempts + 1, claimedAt: now.toISOString(), claimToken: randomUUID(), lastError: void 0 };
473
+ claimed.push((await tx.put(row.id, next2, row.revision)).value);
474
+ }
475
+ if (claimed.length >= limit || rows.length < 100) break;
476
+ const next = rows.at(-1).id;
477
+ if (next <= after) throw new PluginError(409, "Outbox page did not advance");
478
+ after = next;
479
+ }
480
+ return claimed;
481
+ });
482
+ },
483
+ async markPublished(id2, claimToken, publishedAt) {
484
+ await options.repository.run(p, async (tx) => {
485
+ const row = await tx.get(outboxKey(id2));
486
+ if (!row || row.value.status !== "processing" || row.value.claimToken !== claimToken) throw new PluginError(409, "Outbox claim changed");
487
+ await tx.put(row.id, { ...row.value, status: "published", publishedAt: publishedAt.toISOString(), claimedAt: void 0, claimToken: void 0 }, row.revision);
488
+ });
489
+ },
490
+ async markFailed(id2, claimToken, error, retryAt) {
491
+ await options.repository.run(p, async (tx) => {
492
+ const row = await tx.get(outboxKey(id2));
493
+ if (!row || row.value.status !== "processing" || row.value.claimToken !== claimToken) throw new PluginError(409, "Outbox claim changed");
494
+ await tx.put(row.id, { ...row.value, status: retryAt ? "pending" : "failed", availableAt: retryAt?.toISOString() ?? row.value.availableAt, claimedAt: void 0, claimToken: void 0, lastError: error }, row.revision);
495
+ });
496
+ }
497
+ };
498
+ }
499
+ function createAtomicOutboxDispatcher(options) {
500
+ const { repository, principal, staleAfterMs, now, ...dispatcher } = options;
501
+ return new OutboxDispatcher({ ...dispatcher, now, store: createAtomicOutboxStore({ repository, principal, staleAfterMs, now }) });
502
+ }
503
+ async function requeueOutboxEvent(tx, tenantId, id2, availableAt = (/* @__PURE__ */ new Date()).toISOString()) {
504
+ if (!id2 || id2.length > 256 || !Number.isFinite(Date.parse(availableAt))) throw new PluginError(400, "Invalid outbox recovery");
505
+ const row = await tx.get(outboxKey(id2));
506
+ if (!row || row.value.tenantId !== tenantId || row.value.id !== id2 || row.value.status !== "failed") throw new PluginError(409, "Failed outbox event unavailable");
507
+ const next = { ...row.value, status: "pending", availableAt, claimedAt: void 0, claimToken: void 0, lastError: void 0 };
508
+ await tx.put(row.id, next, row.revision);
509
+ return next;
510
+ }
511
+
512
+ // src/plugin-data/scoped-records.ts
513
+ import Ajv2 from "ajv";
514
+ function uniqueRecordKey(name, values) {
515
+ if (!/^[a-z][a-z0-9_-]{0,60}$/.test(name) || !values.length) throw new Error("Invalid trusted uniqueness declaration");
516
+ if (values.some((value) => typeof value !== "string" || !value.trim())) throw new PluginError(409, "Unique identity fields must be nonempty strings");
517
+ return `unique/${atomicHash([name, values])}`;
518
+ }
519
+ var ScopedRecordService = class _ScopedRecordService {
520
+ /**
521
+ * Compiles strict noncoercing command schemas for one trusted record definition.
522
+ * @param host - Durable tenant-scoped store, live authorization, optional clock and shared atomic repository.
523
+ * @param definition - Plugin-owned operation names, schemas, reducers, paging and identity rules.
524
+ * @param atomicSession - Optional already active internal session used for joined composition/uniqueness; its lifetime must not exceed the enclosing repository callback.
525
+ * @throws Throws when durable store or live authorization is missing, or strict schema compilation fails.
526
+ *
527
+ * @example
528
+ * ```typescript
529
+ * import { ScopedRecordService, type ScopedRecordHost, type ScopedRecordDefinition } from '@alneas/eaos-server/plugin-data';
530
+ * type Row = { tenantId: string; id: string; title: string };
531
+ * type RevisionedRow = Row & { revision: number };
532
+ * export function connectRecords(host: ScopedRecordHost<Row>, definition: ScopedRecordDefinition<Row, RevisionedRow>) { return new ScopedRecordService(host, definition); }
533
+ * ```
534
+ */
535
+ constructor(host, definition, atomicSession) {
536
+ this.host = host;
537
+ this.definition = definition;
538
+ this.atomicSession = atomicSession;
539
+ if (!host?.store || typeof host.authorize !== "function") throw new Error("Durable scoped record store and live authorization required");
540
+ const ajv2 = new Ajv2({ strict: true, coerceTypes: false });
541
+ this.validators = new Map(Object.entries(definition.schemas).map(([name, schema]) => [name, ajv2.compile(schema)]));
542
+ }
543
+ host;
544
+ definition;
545
+ atomicSession;
546
+ validators;
547
+ async allow(principal, operation, input, current) {
548
+ assertPrincipal(principal);
549
+ if (await this.host.authorize(structuredClone(principal), operation, structuredClone(input), current && structuredClone(current)) !== true) throw new PluginError(403, `${this.definition.label} access denied`);
550
+ }
551
+ /**
552
+ * Exposes the same live tenant/permission check to a plugin's read-only
553
+ * domain previews. Preview operations do not persist records, but they must
554
+ * still be authorized through the host before invoking domain logic.
555
+ */
556
+ async authorizeOperation(principal, operation, input, current) {
557
+ await this.allow(principal, operation, input, current);
558
+ }
559
+ currentTime() {
560
+ return this.host.now?.() ?? /* @__PURE__ */ new Date();
561
+ }
562
+ excluded(id2) {
563
+ return id2.startsWith("audit/") || !!this.definition.unique?.length && id2.startsWith("unique/") || Object.values(this.definition.schemas).some((schema) => schema?.properties?.commandId) && /^(?:command|event)\//.test(id2) || this.definition.excludedIdPrefixes?.some((prefix) => id2.startsWith(prefix)) === true;
564
+ }
565
+ decode(principal, record) {
566
+ if (record.id !== record.value.id || record.value.tenantId !== principal.tenantId || !Number.isSafeInteger(record.revision) || record.revision < 1) throw new Error(`Invalid scoped ${this.definition.label} record`);
567
+ return { ...structuredClone(record.value), revision: record.revision };
568
+ }
569
+ async load(principal, id2) {
570
+ if (this.excluded(id2)) throw new PluginError(404, `${this.definition.label} not found`);
571
+ const record = await this.host.store.read(principal.tenantId, id2);
572
+ if (!record) throw new PluginError(404, `${this.definition.label} not found`);
573
+ return this.decode(principal, record);
574
+ }
575
+ matches(args, value) {
576
+ return (!this.definition.filter || this.definition.filter(args, value)) && (!args.search || this.definition.searchableFields?.some((key) => typeof value[key] === "string" && String(value[key]).toLocaleLowerCase().includes(args.search.toLocaleLowerCase()))) === true;
577
+ }
578
+ /** Read all authorized records for plugin-owned reports; providers must support keyset paging. */
579
+ async readAllAuthorized(principal, operation, input, accept) {
580
+ if (this.host.repository) return this.host.repository.run(principal, (tx) => this.readAllFromStore(principal, operation, input, accept, createAtomicRecordStore(tx, principal)));
581
+ return this.readAllFromStore(principal, operation, input, accept, this.host.store);
582
+ }
583
+ async readAllFromStore(principal, operation, input, accept, store) {
584
+ await this.allow(principal, operation, input);
585
+ if (!store.listPage) throw new PluginError(409, "Paged record storage required for complete reports");
586
+ const values = [];
587
+ let after = "";
588
+ for (; ; ) {
589
+ const rows = await store.listPage(principal.tenantId, 100, after);
590
+ for (const raw of rows) {
591
+ if (this.excluded(raw.id)) continue;
592
+ const value = this.decode(principal, raw);
593
+ if (accept(value) && await this.host.authorize(structuredClone(principal), operation, structuredClone(input), structuredClone(value)) === true) values.push(value);
594
+ }
595
+ if (rows.length < 100) break;
596
+ const next = rows.at(-1).id;
597
+ if (next <= after) throw new Error("Record page did not advance");
598
+ after = next;
599
+ }
600
+ await this.allow(principal, operation, input);
601
+ return values;
602
+ }
603
+ async reserveUnique(value, previous) {
604
+ if (!this.definition.unique?.length) return;
605
+ const tx = this.atomicSession;
606
+ if (!tx) throw new PluginError(409, "Transactional storage required for unique business identities");
607
+ for (const constraint of this.definition.unique) {
608
+ if (!constraint.fields.length) throw new Error("Invalid trusted uniqueness declaration");
609
+ const key = (record) => uniqueRecordKey(constraint.name, constraint.fields.map((field) => record[field]));
610
+ const applies = (record) => {
611
+ const active = constraint.when ? constraint.when(structuredClone(record)) : true;
612
+ if (typeof active !== "boolean") throw new Error("Unique predicate must return a boolean");
613
+ return active;
614
+ };
615
+ const nextActive = applies(value), oldActive = previous ? applies(previous) : false, nextKey = nextActive ? key(value) : null, oldKey = previous && oldActive ? key(previous) : null;
616
+ if (nextKey) {
617
+ const reservation = await tx.get(nextKey);
618
+ if (reservation && reservation.value.recordId !== null && reservation.value.recordId !== value.id) throw new PluginError(409, `${this.definition.label} identity already exists`);
619
+ if (!reservation || reservation.value.recordId === null) await tx.put(nextKey, { recordId: value.id }, reservation?.revision ?? 0);
620
+ }
621
+ if (oldKey && oldKey !== nextKey) {
622
+ const old = await tx.get(oldKey);
623
+ if (!old || old.value.recordId !== value.id) throw new PluginError(409, "Unique identity reservation conflicts");
624
+ await tx.put(oldKey, { recordId: null }, old.revision);
625
+ }
626
+ }
627
+ }
628
+ /**
629
+ * Dispatches one admitted record operation and optionally joins it to durable command/audit receipt handling.
630
+ * @param principal - Verified current tenant/user principal; host authorization is rechecked throughout the operation.
631
+ * @param operation - Declared list/get/create/update/archive or plugin custom reducer key.
632
+ * @param input - Operation-schema input; mutations require matching record revision and optional durable commandId when declared.
633
+ * @returns List operations return the configured listKey with scoped records and paging metadata; get/mutations return a decoded revisioned record. Paged results may be incomplete and carry nextCursor/mayHaveMore.
634
+ * @throws Rejects invalid principal (401), denied/revoked admission (403), schema/operation errors (400), missing/excluded record (404), stale revision/uniqueness/CAS conflict or missing required transactional capability (409), foreign/corrupt records, and propagated domain/store/clock/callback failures.
635
+ *
636
+ * @remarks
637
+ * Pagination scans at most 10000 rows per request, selecting authorized/filter-matching records. Without paging, filtering applies only to the bounded store result. Repository writes with declared commandId replay the stored identical result; other repository writes append audit. Recheck live resource permission before replay, and reload uncertain plain-store outcomes rather than retrying blindly.
638
+ *
639
+ * @example
640
+ * ```typescript
641
+ * import { ScopedRecordService, type PluginPrincipal } from '@alneas/eaos-server/plugin-data';
642
+ * export function dispatch<T extends {tenantId: string; id: string}, S extends T & {revision: number}>(service: ScopedRecordService<T, S>, principal: PluginPrincipal, operation: string, input: unknown) { return service.execute(principal, operation, input); }
643
+ * ```
644
+ */
645
+ async execute(principal, operation, input) {
646
+ if (this.host.repository) {
647
+ await this.allow(principal, operation, input);
648
+ const p = structuredClone(principal);
649
+ return this.host.repository.run(p, async (tx) => {
650
+ const service = new _ScopedRecordService({ ...this.host, repository: void 0, store: createAtomicRecordStore(tx, p) }, this.definition, tx);
651
+ const write = operation !== this.definition.operations.list && operation !== this.definition.operations.get;
652
+ const journaled = write && this.definition.schemas[operation]?.properties?.commandId && typeof input?.commandId === "string";
653
+ const perform = () => service.execute(p, operation, input);
654
+ if (journaled && !this.validators.get(operation)?.(input)) throw new PluginError(400, `Invalid ${this.definition.label} command`);
655
+ const result = journaled ? await atomicCommand(tx, p, operation, input, perform) : await perform();
656
+ if (write && !journaled) await appendAtomicAudit(tx, p, operation, input, result);
657
+ const resource = operation === this.definition.operations.list ? void 0 : await service.load(p, result.id);
658
+ await this.allow(p, operation, input, resource);
659
+ return result;
660
+ });
661
+ }
662
+ await this.allow(principal, operation, input);
663
+ const validate = this.validators.get(operation);
664
+ if (!validate || !validate(input)) throw new PluginError(400, `Invalid ${this.definition.label} command`);
665
+ if (!this.atomicSession && operation !== this.definition.operations.list && operation !== this.definition.operations.get && this.definition.schemas[operation]?.properties?.commandId && typeof input?.commandId === "string") throw new PluginError(409, "Transactional storage required for durable command receipts");
666
+ const args = input, at = (this.host.now?.() ?? /* @__PURE__ */ new Date()).toISOString(), ops = this.definition.operations;
667
+ if (this.definition.afterWrite && !this.atomicSession && operation !== ops.list && operation !== ops.get) throw new PluginError(409, "Transactional storage required for owning record composition");
668
+ if (operation === ops.list) {
669
+ const limit = args.limit ?? 50, values = [];
670
+ if (this.definition.pagination && this.host.store.listPage) {
671
+ const { cursor, ...criteria } = args, scope = [principal.tenantId, principal.userId, operation, criteria];
672
+ let position = parseRecordCursor(scope, cursor), finished = false, scannedCount = 0;
673
+ while (values.length < limit && scannedCount < 1e4) {
674
+ const rows = await this.host.store.listPage(principal.tenantId, 100, position);
675
+ if (!rows.length) {
676
+ finished = true;
677
+ break;
678
+ }
679
+ for (const raw of rows) {
680
+ position = raw.id;
681
+ scannedCount++;
682
+ if (this.excluded(raw.id)) continue;
683
+ const value = this.decode(principal, raw);
684
+ if (this.matches(args, value) && await this.host.authorize(structuredClone(principal), operation, { ...criteria, id: value.id }, structuredClone(value)) === true) values.push(value);
685
+ if (values.length === limit) break;
686
+ }
687
+ if (values.length < limit && rows.length < 100) {
688
+ finished = true;
689
+ break;
690
+ }
691
+ }
692
+ await this.allow(principal, operation, input);
693
+ return { [this.definition.listKey]: values, scannedLimit: limit, scannedCount, mayHaveMore: !finished, nextCursor: finished ? null : recordCursor(scope, position) };
694
+ }
695
+ if (args.cursor) throw new PluginError(409, "Paged record storage unavailable");
696
+ const records = await this.host.store.list(principal.tenantId, limit);
697
+ for (const raw of records) {
698
+ if (this.excluded(raw.id)) continue;
699
+ const value = this.decode(principal, raw);
700
+ if (this.matches(args, value) && await this.host.authorize(structuredClone(principal), operation, { id: value.id }, structuredClone(value)) === true) values.push(value);
701
+ }
702
+ await this.allow(principal, operation, input);
703
+ return { [this.definition.listKey]: values, scannedLimit: limit, mayHaveMore: records.length === limit };
704
+ }
705
+ if (operation === ops.create) {
706
+ const value = this.definition.create(args, principal, at);
707
+ if (value.tenantId !== principal.tenantId || !value.id || this.excluded(value.id)) throw new Error(`Invalid ${this.definition.label} scope`);
708
+ await this.allow(principal, operation, value);
709
+ await this.reserveUnique(value);
710
+ await this.host.store.create(principal.tenantId, { id: value.id, revision: 1, value });
711
+ const created = { ...value, revision: 1 };
712
+ if (this.definition.afterWrite) {
713
+ await this.definition.afterWrite(structuredClone(principal), operation, structuredClone(args), structuredClone(created));
714
+ await this.allow(principal, operation, input, created);
715
+ }
716
+ return created;
717
+ }
718
+ const current = await this.load(principal, textInput(args.id, "id", 128));
719
+ await this.allow(principal, operation, input, current);
720
+ if (operation === ops.get) return current;
721
+ const previous = structuredClone(current);
722
+ const expected = revisionInput(args.revision);
723
+ if (expected !== current.revision) throw new PluginError(409, `${this.definition.label} changed`);
724
+ if (operation === ops.archive) {
725
+ this.definition.archive(current, at, args, principal);
726
+ } else if (operation === ops.update) {
727
+ const patch = args.patch;
728
+ const patchSchema = this.definition.schemas[ops.update]?.properties?.patch?.properties;
729
+ if (patchSchema) exactKeys(patch, Object.keys(patchSchema));
730
+ this.definition.update(current, patch, at);
731
+ } else if (this.definition.custom?.[operation]) {
732
+ this.definition.custom[operation](current, args, at, principal);
733
+ } else {
734
+ throw new PluginError(400, `Unknown ${this.definition.label} operation`);
735
+ }
736
+ await this.allow(principal, operation, input, current);
737
+ await this.reserveUnique(current, previous);
738
+ const nextRevision = expected + 1;
739
+ const { revision: _revision, ...payload } = current;
740
+ if (!await this.host.store.replace(principal.tenantId, { id: current.id, revision: nextRevision, value: payload }, expected)) throw new PluginError(409, `${this.definition.label} changed`);
741
+ current.revision = nextRevision;
742
+ if (this.definition.afterWrite) {
743
+ await this.definition.afterWrite(structuredClone(principal), operation, structuredClone(args), structuredClone(current), previous);
744
+ await this.allow(principal, operation, input, current);
745
+ }
746
+ return current;
747
+ }
748
+ };
749
+
750
+ // src/plugin-data/composite.ts
751
+ function createCompositePluginService(services, operationOwners) {
752
+ const owned = new Map(Object.entries(operationOwners));
753
+ for (const [operation, owner] of owned) {
754
+ if (!services[owner]) throw new Error(`Unknown composite service: ${owner} (${operation})`);
755
+ }
756
+ return {
757
+ async execute(principal, operation, input) {
758
+ const owner = owned.get(operation);
759
+ if (!owner) throw new PluginError(400, "Unknown bundled plugin operation");
760
+ return services[owner].execute(principal, operation, input);
761
+ }
762
+ };
763
+ }
764
+
765
+ // src/plugin-data/annotations.ts
766
+ var annotationOperations = ["get_annotations", "set_check", "add_comment", "resolve_comment"];
767
+ function annotationSchemas(target) {
768
+ const id2 = { type: "string", pattern: "^[a-zA-Z0-9_-]{1,80}$" }, revision = { type: "integer", minimum: 0, maximum: 9007199254740990 };
769
+ const visibility = { enum: ["private", "shared"] }, anchorId = { anyOf: [{ type: "string", minLength: 1, maxLength: 1e3 }, { type: "null" }] };
770
+ const schema = (properties, required) => ({ type: "object", additionalProperties: false, required: [...Object.keys(target), ...required], properties: { ...target, ...properties } });
771
+ return {
772
+ get_annotations: schema({ visibility, cursor: { type: "string", maxLength: 1e3 } }, ["visibility"]),
773
+ set_check: schema({ commandId: id2, revision, itemId: { type: "string", minLength: 1, maxLength: 1e3 }, checked: { type: "boolean" } }, ["commandId", "revision", "itemId", "checked"]),
774
+ add_comment: schema({ commandId: id2, visibility, anchorId, body: { type: "string", minLength: 1, maxLength: 4e3 } }, ["commandId", "visibility", "anchorId", "body"]),
775
+ resolve_comment: schema({ commandId: id2, revision, visibility, commentId: id2, resolved: { type: "boolean" } }, ["commandId", "revision", "visibility", "commentId", "resolved"])
776
+ };
777
+ }
778
+ async function documentAnnotations(tx, p, target, operation, a, now) {
779
+ if (!/^[a-f0-9]{64}$/.test(target.scope)) throw new Error("Trusted annotation scope required");
780
+ const owner = atomicHash(p.userId), checksKey = `checks/${target.scope}/${owner}`;
781
+ const prefix = `comments/${target.scope}/${a.visibility === "shared" ? "shared" : owner}/`;
782
+ const visible = (row) => ({ ...row.value, revision: row.revision, canResolve: row.value.authorId === p.userId });
783
+ if (operation === "get_annotations") {
784
+ const scope = [p.tenantId, p.userId, target.scope, a.visibility], after = parseRecordCursor(scope, a.cursor);
785
+ if (after && !after.startsWith(prefix)) throw new PluginError(400, "Invalid comment cursor");
786
+ const checks = await tx.get(checksKey), rows = await tx.list(prefix, after, 51);
787
+ return { checkIds: target.checkIds, checks: checks?.value ?? {}, revision: checks?.revision ?? 0, comments: rows.slice(0, 50).map(visible), ...rows.length > 50 ? { cursor: recordCursor(scope, rows[49].id) } : {} };
788
+ }
789
+ if (operation === "set_check") {
790
+ if (!target.checkIds.includes(a.itemId)) throw new PluginError(400, "Unknown reading check");
791
+ const old = await tx.get(checksKey), value = { ...old?.value };
792
+ if (a.checked) Object.defineProperty(value, a.itemId, { value: true, enumerable: true, configurable: true, writable: true });
793
+ else delete value[a.itemId];
794
+ if (Object.keys(value).length > 1e3) throw new PluginError(400, "Reading check capacity exceeded");
795
+ const saved = await tx.put(checksKey, value, a.revision);
796
+ return { checks: saved.value, revision: saved.revision };
797
+ }
798
+ if (operation === "add_comment") {
799
+ if (!a.body.trim() || a.anchorId !== null && !target.anchorIds.includes(a.anchorId)) throw new PluginError(400, "Comment body and valid anchor required");
800
+ const comment = { id: crypto.randomUUID(), anchorId: a.anchorId, body: a.body.trim(), visibility: a.visibility, authorId: p.userId, createdAt: now, resolved: false };
801
+ return visible(await tx.put(prefix + comment.id, comment, 0));
802
+ }
803
+ if (operation === "resolve_comment") {
804
+ const row = await tx.get(prefix + a.commentId);
805
+ if (!row) throw new PluginError(404, "Comment unavailable");
806
+ if (row.value.authorId !== p.userId) throw new PluginError(403, "Only the comment author may resolve it");
807
+ return visible(await tx.put(row.id, { ...row.value, resolved: a.resolved }, a.revision));
808
+ }
809
+ throw new PluginError(400, "Unknown annotation operation");
810
+ }
811
+
812
+ // src/plugin-data/delivery.ts
813
+ import Ajv3 from "ajv";
814
+ import { randomUUID as randomUUID2 } from "node:crypto";
815
+ var deliveryOperations = ["enqueue_delivery", "get_delivery", "run_delivery", "recover_delivery", "cancel_delivery"];
816
+ var id = { type: "string", pattern: "^[a-zA-Z0-9_-]{1,80}$" };
817
+ var hash = { type: "string", pattern: "^[a-f0-9]{64}$" };
818
+ var enqueueDeliverySchema = { type: "object", additionalProperties: false, required: ["commandId", "jobId", "channel", "kind", "target", "contentHash", "approvalRequestId"], properties: { commandId: id, jobId: id, channel: id, kind: { enum: ["publish", "withdraw"] }, target: { type: "object", maxProperties: 10, additionalProperties: true }, contentHash: hash, approvalRequestId: id } };
819
+ var deliveryJobSchema = { type: "object", additionalProperties: false, required: ["jobId"], properties: { jobId: id } };
820
+ var recoverDeliverySchema = { type: "object", additionalProperties: false, required: ["jobId", "revision"], properties: { jobId: id, revision: { type: "integer", minimum: 1, maximum: 9007199254740990 } } };
821
+ var ajv = new Ajv3({ strict: true });
822
+ var validators = { enqueue_delivery: ajv.compile(enqueueDeliverySchema), cancel_delivery: ajv.compile(deliveryJobSchema), get_delivery: ajv.compile(deliveryJobSchema), run_delivery: ajv.compile(deliveryJobSchema), recover_delivery: ajv.compile(recoverDeliverySchema) };
823
+ var ArtifactDeliveryService = class {
824
+ /**
825
+ * Binds the host atomic delivery ledger, public publication ports and live operation authorization.
826
+ * @param options - Real atomic repository, idempotent host delivery ports and current operation/resource authorization; construction does not validate or execute those ports.
827
+ *
828
+ * @example
829
+ * ```typescript
830
+ * import { ArtifactDeliveryService } from '@alneas/eaos-server/plugin-data';
831
+ * export function connectDelivery(options: ConstructorParameters<typeof ArtifactDeliveryService>[0]) { return new ArtifactDeliveryService(options); }
832
+ * ```
833
+ */
834
+ constructor(options) {
835
+ this.options = options;
836
+ }
837
+ options;
838
+ async allow(p, op, a, j) {
839
+ assertPrincipal(p);
840
+ if (await this.options.authorize(structuredClone(p), op, structuredClone(a), structuredClone(j)) !== true) throw new PluginError(403, "Delivery access denied");
841
+ }
842
+ async verify(p, j) {
843
+ if (await this.options.ports.verify(p, structuredClone(j)) !== true) throw new PluginError(403, "Public delivery approval unavailable");
844
+ }
845
+ /**
846
+ * Enqueues, reads, runs, cancels or explicitly recovers one durable approved delivery job.
847
+ * @param p - Verified current tenant principal; operation/resource admission is rechecked before transitions.
848
+ * @param op - One of deliveryOperations; each input is checked by its closed schema.
849
+ * @param a - jobId for every operation; enqueue additionally needs commandId/channel/kind/target/contentHash/approvalRequestId, and recovery needs the exact running job revision.
850
+ * @returns The durable AtomicRow<DeliveryJob>, possibly an existing identical enqueue receipt or already terminal job; query get_delivery for current state after receipt replay.
851
+ * @throws Rejects invalid principal/input (401/400), denied live/public approval (403), absent job (404), busy channel/worker, stale recovery or unavailable cancellation port (409), invalid built artifact and propagated repository/port failures; effects may already have occurred.
852
+ *
853
+ * @remarks
854
+ * Publish starts at build, withdrawal at activate; completion follows successful purge. Cancellation requires a reconciliation port and never removes a newer host version. Completed jobs require a new withdrawal job instead of cancellation. Recovery requires exact revision and drained(old token), marks failed and clears ownership for retry. Failure retains the last durable phase and a sanitized error, while the original host error may reject the call.
855
+ *
856
+ * @example
857
+ * ```typescript
858
+ * import { ArtifactDeliveryService, type PluginPrincipal } from '@alneas/eaos-server/plugin-data';
859
+ * export function readDelivery(service: ArtifactDeliveryService, principal: PluginPrincipal, jobId: string) { return service.execute(principal, 'get_delivery', { jobId }); }
860
+ * ```
861
+ */
862
+ async execute(p, op, a) {
863
+ p = structuredClone(p);
864
+ a = structuredClone(a);
865
+ await this.allow(p, op, a);
866
+ if (!Object.hasOwn(validators, op) || !validators[op](a)) throw new PluginError(400, "Invalid delivery job");
867
+ const repo = this.options.repository, ports = this.options.ports, key = "delivery/" + a.jobId;
868
+ if (op === "enqueue_delivery") {
869
+ if (!a.commandId || !a.channel || !a.target || !["publish", "withdraw"].includes(a.kind ?? "") || !/^[a-f0-9]{64}$/.test(a.contentHash ?? "") || !a.approvalRequestId || JSON.stringify(a.target).length > 4e3) throw new PluginError(400, "Invalid delivery request");
870
+ await this.verify(p, a);
871
+ return repo.run(p, async (tx) => atomicCommand(tx, p, op, a, async () => {
872
+ const j = { jobId: a.jobId, channel: a.channel, kind: a.kind, target: a.target, contentHash: a.contentHash, approvalRequestId: a.approvalRequestId, requestedBy: p.userId, createdAt: (/* @__PURE__ */ new Date()).toISOString(), state: "queued", phase: a.kind === "publish" ? "build" : "activate", attempts: 0 };
873
+ const channel = "delivery-channel/" + atomicHash(j.channel), lock = await tx.get(channel);
874
+ if (lock?.value.jobId) throw new PluginError(409, "Channel has unfinished delivery");
875
+ await this.allow(p, op, a, j);
876
+ await tx.put(channel, { jobId: j.jobId }, lock?.revision ?? 0);
877
+ return tx.put(key, j, 0);
878
+ }));
879
+ }
880
+ const row = await repo.run(p, (tx) => tx.get(key));
881
+ if (!row) throw new PluginError(404, "Delivery unavailable");
882
+ await this.allow(p, op, a, row.value);
883
+ if (op === "get_delivery") return row;
884
+ if (op === "recover_delivery") {
885
+ if (row.revision !== a.revision || row.value.state !== "running" || await ports.drained?.(p, structuredClone(row.value)) !== true) throw new PluginError(409, "Recovery requires a drained worker at this revision");
886
+ return repo.run(p, async (tx) => {
887
+ await this.allow(p, op, a, row.value);
888
+ const j = { ...row.value, state: "failed" };
889
+ delete j.token;
890
+ return tx.put(key, j, row.revision);
891
+ });
892
+ }
893
+ if (["done", "cancelled"].includes(row.value.state)) {
894
+ if (op === "cancel_delivery" && row.value.state === "done") throw new PluginError(409, "Use a new withdrawal job for completed delivery");
895
+ return row;
896
+ }
897
+ if (row.value.state === "running") throw new PluginError(409, "Delivery worker already running");
898
+ if (op === "cancel_delivery" && !ports.cancel) throw new PluginError(409, "Cancellation reconciliation port unavailable");
899
+ if (op !== "cancel_delivery" && !["purge", "cancel"].includes(row.value.phase)) await this.verify(p, row.value);
900
+ let current = await repo.run(p, async (tx) => {
901
+ await this.allow(p, op, a, row.value);
902
+ return tx.put(key, { ...row.value, ...op === "cancel_delivery" ? { phase: "cancel" } : {}, state: "running", token: randomUUID2(), attempts: row.value.attempts + 1, error: void 0 }, row.revision);
903
+ });
904
+ try {
905
+ while (current.value.phase !== "done") {
906
+ const j = current.value;
907
+ await this.allow(p, op, a, j);
908
+ if (!["purge", "cancel"].includes(j.phase)) await this.verify(p, j);
909
+ let patch;
910
+ if (j.phase === "cancel") {
911
+ await this.allow(p, "cancel_delivery", a, j);
912
+ if (!ports.cancel) throw new PluginError(409, "Cancellation reconciliation port unavailable");
913
+ await ports.cancel(p, structuredClone(j));
914
+ patch = { phase: "done", state: "cancelled" };
915
+ } else if (j.phase === "build") {
916
+ const artifact = await ports.build(p, structuredClone(j));
917
+ if (!artifact || typeof artifact.key !== "string" || artifact.key.length > 1e3 || !/^[a-f0-9]{64}$/.test(artifact.contentHash) || !Number.isSafeInteger(artifact.bytes) || artifact.bytes < 1) throw new Error("Invalid delivery artifact");
918
+ patch = { artifact, phase: "activate" };
919
+ } else if (j.phase === "activate") {
920
+ await ports.activate(p, structuredClone(j));
921
+ patch = { phase: "purge" };
922
+ } else {
923
+ await ports.purge(p, structuredClone(j));
924
+ patch = { phase: "done", state: "done" };
925
+ }
926
+ current = await repo.run(p, async (tx) => {
927
+ await this.allow(p, op, a, j);
928
+ const saved = await tx.put(key, { ...j, ...patch }, current.revision);
929
+ if (patch.phase === "done") {
930
+ const ck = "delivery-channel/" + atomicHash(j.channel), lock = await tx.get(ck);
931
+ if (lock?.value.jobId !== j.jobId) throw new PluginError(409, "Delivery channel ownership changed");
932
+ await tx.put(ck, { jobId: null }, lock.revision);
933
+ }
934
+ return saved;
935
+ });
936
+ }
937
+ return current;
938
+ } catch (e) {
939
+ await repo.run(p, async (tx) => {
940
+ const fresh = await tx.get(key);
941
+ if (fresh && fresh.value.token === current.value.token && fresh.value.state === "running") await tx.put(key, { ...fresh.value, state: "failed", error: "Delivery step failed; inspect host logs and retry" }, fresh.revision);
942
+ });
943
+ throw e;
944
+ }
945
+ }
946
+ };
947
+
948
+ // src/plugin-data/index.ts
949
+ function createMcpToolBinder(service, tools, key) {
950
+ return tools.map((tool) => ({ ...tool, handler: (args, context) => tool.handler(args, { ...context, [key]: service }) }));
951
+ }
952
+ function createMcpOperationHandler(operation, key) {
953
+ return async (args, context) => {
954
+ const service = context?.[key];
955
+ if (!service || typeof service.execute !== "function") throw new Error("Use createPlugin with verified principal");
956
+ return service.execute(context ?? {}, operation, args);
957
+ };
958
+ }
959
+ var PluginError = class extends Error {
960
+ /**
961
+ * Creates an intentional plugin failure with one supported client-facing HTTP status.
962
+ * @param status - Supported bad-input/authentication/permission/missing/conflict/gone HTTP status.
963
+ * @param message - Safe public error description exposed by createPluginRouter.
964
+ *
965
+ * @example
966
+ * ```typescript
967
+ * import { PluginError } from '@alneas/eaos-server/plugin-data';
968
+ * const denied = new PluginError(403, 'Record access denied');
969
+ * ```
970
+ */
971
+ constructor(status, message) {
972
+ super(message);
973
+ this.status = status;
974
+ }
975
+ status;
976
+ };
977
+ function storagePayload2(policy, value) {
978
+ try {
979
+ return assertPayloadWithinPolicy2(policy, value);
980
+ } catch (error) {
981
+ if (error instanceof Error && error.message === "Storage policy payload limit exceeded") throw new PluginError(409, error.message);
982
+ throw error;
983
+ }
984
+ }
985
+ function assertPrincipal(p) {
986
+ if (!p || typeof p.tenantId !== "string" || !p.tenantId.trim() || typeof p.userId !== "string" || !p.userId.trim() || !Array.isArray(p.roles) || p.roles.some((r) => typeof r !== "string")) throw new PluginError(401, "Authenticated principal required");
987
+ }
988
+ function createPluginRouter(resolvePrincipal, options = {}) {
989
+ const maxSize = options.maxBodyBytes ?? 128 * 1024;
990
+ if (!Number.isSafeInteger(maxSize) || maxSize < 1024 || maxSize > 8 * 1024 * 1024) throw Error("Invalid plugin request body bound");
991
+ const app = new Hono();
992
+ app.use("*", bodyLimit({ maxSize, onError: (c) => c.json({ error: "Request body too large" }, 413) }));
993
+ app.use("*", async (c, next) => {
994
+ const p = await resolvePrincipal(c);
995
+ if (!p) throw new PluginError(401, "Unauthorized");
996
+ assertPrincipal(p);
997
+ c.set("principal", p);
998
+ await next();
999
+ });
1000
+ app.onError((e, c) => c.json({ error: e instanceof PluginError ? e.message : "Plugin operation failed" }, e instanceof PluginError ? e.status : 500));
1001
+ return app;
1002
+ }
1003
+ function createStandardPluginServer(manifest, ServiceClass, bindTools, options, resolvePrincipal, configureRoutes, routerOptions) {
1004
+ if (typeof resolvePrincipal !== "function") throw new Error("Verified principal resolver required");
1005
+ const service = new ServiceClass(options), routes = createPluginRouter(resolvePrincipal, routerOptions);
1006
+ configureRoutes?.(routes, service);
1007
+ routes.post("/:operation", async (c) => c.json(await service.execute(c.get("principal"), c.req.param("operation"), await jsonBody(c))));
1008
+ return { ...manifest, createRoutes: () => routes, mcp: { tools: bindTools(service) } };
1009
+ }
1010
+ async function jsonBody(c) {
1011
+ if (!/^application\/json(?:;|$)/i.test(c.req.header("Content-Type") ?? "")) throw new PluginError(400, "JSON body required");
1012
+ const value = await c.req.json().catch(() => null);
1013
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new PluginError(400, "JSON object required");
1014
+ return value;
1015
+ }
1016
+ function exactKeys(value, keys) {
1017
+ if (Object.keys(value).some((k) => !keys.includes(k))) throw new PluginError(400, "Unknown input field");
1018
+ }
1019
+ function textInput(value, name, max = 256) {
1020
+ if (typeof value !== "string" || !value.trim() || value.length > max) throw new PluginError(400, `Invalid ${name}`);
1021
+ return value;
1022
+ }
1023
+ function revisionInput(value) {
1024
+ if (!Number.isSafeInteger(value) || Number(value) < 1) throw new PluginError(400, "Revision required");
1025
+ return Number(value);
1026
+ }
1027
+ function validateNamespace(namespace) {
1028
+ if (!/^[a-z][a-z0-9_]{0,62}$/.test(namespace)) throw new Error("Invalid plugin storage namespace");
1029
+ return `${namespace}:`;
1030
+ }
1031
+ function createSqlRecordStore(table, driver, options = {}) {
1032
+ if (!/^[a-z][a-z0-9_]{0,62}$/.test(table)) throw new Error("Invalid plugin table");
1033
+ const prefix = options.namespace ? validateNamespace(options.namespace) : "";
1034
+ const storagePolicy = resolvePluginStoragePolicy2(options.storagePolicy);
1035
+ const documents = options.documents && structuredClone(options.documents);
1036
+ if (documents?.some((d) => d.table !== table || d.namespace !== options.namespace)) throw Error("JSON document storage binding differs");
1037
+ const validateDocument = documents && createRecordDocumentValidator(documents);
1038
+ const physicalId = (id2) => {
1039
+ if (prefix && id2.length + prefix.length > 256) throw new PluginError(400, "Record id is too long for the shared plugin table");
1040
+ return `${prefix}${id2}`;
1041
+ };
1042
+ const logicalId = (id2) => prefix ? id2.startsWith(prefix) ? id2.slice(prefix.length) : null : id2;
1043
+ const bind = (i) => driver.dialect === "postgres" ? `$${i}` : driver.dialect === "oracle" ? `:${i}` : "?";
1044
+ const decode = (tenant, row) => {
1045
+ const id2 = logicalId(String(row.id ?? row.ID));
1046
+ if (id2 === null || !id2) throw new Error("Record belongs to another plugin storage namespace");
1047
+ const record = { id: id2, revision: Number(row.revision ?? row.REVISION), value: JSON.parse(String(row.payload ?? row.PAYLOAD)) };
1048
+ validateDocument?.(tenant, record);
1049
+ return record;
1050
+ };
1051
+ const listPage = async (tenant, limit, after = "") => {
1052
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw new PluginError(400, "Limit must be between 1 and 100");
1053
+ if (after && (typeof after !== "string" || !/^[A-Za-z0-9_.:/-]{1,240}$/.test(after))) throw new PluginError(400, "Invalid page position");
1054
+ const bindings = [tenant], filters = [`tenant_id = ${bind(1)}`];
1055
+ if (prefix) {
1056
+ bindings.push(prefix, prefix.slice(0, -1) + ";");
1057
+ filters.push(`id >= ${bind(bindings.length - 1)} AND id < ${bind(bindings.length)}`);
1058
+ }
1059
+ if (after) {
1060
+ bindings.push(physicalId(after));
1061
+ filters.push(`id > ${bind(bindings.length)}`);
1062
+ }
1063
+ const tail = driver.dialect === "oracle" ? `FETCH FIRST ${limit} ROWS ONLY` : `LIMIT ${limit}`;
1064
+ return (await driver.execute(tenant, `SELECT id, revision, payload FROM ${table} WHERE ${filters.join(" AND ")} ORDER BY id ${tail}`, bindings)).rows?.map((row) => decode(tenant, row)) ?? [];
1065
+ };
1066
+ return {
1067
+ async read(tenant, id2) {
1068
+ const result = await driver.execute(tenant, `SELECT id, revision, payload FROM ${table} WHERE tenant_id = ${bind(1)} AND id = ${bind(2)}`, [tenant, physicalId(id2)]);
1069
+ return result.rows?.[0] ? decode(tenant, result.rows[0]) : null;
1070
+ },
1071
+ list: (tenant, limit) => listPage(tenant, limit),
1072
+ listPage,
1073
+ async create(tenant, record) {
1074
+ if (validateDocument && record.revision !== 1) throw new PluginError(409, "New record revision must be 1");
1075
+ validateDocument?.(tenant, record);
1076
+ const payload = storagePayload2(storagePolicy, record.value);
1077
+ try {
1078
+ await driver.execute(tenant, `INSERT INTO ${table} (tenant_id, id, revision, payload) VALUES (${bind(1)}, ${bind(2)}, ${bind(3)}, ${bind(4)})`, [tenant, physicalId(record.id), record.revision, payload]);
1079
+ } catch (error) {
1080
+ if (["SQLITE_CONSTRAINT_PRIMARYKEY", "SQLITE_CONSTRAINT_UNIQUE", "23505", "ORA-00001"].includes(error?.code) || error?.errorNum === 1) throw new PluginError(409, "Record already exists");
1081
+ throw error;
1082
+ }
1083
+ },
1084
+ async replace(tenant, record, expected) {
1085
+ if (!Number.isSafeInteger(expected) || expected < 1 || record.revision !== expected + 1) throw new Error("Invalid revision transition");
1086
+ validateDocument?.(tenant, record);
1087
+ const result = await driver.execute(tenant, `UPDATE ${table} SET revision = ${bind(1)}, payload = ${bind(2)} WHERE tenant_id = ${bind(3)} AND id = ${bind(4)} AND revision = ${bind(5)}`, [record.revision, storagePayload2(storagePolicy, record.value), tenant, physicalId(record.id), expected]);
1088
+ if (typeof result.affectedRows !== "number") throw new Error("Record driver must report affectedRows");
1089
+ return result.affectedRows === 1;
1090
+ },
1091
+ async delete(tenant, id2, expected) {
1092
+ if (documents?.filter((d) => id2.startsWith(d.recordPrefix ?? "")).sort((a, b) => (b.recordPrefix ?? "").length - (a.recordPrefix ?? "").length)[0]?.immutable) throw new PluginError(409, "Immutable document cannot be deleted");
1093
+ const hasRevision = typeof expected === "number";
1094
+ const sql = hasRevision ? `DELETE FROM ${table} WHERE tenant_id = ${bind(1)} AND id = ${bind(2)} AND revision = ${bind(3)}` : `DELETE FROM ${table} WHERE tenant_id = ${bind(1)} AND id = ${bind(2)}`;
1095
+ const bindings = hasRevision ? [tenant, physicalId(id2), expected] : [tenant, physicalId(id2)];
1096
+ const result = await driver.execute(tenant, sql, bindings);
1097
+ return (result.affectedRows ?? 0) >= 1;
1098
+ }
1099
+ };
1100
+ }
1101
+ function normalizeD1ConstraintError(error) {
1102
+ const message = error instanceof Error ? error.message : String(error);
1103
+ if (/UNIQUE constraint failed|PRIMARY KEY constraint failed|SQLITE_CONSTRAINT/i.test(message)) {
1104
+ const constraintErr = new Error(message);
1105
+ constraintErr.code = "SQLITE_CONSTRAINT_PRIMARYKEY";
1106
+ throw constraintErr;
1107
+ }
1108
+ throw error instanceof Error ? error : new Error(message);
1109
+ }
1110
+ function createCloudflareD1Driver(options) {
1111
+ const hasBinding = Boolean(options.binding && typeof options.binding.prepare === "function");
1112
+ const hasHttp = Boolean(options.accountId?.trim() && options.databaseId?.trim() && options.apiToken?.trim());
1113
+ if (!hasBinding && !hasHttp) {
1114
+ throw new Error("Cloudflare D1 driver requires either a Worker D1 binding or accountId, databaseId, and apiToken");
1115
+ }
1116
+ const bootstrapTable = options.autoBootstrapTable;
1117
+ if (bootstrapTable && !/^[a-z][a-z0-9_]{0,62}$/.test(bootstrapTable)) {
1118
+ throw new Error("Invalid D1 bootstrap table name");
1119
+ }
1120
+ const fetcher = options.fetch ?? globalThis.fetch;
1121
+ const apiBase = (options.apiBaseUrl ?? "https://api.cloudflare.com/client/v4").replace(/\/$/, "");
1122
+ let bootstrapped = !bootstrapTable;
1123
+ const runRaw = async (sql, bindings) => {
1124
+ const isSelect = /^\s*SELECT\b/i.test(sql);
1125
+ try {
1126
+ if (hasBinding && options.binding) {
1127
+ const stmt = options.binding.prepare(sql).bind(...bindings);
1128
+ if (isSelect) {
1129
+ const res2 = await stmt.all();
1130
+ return { rows: res2.results ?? [], affectedRows: res2.meta?.changes ?? 0 };
1131
+ }
1132
+ const res = await stmt.run();
1133
+ return { rows: [], affectedRows: res.meta?.changes ?? 0 };
1134
+ }
1135
+ const url = `${apiBase}/accounts/${encodeURIComponent(options.accountId)}/d1/database/${encodeURIComponent(options.databaseId)}/query`;
1136
+ const response = await fetcher(url, {
1137
+ method: "POST",
1138
+ headers: {
1139
+ "Authorization": `Bearer ${options.apiToken}`,
1140
+ "Content-Type": "application/json"
1141
+ },
1142
+ body: JSON.stringify({ sql, params: bindings })
1143
+ });
1144
+ const body = await response.json().catch(() => null);
1145
+ if (!response.ok || !body?.success) {
1146
+ const errMsg = body?.errors?.map((e) => e.message).filter(Boolean).join("; ") || `D1 HTTP query failed (${response.status})`;
1147
+ return normalizeD1ConstraintError(new Error(errMsg));
1148
+ }
1149
+ const first = body.result?.[0];
1150
+ return {
1151
+ rows: first?.results ?? [],
1152
+ affectedRows: typeof first?.meta?.changes === "number" ? first.meta.changes : 0
1153
+ };
1154
+ } catch (error) {
1155
+ return normalizeD1ConstraintError(error);
1156
+ }
1157
+ };
1158
+ return {
1159
+ dialect: "sqlite",
1160
+ async execute(_tenantId, sql, bindings) {
1161
+ if (!bootstrapped && bootstrapTable) {
1162
+ await runRaw(
1163
+ `CREATE TABLE IF NOT EXISTS ${bootstrapTable} (tenant_id TEXT NOT NULL, id TEXT NOT NULL, revision INTEGER NOT NULL, payload TEXT NOT NULL, PRIMARY KEY (tenant_id, id))`,
1164
+ []
1165
+ );
1166
+ bootstrapped = true;
1167
+ }
1168
+ return runRaw(sql, bindings);
1169
+ }
1170
+ };
1171
+ }
1172
+ function createPluginRecordStore(manifest, driver, catalog) {
1173
+ const storage = manifest.schema.storage;
1174
+ if (storage) {
1175
+ if (!catalog) throw Error("Contract-backed record storage requires a trusted schema catalog");
1176
+ const documents = recordDocuments(catalog, storage);
1177
+ return createSqlRecordStore(storage.table, driver, { namespace: storage.namespace, storagePolicy: manifest.schema.storagePolicy, ...documents?.length ? { documents } : {} });
1178
+ }
1179
+ if (manifest.schema.ownedTables.length !== 1) throw new Error("Plugin manifest must declare one storage table or shared storage metadata");
1180
+ return createSqlRecordStore(manifest.schema.ownedTables[0], driver, { storagePolicy: manifest.schema.storagePolicy });
1181
+ }
1182
+
1183
+ // src/plugin-data/json-documents.ts
1184
+ function createValidatedRecordHost(host, documents) {
1185
+ const copy = { ...host };
1186
+ if (host.store) copy.store = createValidatedRecordStore(host.store, documents);
1187
+ if (host.repository) copy.repository = createValidatedAtomicRepository(host.repository, documents);
1188
+ if (host.transaction) {
1189
+ const transaction = host.transaction.bind(host);
1190
+ copy.transaction = (principal, work) => transaction(principal, (store) => work(createValidatedRecordStore(store, documents)));
1191
+ }
1192
+ return new Proxy(copy, { get(target, key, receiver) {
1193
+ if (key === "store" || key === "repository" || key === "transaction") return Reflect.get(target, key, receiver);
1194
+ const value = Reflect.get(host, key, host);
1195
+ return typeof value === "function" ? value.bind(host) : value;
1196
+ } });
1197
+ }
1198
+ function recordDocuments(catalog, binding) {
1199
+ assertDataContractBinding(catalog, binding);
1200
+ const columns = catalog.tables.find((t) => t.name === binding.table).schema.properties;
1201
+ if (!columns.id || !columns.revision || !columns.payload) throw Error("Record stores require an envelope table with id, revision and payload");
1202
+ const documents = catalog.contracts.find((c) => c.id === binding.contractId).jsonDocuments?.filter((d) => d.table === binding.table && d.column === "payload" && d.namespace === binding.namespace) ?? [];
1203
+ if (binding.jsonDocuments !== void 0 && (!binding.jsonDocuments.length || new Set(binding.jsonDocuments).size !== binding.jsonDocuments.length || binding.jsonDocuments.some((id2) => !documents.some((d) => d.id === id2)))) throw Error("Declared JSON document contract is unavailable for this storage binding");
1204
+ return documents;
1205
+ }
1206
+ function createRecordDocumentValidator(documents) {
1207
+ if (!documents.length) throw Error("Record document contracts are required");
1208
+ const routes = documents.map((document) => {
1209
+ const copy = structuredClone(document);
1210
+ if (copy.column !== "payload" || copy.schema.type !== "object" || copy.schema.additionalProperties !== false) throw Error("Closed payload document contract required");
1211
+ return { document: copy, validate: createJsonDocumentAssertion(copy) };
1212
+ }).sort((a, b) => (b.document.recordPrefix ?? "").length - (a.document.recordPrefix ?? "").length);
1213
+ const prefixes = /* @__PURE__ */ new Set();
1214
+ for (const route of routes) {
1215
+ const prefix = route.document.recordPrefix ?? "";
1216
+ if (prefixes.has(prefix)) throw Error("Ambiguous record document route");
1217
+ prefixes.add(prefix);
1218
+ }
1219
+ return (tenantId, record) => {
1220
+ assertBusinessId(tenantId);
1221
+ assertBusinessId(record.id);
1222
+ if (!Number.isSafeInteger(record.revision) || record.revision < 1) throw new PluginError(409, "Invalid record revision");
1223
+ const route = routes.find((r) => record.id.startsWith(r.document.recordPrefix ?? ""));
1224
+ if (!route) throw new PluginError(409, "Record document contract unavailable");
1225
+ if (route.document.immutable && record.revision !== 1) throw new PluginError(409, "Immutable document cannot be replaced");
1226
+ try {
1227
+ route.validate(record.value);
1228
+ } catch {
1229
+ throw new PluginError(409, `Record differs from JSON contract ${route.document.id}@${route.document.version}`);
1230
+ }
1231
+ const value = record.value;
1232
+ if (Object.hasOwn(value, "tenantId") && value.tenantId !== tenantId) throw new PluginError(409, "JSON document tenant differs");
1233
+ };
1234
+ }
1235
+ function createValidatedRecordStore(store, documents) {
1236
+ const ownedDocuments = structuredClone(documents);
1237
+ const validate = createRecordDocumentValidator(ownedDocuments);
1238
+ const check = (tenant, row) => {
1239
+ validate(tenant, row);
1240
+ return row;
1241
+ };
1242
+ return {
1243
+ async read(tenant, id2) {
1244
+ const row = await store.read(tenant, id2);
1245
+ if (row && row.id !== id2) throw new PluginError(409, "Record identity differs");
1246
+ return row && check(tenant, row);
1247
+ },
1248
+ async list(tenant, limit) {
1249
+ return (await store.list(tenant, limit)).map((row) => check(tenant, row));
1250
+ },
1251
+ ...store.listPage ? { async listPage(tenant, limit, after) {
1252
+ return (await store.listPage(tenant, limit, after)).map((row) => check(tenant, row));
1253
+ } } : {},
1254
+ async create(tenant, row) {
1255
+ if (row.revision !== 1) throw new PluginError(409, "New record revision must be 1");
1256
+ validate(tenant, row);
1257
+ await store.create(tenant, row);
1258
+ },
1259
+ async replace(tenant, row, expected) {
1260
+ if (!Number.isSafeInteger(expected) || expected < 1 || row.revision !== expected + 1) throw new PluginError(409, "Invalid revision transition");
1261
+ validate(tenant, row);
1262
+ return store.replace(tenant, row, expected);
1263
+ },
1264
+ ...store.delete ? { delete: async (tenant, id2, expected) => {
1265
+ const immutable = [...ownedDocuments].sort((a, b) => (b.recordPrefix ?? "").length - (a.recordPrefix ?? "").length).find((d) => id2.startsWith(d.recordPrefix ?? ""))?.immutable;
1266
+ if (immutable) throw new PluginError(409, "Immutable document cannot be deleted");
1267
+ return store.delete(tenant, id2, expected);
1268
+ } } : {}
1269
+ };
1270
+ }
1271
+ function createValidatedAtomicRepository(repository, documents) {
1272
+ const validate = createRecordDocumentValidator(structuredClone(documents));
1273
+ return { async run(principal, work) {
1274
+ assertPrincipal(principal);
1275
+ const p = structuredClone(principal);
1276
+ return repository.run(p, async (source) => {
1277
+ let active = true;
1278
+ const check = () => {
1279
+ if (!active) throw Error("Transaction session expired");
1280
+ };
1281
+ const read = (row, id2) => {
1282
+ check();
1283
+ if (row) {
1284
+ if (id2 !== void 0 && row.id !== id2) throw Error("Record identity differs");
1285
+ validate(p.tenantId, row);
1286
+ }
1287
+ return row;
1288
+ };
1289
+ const preflight = (id2, value, expected) => {
1290
+ check();
1291
+ if (!Number.isSafeInteger(expected) || expected < 0 || expected >= Number.MAX_SAFE_INTEGER) throw Error("Invalid expected revision");
1292
+ const row = { id: id2, value: structuredClone(value), revision: expected + 1 };
1293
+ validate(p.tenantId, row);
1294
+ return row;
1295
+ };
1296
+ const session = {
1297
+ async get(id2) {
1298
+ check();
1299
+ return read(await source.get(id2), id2);
1300
+ },
1301
+ async list(prefix, after, limit) {
1302
+ check();
1303
+ const rows = await source.list(prefix, after, limit);
1304
+ return rows.map((row) => {
1305
+ if (!row.id.startsWith(prefix)) throw Error("Record list scope differs");
1306
+ return read(row);
1307
+ });
1308
+ },
1309
+ async put(id2, value, expected) {
1310
+ const row = preflight(id2, value, expected);
1311
+ const saved = read(await source.put(id2, row.value, expected), id2);
1312
+ if (saved.revision !== row.revision) throw Error("Saved revision differs");
1313
+ return saved;
1314
+ },
1315
+ ...source.getMany ? { async getMany(ids) {
1316
+ check();
1317
+ const rows = await source.getMany(ids);
1318
+ if (rows.length !== ids.length) throw Error("Batch read length differs");
1319
+ return rows.map((row, i) => read(row, ids[i]));
1320
+ } } : {},
1321
+ ...source.putMany ? { async putMany(changes) {
1322
+ check();
1323
+ if (new Set(changes.map((c) => c.id)).size !== changes.length) throw Error("Duplicate batch record key");
1324
+ const rows = changes.map((c) => preflight(c.id, c.value, c.expected));
1325
+ const saved = await source.putMany(changes.map((c, i) => ({ ...c, value: rows[i].value })));
1326
+ if (saved.length !== rows.length) throw Error("Batch write length differs");
1327
+ return saved.map((row, i) => {
1328
+ const result = read(row, rows[i].id);
1329
+ if (result.revision !== rows[i].revision) throw Error("Saved revision differs");
1330
+ return result;
1331
+ });
1332
+ } } : {}
1333
+ };
1334
+ const release = inheritAtomicSqlSession(source, session);
1335
+ try {
1336
+ return await work(session);
1337
+ } finally {
1338
+ active = false;
1339
+ release();
1340
+ }
1341
+ });
1342
+ } };
1343
+ }
1344
+
1345
+ export {
1346
+ createValidatedRecordHost,
1347
+ recordDocuments,
1348
+ createRecordDocumentValidator,
1349
+ createValidatedRecordStore,
1350
+ createValidatedAtomicRepository,
1351
+ RecordCommandService,
1352
+ appendAtomicAudit,
1353
+ readAtomicAudit,
1354
+ verifyAtomicAudit,
1355
+ joinedAtomicSqlExecutor,
1356
+ atomicHash,
1357
+ atomicCommand,
1358
+ recordCursor,
1359
+ parseRecordCursor,
1360
+ createAtomicRepository,
1361
+ createAtomicRecordStore,
1362
+ createPluginAtomicRepository,
1363
+ createSqliteAtomicDriver,
1364
+ createOracleAtomicDriver,
1365
+ enqueueOutboxEvent,
1366
+ createAtomicWorkflowEventSink,
1367
+ createAtomicOutboxStore,
1368
+ createAtomicOutboxDispatcher,
1369
+ requeueOutboxEvent,
1370
+ uniqueRecordKey,
1371
+ ScopedRecordService,
1372
+ createCompositePluginService,
1373
+ annotationOperations,
1374
+ annotationSchemas,
1375
+ documentAnnotations,
1376
+ deliveryOperations,
1377
+ enqueueDeliverySchema,
1378
+ deliveryJobSchema,
1379
+ recoverDeliverySchema,
1380
+ ArtifactDeliveryService,
1381
+ createMcpToolBinder,
1382
+ createMcpOperationHandler,
1383
+ PluginError,
1384
+ assertPrincipal,
1385
+ createPluginRouter,
1386
+ createStandardPluginServer,
1387
+ jsonBody,
1388
+ exactKeys,
1389
+ textInput,
1390
+ revisionInput,
1391
+ createSqlRecordStore,
1392
+ createCloudflareD1Driver,
1393
+ createPluginRecordStore
1394
+ };
1395
+ //# sourceMappingURL=chunk-INCHXAFK.js.map