@btst/stack 3.0.0-rc.2 → 3.0.0-rc.3

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 (1168) hide show
  1. package/README.md +118 -38
  2. package/dist/api/index.cjs +133 -29
  3. package/dist/api/index.d.cts +14 -32
  4. package/dist/api/index.d.mts +14 -32
  5. package/dist/api/index.d.ts +14 -32
  6. package/dist/api/index.mjs +133 -28
  7. package/dist/authorization/client.cjs +154 -0
  8. package/dist/authorization/client.d.cts +70 -0
  9. package/dist/authorization/client.d.mts +70 -0
  10. package/dist/authorization/client.d.ts +70 -0
  11. package/dist/authorization/client.mjs +152 -0
  12. package/dist/authorization/index.cjs +371 -0
  13. package/dist/authorization/index.d.cts +201 -0
  14. package/dist/authorization/index.d.mts +201 -0
  15. package/dist/authorization/index.d.ts +201 -0
  16. package/dist/authorization/index.mjs +365 -0
  17. package/dist/authorization/remote.cjs +117 -0
  18. package/dist/authorization/remote.d.cts +79 -0
  19. package/dist/authorization/remote.d.mts +79 -0
  20. package/dist/authorization/remote.d.ts +79 -0
  21. package/dist/authorization/remote.mjs +111 -0
  22. package/dist/authorization/server.cjs +77 -0
  23. package/dist/authorization/server.d.cts +51 -0
  24. package/dist/authorization/server.d.mts +51 -0
  25. package/dist/authorization/server.d.ts +51 -0
  26. package/dist/authorization/server.mjs +73 -0
  27. package/dist/client/components/index.cjs +1 -0
  28. package/dist/client/components/index.d.cts +23 -6
  29. package/dist/client/components/index.d.mts +23 -6
  30. package/dist/client/components/index.d.ts +23 -6
  31. package/dist/client/components/index.mjs +1 -1
  32. package/dist/client/hooks/index.cjs +8 -0
  33. package/dist/client/hooks/index.d.cts +25 -0
  34. package/dist/client/hooks/index.d.mts +25 -0
  35. package/dist/client/hooks/index.d.ts +25 -0
  36. package/dist/client/hooks/index.mjs +2 -0
  37. package/dist/client/index.cjs +38 -7
  38. package/dist/client/index.d.cts +30 -100
  39. package/dist/client/index.d.mts +30 -100
  40. package/dist/client/index.d.ts +30 -100
  41. package/dist/client/index.mjs +38 -7
  42. package/dist/client/server.cjs +96 -0
  43. package/dist/client/server.d.cts +57 -0
  44. package/dist/client/server.d.mts +57 -0
  45. package/dist/client/server.d.ts +57 -0
  46. package/dist/client/server.mjs +92 -0
  47. package/dist/components/auto-form/index.d.cts +2 -2
  48. package/dist/components/auto-form/index.d.mts +2 -2
  49. package/dist/components/auto-form/index.d.ts +2 -2
  50. package/dist/components/form-builder/index.d.cts +1 -1
  51. package/dist/components/form-builder/index.d.mts +1 -1
  52. package/dist/components/form-builder/index.d.ts +1 -1
  53. package/dist/components/stepped-auto-form/index.d.cts +1 -1
  54. package/dist/components/stepped-auto-form/index.d.mts +1 -1
  55. package/dist/components/stepped-auto-form/index.d.ts +1 -1
  56. package/dist/context/index.cjs +8 -2
  57. package/dist/context/index.d.cts +111 -89
  58. package/dist/context/index.d.mts +111 -89
  59. package/dist/context/index.d.ts +111 -89
  60. package/dist/context/index.mjs +3 -1
  61. package/dist/index.cjs +1 -2
  62. package/dist/index.d.cts +8 -3
  63. package/dist/index.d.mts +8 -3
  64. package/dist/index.d.ts +8 -3
  65. package/dist/index.mjs +1 -1
  66. package/dist/next/index.d.cts +19 -7
  67. package/dist/next/index.d.mts +19 -7
  68. package/dist/next/index.d.ts +19 -7
  69. package/dist/next/server.cjs +7 -0
  70. package/dist/next/server.d.cts +56 -0
  71. package/dist/next/server.d.mts +56 -0
  72. package/dist/next/server.d.ts +56 -0
  73. package/dist/next/server.mjs +1 -0
  74. package/dist/packages/stack/src/authorization/json.cjs +57 -0
  75. package/dist/packages/stack/src/authorization/json.mjs +55 -0
  76. package/dist/packages/stack/src/client/components/compose.cjs +91 -9
  77. package/dist/packages/stack/src/client/components/compose.mjs +92 -11
  78. package/dist/packages/stack/src/client/runtime.cjs +295 -0
  79. package/dist/packages/stack/src/client/runtime.mjs +293 -0
  80. package/dist/packages/stack/src/context/auth.cjs +141 -73
  81. package/dist/packages/stack/src/context/auth.mjs +139 -73
  82. package/dist/packages/stack/src/context/provider.cjs +11 -5
  83. package/dist/packages/stack/src/context/provider.mjs +11 -5
  84. package/dist/packages/stack/src/context/router.cjs +9 -0
  85. package/dist/packages/stack/src/context/router.mjs +7 -0
  86. package/dist/packages/stack/src/context/site-navigation.cjs +40 -0
  87. package/dist/packages/stack/src/context/site-navigation.mjs +38 -0
  88. package/dist/packages/stack/src/next/layout.cjs +27 -0
  89. package/dist/packages/stack/src/next/layout.mjs +25 -0
  90. package/dist/packages/stack/src/plugin-registration.cjs +59 -0
  91. package/dist/packages/stack/src/plugin-registration.mjs +57 -0
  92. package/dist/packages/stack/src/plugins/ai-chat/api/lifecycle-migrations.cjs +18 -0
  93. package/dist/packages/stack/src/plugins/ai-chat/api/lifecycle-migrations.mjs +16 -0
  94. package/dist/packages/stack/src/plugins/ai-chat/api/operations.cjs +1341 -0
  95. package/dist/packages/stack/src/plugins/ai-chat/api/operations.mjs +1333 -0
  96. package/dist/packages/stack/src/plugins/ai-chat/api/plugin.cjs +82 -640
  97. package/dist/packages/stack/src/plugins/ai-chat/api/plugin.mjs +78 -641
  98. package/dist/packages/stack/src/plugins/ai-chat/api/serializers.cjs +23 -0
  99. package/dist/packages/stack/src/plugins/ai-chat/api/serializers.mjs +20 -0
  100. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-input.cjs +183 -111
  101. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-input.mjs +187 -115
  102. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.cjs +562 -116
  103. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.mjs +567 -121
  104. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.cjs +6 -6
  105. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.mjs +6 -6
  106. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-message.cjs +1 -1
  107. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-message.mjs +1 -1
  108. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-sidebar.cjs +45 -29
  109. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-sidebar.mjs +46 -30
  110. package/dist/packages/stack/src/plugins/ai-chat/client/components/pages/404-page.cjs +1 -1
  111. package/dist/packages/stack/src/plugins/ai-chat/client/components/pages/404-page.mjs +1 -1
  112. package/dist/packages/stack/src/plugins/ai-chat/client/components/pages/chat-page.cjs +46 -7
  113. package/dist/packages/stack/src/plugins/ai-chat/client/components/pages/chat-page.internal.cjs +5 -3
  114. package/dist/packages/stack/src/plugins/ai-chat/client/components/pages/chat-page.internal.mjs +5 -3
  115. package/dist/packages/stack/src/plugins/ai-chat/client/components/pages/chat-page.mjs +48 -9
  116. package/dist/packages/stack/src/plugins/ai-chat/client/components/shared/default-error.cjs +1 -1
  117. package/dist/packages/stack/src/plugins/ai-chat/client/components/shared/default-error.mjs +1 -1
  118. package/dist/packages/stack/src/plugins/ai-chat/client/components/tool-call-display.cjs +1 -1
  119. package/dist/packages/stack/src/plugins/ai-chat/client/components/tool-call-display.mjs +1 -1
  120. package/dist/packages/stack/src/plugins/ai-chat/client/hooks/ai-chat-resource.cjs +1 -1
  121. package/dist/packages/stack/src/plugins/ai-chat/client/hooks/ai-chat-resource.mjs +1 -1
  122. package/dist/packages/stack/src/plugins/ai-chat/client/hooks/chat-hooks.cjs +179 -19
  123. package/dist/packages/stack/src/plugins/ai-chat/client/hooks/chat-hooks.mjs +180 -21
  124. package/dist/packages/stack/src/plugins/ai-chat/client/navigation.cjs +11 -0
  125. package/dist/packages/stack/src/plugins/ai-chat/client/navigation.mjs +9 -0
  126. package/dist/packages/stack/src/plugins/ai-chat/client/overrides.cjs +24 -0
  127. package/dist/packages/stack/src/plugins/ai-chat/client/overrides.mjs +21 -1
  128. package/dist/packages/stack/src/plugins/ai-chat/client/plugin.cjs +152 -82
  129. package/dist/packages/stack/src/plugins/ai-chat/client/plugin.mjs +152 -82
  130. package/dist/packages/stack/src/plugins/api/create-endpoint.cjs +27 -9
  131. package/dist/packages/stack/src/plugins/api/create-endpoint.mjs +27 -10
  132. package/dist/packages/stack/src/plugins/api/endpoint-inventory.cjs +125 -0
  133. package/dist/packages/stack/src/plugins/api/endpoint-inventory.mjs +123 -0
  134. package/dist/packages/stack/src/plugins/api/operation.cjs +208 -0
  135. package/dist/packages/stack/src/plugins/api/operation.mjs +198 -0
  136. package/dist/packages/stack/src/plugins/blog/api/lifecycle-migrations.cjs +17 -0
  137. package/dist/packages/stack/src/plugins/blog/api/lifecycle-migrations.mjs +15 -0
  138. package/dist/packages/stack/src/plugins/blog/api/mutations.cjs +10 -2
  139. package/dist/packages/stack/src/plugins/blog/api/mutations.mjs +10 -2
  140. package/dist/packages/stack/src/plugins/blog/api/operations.cjs +455 -0
  141. package/dist/packages/stack/src/plugins/blog/api/operations.mjs +448 -0
  142. package/dist/packages/stack/src/plugins/blog/api/plugin.cjs +51 -298
  143. package/dist/packages/stack/src/plugins/blog/api/plugin.mjs +55 -304
  144. package/dist/packages/stack/src/plugins/blog/client/components/forms/image-field.cjs +2 -1
  145. package/dist/packages/stack/src/plugins/blog/client/components/forms/image-field.mjs +2 -1
  146. package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.cjs +2 -1
  147. package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.mjs +2 -1
  148. package/dist/packages/stack/src/plugins/blog/client/components/forms/post-forms.cjs +82 -67
  149. package/dist/packages/stack/src/plugins/blog/client/components/forms/post-forms.mjs +84 -69
  150. package/dist/packages/stack/src/plugins/blog/client/components/pages/404-page.cjs +2 -1
  151. package/dist/packages/stack/src/plugins/blog/client/components/pages/404-page.mjs +2 -1
  152. package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.cjs +22 -4
  153. package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.internal.cjs +2 -1
  154. package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.internal.mjs +2 -1
  155. package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.mjs +23 -5
  156. package/dist/packages/stack/src/plugins/blog/client/components/pages/home-page.cjs +6 -2
  157. package/dist/packages/stack/src/plugins/blog/client/components/pages/home-page.internal.cjs +2 -1
  158. package/dist/packages/stack/src/plugins/blog/client/components/pages/home-page.internal.mjs +2 -1
  159. package/dist/packages/stack/src/plugins/blog/client/components/pages/home-page.mjs +6 -2
  160. package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.cjs +4 -2
  161. package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.internal.cjs +2 -1
  162. package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.internal.mjs +2 -1
  163. package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.mjs +4 -2
  164. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.cjs +30 -3
  165. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.cjs +2 -1
  166. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.mjs +2 -1
  167. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.mjs +31 -4
  168. package/dist/packages/stack/src/plugins/blog/client/components/pages/tag-page.cjs +4 -1
  169. package/dist/packages/stack/src/plugins/blog/client/components/pages/tag-page.internal.cjs +2 -1
  170. package/dist/packages/stack/src/plugins/blog/client/components/pages/tag-page.internal.mjs +2 -1
  171. package/dist/packages/stack/src/plugins/blog/client/components/pages/tag-page.mjs +4 -1
  172. package/dist/packages/stack/src/plugins/blog/client/components/shared/collapsible-tag-list.cjs +2 -1
  173. package/dist/packages/stack/src/plugins/blog/client/components/shared/collapsible-tag-list.mjs +2 -1
  174. package/dist/packages/stack/src/plugins/blog/client/components/shared/default-error.cjs +2 -1
  175. package/dist/packages/stack/src/plugins/blog/client/components/shared/default-error.mjs +2 -1
  176. package/dist/packages/stack/src/plugins/blog/client/components/shared/on-this-page.cjs +3 -2
  177. package/dist/packages/stack/src/plugins/blog/client/components/shared/on-this-page.mjs +3 -2
  178. package/dist/packages/stack/src/plugins/blog/client/components/shared/page-wrapper.cjs +2 -1
  179. package/dist/packages/stack/src/plugins/blog/client/components/shared/page-wrapper.mjs +2 -1
  180. package/dist/packages/stack/src/plugins/blog/client/components/shared/post-card.cjs +2 -1
  181. package/dist/packages/stack/src/plugins/blog/client/components/shared/post-card.mjs +2 -1
  182. package/dist/packages/stack/src/plugins/blog/client/components/shared/posts-list.cjs +2 -1
  183. package/dist/packages/stack/src/plugins/blog/client/components/shared/posts-list.mjs +2 -1
  184. package/dist/packages/stack/src/plugins/blog/client/components/shared/recent-posts-carousel.cjs +2 -1
  185. package/dist/packages/stack/src/plugins/blog/client/components/shared/recent-posts-carousel.mjs +2 -1
  186. package/dist/packages/stack/src/plugins/blog/client/components/shared/search-input.cjs +2 -2
  187. package/dist/packages/stack/src/plugins/blog/client/components/shared/search-input.mjs +1 -1
  188. package/dist/packages/stack/src/plugins/blog/client/constants.cjs +5 -0
  189. package/dist/packages/stack/src/plugins/blog/client/constants.mjs +3 -0
  190. package/dist/packages/stack/src/plugins/blog/client/hooks/blog-resource.cjs +2 -1
  191. package/dist/packages/stack/src/plugins/blog/client/hooks/blog-resource.mjs +2 -1
  192. package/dist/packages/stack/src/plugins/blog/client/plugin.cjs +169 -163
  193. package/dist/packages/stack/src/plugins/blog/client/plugin.mjs +169 -163
  194. package/dist/packages/stack/src/plugins/client/resource/hooks.cjs +31 -16
  195. package/dist/packages/stack/src/plugins/client/resource/hooks.mjs +31 -16
  196. package/dist/packages/stack/src/plugins/client/resource/internal.cjs +66 -52
  197. package/dist/packages/stack/src/plugins/client/resource/internal.mjs +66 -52
  198. package/dist/packages/stack/src/plugins/client/resource/use-form.cjs +20 -17
  199. package/dist/packages/stack/src/plugins/client/resource/use-form.mjs +20 -17
  200. package/dist/packages/stack/src/plugins/client/resource/use-select.cjs +39 -33
  201. package/dist/packages/stack/src/plugins/client/resource/use-select.mjs +39 -33
  202. package/dist/packages/stack/src/plugins/cms/api/lifecycle-migrations.cjs +13 -0
  203. package/dist/packages/stack/src/plugins/cms/api/lifecycle-migrations.mjs +11 -0
  204. package/dist/packages/stack/src/plugins/cms/api/operations.cjs +1763 -0
  205. package/dist/packages/stack/src/plugins/cms/api/operations.mjs +1756 -0
  206. package/dist/packages/stack/src/plugins/cms/api/plugin.cjs +165 -752
  207. package/dist/packages/stack/src/plugins/cms/api/plugin.mjs +164 -755
  208. package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.cjs +2 -1
  209. package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.mjs +2 -1
  210. package/dist/packages/stack/src/plugins/cms/client/components/forms/file-upload.cjs +2 -1
  211. package/dist/packages/stack/src/plugins/cms/client/components/forms/file-upload.mjs +2 -1
  212. package/dist/packages/stack/src/plugins/cms/client/components/forms/relation-field.cjs +2 -1
  213. package/dist/packages/stack/src/plugins/cms/client/components/forms/relation-field.mjs +2 -1
  214. package/dist/packages/stack/src/plugins/cms/client/components/inverse-relations-panel.cjs +23 -13
  215. package/dist/packages/stack/src/plugins/cms/client/components/inverse-relations-panel.mjs +24 -14
  216. package/dist/packages/stack/src/plugins/cms/client/components/pages/404-page.cjs +5 -4
  217. package/dist/packages/stack/src/plugins/cms/client/components/pages/404-page.mjs +6 -5
  218. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-editor-page.cjs +40 -7
  219. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-editor-page.internal.cjs +15 -9
  220. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-editor-page.internal.mjs +14 -8
  221. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-editor-page.mjs +41 -8
  222. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-list-page.cjs +7 -1
  223. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-list-page.internal.cjs +42 -29
  224. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-list-page.internal.mjs +40 -27
  225. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-list-page.mjs +7 -1
  226. package/dist/packages/stack/src/plugins/cms/client/components/pages/dashboard-page.cjs +4 -1
  227. package/dist/packages/stack/src/plugins/cms/client/components/pages/dashboard-page.internal.cjs +13 -7
  228. package/dist/packages/stack/src/plugins/cms/client/components/pages/dashboard-page.internal.mjs +12 -6
  229. package/dist/packages/stack/src/plugins/cms/client/components/pages/dashboard-page.mjs +4 -1
  230. package/dist/packages/stack/src/plugins/cms/client/components/shared/page-wrapper.cjs +2 -1
  231. package/dist/packages/stack/src/plugins/cms/client/components/shared/page-wrapper.mjs +2 -1
  232. package/dist/packages/stack/src/plugins/cms/client/constants.cjs +5 -0
  233. package/dist/packages/stack/src/plugins/cms/client/constants.mjs +3 -0
  234. package/dist/packages/stack/src/plugins/cms/client/hooks/cms-resource.cjs +2 -1
  235. package/dist/packages/stack/src/plugins/cms/client/hooks/cms-resource.mjs +2 -1
  236. package/dist/packages/stack/src/plugins/cms/client/overrides.cjs +26 -0
  237. package/dist/packages/stack/src/plugins/cms/client/overrides.mjs +24 -0
  238. package/dist/packages/stack/src/plugins/cms/client/plugin.cjs +102 -81
  239. package/dist/packages/stack/src/plugins/cms/client/plugin.mjs +103 -82
  240. package/dist/packages/stack/src/plugins/comments/api/getters.cjs +6 -2
  241. package/dist/packages/stack/src/plugins/comments/api/getters.mjs +6 -3
  242. package/dist/packages/stack/src/plugins/comments/api/lifecycle-migrations.cjs +18 -0
  243. package/dist/packages/stack/src/plugins/comments/api/lifecycle-migrations.mjs +16 -0
  244. package/dist/packages/stack/src/plugins/comments/api/mutations.cjs +179 -68
  245. package/dist/packages/stack/src/plugins/comments/api/mutations.mjs +179 -68
  246. package/dist/packages/stack/src/plugins/comments/api/operations.cjs +453 -0
  247. package/dist/packages/stack/src/plugins/comments/api/operations.mjs +445 -0
  248. package/dist/packages/stack/src/plugins/comments/api/plugin.cjs +51 -276
  249. package/dist/packages/stack/src/plugins/comments/api/plugin.mjs +48 -277
  250. package/dist/packages/stack/src/plugins/comments/client/components/comment-count.cjs +32 -5
  251. package/dist/packages/stack/src/plugins/comments/client/components/comment-count.mjs +34 -7
  252. package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.cjs +160 -104
  253. package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.mjs +161 -105
  254. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.cjs +14 -2
  255. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.internal.cjs +141 -71
  256. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.internal.mjs +142 -72
  257. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.mjs +14 -2
  258. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-state.cjs +12 -0
  259. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-state.mjs +9 -0
  260. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.cjs +2 -1
  261. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.internal.cjs +40 -26
  262. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.internal.mjs +40 -26
  263. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.mjs +2 -1
  264. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.cjs +9 -1
  265. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.internal.cjs +25 -20
  266. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.internal.mjs +26 -21
  267. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.mjs +9 -1
  268. package/dist/packages/stack/src/plugins/comments/client/components/shared/page-wrapper.cjs +2 -1
  269. package/dist/packages/stack/src/plugins/comments/client/components/shared/page-wrapper.mjs +2 -1
  270. package/dist/packages/stack/src/plugins/comments/client/components/shared/pagination.cjs +2 -1
  271. package/dist/packages/stack/src/plugins/comments/client/components/shared/pagination.mjs +2 -1
  272. package/dist/packages/stack/src/plugins/comments/client/constants.cjs +5 -0
  273. package/dist/packages/stack/src/plugins/comments/client/constants.mjs +3 -0
  274. package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.cjs +323 -290
  275. package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.mjs +325 -292
  276. package/dist/packages/stack/src/plugins/comments/client/localization/comments-thread.cjs +1 -0
  277. package/dist/packages/stack/src/plugins/comments/client/localization/comments-thread.mjs +1 -0
  278. package/dist/packages/stack/src/plugins/comments/client/plugin.cjs +78 -46
  279. package/dist/packages/stack/src/plugins/comments/client/plugin.mjs +80 -48
  280. package/dist/packages/stack/src/plugins/form-builder/api/getters.cjs +9 -0
  281. package/dist/packages/stack/src/plugins/form-builder/api/getters.mjs +9 -1
  282. package/dist/packages/stack/src/plugins/form-builder/api/lifecycle-migrations.cjs +15 -0
  283. package/dist/packages/stack/src/plugins/form-builder/api/lifecycle-migrations.mjs +13 -0
  284. package/dist/packages/stack/src/plugins/form-builder/api/operations.cjs +1093 -0
  285. package/dist/packages/stack/src/plugins/form-builder/api/operations.mjs +1082 -0
  286. package/dist/packages/stack/src/plugins/form-builder/api/plugin.cjs +97 -466
  287. package/dist/packages/stack/src/plugins/form-builder/api/plugin.mjs +92 -468
  288. package/dist/packages/stack/src/plugins/form-builder/api/query-key-defs.cjs +5 -0
  289. package/dist/packages/stack/src/plugins/form-builder/api/query-key-defs.mjs +5 -0
  290. package/dist/packages/stack/src/plugins/form-builder/client/components/forms/form-renderer.cjs +41 -11
  291. package/dist/packages/stack/src/plugins/form-builder/client/components/forms/form-renderer.mjs +42 -12
  292. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/404-page.cjs +8 -5
  293. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/404-page.mjs +9 -6
  294. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-builder-page.cjs +36 -3
  295. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-builder-page.internal.cjs +32 -11
  296. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-builder-page.internal.mjs +35 -14
  297. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-builder-page.mjs +37 -4
  298. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-list-page.cjs +6 -1
  299. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-list-page.internal.cjs +34 -26
  300. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-list-page.internal.mjs +34 -26
  301. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-list-page.mjs +6 -1
  302. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.cjs +24 -7
  303. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.cjs +72 -40
  304. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.mjs +76 -44
  305. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.mjs +25 -8
  306. package/dist/packages/stack/src/plugins/form-builder/client/components/shared/page-wrapper.cjs +2 -1
  307. package/dist/packages/stack/src/plugins/form-builder/client/components/shared/page-wrapper.mjs +2 -1
  308. package/dist/packages/stack/src/plugins/form-builder/client/constants.cjs +5 -0
  309. package/dist/packages/stack/src/plugins/form-builder/client/constants.mjs +3 -0
  310. package/dist/packages/stack/src/plugins/form-builder/client/hooks/form-builder-hooks.cjs +29 -0
  311. package/dist/packages/stack/src/plugins/form-builder/client/hooks/form-builder-hooks.mjs +28 -1
  312. package/dist/packages/stack/src/plugins/form-builder/client/hooks/form-builder-resource.cjs +2 -1
  313. package/dist/packages/stack/src/plugins/form-builder/client/hooks/form-builder-resource.mjs +2 -1
  314. package/dist/packages/stack/src/plugins/form-builder/client/plugin.cjs +113 -86
  315. package/dist/packages/stack/src/plugins/form-builder/client/plugin.mjs +114 -87
  316. package/dist/packages/stack/src/plugins/form-builder/schemas.cjs +9 -3
  317. package/dist/packages/stack/src/plugins/form-builder/schemas.mjs +9 -3
  318. package/dist/packages/stack/src/plugins/kanban/api/getters.cjs +47 -36
  319. package/dist/packages/stack/src/plugins/kanban/api/getters.mjs +47 -37
  320. package/dist/packages/stack/src/plugins/kanban/api/lifecycle-migrations.cjs +23 -0
  321. package/dist/packages/stack/src/plugins/kanban/api/lifecycle-migrations.mjs +21 -0
  322. package/dist/packages/stack/src/plugins/kanban/api/operations.cjs +1552 -0
  323. package/dist/packages/stack/src/plugins/kanban/api/operations.mjs +1543 -0
  324. package/dist/packages/stack/src/plugins/kanban/api/plugin.cjs +64 -631
  325. package/dist/packages/stack/src/plugins/kanban/api/plugin.mjs +62 -634
  326. package/dist/packages/stack/src/plugins/kanban/api/serializers.cjs +21 -0
  327. package/dist/packages/stack/src/plugins/kanban/api/serializers.mjs +21 -1
  328. package/dist/packages/stack/src/plugins/kanban/client/components/forms/board-form.cjs +2 -1
  329. package/dist/packages/stack/src/plugins/kanban/client/components/forms/board-form.mjs +2 -1
  330. package/dist/packages/stack/src/plugins/kanban/client/components/forms/column-form.cjs +2 -1
  331. package/dist/packages/stack/src/plugins/kanban/client/components/forms/column-form.mjs +2 -1
  332. package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.cjs +72 -54
  333. package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.mjs +74 -56
  334. package/dist/packages/stack/src/plugins/kanban/client/components/pages/404-page.cjs +4 -6
  335. package/dist/packages/stack/src/plugins/kanban/client/components/pages/404-page.mjs +5 -7
  336. package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.cjs +22 -6
  337. package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.internal.cjs +82 -77
  338. package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.internal.mjs +83 -78
  339. package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.mjs +23 -7
  340. package/dist/packages/stack/src/plugins/kanban/client/components/pages/boards-list-page.cjs +2 -1
  341. package/dist/packages/stack/src/plugins/kanban/client/components/pages/boards-list-page.internal.cjs +34 -24
  342. package/dist/packages/stack/src/plugins/kanban/client/components/pages/boards-list-page.internal.mjs +35 -25
  343. package/dist/packages/stack/src/plugins/kanban/client/components/pages/boards-list-page.mjs +2 -1
  344. package/dist/packages/stack/src/plugins/kanban/client/components/pages/new-board-page.cjs +2 -1
  345. package/dist/packages/stack/src/plugins/kanban/client/components/pages/new-board-page.internal.cjs +6 -9
  346. package/dist/packages/stack/src/plugins/kanban/client/components/pages/new-board-page.internal.mjs +7 -10
  347. package/dist/packages/stack/src/plugins/kanban/client/components/pages/new-board-page.mjs +2 -1
  348. package/dist/packages/stack/src/plugins/kanban/client/components/shared/column-content.cjs +72 -45
  349. package/dist/packages/stack/src/plugins/kanban/client/components/shared/column-content.mjs +73 -46
  350. package/dist/packages/stack/src/plugins/kanban/client/components/shared/default-error.cjs +2 -1
  351. package/dist/packages/stack/src/plugins/kanban/client/components/shared/default-error.mjs +2 -1
  352. package/dist/packages/stack/src/plugins/kanban/client/components/shared/kanban-board.cjs +4 -4
  353. package/dist/packages/stack/src/plugins/kanban/client/components/shared/kanban-board.mjs +4 -4
  354. package/dist/packages/stack/src/plugins/kanban/client/components/shared/permission-access-any.cjs +23 -0
  355. package/dist/packages/stack/src/plugins/kanban/client/components/shared/permission-access-any.mjs +21 -0
  356. package/dist/packages/stack/src/plugins/kanban/client/components/shared/task-card.cjs +54 -17
  357. package/dist/packages/stack/src/plugins/kanban/client/components/shared/task-card.mjs +55 -18
  358. package/dist/packages/stack/src/plugins/kanban/client/components/shared/user-avatar.cjs +2 -1
  359. package/dist/packages/stack/src/plugins/kanban/client/components/shared/user-avatar.mjs +2 -1
  360. package/dist/packages/stack/src/plugins/kanban/client/constants.cjs +5 -0
  361. package/dist/packages/stack/src/plugins/kanban/client/constants.mjs +3 -0
  362. package/dist/packages/stack/src/plugins/kanban/client/hooks/kanban-hooks.cjs +38 -9
  363. package/dist/packages/stack/src/plugins/kanban/client/hooks/kanban-hooks.mjs +39 -10
  364. package/dist/packages/stack/src/plugins/kanban/client/hooks/kanban-resource.cjs +2 -1
  365. package/dist/packages/stack/src/plugins/kanban/client/hooks/kanban-resource.mjs +2 -1
  366. package/dist/packages/stack/src/plugins/kanban/client/plugin.cjs +177 -104
  367. package/dist/packages/stack/src/plugins/kanban/client/plugin.mjs +178 -105
  368. package/dist/packages/stack/src/plugins/kanban/schemas.cjs +32 -23
  369. package/dist/packages/stack/src/plugins/kanban/schemas.mjs +33 -21
  370. package/dist/packages/stack/src/plugins/media/api/lifecycle-migrations.cjs +9 -0
  371. package/dist/packages/stack/src/plugins/media/api/lifecycle-migrations.mjs +7 -0
  372. package/dist/packages/stack/src/plugins/media/api/mutations.cjs +7 -2
  373. package/dist/packages/stack/src/plugins/media/api/mutations.mjs +7 -2
  374. package/dist/packages/stack/src/plugins/media/api/operations.cjs +1299 -0
  375. package/dist/packages/stack/src/plugins/media/api/operations.mjs +1290 -0
  376. package/dist/packages/stack/src/plugins/media/api/plugin.cjs +131 -508
  377. package/dist/packages/stack/src/plugins/media/api/plugin.mjs +127 -510
  378. package/dist/packages/stack/src/plugins/media/api/query-key-defs.cjs +26 -2
  379. package/dist/packages/stack/src/plugins/media/api/query-key-defs.mjs +25 -3
  380. package/dist/packages/stack/src/plugins/media/api/serializers.cjs +4 -2
  381. package/dist/packages/stack/src/plugins/media/api/serializers.mjs +4 -2
  382. package/dist/packages/stack/src/plugins/media/asset-url.cjs +13 -0
  383. package/dist/packages/stack/src/plugins/media/asset-url.mjs +11 -0
  384. package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-card.cjs +10 -10
  385. package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-card.mjs +12 -12
  386. package/dist/packages/stack/src/plugins/media/client/components/media-picker/folder-tree.cjs +47 -27
  387. package/dist/packages/stack/src/plugins/media/client/components/media-picker/folder-tree.mjs +49 -29
  388. package/dist/packages/stack/src/plugins/media/client/components/media-picker/index.cjs +225 -180
  389. package/dist/packages/stack/src/plugins/media/client/components/media-picker/index.mjs +228 -183
  390. package/dist/packages/stack/src/plugins/media/client/components/media-picker/upload-tab.cjs +1 -8
  391. package/dist/packages/stack/src/plugins/media/client/components/media-picker/upload-tab.mjs +2 -9
  392. package/dist/packages/stack/src/plugins/media/client/components/media-picker/url-tab.cjs +0 -6
  393. package/dist/packages/stack/src/plugins/media/client/components/media-picker/url-tab.mjs +1 -7
  394. package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.cjs +4 -2
  395. package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.internal.cjs +92 -75
  396. package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.internal.mjs +93 -76
  397. package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.mjs +4 -2
  398. package/dist/packages/stack/src/plugins/media/client/components/upload-permission-check.cjs +52 -0
  399. package/dist/packages/stack/src/plugins/media/client/components/upload-permission-check.mjs +49 -0
  400. package/dist/packages/stack/src/plugins/media/client/constants.cjs +5 -0
  401. package/dist/packages/stack/src/plugins/media/client/constants.mjs +3 -0
  402. package/dist/packages/stack/src/plugins/media/client/hooks/media-resource.cjs +2 -1
  403. package/dist/packages/stack/src/plugins/media/client/hooks/media-resource.mjs +2 -1
  404. package/dist/packages/stack/src/plugins/media/client/hooks/use-media.cjs +214 -43
  405. package/dist/packages/stack/src/plugins/media/client/hooks/use-media.mjs +217 -46
  406. package/dist/packages/stack/src/plugins/media/client/plugin.cjs +122 -62
  407. package/dist/packages/stack/src/plugins/media/client/plugin.mjs +123 -63
  408. package/dist/packages/stack/src/plugins/media/client/upload.cjs +121 -52
  409. package/dist/packages/stack/src/plugins/media/client/upload.mjs +121 -53
  410. package/dist/packages/stack/src/plugins/media/db.cjs +8 -0
  411. package/dist/packages/stack/src/plugins/media/db.mjs +8 -0
  412. package/dist/packages/stack/src/plugins/open-api/api/generator.cjs +36 -5
  413. package/dist/packages/stack/src/plugins/open-api/api/generator.mjs +36 -5
  414. package/dist/packages/stack/src/plugins/open-api/api/plugin.cjs +22 -11
  415. package/dist/packages/stack/src/plugins/open-api/api/plugin.mjs +22 -11
  416. package/dist/packages/stack/src/plugins/route-docs/client/components/pages/docs-page.cjs +65 -34
  417. package/dist/packages/stack/src/plugins/route-docs/client/components/pages/docs-page.mjs +56 -25
  418. package/dist/packages/stack/src/plugins/route-docs/client/constants.cjs +5 -0
  419. package/dist/packages/stack/src/plugins/route-docs/client/constants.mjs +3 -0
  420. package/dist/packages/stack/src/plugins/route-docs/client/hooks.cjs +9 -8
  421. package/dist/packages/stack/src/plugins/route-docs/client/hooks.mjs +10 -9
  422. package/dist/packages/stack/src/plugins/route-docs/client/plugin.cjs +143 -75
  423. package/dist/packages/stack/src/plugins/route-docs/client/plugin.mjs +144 -74
  424. package/dist/packages/stack/src/plugins/route-docs/client/schema.cjs +26 -0
  425. package/dist/packages/stack/src/plugins/route-docs/client/schema.mjs +23 -0
  426. package/dist/packages/stack/src/plugins/route-docs/generator.cjs +7 -4
  427. package/dist/packages/stack/src/plugins/route-docs/generator.mjs +7 -4
  428. package/dist/packages/stack/src/plugins/ui-builder/client/components/page-renderer.cjs +41 -12
  429. package/dist/packages/stack/src/plugins/ui-builder/client/components/page-renderer.mjs +42 -13
  430. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.cjs +37 -3
  431. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.cjs +32 -20
  432. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.mjs +34 -22
  433. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.mjs +38 -4
  434. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-list-page.cjs +8 -2
  435. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-list-page.internal.cjs +37 -19
  436. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-list-page.internal.mjs +38 -20
  437. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-list-page.mjs +8 -2
  438. package/dist/packages/stack/src/plugins/ui-builder/client/components/shared/default-error.cjs +2 -1
  439. package/dist/packages/stack/src/plugins/ui-builder/client/components/shared/default-error.mjs +2 -1
  440. package/dist/packages/stack/src/plugins/ui-builder/client/components/shared/page-wrapper.cjs +2 -1
  441. package/dist/packages/stack/src/plugins/ui-builder/client/components/shared/page-wrapper.mjs +2 -1
  442. package/dist/packages/stack/src/plugins/ui-builder/client/constants.cjs +5 -0
  443. package/dist/packages/stack/src/plugins/ui-builder/client/constants.mjs +3 -0
  444. package/dist/packages/stack/src/plugins/ui-builder/client/hooks/ui-builder-resource.cjs +2 -1
  445. package/dist/packages/stack/src/plugins/ui-builder/client/hooks/ui-builder-resource.mjs +2 -1
  446. package/dist/packages/stack/src/plugins/ui-builder/client/overrides.cjs +8 -0
  447. package/dist/packages/stack/src/plugins/ui-builder/client/overrides.mjs +6 -0
  448. package/dist/packages/stack/src/plugins/ui-builder/client/plugin.cjs +93 -53
  449. package/dist/packages/stack/src/plugins/ui-builder/client/plugin.mjs +94 -54
  450. package/dist/packages/stack/src/plugins/utils.cjs +4 -12
  451. package/dist/packages/stack/src/plugins/utils.mjs +5 -12
  452. package/dist/packages/stack/src/react-router/layout.cjs +14 -0
  453. package/dist/packages/stack/src/react-router/layout.mjs +12 -0
  454. package/dist/packages/stack/src/react-router/page.cjs +10 -5
  455. package/dist/packages/stack/src/react-router/page.mjs +11 -6
  456. package/dist/packages/stack/src/shared/initial-identity.cjs +27 -0
  457. package/dist/packages/stack/src/shared/initial-identity.mjs +23 -0
  458. package/dist/packages/stack/src/tanstack/layout.cjs +10 -0
  459. package/dist/packages/stack/src/tanstack/layout.mjs +8 -0
  460. package/dist/packages/stack/src/tanstack/page.cjs +6 -2
  461. package/dist/packages/stack/src/tanstack/page.mjs +6 -2
  462. package/dist/plugins/ai-chat/api/index.cjs +10 -0
  463. package/dist/plugins/ai-chat/api/index.d.cts +31 -5
  464. package/dist/plugins/ai-chat/api/index.d.mts +31 -5
  465. package/dist/plugins/ai-chat/api/index.d.ts +31 -5
  466. package/dist/plugins/ai-chat/api/index.mjs +3 -1
  467. package/dist/plugins/ai-chat/client/components/index.d.cts +1 -1
  468. package/dist/plugins/ai-chat/client/components/index.d.mts +1 -1
  469. package/dist/plugins/ai-chat/client/components/index.d.ts +1 -1
  470. package/dist/plugins/ai-chat/client/hooks/index.cjs +1 -0
  471. package/dist/plugins/ai-chat/client/hooks/index.d.cts +14 -8
  472. package/dist/plugins/ai-chat/client/hooks/index.d.mts +14 -8
  473. package/dist/plugins/ai-chat/client/hooks/index.d.ts +14 -8
  474. package/dist/plugins/ai-chat/client/hooks/index.mjs +1 -1
  475. package/dist/plugins/ai-chat/client/index.d.cts +21 -25
  476. package/dist/plugins/ai-chat/client/index.d.mts +21 -25
  477. package/dist/plugins/ai-chat/client/index.d.ts +21 -25
  478. package/dist/plugins/ai-chat/permissions.cjs +90 -0
  479. package/dist/plugins/ai-chat/permissions.d.cts +84 -0
  480. package/dist/plugins/ai-chat/permissions.d.mts +84 -0
  481. package/dist/plugins/ai-chat/permissions.d.ts +84 -0
  482. package/dist/plugins/ai-chat/permissions.mjs +88 -0
  483. package/dist/plugins/ai-chat/query-keys.cjs +116 -18
  484. package/dist/plugins/ai-chat/query-keys.d.cts +6 -2
  485. package/dist/plugins/ai-chat/query-keys.d.mts +6 -2
  486. package/dist/plugins/ai-chat/query-keys.d.ts +6 -2
  487. package/dist/plugins/ai-chat/query-keys.mjs +116 -19
  488. package/dist/plugins/api/index.cjs +4 -0
  489. package/dist/plugins/api/index.d.cts +13 -18
  490. package/dist/plugins/api/index.d.mts +13 -18
  491. package/dist/plugins/api/index.d.ts +13 -18
  492. package/dist/plugins/api/index.mjs +1 -0
  493. package/dist/plugins/blog/api/index.cjs +9 -2
  494. package/dist/plugins/blog/api/index.d.cts +118 -4
  495. package/dist/plugins/blog/api/index.d.mts +118 -4
  496. package/dist/plugins/blog/api/index.d.ts +118 -4
  497. package/dist/plugins/blog/api/index.mjs +4 -1
  498. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  499. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  500. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  501. package/dist/plugins/blog/client/index.d.cts +210 -226
  502. package/dist/plugins/blog/client/index.d.mts +210 -226
  503. package/dist/plugins/blog/client/index.d.ts +210 -226
  504. package/dist/plugins/blog/permissions.cjs +47 -0
  505. package/dist/plugins/blog/permissions.d.cts +49 -0
  506. package/dist/plugins/blog/permissions.d.mts +49 -0
  507. package/dist/plugins/blog/permissions.d.ts +49 -0
  508. package/dist/plugins/blog/permissions.mjs +45 -0
  509. package/dist/plugins/blog/query-keys.d.cts +7 -3
  510. package/dist/plugins/blog/query-keys.d.mts +7 -3
  511. package/dist/plugins/blog/query-keys.d.ts +7 -3
  512. package/dist/plugins/client/hooks/index.d.cts +2 -2
  513. package/dist/plugins/client/hooks/index.d.mts +2 -2
  514. package/dist/plugins/client/hooks/index.d.ts +2 -2
  515. package/dist/plugins/client/index.cjs +3 -0
  516. package/dist/plugins/client/index.d.cts +30 -10
  517. package/dist/plugins/client/index.d.mts +30 -10
  518. package/dist/plugins/client/index.d.ts +30 -10
  519. package/dist/plugins/client/index.mjs +3 -0
  520. package/dist/plugins/cms/api/index.cjs +7 -0
  521. package/dist/plugins/cms/api/index.d.cts +173 -4
  522. package/dist/plugins/cms/api/index.d.mts +173 -4
  523. package/dist/plugins/cms/api/index.d.ts +173 -4
  524. package/dist/plugins/cms/api/index.mjs +2 -0
  525. package/dist/plugins/cms/client/hooks/index.d.cts +1 -1
  526. package/dist/plugins/cms/client/hooks/index.d.mts +1 -1
  527. package/dist/plugins/cms/client/hooks/index.d.ts +1 -1
  528. package/dist/plugins/cms/client/index.d.cts +188 -184
  529. package/dist/plugins/cms/client/index.d.mts +188 -184
  530. package/dist/plugins/cms/client/index.d.ts +188 -184
  531. package/dist/plugins/cms/permissions.cjs +46 -0
  532. package/dist/plugins/cms/permissions.d.cts +42 -0
  533. package/dist/plugins/cms/permissions.d.mts +42 -0
  534. package/dist/plugins/cms/permissions.d.ts +42 -0
  535. package/dist/plugins/cms/permissions.mjs +44 -0
  536. package/dist/plugins/cms/query-keys.d.cts +4 -4
  537. package/dist/plugins/cms/query-keys.d.mts +4 -4
  538. package/dist/plugins/cms/query-keys.d.ts +4 -4
  539. package/dist/plugins/comments/api/index.cjs +10 -0
  540. package/dist/plugins/comments/api/index.d.cts +54 -18
  541. package/dist/plugins/comments/api/index.d.mts +54 -18
  542. package/dist/plugins/comments/api/index.d.ts +54 -18
  543. package/dist/plugins/comments/api/index.mjs +3 -0
  544. package/dist/plugins/comments/client/components/index.d.cts +2 -2
  545. package/dist/plugins/comments/client/components/index.d.mts +2 -2
  546. package/dist/plugins/comments/client/components/index.d.ts +2 -2
  547. package/dist/plugins/comments/client/hooks/index.d.cts +12 -18
  548. package/dist/plugins/comments/client/hooks/index.d.mts +12 -18
  549. package/dist/plugins/comments/client/hooks/index.d.ts +12 -18
  550. package/dist/plugins/comments/client/index.d.cts +96 -114
  551. package/dist/plugins/comments/client/index.d.mts +96 -114
  552. package/dist/plugins/comments/client/index.d.ts +96 -114
  553. package/dist/plugins/comments/permissions.cjs +70 -0
  554. package/dist/plugins/comments/permissions.d.cts +76 -0
  555. package/dist/plugins/comments/permissions.d.mts +76 -0
  556. package/dist/plugins/comments/permissions.d.ts +76 -0
  557. package/dist/plugins/comments/permissions.mjs +68 -0
  558. package/dist/plugins/comments/query-keys.cjs +1 -1
  559. package/dist/plugins/comments/query-keys.d.cts +16 -19
  560. package/dist/plugins/comments/query-keys.d.mts +16 -19
  561. package/dist/plugins/comments/query-keys.d.ts +16 -19
  562. package/dist/plugins/comments/query-keys.mjs +1 -1
  563. package/dist/plugins/form-builder/api/index.cjs +8 -0
  564. package/dist/plugins/form-builder/api/index.d.cts +115 -4
  565. package/dist/plugins/form-builder/api/index.d.mts +115 -4
  566. package/dist/plugins/form-builder/api/index.d.ts +115 -4
  567. package/dist/plugins/form-builder/api/index.mjs +4 -1
  568. package/dist/plugins/form-builder/client/components/index.d.cts +6 -2
  569. package/dist/plugins/form-builder/client/components/index.d.mts +6 -2
  570. package/dist/plugins/form-builder/client/components/index.d.ts +6 -2
  571. package/dist/plugins/form-builder/client/hooks/index.cjs +2 -0
  572. package/dist/plugins/form-builder/client/hooks/index.d.cts +34 -8
  573. package/dist/plugins/form-builder/client/hooks/index.d.mts +34 -8
  574. package/dist/plugins/form-builder/client/hooks/index.d.ts +34 -8
  575. package/dist/plugins/form-builder/client/hooks/index.mjs +1 -1
  576. package/dist/plugins/form-builder/client/index.d.cts +153 -174
  577. package/dist/plugins/form-builder/client/index.d.mts +153 -174
  578. package/dist/plugins/form-builder/client/index.d.ts +153 -174
  579. package/dist/plugins/form-builder/permissions.cjs +95 -0
  580. package/dist/plugins/form-builder/permissions.d.cts +94 -0
  581. package/dist/plugins/form-builder/permissions.d.mts +94 -0
  582. package/dist/plugins/form-builder/permissions.d.ts +94 -0
  583. package/dist/plugins/form-builder/permissions.mjs +93 -0
  584. package/dist/plugins/form-builder/query-keys.cjs +11 -4
  585. package/dist/plugins/form-builder/query-keys.d.cts +38 -12
  586. package/dist/plugins/form-builder/query-keys.d.mts +38 -12
  587. package/dist/plugins/form-builder/query-keys.d.ts +38 -12
  588. package/dist/plugins/form-builder/query-keys.mjs +11 -4
  589. package/dist/plugins/kanban/api/index.cjs +9 -0
  590. package/dist/plugins/kanban/api/index.d.cts +137 -5
  591. package/dist/plugins/kanban/api/index.d.mts +137 -5
  592. package/dist/plugins/kanban/api/index.d.ts +137 -5
  593. package/dist/plugins/kanban/api/index.mjs +5 -2
  594. package/dist/plugins/kanban/client/components/index.d.cts +18 -10
  595. package/dist/plugins/kanban/client/components/index.d.mts +18 -10
  596. package/dist/plugins/kanban/client/components/index.d.ts +18 -10
  597. package/dist/plugins/kanban/client/hooks/index.d.cts +15 -7
  598. package/dist/plugins/kanban/client/hooks/index.d.mts +15 -7
  599. package/dist/plugins/kanban/client/hooks/index.d.ts +15 -7
  600. package/dist/plugins/kanban/client/index.d.cts +32 -28
  601. package/dist/plugins/kanban/client/index.d.mts +32 -28
  602. package/dist/plugins/kanban/client/index.d.ts +32 -28
  603. package/dist/plugins/kanban/permissions.cjs +101 -0
  604. package/dist/plugins/kanban/permissions.d.cts +109 -0
  605. package/dist/plugins/kanban/permissions.d.mts +109 -0
  606. package/dist/plugins/kanban/permissions.d.ts +109 -0
  607. package/dist/plugins/kanban/permissions.mjs +99 -0
  608. package/dist/plugins/kanban/query-keys.cjs +14 -15
  609. package/dist/plugins/kanban/query-keys.d.cts +63 -42
  610. package/dist/plugins/kanban/query-keys.d.mts +63 -42
  611. package/dist/plugins/kanban/query-keys.d.ts +63 -42
  612. package/dist/plugins/kanban/query-keys.mjs +14 -15
  613. package/dist/plugins/media/api/adapters/local.d.cts +1 -1
  614. package/dist/plugins/media/api/adapters/local.d.mts +1 -1
  615. package/dist/plugins/media/api/adapters/local.d.ts +1 -1
  616. package/dist/plugins/media/api/adapters/s3.d.cts +1 -1
  617. package/dist/plugins/media/api/adapters/s3.d.mts +1 -1
  618. package/dist/plugins/media/api/adapters/s3.d.ts +1 -1
  619. package/dist/plugins/media/api/adapters/vercel-blob.cjs +46 -0
  620. package/dist/plugins/media/api/adapters/vercel-blob.d.cts +6 -9
  621. package/dist/plugins/media/api/adapters/vercel-blob.d.mts +6 -9
  622. package/dist/plugins/media/api/adapters/vercel-blob.d.ts +6 -9
  623. package/dist/plugins/media/api/adapters/vercel-blob.mjs +46 -0
  624. package/dist/plugins/media/api/index.cjs +10 -0
  625. package/dist/plugins/media/api/index.d.cts +93 -6
  626. package/dist/plugins/media/api/index.d.mts +93 -6
  627. package/dist/plugins/media/api/index.d.ts +93 -6
  628. package/dist/plugins/media/api/index.mjs +2 -0
  629. package/dist/plugins/media/client/components/index.d.cts +1 -1
  630. package/dist/plugins/media/client/components/index.d.mts +1 -1
  631. package/dist/plugins/media/client/components/index.d.ts +1 -1
  632. package/dist/plugins/media/client/hooks/index.d.cts +20 -10
  633. package/dist/plugins/media/client/hooks/index.d.mts +20 -10
  634. package/dist/plugins/media/client/hooks/index.d.ts +20 -10
  635. package/dist/plugins/media/client/index.cjs +1 -0
  636. package/dist/plugins/media/client/index.d.cts +112 -100
  637. package/dist/plugins/media/client/index.d.mts +112 -100
  638. package/dist/plugins/media/client/index.d.ts +112 -100
  639. package/dist/plugins/media/client/index.mjs +1 -1
  640. package/dist/plugins/media/permissions.cjs +63 -0
  641. package/dist/plugins/media/permissions.d.cts +64 -0
  642. package/dist/plugins/media/permissions.d.mts +64 -0
  643. package/dist/plugins/media/permissions.d.ts +64 -0
  644. package/dist/plugins/media/permissions.mjs +61 -0
  645. package/dist/plugins/media/query-keys.cjs +29 -17
  646. package/dist/plugins/media/query-keys.d.cts +24 -31
  647. package/dist/plugins/media/query-keys.d.mts +24 -31
  648. package/dist/plugins/media/query-keys.d.ts +24 -31
  649. package/dist/plugins/media/query-keys.mjs +30 -18
  650. package/dist/plugins/open-api/api/index.d.cts +9 -4
  651. package/dist/plugins/open-api/api/index.d.mts +9 -4
  652. package/dist/plugins/open-api/api/index.d.ts +9 -4
  653. package/dist/plugins/route-docs/client/index.cjs +2 -2
  654. package/dist/plugins/route-docs/client/index.d.cts +54 -53
  655. package/dist/plugins/route-docs/client/index.d.mts +54 -53
  656. package/dist/plugins/route-docs/client/index.d.ts +54 -53
  657. package/dist/plugins/route-docs/client/index.mjs +2 -1
  658. package/dist/plugins/ui-builder/client/components/index.d.cts +8 -5
  659. package/dist/plugins/ui-builder/client/components/index.d.mts +8 -5
  660. package/dist/plugins/ui-builder/client/components/index.d.ts +8 -5
  661. package/dist/plugins/ui-builder/client/hooks/index.d.cts +5 -5
  662. package/dist/plugins/ui-builder/client/hooks/index.d.mts +5 -5
  663. package/dist/plugins/ui-builder/client/hooks/index.d.ts +5 -5
  664. package/dist/plugins/ui-builder/client/index.d.cts +151 -199
  665. package/dist/plugins/ui-builder/client/index.d.mts +151 -199
  666. package/dist/plugins/ui-builder/client/index.d.ts +151 -199
  667. package/dist/plugins/ui-builder/index.d.cts +11 -8
  668. package/dist/plugins/ui-builder/index.d.mts +11 -8
  669. package/dist/plugins/ui-builder/index.d.ts +11 -8
  670. package/dist/plugins/ui-builder/query-keys.d.cts +6 -6
  671. package/dist/plugins/ui-builder/query-keys.d.mts +6 -6
  672. package/dist/plugins/ui-builder/query-keys.d.ts +6 -6
  673. package/dist/react-router/index.cjs +2 -0
  674. package/dist/react-router/index.d.cts +29 -5
  675. package/dist/react-router/index.d.mts +29 -5
  676. package/dist/react-router/index.d.ts +29 -5
  677. package/dist/react-router/index.mjs +1 -0
  678. package/dist/shared/stack.0O62uXqP.d.ts +613 -0
  679. package/dist/shared/stack.0y6iTP03.d.ts +533 -0
  680. package/dist/shared/{stack.CbZenbej.d.cts → stack.1ukVJym7.d.cts} +3 -4
  681. package/dist/shared/{stack.CbZenbej.d.mts → stack.1ukVJym7.d.mts} +3 -4
  682. package/dist/shared/{stack.CbZenbej.d.ts → stack.1ukVJym7.d.ts} +3 -4
  683. package/dist/shared/stack.3Sr6qRtT.d.ts +507 -0
  684. package/dist/shared/{stack.9BAOaSqE.d.cts → stack.5_zzCejP.d.cts} +16 -14
  685. package/dist/shared/{stack.9BAOaSqE.d.mts → stack.5_zzCejP.d.mts} +16 -14
  686. package/dist/shared/{stack.9BAOaSqE.d.ts → stack.5_zzCejP.d.ts} +16 -14
  687. package/dist/shared/stack.B1UrNoVl.d.mts +533 -0
  688. package/dist/shared/{stack.B2xi4jj7.d.cts → stack.B1srlBud.d.mts} +2 -2
  689. package/dist/shared/stack.B3_aDcYL.d.cts +281 -0
  690. package/dist/shared/stack.B3_aDcYL.d.mts +281 -0
  691. package/dist/shared/stack.B3_aDcYL.d.ts +281 -0
  692. package/dist/shared/stack.B5yPgHsp.d.mts +507 -0
  693. package/dist/shared/stack.B8PG21zX.d.mts +764 -0
  694. package/dist/shared/stack.BFP_9pRX.d.cts +707 -0
  695. package/dist/shared/{stack.DzwZw-Ck.d.cts → stack.BGzgFx3R.d.cts} +2 -2
  696. package/dist/shared/stack.BJx1ZXrN.d.cts +118 -0
  697. package/dist/shared/{stack.7b3vsM8_.d.ts → stack.BK9Z2dcL.d.ts} +2 -2
  698. package/dist/shared/stack.BMHoxAn7.d.mts +707 -0
  699. package/dist/shared/stack.BMmvEBH_.d.ts +118 -0
  700. package/dist/shared/stack.BOZRaVpu.d.cts +1184 -0
  701. package/dist/shared/stack.BPLykw4s.d.ts +436 -0
  702. package/dist/shared/{stack.aqVZ6PEW.d.mts → stack.BRvqQWGt.d.cts} +40 -1
  703. package/dist/shared/{stack.aqVZ6PEW.d.cts → stack.BRvqQWGt.d.mts} +40 -1
  704. package/dist/shared/{stack.aqVZ6PEW.d.ts → stack.BRvqQWGt.d.ts} +40 -1
  705. package/dist/shared/stack.BSACE667.d.cts +23 -0
  706. package/dist/shared/stack.BYMentwB.d.cts +47 -0
  707. package/dist/shared/stack.BYMentwB.d.mts +47 -0
  708. package/dist/shared/stack.BYMentwB.d.ts +47 -0
  709. package/dist/shared/stack.Bt8hfw9o.d.ts +707 -0
  710. package/dist/shared/{stack.D7bewlgt.d.mts → stack.Bu2IPOLi.d.cts} +1 -0
  711. package/dist/shared/{stack.D7bewlgt.d.ts → stack.Bu2IPOLi.d.mts} +1 -0
  712. package/dist/shared/{stack.D7bewlgt.d.cts → stack.Bu2IPOLi.d.ts} +1 -0
  713. package/dist/shared/stack.BuxUQqL8.d.ts +408 -0
  714. package/dist/shared/stack.BzWGB5MG.d.cts +436 -0
  715. package/dist/shared/stack.C3bU9w6L.d.ts +351 -0
  716. package/dist/shared/stack.C4oVW2qK.d.cts +507 -0
  717. package/dist/shared/stack.CKXzdGiJ.d.cts +3 -0
  718. package/dist/shared/stack.CKXzdGiJ.d.mts +3 -0
  719. package/dist/shared/stack.CKXzdGiJ.d.ts +3 -0
  720. package/dist/shared/{stack.C3GBG8in.d.ts → stack.COrJrztD.d.cts} +1 -1
  721. package/dist/shared/{stack.C3GBG8in.d.cts → stack.COrJrztD.d.mts} +1 -1
  722. package/dist/shared/{stack.C3GBG8in.d.mts → stack.COrJrztD.d.ts} +1 -1
  723. package/dist/shared/stack.CX83YNuS.d.cts +408 -0
  724. package/dist/shared/stack.Cbd7sD5M.d.mts +118 -0
  725. package/dist/shared/{stack.B9NtGpAR.d.ts → stack.CcjENrr4.d.cts} +1 -5
  726. package/dist/shared/stack.CfK5NKlK.d.mts +613 -0
  727. package/dist/shared/{stack.oVIdV7XS.d.mts → stack.CjCSDfWy.d.mts} +2 -2
  728. package/dist/shared/stack.Cmg7lzzy.d.cts +44 -0
  729. package/dist/shared/stack.Cmg7lzzy.d.mts +44 -0
  730. package/dist/shared/stack.Cmg7lzzy.d.ts +44 -0
  731. package/dist/shared/stack.CpE1mS3p.d.ts +331 -0
  732. package/dist/shared/stack.CskhFw3z.d.mts +23 -0
  733. package/dist/shared/stack.CvlC5kqe.d.cts +331 -0
  734. package/dist/shared/stack.CxKc9evq.d.ts +23 -0
  735. package/dist/shared/stack.CyalJ0j6.d.ts +764 -0
  736. package/dist/shared/stack.D6Pg-ufX.d.cts +764 -0
  737. package/dist/shared/{stack.lkebw2nj.d.mts → stack.DVEjWKCT.d.cts} +19 -5
  738. package/dist/shared/{stack.lkebw2nj.d.ts → stack.DVEjWKCT.d.mts} +19 -5
  739. package/dist/shared/{stack.lkebw2nj.d.cts → stack.DVEjWKCT.d.ts} +19 -5
  740. package/dist/shared/{stack.Cp_6mO5s.d.mts → stack.DbbvHbhn.d.mts} +1 -5
  741. package/dist/shared/stack.DiuRuzIs.d.cts +613 -0
  742. package/dist/shared/{stack.BMlLOMED.d.cts → stack.DjGixVx3.d.cts} +6 -2
  743. package/dist/shared/{stack.BMlLOMED.d.mts → stack.DjGixVx3.d.mts} +6 -2
  744. package/dist/shared/{stack.BMlLOMED.d.ts → stack.DjGixVx3.d.ts} +6 -2
  745. package/dist/shared/stack.DlBoNqYu.d.ts +1184 -0
  746. package/dist/shared/stack.DlCycSM-.d.mts +331 -0
  747. package/dist/shared/{stack.DycBcTeg.d.mts → stack.DmpPDPxA.d.cts} +2 -2
  748. package/dist/shared/{stack.DseBJmGF.d.ts → stack.Dpg_JjIo.d.ts} +2 -2
  749. package/dist/shared/stack.Dvy7FmaA.d.mts +1184 -0
  750. package/dist/shared/stack.DxHPKr2R.d.mts +436 -0
  751. package/dist/shared/stack.Egr3RWhA.d.mts +408 -0
  752. package/dist/shared/stack.Mak7pzIV.d.cts +533 -0
  753. package/dist/shared/stack.OYT8WU4n.d.mts +351 -0
  754. package/dist/shared/{stack.CPsYC2-Z.d.cts → stack.fdi94T4S.d.cts} +7 -7
  755. package/dist/shared/{stack.CPsYC2-Z.d.mts → stack.fdi94T4S.d.mts} +7 -7
  756. package/dist/shared/{stack.CPsYC2-Z.d.ts → stack.fdi94T4S.d.ts} +7 -7
  757. package/dist/shared/{stack.Dlgtumru.d.cts → stack.kaCV7vEL.d.ts} +1 -5
  758. package/dist/shared/stack.v2O65JqF.d.cts +351 -0
  759. package/dist/tanstack/index.cjs +2 -0
  760. package/dist/tanstack/index.d.cts +27 -5
  761. package/dist/tanstack/index.d.mts +27 -5
  762. package/dist/tanstack/index.d.ts +27 -5
  763. package/dist/tanstack/index.mjs +1 -0
  764. package/dist/tanstack/server.cjs +9 -0
  765. package/dist/tanstack/server.d.cts +15 -0
  766. package/dist/tanstack/server.d.mts +15 -0
  767. package/dist/tanstack/server.d.ts +15 -0
  768. package/dist/tanstack/server.mjs +7 -0
  769. package/package.json +198 -3
  770. package/src/__tests__/ai-chat-client-runtime.test.ts +481 -0
  771. package/src/__tests__/ai-chat-client-runtime.typecheck.tsx +130 -0
  772. package/src/__tests__/ai-chat-lifecycle-migrations.typecheck.ts +61 -0
  773. package/src/__tests__/ai-chat-query-keys.test.ts +125 -22
  774. package/src/__tests__/auth-provider.test.tsx +41 -314
  775. package/src/__tests__/auth-test-utils.ts +24 -0
  776. package/src/__tests__/authorization-client.test.tsx +767 -0
  777. package/src/__tests__/authorization-contraction.test.ts +182 -0
  778. package/src/__tests__/authorization-remote.test.ts +222 -0
  779. package/src/__tests__/authorization-server.test.ts +90 -0
  780. package/src/__tests__/authorization.test.ts +652 -0
  781. package/src/__tests__/authorization.typecheck.ts +671 -0
  782. package/src/__tests__/backend-plugin-factories.test.ts +54 -0
  783. package/src/__tests__/backend-plugin-factories.typecheck.ts +133 -0
  784. package/src/__tests__/client-plugin-ssr-loaders.test.ts +304 -138
  785. package/src/__tests__/client-runtime.test.ts +1021 -0
  786. package/src/__tests__/client-runtime.typecheck.ts +107 -0
  787. package/src/__tests__/client-stack-test-utils.ts +36 -0
  788. package/src/__tests__/content-lifecycle-migrations.typecheck.ts +122 -0
  789. package/src/__tests__/endpoint-inventory.test.ts +768 -0
  790. package/src/__tests__/form-builder-query-keys.test.ts +32 -0
  791. package/src/__tests__/i18n-provider.test.tsx +4 -3
  792. package/src/__tests__/initial-identity-hydration.test.tsx +156 -0
  793. package/src/__tests__/initial-identity-layouts.test.tsx +204 -0
  794. package/src/__tests__/kanban-query-keys.test.ts +189 -2
  795. package/src/__tests__/managed-origin-hydration.browser.test.tsx +163 -0
  796. package/src/__tests__/media-query-keys.test.ts +91 -17
  797. package/src/__tests__/notify-provider.test.tsx +3 -2
  798. package/src/__tests__/package-metadata.test.ts +294 -0
  799. package/src/__tests__/page-component-overrides.test.tsx +3 -4
  800. package/src/__tests__/plugin-registration.test.tsx +183 -0
  801. package/src/__tests__/plugin-registration.typecheck.tsx +139 -0
  802. package/src/__tests__/plugins.test.tsx +72 -42
  803. package/src/__tests__/resource-factory.test.tsx +79 -2
  804. package/src/__tests__/resource-use-form.test.tsx +2 -2
  805. package/src/__tests__/resource-use-select.test.tsx +2 -4
  806. package/src/__tests__/router-overrides.test.tsx +20 -8
  807. package/src/__tests__/sitemap.test.ts +38 -22
  808. package/src/__tests__/stack-api.test.ts +39 -34
  809. package/src/__tests__/stack-auth.test.ts +44 -140
  810. package/src/__tests__/stack-constructors.test.ts +96 -0
  811. package/src/__tests__/stack-constructors.typecheck.ts +115 -0
  812. package/src/__tests__/use-list-state.test.tsx +7 -6
  813. package/src/__tests__/workflow-lifecycle-migrations.typecheck.ts +150 -0
  814. package/src/api/index.ts +204 -99
  815. package/src/authorization/client.tsx +310 -0
  816. package/src/authorization/index.ts +873 -0
  817. package/src/authorization/json.ts +70 -0
  818. package/src/authorization/remote.ts +234 -0
  819. package/src/authorization/server.ts +171 -0
  820. package/src/client/__tests__/server-origin.test.ts +119 -0
  821. package/src/client/components/compose.tsx +126 -18
  822. package/src/client/hooks/index.ts +12 -0
  823. package/src/client/hooks/use-list-state.ts +7 -13
  824. package/src/client/index.ts +105 -50
  825. package/src/client/runtime.ts +414 -0
  826. package/src/client/server.ts +174 -0
  827. package/src/context/auth.tsx +205 -154
  828. package/src/context/index.ts +10 -4
  829. package/src/context/provider.tsx +96 -57
  830. package/src/context/router.tsx +7 -0
  831. package/src/context/site-navigation.ts +52 -0
  832. package/src/next/layout.tsx +81 -0
  833. package/src/next/page.tsx +16 -3
  834. package/src/next/router.tsx +1 -1
  835. package/src/next/server.ts +5 -0
  836. package/src/plugin-registration.ts +70 -0
  837. package/src/plugins/ai-chat/__tests__/authorization.test.ts +2051 -0
  838. package/src/plugins/ai-chat/__tests__/chat-hooks.test.tsx +147 -0
  839. package/src/plugins/ai-chat/__tests__/chat-input-authorization.test.tsx +188 -0
  840. package/src/plugins/ai-chat/__tests__/client-sweep.test.tsx +1506 -34
  841. package/src/plugins/ai-chat/__tests__/hooks.test.tsx +131 -23
  842. package/src/plugins/ai-chat/api/getters.ts +2 -2
  843. package/src/plugins/ai-chat/api/index.ts +6 -1
  844. package/src/plugins/ai-chat/api/lifecycle-migrations.ts +20 -0
  845. package/src/plugins/ai-chat/api/operations.ts +1865 -0
  846. package/src/plugins/ai-chat/api/plugin.ts +78 -1168
  847. package/src/plugins/ai-chat/api/serializers.ts +30 -0
  848. package/src/plugins/ai-chat/client/components/chat-input.tsx +228 -129
  849. package/src/plugins/ai-chat/client/components/chat-interface.tsx +776 -183
  850. package/src/plugins/ai-chat/client/components/chat-layout.tsx +6 -7
  851. package/src/plugins/ai-chat/client/components/chat-message.tsx +1 -1
  852. package/src/plugins/ai-chat/client/components/chat-sidebar.tsx +57 -37
  853. package/src/plugins/ai-chat/client/components/pages/404-page.tsx +1 -1
  854. package/src/plugins/ai-chat/client/components/pages/chat-page.internal.tsx +5 -3
  855. package/src/plugins/ai-chat/client/components/pages/chat-page.tsx +56 -15
  856. package/src/plugins/ai-chat/client/components/shared/default-error.tsx +1 -1
  857. package/src/plugins/ai-chat/client/components/tool-call-display.tsx +1 -1
  858. package/src/plugins/ai-chat/client/hooks/ai-chat-resource.ts +1 -1
  859. package/src/plugins/ai-chat/client/hooks/chat-hooks.tsx +253 -20
  860. package/src/plugins/ai-chat/client/index.ts +1 -0
  861. package/src/plugins/ai-chat/client/navigation.ts +11 -0
  862. package/src/plugins/ai-chat/client/overrides.ts +47 -11
  863. package/src/plugins/ai-chat/client/plugin.tsx +208 -111
  864. package/src/plugins/ai-chat/permissions.ts +89 -0
  865. package/src/plugins/ai-chat/query-keys.ts +183 -25
  866. package/src/plugins/ai-chat/types.ts +7 -5
  867. package/src/plugins/api/create-endpoint.ts +40 -22
  868. package/src/plugins/api/endpoint-inventory.ts +206 -0
  869. package/src/plugins/api/index.ts +38 -4
  870. package/src/plugins/api/operation.ts +600 -0
  871. package/src/plugins/blog/__tests__/authorization.test.ts +1366 -0
  872. package/src/plugins/blog/__tests__/client-runtime.browser.test.tsx +257 -0
  873. package/src/plugins/blog/__tests__/client-runtime.test.ts +283 -0
  874. package/src/plugins/blog/__tests__/client-runtime.typecheck.tsx +98 -0
  875. package/src/plugins/blog/__tests__/client-sweep.test.tsx +212 -37
  876. package/src/plugins/blog/api/getters.ts +3 -3
  877. package/src/plugins/blog/api/index.ts +2 -0
  878. package/src/plugins/blog/api/lifecycle-migrations.ts +19 -0
  879. package/src/plugins/blog/api/mutations.ts +26 -5
  880. package/src/plugins/blog/api/operations.ts +727 -0
  881. package/src/plugins/blog/api/plugin.ts +99 -504
  882. package/src/plugins/blog/client/components/forms/image-field.tsx +2 -1
  883. package/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.tsx +2 -1
  884. package/src/plugins/blog/client/components/forms/post-forms.tsx +61 -33
  885. package/src/plugins/blog/client/components/pages/404-page.tsx +3 -1
  886. package/src/plugins/blog/client/components/pages/edit-post-page.internal.tsx +2 -1
  887. package/src/plugins/blog/client/components/pages/edit-post-page.tsx +28 -5
  888. package/src/plugins/blog/client/components/pages/home-page.internal.tsx +2 -1
  889. package/src/plugins/blog/client/components/pages/home-page.tsx +7 -4
  890. package/src/plugins/blog/client/components/pages/new-post-page.internal.tsx +2 -1
  891. package/src/plugins/blog/client/components/pages/new-post-page.tsx +5 -2
  892. package/src/plugins/blog/client/components/pages/post-page.internal.tsx +2 -1
  893. package/src/plugins/blog/client/components/pages/post-page.tsx +38 -4
  894. package/src/plugins/blog/client/components/pages/tag-page.internal.tsx +2 -1
  895. package/src/plugins/blog/client/components/pages/tag-page.tsx +5 -1
  896. package/src/plugins/blog/client/components/shared/collapsible-tag-list.tsx +2 -1
  897. package/src/plugins/blog/client/components/shared/default-error.tsx +3 -1
  898. package/src/plugins/blog/client/components/shared/on-this-page.tsx +5 -2
  899. package/src/plugins/blog/client/components/shared/page-wrapper.tsx +2 -1
  900. package/src/plugins/blog/client/components/shared/post-card.tsx +2 -1
  901. package/src/plugins/blog/client/components/shared/posts-list.tsx +2 -1
  902. package/src/plugins/blog/client/components/shared/recent-posts-carousel.tsx +2 -1
  903. package/src/plugins/blog/client/components/shared/search-input.tsx +1 -1
  904. package/src/plugins/blog/client/constants.ts +2 -0
  905. package/src/plugins/blog/client/hooks/blog-resource.ts +2 -1
  906. package/src/plugins/blog/client/overrides.ts +1 -6
  907. package/src/plugins/blog/client/plugin.tsx +145 -129
  908. package/src/plugins/blog/permissions.ts +44 -0
  909. package/src/plugins/client/index.ts +108 -7
  910. package/src/plugins/client/resource/hooks.tsx +41 -18
  911. package/src/plugins/client/resource/internal.ts +77 -61
  912. package/src/plugins/client/resource/queries.ts +3 -4
  913. package/src/plugins/client/resource/use-form.ts +23 -20
  914. package/src/plugins/client/resource/use-select.ts +40 -34
  915. package/src/plugins/cms/__tests__/authorization.test.ts +2188 -0
  916. package/src/plugins/cms/__tests__/client-runtime.browser.test.tsx +245 -0
  917. package/src/plugins/cms/__tests__/client-runtime.test.ts +320 -0
  918. package/src/plugins/cms/__tests__/client-runtime.typecheck.tsx +208 -0
  919. package/src/plugins/cms/__tests__/client-sweep.test.tsx +202 -50
  920. package/src/plugins/cms/api/getters.ts +4 -4
  921. package/src/plugins/cms/api/index.ts +20 -0
  922. package/src/plugins/cms/api/lifecycle-migrations.ts +15 -0
  923. package/src/plugins/cms/api/mutations.ts +2 -2
  924. package/src/plugins/cms/api/operations.ts +2218 -0
  925. package/src/plugins/cms/api/plugin.ts +169 -1047
  926. package/src/plugins/cms/api/relations.ts +3 -1
  927. package/src/plugins/cms/client/components/forms/content-form.tsx +2 -1
  928. package/src/plugins/cms/client/components/forms/file-upload.tsx +3 -1
  929. package/src/plugins/cms/client/components/forms/relation-field.tsx +3 -1
  930. package/src/plugins/cms/client/components/inverse-relations-panel.tsx +29 -17
  931. package/src/plugins/cms/client/components/pages/404-page.tsx +7 -5
  932. package/src/plugins/cms/client/components/pages/content-editor-page.internal.tsx +19 -10
  933. package/src/plugins/cms/client/components/pages/content-editor-page.tsx +50 -12
  934. package/src/plugins/cms/client/components/pages/content-list-page.internal.tsx +48 -34
  935. package/src/plugins/cms/client/components/pages/content-list-page.tsx +8 -1
  936. package/src/plugins/cms/client/components/pages/dashboard-page.internal.tsx +15 -8
  937. package/src/plugins/cms/client/components/pages/dashboard-page.tsx +5 -1
  938. package/src/plugins/cms/client/components/shared/page-wrapper.tsx +2 -1
  939. package/src/plugins/cms/client/constants.ts +2 -0
  940. package/src/plugins/cms/client/hooks/cms-resource.ts +2 -1
  941. package/src/plugins/cms/client/index.ts +6 -1
  942. package/src/plugins/cms/client/overrides.ts +46 -6
  943. package/src/plugins/cms/client/plugin.tsx +135 -80
  944. package/src/plugins/cms/permissions.ts +43 -0
  945. package/src/plugins/cms/types.ts +174 -46
  946. package/src/plugins/comments/__tests__/authorization.test.ts +1570 -0
  947. package/src/plugins/comments/__tests__/client-runtime.browser.test.tsx +207 -0
  948. package/src/plugins/comments/__tests__/client-runtime.test.ts +224 -0
  949. package/src/plugins/comments/__tests__/client-runtime.typecheck.tsx +85 -0
  950. package/src/plugins/comments/__tests__/client-sweep.test.tsx +445 -82
  951. package/src/plugins/comments/api/getters.ts +19 -3
  952. package/src/plugins/comments/api/index.ts +2 -6
  953. package/src/plugins/comments/api/lifecycle-migrations.ts +20 -0
  954. package/src/plugins/comments/api/mutations.ts +261 -88
  955. package/src/plugins/comments/api/operations.ts +742 -0
  956. package/src/plugins/comments/api/plugin.ts +76 -548
  957. package/src/plugins/comments/client/components/comment-count.tsx +38 -5
  958. package/src/plugins/comments/client/components/comment-thread.tsx +148 -110
  959. package/src/plugins/comments/client/components/pages/moderation-page.internal.tsx +144 -78
  960. package/src/plugins/comments/client/components/pages/moderation-page.tsx +18 -2
  961. package/src/plugins/comments/client/components/pages/moderation-state.ts +13 -0
  962. package/src/plugins/comments/client/components/pages/my-comments-page.internal.tsx +38 -30
  963. package/src/plugins/comments/client/components/pages/my-comments-page.tsx +3 -1
  964. package/src/plugins/comments/client/components/pages/resource-comments-page.internal.tsx +29 -30
  965. package/src/plugins/comments/client/components/pages/resource-comments-page.tsx +10 -1
  966. package/src/plugins/comments/client/components/shared/page-wrapper.tsx +2 -1
  967. package/src/plugins/comments/client/components/shared/pagination.tsx +2 -1
  968. package/src/plugins/comments/client/constants.ts +2 -0
  969. package/src/plugins/comments/client/hooks/use-comments.tsx +430 -421
  970. package/src/plugins/comments/client/localization/comments-thread.ts +1 -0
  971. package/src/plugins/comments/client/plugin.tsx +113 -72
  972. package/src/plugins/comments/permissions.ts +68 -0
  973. package/src/plugins/comments/query-keys.ts +9 -10
  974. package/src/plugins/comments/schemas.ts +7 -8
  975. package/src/plugins/form-builder/__tests__/authorization.test.ts +1600 -0
  976. package/src/plugins/form-builder/__tests__/client-runtime.browser.test.tsx +255 -0
  977. package/src/plugins/form-builder/__tests__/client-runtime.test.ts +132 -0
  978. package/src/plugins/form-builder/__tests__/client-runtime.typecheck.tsx +77 -0
  979. package/src/plugins/form-builder/__tests__/client-sweep.test.tsx +350 -54
  980. package/src/plugins/form-builder/__tests__/form-renderer-authorization.test.tsx +162 -0
  981. package/src/plugins/form-builder/api/getters.ts +21 -5
  982. package/src/plugins/form-builder/api/index.ts +31 -0
  983. package/src/plugins/form-builder/api/lifecycle-migrations.ts +17 -0
  984. package/src/plugins/form-builder/api/operations.ts +1493 -0
  985. package/src/plugins/form-builder/api/plugin.ts +125 -632
  986. package/src/plugins/form-builder/api/query-key-defs.ts +7 -0
  987. package/src/plugins/form-builder/api/serializers.ts +1 -0
  988. package/src/plugins/form-builder/client/components/forms/form-renderer.tsx +49 -19
  989. package/src/plugins/form-builder/client/components/pages/404-page.tsx +9 -9
  990. package/src/plugins/form-builder/client/components/pages/form-builder-page.internal.tsx +54 -30
  991. package/src/plugins/form-builder/client/components/pages/form-builder-page.tsx +42 -9
  992. package/src/plugins/form-builder/client/components/pages/form-list-page.internal.tsx +46 -34
  993. package/src/plugins/form-builder/client/components/pages/form-list-page.tsx +6 -2
  994. package/src/plugins/form-builder/client/components/pages/submissions-page.internal.tsx +98 -64
  995. package/src/plugins/form-builder/client/components/pages/submissions-page.tsx +29 -9
  996. package/src/plugins/form-builder/client/components/shared/page-wrapper.tsx +2 -1
  997. package/src/plugins/form-builder/client/constants.ts +2 -0
  998. package/src/plugins/form-builder/client/hooks/form-builder-hooks.tsx +68 -4
  999. package/src/plugins/form-builder/client/hooks/form-builder-resource.ts +2 -1
  1000. package/src/plugins/form-builder/client/hooks/index.tsx +1 -0
  1001. package/src/plugins/form-builder/client/overrides.ts +1 -6
  1002. package/src/plugins/form-builder/client/plugin.tsx +128 -96
  1003. package/src/plugins/form-builder/permissions.ts +94 -0
  1004. package/src/plugins/form-builder/query-keys.ts +28 -3
  1005. package/src/plugins/form-builder/schemas.ts +9 -1
  1006. package/src/plugins/form-builder/types.ts +173 -41
  1007. package/src/plugins/kanban/__tests__/authorization.test.ts +1382 -0
  1008. package/src/plugins/kanban/__tests__/client-runtime.browser.test.tsx +302 -0
  1009. package/src/plugins/kanban/__tests__/client-runtime.test.ts +130 -0
  1010. package/src/plugins/kanban/__tests__/client-runtime.typecheck.tsx +81 -0
  1011. package/src/plugins/kanban/__tests__/client-sweep.test.tsx +418 -33
  1012. package/src/plugins/kanban/__tests__/identity-partition.test.tsx +306 -0
  1013. package/src/plugins/kanban/api/getters.ts +65 -50
  1014. package/src/plugins/kanban/api/index.ts +55 -4
  1015. package/src/plugins/kanban/api/lifecycle-migrations.ts +25 -0
  1016. package/src/plugins/kanban/api/mutations.ts +3 -3
  1017. package/src/plugins/kanban/api/operations.ts +2065 -0
  1018. package/src/plugins/kanban/api/plugin.ts +83 -1013
  1019. package/src/plugins/kanban/api/serializers.ts +26 -0
  1020. package/src/plugins/kanban/client/components/forms/board-form.tsx +3 -1
  1021. package/src/plugins/kanban/client/components/forms/column-form.tsx +3 -1
  1022. package/src/plugins/kanban/client/components/forms/task-form.tsx +92 -74
  1023. package/src/plugins/kanban/client/components/pages/404-page.tsx +6 -9
  1024. package/src/plugins/kanban/client/components/pages/board-page.internal.tsx +57 -74
  1025. package/src/plugins/kanban/client/components/pages/board-page.tsx +31 -7
  1026. package/src/plugins/kanban/client/components/pages/boards-list-page.internal.tsx +54 -45
  1027. package/src/plugins/kanban/client/components/pages/boards-list-page.tsx +2 -1
  1028. package/src/plugins/kanban/client/components/pages/new-board-page.internal.tsx +8 -12
  1029. package/src/plugins/kanban/client/components/pages/new-board-page.tsx +2 -1
  1030. package/src/plugins/kanban/client/components/shared/column-content.tsx +61 -48
  1031. package/src/plugins/kanban/client/components/shared/default-error.tsx +3 -1
  1032. package/src/plugins/kanban/client/components/shared/kanban-board.tsx +6 -6
  1033. package/src/plugins/kanban/client/components/shared/permission-access-any.tsx +32 -0
  1034. package/src/plugins/kanban/client/components/shared/task-card.tsx +64 -20
  1035. package/src/plugins/kanban/client/components/shared/user-avatar.tsx +3 -1
  1036. package/src/plugins/kanban/client/constants.ts +2 -0
  1037. package/src/plugins/kanban/client/hooks/kanban-hooks.tsx +55 -10
  1038. package/src/plugins/kanban/client/hooks/kanban-resource.ts +2 -1
  1039. package/src/plugins/kanban/client/overrides.ts +0 -5
  1040. package/src/plugins/kanban/client/plugin.tsx +201 -113
  1041. package/src/plugins/kanban/permissions.ts +100 -0
  1042. package/src/plugins/kanban/query-keys.ts +61 -21
  1043. package/src/plugins/kanban/schemas.ts +44 -18
  1044. package/src/plugins/kanban/types.ts +381 -1
  1045. package/src/plugins/media/__tests__/authorization.test.ts +1769 -0
  1046. package/src/plugins/media/__tests__/client-runtime.browser.test.tsx +975 -0
  1047. package/src/plugins/media/__tests__/client-runtime.test.ts +539 -0
  1048. package/src/plugins/media/__tests__/client-runtime.typecheck.tsx +135 -0
  1049. package/src/plugins/media/__tests__/client-sweep.test.tsx +292 -24
  1050. package/src/plugins/media/__tests__/identity-partition.test.tsx +741 -0
  1051. package/src/plugins/media/__tests__/plugin.test.ts +68 -28
  1052. package/src/plugins/media/__tests__/query-key-defs.test.ts +32 -2
  1053. package/src/plugins/media/api/adapters/vercel-blob.ts +84 -8
  1054. package/src/plugins/media/api/getters.ts +6 -6
  1055. package/src/plugins/media/api/index.ts +3 -0
  1056. package/src/plugins/media/api/lifecycle-migrations.ts +11 -0
  1057. package/src/plugins/media/api/mutations.ts +19 -13
  1058. package/src/plugins/media/api/operations.ts +1873 -0
  1059. package/src/plugins/media/api/plugin.ts +211 -899
  1060. package/src/plugins/media/api/query-key-defs.ts +66 -4
  1061. package/src/plugins/media/api/serializers.ts +4 -2
  1062. package/src/plugins/media/api/storage-adapter.ts +22 -4
  1063. package/src/plugins/media/asset-url.ts +14 -0
  1064. package/src/plugins/media/client/components/media-picker/asset-card.tsx +105 -98
  1065. package/src/plugins/media/client/components/media-picker/folder-tree.tsx +82 -60
  1066. package/src/plugins/media/client/components/media-picker/index.tsx +235 -171
  1067. package/src/plugins/media/client/components/media-picker/upload-tab.tsx +2 -10
  1068. package/src/plugins/media/client/components/media-picker/url-tab.tsx +1 -8
  1069. package/src/plugins/media/client/components/pages/library-page.internal.tsx +103 -82
  1070. package/src/plugins/media/client/components/pages/library-page.tsx +4 -2
  1071. package/src/plugins/media/client/components/upload-permission-check.tsx +75 -0
  1072. package/src/plugins/media/client/constants.ts +2 -0
  1073. package/src/plugins/media/client/hooks/media-resource.ts +2 -1
  1074. package/src/plugins/media/client/hooks/use-media.tsx +292 -49
  1075. package/src/plugins/media/client/index.ts +16 -3
  1076. package/src/plugins/media/client/overrides.ts +6 -17
  1077. package/src/plugins/media/client/plugin.tsx +188 -123
  1078. package/src/plugins/media/client/upload.ts +170 -53
  1079. package/src/plugins/media/db.ts +8 -0
  1080. package/src/plugins/media/permissions.ts +61 -0
  1081. package/src/plugins/media/query-keys.ts +63 -19
  1082. package/src/plugins/media/types.ts +8 -2
  1083. package/src/plugins/open-api/__tests__/route-introspection.test.ts +277 -0
  1084. package/src/plugins/open-api/api/generator.ts +49 -8
  1085. package/src/plugins/open-api/api/plugin.ts +36 -14
  1086. package/src/plugins/route-docs/client/components/pages/docs-page.tsx +63 -23
  1087. package/src/plugins/route-docs/client/constants.ts +2 -0
  1088. package/src/plugins/route-docs/client/hooks.ts +23 -13
  1089. package/src/plugins/route-docs/client/index.ts +7 -1
  1090. package/src/plugins/route-docs/client/plugin.tsx +222 -145
  1091. package/src/plugins/route-docs/client/schema.ts +31 -0
  1092. package/src/plugins/route-docs/generator.ts +9 -5
  1093. package/src/plugins/ui-builder/__tests__/client-sweep.test.tsx +427 -48
  1094. package/src/plugins/ui-builder/client/components/page-renderer.tsx +58 -16
  1095. package/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.tsx +47 -26
  1096. package/src/plugins/ui-builder/client/components/pages/page-builder-page.tsx +43 -8
  1097. package/src/plugins/ui-builder/client/components/pages/page-list-page.internal.tsx +38 -19
  1098. package/src/plugins/ui-builder/client/components/pages/page-list-page.tsx +8 -2
  1099. package/src/plugins/ui-builder/client/components/shared/default-error.tsx +2 -1
  1100. package/src/plugins/ui-builder/client/components/shared/page-wrapper.tsx +2 -1
  1101. package/src/plugins/ui-builder/client/constants.ts +2 -0
  1102. package/src/plugins/ui-builder/client/hooks/ui-builder-resource.ts +2 -1
  1103. package/src/plugins/ui-builder/client/index.ts +4 -1
  1104. package/src/plugins/ui-builder/client/overrides.ts +16 -21
  1105. package/src/plugins/ui-builder/client/plugin.tsx +119 -66
  1106. package/src/plugins/ui-builder/client/registry.ts +3 -5
  1107. package/src/plugins/ui-builder/types.ts +7 -4
  1108. package/src/plugins/utils.ts +5 -1
  1109. package/src/react-router/index.tsx +5 -0
  1110. package/src/react-router/layout.ts +43 -0
  1111. package/src/react-router/page.tsx +12 -5
  1112. package/src/react-router/router.tsx +1 -1
  1113. package/src/shared/auth-types.ts +19 -70
  1114. package/src/shared/entry-factories.ts +3 -3
  1115. package/src/shared/initial-identity.ts +73 -0
  1116. package/src/shared/list-state.ts +12 -0
  1117. package/src/shared/types.ts +1 -0
  1118. package/src/tanstack/index.tsx +5 -0
  1119. package/src/tanstack/layout.ts +36 -0
  1120. package/src/tanstack/page.tsx +7 -2
  1121. package/src/tanstack/router.tsx +1 -1
  1122. package/src/tanstack/server.ts +18 -0
  1123. package/src/types.ts +557 -65
  1124. package/dist/shared/stack.B8vdOMnQ.d.cts +0 -329
  1125. package/dist/shared/stack.BISAZ5LX.d.mts +0 -492
  1126. package/dist/shared/stack.BL5PvShn.d.mts +0 -193
  1127. package/dist/shared/stack.Be1QIHEn.d.cts +0 -23
  1128. package/dist/shared/stack.Be1QIHEn.d.mts +0 -23
  1129. package/dist/shared/stack.Be1QIHEn.d.ts +0 -23
  1130. package/dist/shared/stack.BjvOk_Bs.d.cts +0 -492
  1131. package/dist/shared/stack.BozPgbrZ.d.cts +0 -201
  1132. package/dist/shared/stack.BozPgbrZ.d.mts +0 -201
  1133. package/dist/shared/stack.BozPgbrZ.d.ts +0 -201
  1134. package/dist/shared/stack.BqTR6qi2.d.ts +0 -193
  1135. package/dist/shared/stack.BsmDN8r9.d.cts +0 -406
  1136. package/dist/shared/stack.C1ATQ3pd.d.ts +0 -416
  1137. package/dist/shared/stack.C4_gEENS.d.cts +0 -97
  1138. package/dist/shared/stack.C4_gEENS.d.mts +0 -97
  1139. package/dist/shared/stack.C4_gEENS.d.ts +0 -97
  1140. package/dist/shared/stack.C5Y5lViI.d.cts +0 -461
  1141. package/dist/shared/stack.C8SwdUz8.d.ts +0 -461
  1142. package/dist/shared/stack.CBsp9rDM.d.cts +0 -193
  1143. package/dist/shared/stack.CKGjOI3Z.d.cts +0 -840
  1144. package/dist/shared/stack.Cf-DbL1A.d.ts +0 -329
  1145. package/dist/shared/stack.CuI_-z-_.d.cts +0 -560
  1146. package/dist/shared/stack.D2RVkQD8.d.mts +0 -560
  1147. package/dist/shared/stack.DHTvwoNG.d.mts +0 -329
  1148. package/dist/shared/stack.DJaKVY7v.d.cts +0 -76
  1149. package/dist/shared/stack.DJaKVY7v.d.mts +0 -76
  1150. package/dist/shared/stack.DJaKVY7v.d.ts +0 -76
  1151. package/dist/shared/stack.DKZMaVr6.d.cts +0 -416
  1152. package/dist/shared/stack.DOzsgzkY.d.ts +0 -406
  1153. package/dist/shared/stack.DQq8DEIv.d.mts +0 -461
  1154. package/dist/shared/stack.DXrOtuek.d.ts +0 -560
  1155. package/dist/shared/stack.DZHEHd-S.d.mts +0 -840
  1156. package/dist/shared/stack.Dd-8shBE.d.ts +0 -492
  1157. package/dist/shared/stack.EOPVnJrF.d.cts +0 -39
  1158. package/dist/shared/stack.EOPVnJrF.d.mts +0 -39
  1159. package/dist/shared/stack.EOPVnJrF.d.ts +0 -39
  1160. package/dist/shared/stack.NGKtwA6S.d.mts +0 -406
  1161. package/dist/shared/stack.WELCDuMs.d.ts +0 -840
  1162. package/dist/shared/stack.aXdBOJAR.d.cts +0 -173
  1163. package/dist/shared/stack.aXdBOJAR.d.mts +0 -173
  1164. package/dist/shared/stack.aXdBOJAR.d.ts +0 -173
  1165. package/dist/shared/stack.oBokjN_J.d.mts +0 -416
  1166. package/dist/shared/{stack.D18Dq3ve.d.mts → stack.Csi-4Mgz.d.cts} +1 -1
  1167. package/dist/shared/{stack.D18Dq3ve.d.ts → stack.Csi-4Mgz.d.mts} +1 -1
  1168. package/dist/shared/{stack.D18Dq3ve.d.cts → stack.Csi-4Mgz.d.ts} +1 -1
@@ -0,0 +1,2065 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import type { DBAdapter as Adapter } from "@btst/db";
3
+ import {
4
+ type DeepReadonly,
5
+ defineOperation,
6
+ type Operation,
7
+ OperationHttpError,
8
+ type OperationContext,
9
+ } from "@btst/stack/plugins/api";
10
+ import type { PermissionFactsFor } from "@btst/stack/authorization";
11
+ import { z } from "zod";
12
+ import { kanbanPermissions } from "../permissions";
13
+ import {
14
+ BoardListQuerySchema,
15
+ createBoardSchema,
16
+ createColumnSchema,
17
+ createTaskSchema,
18
+ moveTaskSchema,
19
+ reorderColumnsSchema,
20
+ reorderTasksSchema,
21
+ updateBoardSchema,
22
+ updateColumnSchema,
23
+ updateTaskSchema,
24
+ } from "../schemas";
25
+ import type {
26
+ Board,
27
+ BoardWithColumns,
28
+ Column,
29
+ KanbanApiContext,
30
+ KanbanApiResultContext,
31
+ KanbanBackendHooks,
32
+ SerializedBoard,
33
+ SerializedBoardSummary,
34
+ SerializedBoardWithColumns,
35
+ SerializedColumn,
36
+ SerializedTask,
37
+ Task,
38
+ } from "../types";
39
+ import { slugify } from "../utils";
40
+ import { getBoardById, getBoardSummaries } from "./getters";
41
+ import { serializeBoardSummary, serializeTask } from "./serializers";
42
+
43
+ export const BoardIdOperationInputSchema = z.object({ id: z.string() });
44
+ export const UpdateBoardOperationInputSchema = z.object({
45
+ id: z.string(),
46
+ data: updateBoardSchema.omit({ id: true }),
47
+ });
48
+ export const ColumnIdOperationInputSchema = z.object({ id: z.string() });
49
+ export const UpdateColumnOperationInputSchema = z.object({
50
+ id: z.string(),
51
+ data: updateColumnSchema.omit({ id: true }).superRefine((data, context) => {
52
+ if (Object.hasOwn(data, "order") && data.order === undefined) {
53
+ context.addIssue({
54
+ code: "custom",
55
+ path: ["order"],
56
+ message: "Order must be a number when provided.",
57
+ });
58
+ }
59
+ }),
60
+ });
61
+ export const TaskIdOperationInputSchema = z.object({ id: z.string() });
62
+ export const UpdateTaskOperationInputSchema = z.object({
63
+ id: z.string(),
64
+ data: updateTaskSchema.omit({ id: true }),
65
+ });
66
+
67
+ type BoardReadFacts = PermissionFactsFor<typeof kanbanPermissions.board.read>;
68
+ type BoardUpdateFacts = PermissionFactsFor<
69
+ typeof kanbanPermissions.board.update
70
+ >;
71
+ type BoardDeleteFacts = PermissionFactsFor<
72
+ typeof kanbanPermissions.board.delete
73
+ >;
74
+ type ColumnCreateFacts = PermissionFactsFor<
75
+ typeof kanbanPermissions.column.create
76
+ >;
77
+ type ColumnUpdateFacts = PermissionFactsFor<
78
+ typeof kanbanPermissions.column.update
79
+ >;
80
+ type ColumnDeleteFacts = PermissionFactsFor<
81
+ typeof kanbanPermissions.column.delete
82
+ >;
83
+ type ColumnReorderFacts = PermissionFactsFor<
84
+ typeof kanbanPermissions.column.reorder
85
+ >;
86
+ type TaskCreateFacts = PermissionFactsFor<typeof kanbanPermissions.task.create>;
87
+ type TaskUpdateFacts = PermissionFactsFor<typeof kanbanPermissions.task.update>;
88
+ type TaskDeleteFacts = PermissionFactsFor<typeof kanbanPermissions.task.delete>;
89
+ type TaskMoveFacts = PermissionFactsFor<typeof kanbanPermissions.task.move>;
90
+ type TaskReorderFacts = PermissionFactsFor<
91
+ typeof kanbanPermissions.task.reorder
92
+ >;
93
+
94
+ export interface KanbanBoardListResult {
95
+ readonly items: readonly SerializedBoardSummary[];
96
+ readonly total: number;
97
+ readonly limit?: number;
98
+ readonly offset?: number;
99
+ }
100
+
101
+ export type KanbanOperations = {
102
+ readonly listBoards: Operation<
103
+ typeof BoardListQuerySchema,
104
+ typeof kanbanPermissions.board.read,
105
+ KanbanBoardListResult
106
+ >;
107
+ readonly getBoard: Operation<
108
+ typeof BoardIdOperationInputSchema,
109
+ typeof kanbanPermissions.board.read,
110
+ SerializedBoardWithColumns
111
+ >;
112
+ readonly createBoard: Operation<
113
+ typeof createBoardSchema,
114
+ typeof kanbanPermissions.board.create,
115
+ SerializedBoardWithColumns
116
+ >;
117
+ readonly updateBoard: Operation<
118
+ typeof UpdateBoardOperationInputSchema,
119
+ typeof kanbanPermissions.board.update,
120
+ SerializedBoard
121
+ >;
122
+ readonly deleteBoard: Operation<
123
+ typeof BoardIdOperationInputSchema,
124
+ typeof kanbanPermissions.board.delete,
125
+ { readonly success: true }
126
+ >;
127
+ readonly createColumn: Operation<
128
+ typeof createColumnSchema,
129
+ typeof kanbanPermissions.column.create,
130
+ SerializedColumn
131
+ >;
132
+ readonly updateColumn: Operation<
133
+ typeof UpdateColumnOperationInputSchema,
134
+ typeof kanbanPermissions.column.update,
135
+ SerializedColumn
136
+ >;
137
+ readonly deleteColumn: Operation<
138
+ typeof ColumnIdOperationInputSchema,
139
+ typeof kanbanPermissions.column.delete,
140
+ { readonly success: true }
141
+ >;
142
+ readonly reorderColumns: Operation<
143
+ typeof reorderColumnsSchema,
144
+ typeof kanbanPermissions.column.reorder,
145
+ { readonly success: true }
146
+ >;
147
+ readonly createTask: Operation<
148
+ typeof createTaskSchema,
149
+ typeof kanbanPermissions.task.create,
150
+ SerializedTask
151
+ >;
152
+ readonly updateTask: Operation<
153
+ typeof UpdateTaskOperationInputSchema,
154
+ typeof kanbanPermissions.task.update,
155
+ SerializedTask
156
+ >;
157
+ readonly deleteTask: Operation<
158
+ typeof TaskIdOperationInputSchema,
159
+ typeof kanbanPermissions.task.delete,
160
+ { readonly success: true }
161
+ >;
162
+ readonly moveTask: Operation<
163
+ typeof moveTaskSchema,
164
+ typeof kanbanPermissions.task.move,
165
+ SerializedTask
166
+ >;
167
+ readonly reorderTasks: Operation<
168
+ typeof reorderTasksSchema,
169
+ typeof kanbanPermissions.task.reorder,
170
+ { readonly success: true }
171
+ >;
172
+ };
173
+
174
+ /** A domain/HTTP failure raised after Kanban authorization succeeds. */
175
+ export class KanbanOperationError extends OperationHttpError {
176
+ constructor(statusCode: number, message: string, code: string) {
177
+ super(statusCode, message, code);
178
+ this.name = "KanbanOperationError";
179
+ }
180
+ }
181
+
182
+ interface BoardSnapshot {
183
+ readonly id: string;
184
+ readonly ownerId?: string;
185
+ readonly organizationId?: string;
186
+ readonly updatedAt: Date;
187
+ }
188
+
189
+ interface ColumnSnapshot {
190
+ readonly id: string;
191
+ readonly boardId: string;
192
+ readonly updatedAt: Date;
193
+ }
194
+
195
+ interface TaskSnapshot {
196
+ readonly id: string;
197
+ readonly columnId: string;
198
+ readonly assigneeId?: string;
199
+ readonly isArchived: boolean;
200
+ readonly updatedAt: Date;
201
+ }
202
+
203
+ interface ColumnAuthorizationSnapshot {
204
+ readonly board: BoardSnapshot;
205
+ readonly column: ColumnSnapshot;
206
+ }
207
+
208
+ interface TaskAuthorizationSnapshot extends ColumnAuthorizationSnapshot {
209
+ readonly task: TaskSnapshot;
210
+ }
211
+
212
+ interface ColumnReorderSnapshot {
213
+ readonly board: BoardSnapshot;
214
+ readonly columns: readonly ColumnSnapshot[];
215
+ }
216
+
217
+ interface TaskReorderSnapshot extends ColumnAuthorizationSnapshot {
218
+ readonly tasks: readonly TaskSnapshot[];
219
+ }
220
+
221
+ interface TaskMoveSnapshot extends TaskAuthorizationSnapshot {
222
+ readonly targetColumn: ColumnSnapshot;
223
+ readonly sourceTasks: readonly TaskSnapshot[];
224
+ readonly targetTasks: readonly TaskSnapshot[];
225
+ readonly targetOrder: number;
226
+ }
227
+
228
+ function boardSnapshot(board: Board): BoardSnapshot {
229
+ return {
230
+ id: board.id,
231
+ ...(board.ownerId ? { ownerId: board.ownerId } : {}),
232
+ ...(board.organizationId ? { organizationId: board.organizationId } : {}),
233
+ updatedAt: board.updatedAt,
234
+ };
235
+ }
236
+
237
+ function columnSnapshot(column: Column): ColumnSnapshot {
238
+ return {
239
+ id: column.id,
240
+ boardId: column.boardId,
241
+ updatedAt: column.updatedAt,
242
+ };
243
+ }
244
+
245
+ function taskSnapshot(task: Task): TaskSnapshot {
246
+ return {
247
+ id: task.id,
248
+ columnId: task.columnId,
249
+ ...(task.assigneeId ? { assigneeId: task.assigneeId } : {}),
250
+ isArchived: task.isArchived,
251
+ updatedAt: task.updatedAt,
252
+ };
253
+ }
254
+
255
+ function sameDate(left: Date, right: Date) {
256
+ return left.getTime() === right.getTime();
257
+ }
258
+
259
+ function sameBoard(board: Board | null, expected: BoardSnapshot) {
260
+ return (
261
+ board !== null &&
262
+ board.id === expected.id &&
263
+ (board.ownerId || undefined) === expected.ownerId &&
264
+ (board.organizationId || undefined) === expected.organizationId &&
265
+ sameDate(board.updatedAt, expected.updatedAt)
266
+ );
267
+ }
268
+
269
+ function sameColumn(column: Column | null, expected: ColumnSnapshot) {
270
+ return (
271
+ column !== null &&
272
+ column.id === expected.id &&
273
+ column.boardId === expected.boardId &&
274
+ sameDate(column.updatedAt, expected.updatedAt)
275
+ );
276
+ }
277
+
278
+ function sameTask(task: Task | null, expected: TaskSnapshot) {
279
+ return (
280
+ task !== null &&
281
+ task.id === expected.id &&
282
+ task.columnId === expected.columnId &&
283
+ (task.assigneeId || undefined) === expected.assigneeId &&
284
+ task.isArchived === expected.isArchived &&
285
+ sameDate(task.updatedAt, expected.updatedAt)
286
+ );
287
+ }
288
+
289
+ function sameColumnSnapshots(left: ColumnSnapshot, right: ColumnSnapshot) {
290
+ return (
291
+ left.id === right.id &&
292
+ left.boardId === right.boardId &&
293
+ sameDate(left.updatedAt, right.updatedAt)
294
+ );
295
+ }
296
+
297
+ function sameTaskSnapshots(left: TaskSnapshot, right: TaskSnapshot) {
298
+ return (
299
+ left.id === right.id &&
300
+ left.columnId === right.columnId &&
301
+ left.assigneeId === right.assigneeId &&
302
+ left.isArchived === right.isArchived &&
303
+ sameDate(left.updatedAt, right.updatedAt)
304
+ );
305
+ }
306
+
307
+ function sameSnapshotSet<T extends { id: string }>(
308
+ current: readonly T[],
309
+ expected: readonly T[],
310
+ equal: (value: T, snapshot: T) => boolean,
311
+ ) {
312
+ if (current.length !== expected.length) return false;
313
+ const byId = new Map(current.map((value) => [value.id, value]));
314
+ return expected.every((snapshot) => {
315
+ const value = byId.get(snapshot.id);
316
+ return value !== undefined && equal(value, snapshot);
317
+ });
318
+ }
319
+
320
+ function nextSnapshotDate(previous: Date) {
321
+ return new Date(Math.max(Date.now(), previous.getTime() + 1));
322
+ }
323
+
324
+ const AFFECTED_ROW_KEYS = [
325
+ "rowCount",
326
+ "affectedRows",
327
+ "rowsAffected",
328
+ "changes",
329
+ "numUpdatedRows",
330
+ ] as const;
331
+
332
+ function hasPositiveCount(value: unknown): boolean {
333
+ if (typeof value === "number") return Number.isFinite(value) && value > 0;
334
+ if (typeof value === "bigint") return value > 0n;
335
+ return false;
336
+ }
337
+
338
+ function didAffectRow(result: unknown, expectedId: string): boolean {
339
+ if (typeof result === "number" || typeof result === "bigint") {
340
+ return hasPositiveCount(result);
341
+ }
342
+ if (!result || typeof result !== "object") return false;
343
+ const record = result as Record<string, unknown>;
344
+ if ("count" in record) return hasPositiveCount(record.count);
345
+ if (Array.isArray(result)) {
346
+ return result.length > 0 && didAffectRow(result[0], expectedId);
347
+ }
348
+ for (const key of AFFECTED_ROW_KEYS) {
349
+ if (key in record) return hasPositiveCount(record[key]);
350
+ }
351
+ if ("meta" in record) {
352
+ const meta = record.meta;
353
+ return Boolean(
354
+ meta &&
355
+ typeof meta === "object" &&
356
+ "changes" in meta &&
357
+ hasPositiveCount((meta as Record<string, unknown>).changes),
358
+ );
359
+ }
360
+ return record.id === expectedId;
361
+ }
362
+
363
+ const memoryRollbackMarkers = new WeakMap<Adapter, (error: unknown) => void>();
364
+
365
+ function markMemoryRollback(adapter: Adapter, error: unknown) {
366
+ memoryRollbackMarkers.get(adapter)?.(error);
367
+ }
368
+
369
+ /**
370
+ * The published memory adapter rolls back from a whole-database clone but does
371
+ * not isolate overlapping callbacks. Instrument the shared adapter instance
372
+ * so every stack access queues behind whole-database transactions: temporary
373
+ * CAS claims stay private and rollback cannot overwrite a later winner.
374
+ */
375
+ function serializeMemoryOperations(adapter: Adapter): Adapter {
376
+ if (adapter.id !== "memory" || memoryRollbackMarkers.has(adapter)) {
377
+ return adapter;
378
+ }
379
+ const source: Adapter = { ...adapter };
380
+ type TransactionAdapter = Parameters<
381
+ Parameters<Adapter["transaction"]>[0]
382
+ >[0];
383
+ type ActiveAdapter = Omit<Adapter, "transaction"> &
384
+ Partial<Pick<Adapter, "transaction">>;
385
+ type LockContext = {
386
+ owner: object;
387
+ adapter: ActiveAdapter;
388
+ inTransaction?: boolean;
389
+ rollbackError?: unknown;
390
+ rollbackOnly?: boolean;
391
+ };
392
+ const lockContext = new AsyncLocalStorage<LockContext>();
393
+ let tail = Promise.resolve();
394
+ let activeOwner: object | undefined;
395
+ const withLock = async <T>(
396
+ run: (activeAdapter: ActiveAdapter) => Promise<T>,
397
+ ): Promise<T> => {
398
+ const inherited = lockContext.getStore();
399
+ if (inherited && inherited.owner === activeOwner) {
400
+ return run(inherited.adapter);
401
+ }
402
+ let release = () => {};
403
+ const previous = tail;
404
+ tail = new Promise<void>((resolve) => {
405
+ release = resolve;
406
+ });
407
+ await previous;
408
+ const owner = {};
409
+ activeOwner = owner;
410
+ try {
411
+ return await lockContext.run({ owner, adapter: source }, () =>
412
+ run(source),
413
+ );
414
+ } finally {
415
+ if (activeOwner === owner) activeOwner = undefined;
416
+ release();
417
+ }
418
+ };
419
+ const serialized: Adapter = {
420
+ ...source,
421
+ create: ((input) =>
422
+ withLock((active) => active.create(input))) as Adapter["create"],
423
+ findOne: ((input) =>
424
+ withLock((active) => active.findOne(input))) as Adapter["findOne"],
425
+ findMany: ((input) =>
426
+ withLock((active) => active.findMany(input))) as Adapter["findMany"],
427
+ count: (input) => withLock((active) => active.count(input)),
428
+ update: ((input) =>
429
+ withLock((active) => active.update(input))) as Adapter["update"],
430
+ updateMany: (input) => withLock((active) => active.updateMany(input)),
431
+ delete: ((input) =>
432
+ withLock((active) => active.delete(input))) as Adapter["delete"],
433
+ deleteMany: (input) => withLock((active) => active.deleteMany(input)),
434
+ consumeOne: ((input) =>
435
+ withLock((active) => active.consumeOne(input))) as Adapter["consumeOne"],
436
+ transaction: ((callback) =>
437
+ withLock((active) => {
438
+ const context = lockContext.getStore();
439
+ if (!context) throw new TypeError("Missing memory lock context.");
440
+ if (context.inTransaction) {
441
+ return callback(active as TransactionAdapter).catch((error) => {
442
+ context.rollbackOnly = true;
443
+ context.rollbackError = error;
444
+ throw error;
445
+ });
446
+ }
447
+ if (!active.transaction) {
448
+ throw new TypeError("Missing memory transaction adapter.");
449
+ }
450
+ return active.transaction((tx) =>
451
+ lockContext.run(
452
+ { owner: context.owner, adapter: tx, inTransaction: true },
453
+ async () => {
454
+ const transactionContext = lockContext.getStore();
455
+ const result = await callback(tx);
456
+ if (transactionContext?.rollbackOnly) {
457
+ throw transactionContext.rollbackError;
458
+ }
459
+ return result;
460
+ },
461
+ ),
462
+ );
463
+ })) as Adapter["transaction"],
464
+ };
465
+ memoryRollbackMarkers.set(adapter, (error) => {
466
+ const context = lockContext.getStore();
467
+ if (!context || context.owner !== activeOwner || !context.inTransaction) {
468
+ return;
469
+ }
470
+ context.rollbackOnly = true;
471
+ context.rollbackError = error;
472
+ });
473
+ Object.assign(adapter, serialized);
474
+ return adapter;
475
+ }
476
+
477
+ function requireAtomicTransactions(adapter: Adapter) {
478
+ if (
479
+ adapter.id !== "memory" &&
480
+ typeof adapter.options?.adapterConfig.transaction !== "function"
481
+ ) {
482
+ throw new KanbanOperationError(
483
+ 500,
484
+ "Kanban writes require an adapter with isolated transaction support.",
485
+ "ATOMIC_TRANSACTION_REQUIRED",
486
+ );
487
+ }
488
+ }
489
+
490
+ function boardWhere(snapshot: BoardSnapshot) {
491
+ return [
492
+ { field: "id", value: snapshot.id, operator: "eq" as const },
493
+ {
494
+ field: "ownerId",
495
+ value: snapshot.ownerId ?? null,
496
+ operator: "eq" as const,
497
+ },
498
+ {
499
+ field: "organizationId",
500
+ value: snapshot.organizationId ?? null,
501
+ operator: "eq" as const,
502
+ },
503
+ {
504
+ field: "updatedAt",
505
+ value: snapshot.updatedAt,
506
+ operator: "eq" as const,
507
+ },
508
+ ];
509
+ }
510
+
511
+ function columnWhere(snapshot: ColumnSnapshot) {
512
+ return [
513
+ { field: "id", value: snapshot.id, operator: "eq" as const },
514
+ { field: "boardId", value: snapshot.boardId, operator: "eq" as const },
515
+ {
516
+ field: "updatedAt",
517
+ value: snapshot.updatedAt,
518
+ operator: "eq" as const,
519
+ },
520
+ ];
521
+ }
522
+
523
+ function taskWhere(snapshot: TaskSnapshot) {
524
+ return [
525
+ { field: "id", value: snapshot.id, operator: "eq" as const },
526
+ { field: "columnId", value: snapshot.columnId, operator: "eq" as const },
527
+ {
528
+ field: "assigneeId",
529
+ value: snapshot.assigneeId ?? null,
530
+ operator: "eq" as const,
531
+ },
532
+ {
533
+ field: "isArchived",
534
+ value: snapshot.isArchived,
535
+ operator: "eq" as const,
536
+ },
537
+ {
538
+ field: "updatedAt",
539
+ value: snapshot.updatedAt,
540
+ operator: "eq" as const,
541
+ },
542
+ ];
543
+ }
544
+
545
+ async function claimSnapshot(
546
+ adapter: Pick<Adapter, "updateMany">,
547
+ model: "kanbanBoard" | "kanbanColumn" | "kanbanTask",
548
+ snapshot: BoardSnapshot | ColumnSnapshot | TaskSnapshot,
549
+ ) {
550
+ const claimedAt = nextSnapshotDate(snapshot.updatedAt);
551
+ const where =
552
+ model === "kanbanBoard"
553
+ ? boardWhere(snapshot as BoardSnapshot)
554
+ : model === "kanbanColumn"
555
+ ? columnWhere(snapshot as ColumnSnapshot)
556
+ : taskWhere(snapshot as TaskSnapshot);
557
+ const claimed = await adapter.updateMany({
558
+ model,
559
+ where,
560
+ update: { updatedAt: claimedAt },
561
+ });
562
+ if (!didAffectRow(claimed, snapshot.id)) throw staleStateError();
563
+ return claimedAt;
564
+ }
565
+
566
+ async function restoreSnapshot(
567
+ adapter: Pick<Adapter, "updateMany">,
568
+ model: "kanbanBoard" | "kanbanColumn",
569
+ snapshot: BoardSnapshot | ColumnSnapshot,
570
+ claimedAt: Date,
571
+ ) {
572
+ const where =
573
+ model === "kanbanBoard"
574
+ ? boardWhere({ ...(snapshot as BoardSnapshot), updatedAt: claimedAt })
575
+ : columnWhere({ ...(snapshot as ColumnSnapshot), updatedAt: claimedAt });
576
+ const restored = await adapter.updateMany({
577
+ model,
578
+ where,
579
+ update: { updatedAt: snapshot.updatedAt },
580
+ });
581
+ if (!didAffectRow(restored, snapshot.id)) throw staleStateError();
582
+ }
583
+
584
+ async function findBoard(adapter: Pick<Adapter, "findOne">, id: string) {
585
+ return (
586
+ (await adapter.findOne<Board>({
587
+ model: "kanbanBoard",
588
+ where: [{ field: "id", value: id, operator: "eq" as const }],
589
+ })) ?? null
590
+ );
591
+ }
592
+
593
+ async function findColumn(adapter: Pick<Adapter, "findOne">, id: string) {
594
+ return (
595
+ (await adapter.findOne<Column>({
596
+ model: "kanbanColumn",
597
+ where: [{ field: "id", value: id, operator: "eq" as const }],
598
+ })) ?? null
599
+ );
600
+ }
601
+
602
+ async function findTask(adapter: Pick<Adapter, "findOne">, id: string) {
603
+ return (
604
+ (await adapter.findOne<Task>({
605
+ model: "kanbanTask",
606
+ where: [{ field: "id", value: id, operator: "eq" as const }],
607
+ })) ?? null
608
+ );
609
+ }
610
+
611
+ async function listColumns(
612
+ adapter: Pick<Adapter, "findMany">,
613
+ boardId: string,
614
+ ) {
615
+ return adapter.findMany<Column>({
616
+ model: "kanbanColumn",
617
+ where: [{ field: "boardId", value: boardId, operator: "eq" as const }],
618
+ sortBy: { field: "order", direction: "asc" },
619
+ });
620
+ }
621
+
622
+ async function listTasks(adapter: Pick<Adapter, "findMany">, columnId: string) {
623
+ return adapter.findMany<Task>({
624
+ model: "kanbanTask",
625
+ where: [{ field: "columnId", value: columnId, operator: "eq" as const }],
626
+ sortBy: { field: "order", direction: "asc" },
627
+ });
628
+ }
629
+
630
+ function boardNotFoundError() {
631
+ return new KanbanOperationError(404, "Board not found", "BOARD_NOT_FOUND");
632
+ }
633
+
634
+ function columnNotFoundError() {
635
+ return new KanbanOperationError(404, "Column not found", "COLUMN_NOT_FOUND");
636
+ }
637
+
638
+ function taskNotFoundError() {
639
+ return new KanbanOperationError(404, "Task not found", "TASK_NOT_FOUND");
640
+ }
641
+
642
+ function staleStateError() {
643
+ return new KanbanOperationError(
644
+ 409,
645
+ "Kanban state changed while authorization was being evaluated. Retry the operation.",
646
+ "KANBAN_STATE_CHANGED",
647
+ );
648
+ }
649
+
650
+ function invalidColumnSetError() {
651
+ return new KanbanOperationError(
652
+ 400,
653
+ "columnIds must contain every current board column exactly once.",
654
+ "INVALID_COLUMN_SET",
655
+ );
656
+ }
657
+
658
+ function invalidTaskSetError() {
659
+ return new KanbanOperationError(
660
+ 400,
661
+ "taskIds must contain every current column task exactly once.",
662
+ "INVALID_TASK_SET",
663
+ );
664
+ }
665
+
666
+ function invalidTargetColumnError() {
667
+ return new KanbanOperationError(
668
+ 400,
669
+ "The target column must belong to the task's board.",
670
+ "INVALID_TARGET_COLUMN",
671
+ );
672
+ }
673
+
674
+ function sanitizeSlug(value: string) {
675
+ const slug = slugify(value);
676
+ if (!slug) {
677
+ throw new KanbanOperationError(
678
+ 400,
679
+ "Invalid slug: must contain at least one alphanumeric character",
680
+ "INVALID_SLUG",
681
+ );
682
+ }
683
+ return slug;
684
+ }
685
+
686
+ function normalizeError(error: unknown, fallback: string) {
687
+ return error instanceof Error
688
+ ? error
689
+ : new Error(typeof error === "string" ? error : fallback, { cause: error });
690
+ }
691
+
692
+ async function runDomainHook<T>(
693
+ run: () => Promise<T> | T,
694
+ code: string,
695
+ ): Promise<T> {
696
+ try {
697
+ return await run();
698
+ } catch (error) {
699
+ throw new KanbanOperationError(
700
+ 403,
701
+ normalizeError(error, "Kanban operation rejected").message,
702
+ code,
703
+ );
704
+ }
705
+ }
706
+
707
+ function hookContext<TInput, TFacts, TResult>(
708
+ context: OperationContext<TInput, TFacts> & {
709
+ readonly result: DeepReadonly<TResult>;
710
+ },
711
+ legacy?: { body?: unknown; params?: unknown; query?: unknown },
712
+ ): KanbanApiResultContext<TInput, TFacts, TResult>;
713
+ function hookContext<TInput, TFacts>(
714
+ context: OperationContext<TInput, TFacts>,
715
+ legacy?: { body?: unknown; params?: unknown; query?: unknown },
716
+ ): KanbanApiContext<TInput, TFacts>;
717
+ function hookContext<TInput, TFacts, TResult>(
718
+ context:
719
+ | OperationContext<TInput, TFacts>
720
+ | (OperationContext<TInput, TFacts> & {
721
+ readonly result: DeepReadonly<TResult>;
722
+ }),
723
+ legacy: { body?: unknown; params?: unknown; query?: unknown } = {},
724
+ ):
725
+ | KanbanApiContext<TInput, TFacts>
726
+ | KanbanApiResultContext<TInput, TFacts, TResult> {
727
+ return Object.freeze({
728
+ ...context,
729
+ ...(context.request
730
+ ? { request: context.request, headers: context.request.headers }
731
+ : {}),
732
+ ...legacy,
733
+ });
734
+ }
735
+
736
+ async function notifyBoardError<TInput, TFacts>(
737
+ hook:
738
+ | ((
739
+ error: Error,
740
+ context: KanbanApiContext<TInput, TFacts>,
741
+ ) => Promise<void> | void)
742
+ | undefined,
743
+ error: unknown,
744
+ context: OperationContext<TInput, TFacts>,
745
+ legacy: { body?: unknown; params?: unknown; query?: unknown } = {},
746
+ ) {
747
+ const normalized = normalizeError(error, "Kanban board operation failed");
748
+ const lifecycle = hookContext(context, legacy);
749
+ try {
750
+ await hook?.(normalized, lifecycle);
751
+ } catch {
752
+ // Error hooks are observational and must never replace the operation error.
753
+ }
754
+ }
755
+
756
+ function boardFacts(snapshot: BoardSnapshot) {
757
+ return {
758
+ boardId: snapshot.id,
759
+ ...(snapshot.ownerId ? { ownerId: snapshot.ownerId } : {}),
760
+ ...(snapshot.organizationId
761
+ ? { organizationId: snapshot.organizationId }
762
+ : {}),
763
+ };
764
+ }
765
+
766
+ function taskFacts(snapshot: TaskAuthorizationSnapshot) {
767
+ return {
768
+ ...boardFacts(snapshot.board),
769
+ columnId: snapshot.column.id,
770
+ taskId: snapshot.task.id,
771
+ ...(snapshot.task.assigneeId
772
+ ? { assigneeId: snapshot.task.assigneeId }
773
+ : {}),
774
+ isArchived: snapshot.task.isArchived,
775
+ };
776
+ }
777
+
778
+ async function loadColumnAuthorization(
779
+ adapter: Pick<Adapter, "findOne">,
780
+ columnId: string,
781
+ ): Promise<ColumnAuthorizationSnapshot> {
782
+ const column = await findColumn(adapter, columnId);
783
+ if (!column) throw columnNotFoundError();
784
+ const board = await findBoard(adapter, column.boardId);
785
+ if (!board) throw boardNotFoundError();
786
+ return { board: boardSnapshot(board), column: columnSnapshot(column) };
787
+ }
788
+
789
+ async function loadTaskAuthorization(
790
+ adapter: Pick<Adapter, "findOne">,
791
+ taskId: string,
792
+ ): Promise<TaskAuthorizationSnapshot> {
793
+ const task = await findTask(adapter, taskId);
794
+ if (!task) throw taskNotFoundError();
795
+ const parent = await loadColumnAuthorization(adapter, task.columnId);
796
+ return { ...parent, task: taskSnapshot(task) };
797
+ }
798
+
799
+ function sameIdSet(left: readonly string[], right: readonly string[]) {
800
+ return (
801
+ left.length === right.length &&
802
+ new Set(left).size === left.length &&
803
+ left.every((id) => right.includes(id))
804
+ );
805
+ }
806
+
807
+ function operationTask(task: Task) {
808
+ const serialized = serializeTask(task);
809
+ return { ...serialized };
810
+ }
811
+
812
+ function operationColumn(column: BoardWithColumns["columns"][number]) {
813
+ return {
814
+ ...serializeBareColumn(column),
815
+ tasks: column.tasks.map(operationTask),
816
+ };
817
+ }
818
+
819
+ function operationBoard(board: BoardWithColumns) {
820
+ return {
821
+ ...serializeBareBoard(board),
822
+ columns: board.columns.map(operationColumn),
823
+ };
824
+ }
825
+
826
+ function boardSummary(board: Parameters<typeof serializeBoardSummary>[0]) {
827
+ return serializeBoardSummary(board);
828
+ }
829
+
830
+ function serializeBareColumn(column: Column) {
831
+ return {
832
+ ...column,
833
+ createdAt: column.createdAt.toISOString(),
834
+ updatedAt: column.updatedAt.toISOString(),
835
+ };
836
+ }
837
+
838
+ function serializeBareBoard(board: Board) {
839
+ return {
840
+ ...board,
841
+ createdAt: board.createdAt.toISOString(),
842
+ updatedAt: board.updatedAt.toISOString(),
843
+ };
844
+ }
845
+
846
+ async function verifyBoard(
847
+ adapter: Pick<Adapter, "findOne">,
848
+ expected: BoardSnapshot,
849
+ ) {
850
+ if (!sameBoard(await findBoard(adapter, expected.id), expected)) {
851
+ throw staleStateError();
852
+ }
853
+ }
854
+
855
+ async function verifyColumnAuthorization(
856
+ adapter: Pick<Adapter, "findOne">,
857
+ expected: ColumnAuthorizationSnapshot,
858
+ ) {
859
+ await verifyBoard(adapter, expected.board);
860
+ if (
861
+ !sameColumn(await findColumn(adapter, expected.column.id), expected.column)
862
+ ) {
863
+ throw staleStateError();
864
+ }
865
+ }
866
+
867
+ async function verifyTaskAuthorization(
868
+ adapter: Pick<Adapter, "findOne">,
869
+ expected: TaskAuthorizationSnapshot,
870
+ ) {
871
+ await verifyColumnAuthorization(adapter, expected);
872
+ if (!sameTask(await findTask(adapter, expected.task.id), expected.task)) {
873
+ throw staleStateError();
874
+ }
875
+ }
876
+
877
+ async function loadMoveSnapshot(
878
+ adapter: Pick<Adapter, "findOne" | "findMany">,
879
+ primary: TaskAuthorizationSnapshot,
880
+ targetColumnId: string,
881
+ targetOrder: number,
882
+ ): Promise<TaskMoveSnapshot> {
883
+ await verifyTaskAuthorization(adapter, primary);
884
+ const targetColumn = await findColumn(adapter, targetColumnId);
885
+ if (!targetColumn) throw columnNotFoundError();
886
+ if (targetColumn.boardId !== primary.board.id) {
887
+ throw invalidTargetColumnError();
888
+ }
889
+ const [sourceTasks, targetTasks] = await Promise.all([
890
+ listTasks(adapter, primary.column.id),
891
+ targetColumn.id === primary.column.id
892
+ ? Promise.resolve<Task[]>([])
893
+ : listTasks(adapter, targetColumn.id),
894
+ ]);
895
+ return {
896
+ ...primary,
897
+ targetColumn: columnSnapshot(targetColumn),
898
+ sourceTasks: sourceTasks.map(taskSnapshot),
899
+ targetTasks:
900
+ targetColumn.id === primary.column.id
901
+ ? sourceTasks.map(taskSnapshot)
902
+ : targetTasks.map(taskSnapshot),
903
+ targetOrder,
904
+ };
905
+ }
906
+
907
+ async function verifyMoveSnapshot(
908
+ adapter: Pick<Adapter, "findOne" | "findMany">,
909
+ expected: TaskMoveSnapshot,
910
+ ) {
911
+ await verifyTaskAuthorization(adapter, expected);
912
+ if (
913
+ !sameColumn(
914
+ await findColumn(adapter, expected.targetColumn.id),
915
+ expected.targetColumn,
916
+ )
917
+ ) {
918
+ throw staleStateError();
919
+ }
920
+ const sourceTasks = (await listTasks(adapter, expected.column.id)).map(
921
+ taskSnapshot,
922
+ );
923
+ const targetTasks =
924
+ expected.targetColumn.id === expected.column.id
925
+ ? sourceTasks
926
+ : (await listTasks(adapter, expected.targetColumn.id)).map(taskSnapshot);
927
+ if (
928
+ !sameSnapshotSet(sourceTasks, expected.sourceTasks, sameTaskSnapshots) ||
929
+ !sameSnapshotSet(targetTasks, expected.targetTasks, sameTaskSnapshots)
930
+ ) {
931
+ throw staleStateError();
932
+ }
933
+ }
934
+
935
+ async function claimColumnAuthorization(
936
+ adapter: Pick<Adapter, "updateMany">,
937
+ snapshot: ColumnAuthorizationSnapshot,
938
+ ) {
939
+ const boardClaimedAt = await claimSnapshot(
940
+ adapter,
941
+ "kanbanBoard",
942
+ snapshot.board,
943
+ );
944
+ const columnClaimedAt = await claimSnapshot(
945
+ adapter,
946
+ "kanbanColumn",
947
+ snapshot.column,
948
+ );
949
+ return { boardClaimedAt, columnClaimedAt };
950
+ }
951
+
952
+ async function restoreColumnAuthorization(
953
+ adapter: Pick<Adapter, "updateMany">,
954
+ snapshot: ColumnAuthorizationSnapshot,
955
+ claims: { boardClaimedAt: Date; columnClaimedAt: Date },
956
+ ) {
957
+ await restoreSnapshot(
958
+ adapter,
959
+ "kanbanColumn",
960
+ snapshot.column,
961
+ claims.columnClaimedAt,
962
+ );
963
+ await restoreSnapshot(
964
+ adapter,
965
+ "kanbanBoard",
966
+ snapshot.board,
967
+ claims.boardClaimedAt,
968
+ );
969
+ }
970
+
971
+ async function claimMoveSnapshot(
972
+ adapter: Pick<Adapter, "updateMany">,
973
+ snapshot: TaskMoveSnapshot,
974
+ ) {
975
+ const boardClaimedAt = await claimSnapshot(
976
+ adapter,
977
+ "kanbanBoard",
978
+ snapshot.board,
979
+ );
980
+ const sourceColumnClaimedAt = await claimSnapshot(
981
+ adapter,
982
+ "kanbanColumn",
983
+ snapshot.column,
984
+ );
985
+ const targetColumnClaimedAt =
986
+ snapshot.targetColumn.id === snapshot.column.id
987
+ ? sourceColumnClaimedAt
988
+ : await claimSnapshot(adapter, "kanbanColumn", snapshot.targetColumn);
989
+ const taskClaims = new Map<string, Date>();
990
+ const allTasks = new Map<string, TaskSnapshot>();
991
+ for (const task of [...snapshot.sourceTasks, ...snapshot.targetTasks]) {
992
+ allTasks.set(task.id, task);
993
+ }
994
+ for (const task of allTasks.values()) {
995
+ taskClaims.set(task.id, await claimSnapshot(adapter, "kanbanTask", task));
996
+ }
997
+ return {
998
+ boardClaimedAt,
999
+ sourceColumnClaimedAt,
1000
+ targetColumnClaimedAt,
1001
+ taskClaims,
1002
+ };
1003
+ }
1004
+
1005
+ async function applyTaskMove(
1006
+ adapter: Pick<Adapter, "updateMany" | "findOne">,
1007
+ snapshot: TaskMoveSnapshot,
1008
+ claims: Awaited<ReturnType<typeof claimMoveSnapshot>>,
1009
+ extraUpdate: Readonly<Record<string, unknown>>,
1010
+ ) {
1011
+ const sourceTasks = snapshot.sourceTasks.filter(
1012
+ (task) => task.id !== snapshot.task.id,
1013
+ );
1014
+ const targetBase =
1015
+ snapshot.targetColumn.id === snapshot.column.id
1016
+ ? sourceTasks
1017
+ : snapshot.targetTasks.filter((task) => task.id !== snapshot.task.id);
1018
+ const targetOrder = Math.min(snapshot.targetOrder, targetBase.length);
1019
+ const orderedTarget = [...targetBase];
1020
+ orderedTarget.splice(targetOrder, 0, {
1021
+ ...snapshot.task,
1022
+ columnId: snapshot.targetColumn.id,
1023
+ });
1024
+
1025
+ const finalRows = new Map<
1026
+ string,
1027
+ { snapshot: TaskSnapshot; columnId: string; order: number }
1028
+ >();
1029
+ if (snapshot.targetColumn.id !== snapshot.column.id) {
1030
+ sourceTasks.forEach((task, order) => {
1031
+ finalRows.set(task.id, {
1032
+ snapshot: task,
1033
+ columnId: task.columnId,
1034
+ order,
1035
+ });
1036
+ });
1037
+ }
1038
+ orderedTarget.forEach((task, order) => {
1039
+ const original =
1040
+ task.id === snapshot.task.id
1041
+ ? snapshot.task
1042
+ : (snapshot.targetTasks.find((candidate) => candidate.id === task.id) ??
1043
+ task);
1044
+ finalRows.set(task.id, {
1045
+ snapshot: original,
1046
+ columnId: snapshot.targetColumn.id,
1047
+ order,
1048
+ });
1049
+ });
1050
+
1051
+ let moved: Task | null = null;
1052
+ for (const row of finalRows.values()) {
1053
+ const claimedAt = claims.taskClaims.get(row.snapshot.id);
1054
+ if (!claimedAt) throw staleStateError();
1055
+ const matched = await adapter.updateMany({
1056
+ model: "kanbanTask",
1057
+ where: taskWhere({ ...row.snapshot, updatedAt: claimedAt }),
1058
+ update: {
1059
+ ...(row.snapshot.id === snapshot.task.id ? extraUpdate : {}),
1060
+ columnId: row.columnId,
1061
+ order: row.order,
1062
+ updatedAt: nextSnapshotDate(claimedAt),
1063
+ },
1064
+ });
1065
+ if (!didAffectRow(matched, row.snapshot.id)) throw staleStateError();
1066
+ if (row.snapshot.id === snapshot.task.id) {
1067
+ moved = await findTask(adapter, row.snapshot.id);
1068
+ }
1069
+ }
1070
+ if (!moved) throw taskNotFoundError();
1071
+ await restoreSnapshot(
1072
+ adapter,
1073
+ "kanbanColumn",
1074
+ snapshot.column,
1075
+ claims.sourceColumnClaimedAt,
1076
+ );
1077
+ if (snapshot.targetColumn.id !== snapshot.column.id) {
1078
+ await restoreSnapshot(
1079
+ adapter,
1080
+ "kanbanColumn",
1081
+ snapshot.targetColumn,
1082
+ claims.targetColumnClaimedAt,
1083
+ );
1084
+ }
1085
+ await restoreSnapshot(
1086
+ adapter,
1087
+ "kanbanBoard",
1088
+ snapshot.board,
1089
+ claims.boardClaimedAt,
1090
+ );
1091
+ return moved;
1092
+ }
1093
+
1094
+ function sameOptionalBoard(
1095
+ board: Board | null,
1096
+ expected: BoardSnapshot | null,
1097
+ ) {
1098
+ return expected === null ? board === null : sameBoard(board, expected);
1099
+ }
1100
+
1101
+ /** Create the complete Kanban operation inventory for every server transport. */
1102
+ export function createKanbanOperations(
1103
+ sourceAdapter: Adapter,
1104
+ hooks?: KanbanBackendHooks,
1105
+ ): KanbanOperations {
1106
+ const adapter = serializeMemoryOperations(sourceAdapter);
1107
+ const boardSnapshots = new WeakMap<object, BoardSnapshot | null>();
1108
+ const columnSnapshots = new WeakMap<object, ColumnAuthorizationSnapshot>();
1109
+ const taskSnapshots = new WeakMap<object, TaskAuthorizationSnapshot>();
1110
+ const columnReorderSnapshots = new WeakMap<object, ColumnReorderSnapshot>();
1111
+ const taskReorderSnapshots = new WeakMap<object, TaskReorderSnapshot>();
1112
+ const taskMoveSnapshots = new WeakMap<object, TaskMoveSnapshot>();
1113
+ const updateTaskMoveSnapshots = new WeakMap<object, TaskMoveSnapshot>();
1114
+ const reorderedColumns = new WeakMap<object, readonly SerializedColumn[]>();
1115
+ const reorderedTasks = new WeakMap<object, readonly SerializedTask[]>();
1116
+
1117
+ const listBoards = defineOperation({
1118
+ input: BoardListQuerySchema,
1119
+ permission: kanbanPermissions.board.read,
1120
+ facts: () => ({ scope: "collection" as const }),
1121
+ before: async (context) => {
1122
+ await runDomainHook(
1123
+ () =>
1124
+ hooks?.onBeforeListBoards?.(
1125
+ { ...context.input },
1126
+ hookContext(context, { query: context.input }),
1127
+ ),
1128
+ "LIST_BOARDS_REJECTED",
1129
+ );
1130
+ },
1131
+ execute: async ({ input }) => {
1132
+ const result = await getBoardSummaries(adapter, input);
1133
+ return {
1134
+ items: result.items.map(boardSummary),
1135
+ total: result.total,
1136
+ ...(result.limit !== undefined ? { limit: result.limit } : {}),
1137
+ ...(result.offset !== undefined ? { offset: result.offset } : {}),
1138
+ };
1139
+ },
1140
+ after: async (context) => {
1141
+ await hooks?.onAfterListBoards?.(
1142
+ context.result.items,
1143
+ { ...context.input },
1144
+ hookContext(context, { query: context.input }),
1145
+ );
1146
+ },
1147
+ onError: ({ error, ...context }) =>
1148
+ notifyBoardError(hooks?.onErrorListBoards, error, context, {
1149
+ query: context.input,
1150
+ }),
1151
+ });
1152
+
1153
+ const getBoard = defineOperation({
1154
+ input: BoardIdOperationInputSchema,
1155
+ permission: kanbanPermissions.board.read,
1156
+ facts: async ({ input }) => {
1157
+ const board = await findBoard(adapter, input.id);
1158
+ const snapshot = board ? boardSnapshot(board) : null;
1159
+ boardSnapshots.set(input as object, snapshot);
1160
+ return {
1161
+ scope: "record" as const,
1162
+ boardId: input.id,
1163
+ exists: snapshot !== null,
1164
+ ...(snapshot ? boardFacts(snapshot) : {}),
1165
+ } satisfies BoardReadFacts;
1166
+ },
1167
+ before: async (context) => {
1168
+ const expected = boardSnapshots.get(context.input as object) ?? null;
1169
+ if (
1170
+ !sameOptionalBoard(await findBoard(adapter, context.input.id), expected)
1171
+ ) {
1172
+ throw staleStateError();
1173
+ }
1174
+ await runDomainHook(
1175
+ () =>
1176
+ hooks?.onBeforeGetBoard?.(
1177
+ context.input.id,
1178
+ hookContext(context, { params: { id: context.input.id } }),
1179
+ ),
1180
+ "READ_BOARD_REJECTED",
1181
+ );
1182
+ },
1183
+ execute: async (context) => {
1184
+ const expected = boardSnapshots.get(context.input as object) ?? null;
1185
+ const board = await getBoardById(adapter, context.input.id);
1186
+ if (!sameOptionalBoard(board, expected)) {
1187
+ throw staleStateError();
1188
+ }
1189
+ if (!board) throw boardNotFoundError();
1190
+ return operationBoard(board);
1191
+ },
1192
+ after: async (context) => {
1193
+ await hooks?.onAfterGetBoard?.(
1194
+ context.result,
1195
+ hookContext<
1196
+ z.output<typeof BoardIdOperationInputSchema>,
1197
+ BoardReadFacts,
1198
+ SerializedBoardWithColumns
1199
+ >(context, { params: { id: context.input.id } }),
1200
+ );
1201
+ },
1202
+ onError: ({ error, ...context }) =>
1203
+ notifyBoardError(hooks?.onErrorGetBoard, error, context, {
1204
+ params: { id: context.input.id },
1205
+ }),
1206
+ });
1207
+
1208
+ const createBoard = defineOperation({
1209
+ input: createBoardSchema,
1210
+ permission: kanbanPermissions.board.create,
1211
+ facts: () => undefined,
1212
+ execute: async (context) => {
1213
+ const data = { ...context.input };
1214
+ requireAtomicTransactions(adapter);
1215
+ return adapter.transaction(async (tx) => {
1216
+ await runDomainHook(
1217
+ () =>
1218
+ hooks?.onBeforeCreateBoard?.(
1219
+ data,
1220
+ hookContext(context, { body: data }),
1221
+ ),
1222
+ "CREATE_BOARD_REJECTED",
1223
+ );
1224
+ const input = {
1225
+ ...data,
1226
+ slug: sanitizeSlug(data.slug || data.name),
1227
+ ...(context.identity ? { ownerId: context.identity.id } : {}),
1228
+ };
1229
+ const now = new Date();
1230
+ const board = await tx.create<Board>({
1231
+ model: "kanbanBoard",
1232
+ data: {
1233
+ ...input,
1234
+ createdAt: now,
1235
+ updatedAt: now,
1236
+ },
1237
+ });
1238
+ const columns: Column[] = [];
1239
+ for (const [order, title] of [
1240
+ "To Do",
1241
+ "In Progress",
1242
+ "Done",
1243
+ ].entries()) {
1244
+ columns.push(
1245
+ await tx.create<Column>({
1246
+ model: "kanbanColumn",
1247
+ data: {
1248
+ title,
1249
+ boardId: board.id,
1250
+ order,
1251
+ createdAt: now,
1252
+ updatedAt: now,
1253
+ },
1254
+ }),
1255
+ );
1256
+ }
1257
+ return operationBoard({
1258
+ ...board,
1259
+ columns: columns.map((column) => ({ ...column, tasks: [] })),
1260
+ });
1261
+ });
1262
+ },
1263
+ after: async (context) => {
1264
+ await hooks?.onAfterCreateBoard?.(
1265
+ context.result,
1266
+ hookContext<
1267
+ z.output<typeof createBoardSchema>,
1268
+ undefined,
1269
+ SerializedBoardWithColumns
1270
+ >(context, { body: context.input }),
1271
+ );
1272
+ },
1273
+ onError: ({ error, ...context }) => {
1274
+ markMemoryRollback(adapter, error);
1275
+ return notifyBoardError(hooks?.onErrorCreateBoard, error, context, {
1276
+ body: context.input,
1277
+ });
1278
+ },
1279
+ });
1280
+
1281
+ const updateBoard = defineOperation({
1282
+ input: UpdateBoardOperationInputSchema,
1283
+ permission: kanbanPermissions.board.update,
1284
+ facts: async ({ input }) => {
1285
+ const board = await findBoard(adapter, input.id);
1286
+ if (!board) throw boardNotFoundError();
1287
+ const snapshot = boardSnapshot(board);
1288
+ boardSnapshots.set(input as object, snapshot);
1289
+ return boardFacts(snapshot) satisfies BoardUpdateFacts;
1290
+ },
1291
+ execute: async (context) => {
1292
+ const snapshot = boardSnapshots.get(context.input as object);
1293
+ if (!snapshot) throw staleStateError();
1294
+ requireAtomicTransactions(adapter);
1295
+ const data = {
1296
+ ...context.input.data,
1297
+ ...(context.input.data.slug
1298
+ ? { slug: sanitizeSlug(context.input.data.slug) }
1299
+ : {}),
1300
+ };
1301
+ return adapter.transaction(async (tx) => {
1302
+ await verifyBoard(tx, snapshot);
1303
+ const claimedAt = await claimSnapshot(tx, "kanbanBoard", snapshot);
1304
+ await runDomainHook(
1305
+ () =>
1306
+ hooks?.onBeforeUpdateBoard?.(
1307
+ context.input.id,
1308
+ { ...data, id: context.input.id },
1309
+ hookContext(context, {
1310
+ body: data,
1311
+ params: { id: context.input.id },
1312
+ }),
1313
+ ),
1314
+ "UPDATE_BOARD_REJECTED",
1315
+ );
1316
+ const matched = await tx.updateMany({
1317
+ model: "kanbanBoard",
1318
+ where: boardWhere({ ...snapshot, updatedAt: claimedAt }),
1319
+ update: {
1320
+ ...data,
1321
+ updatedAt: nextSnapshotDate(claimedAt),
1322
+ },
1323
+ });
1324
+ if (!didAffectRow(matched, snapshot.id)) throw staleStateError();
1325
+ const updated = await findBoard(tx, snapshot.id);
1326
+ if (!updated) throw boardNotFoundError();
1327
+ return serializeBareBoard(updated);
1328
+ });
1329
+ },
1330
+ after: async (context) => {
1331
+ await hooks?.onAfterUpdateBoard?.(
1332
+ context.result,
1333
+ hookContext(context, {
1334
+ body: context.input.data,
1335
+ params: { id: context.input.id },
1336
+ }),
1337
+ );
1338
+ },
1339
+ onError: ({ error, ...context }) => {
1340
+ markMemoryRollback(adapter, error);
1341
+ return notifyBoardError(hooks?.onErrorUpdateBoard, error, context, {
1342
+ body: context.input.data,
1343
+ params: { id: context.input.id },
1344
+ });
1345
+ },
1346
+ });
1347
+
1348
+ const deleteBoard = defineOperation({
1349
+ input: BoardIdOperationInputSchema,
1350
+ permission: kanbanPermissions.board.delete,
1351
+ facts: async ({ input }) => {
1352
+ const board = await findBoard(adapter, input.id);
1353
+ if (!board) throw boardNotFoundError();
1354
+ const snapshot = boardSnapshot(board);
1355
+ boardSnapshots.set(input as object, snapshot);
1356
+ return boardFacts(snapshot) satisfies BoardDeleteFacts;
1357
+ },
1358
+ execute: async (context) => {
1359
+ const snapshot = boardSnapshots.get(context.input as object);
1360
+ if (!snapshot) throw staleStateError();
1361
+ requireAtomicTransactions(adapter);
1362
+ return adapter.transaction(async (tx) => {
1363
+ await verifyBoard(tx, snapshot);
1364
+ const claimedAt = await claimSnapshot(tx, "kanbanBoard", snapshot);
1365
+ await runDomainHook(
1366
+ () =>
1367
+ hooks?.onBeforeDeleteBoard?.(
1368
+ context.input.id,
1369
+ hookContext(context, {
1370
+ params: { id: context.input.id },
1371
+ }),
1372
+ ),
1373
+ "DELETE_BOARD_REJECTED",
1374
+ );
1375
+ const deleted = await tx.deleteMany({
1376
+ model: "kanbanBoard",
1377
+ where: boardWhere({ ...snapshot, updatedAt: claimedAt }),
1378
+ });
1379
+ if (!didAffectRow(deleted, snapshot.id)) throw staleStateError();
1380
+ return { success: true } as const;
1381
+ });
1382
+ },
1383
+ after: async (context) => {
1384
+ await hooks?.onAfterDeleteBoard?.(
1385
+ context.input.id,
1386
+ hookContext(context, { params: { id: context.input.id } }),
1387
+ );
1388
+ },
1389
+ onError: ({ error, ...context }) => {
1390
+ markMemoryRollback(adapter, error);
1391
+ return notifyBoardError(hooks?.onErrorDeleteBoard, error, context, {
1392
+ params: { id: context.input.id },
1393
+ });
1394
+ },
1395
+ });
1396
+
1397
+ const createColumn = defineOperation({
1398
+ input: createColumnSchema,
1399
+ permission: kanbanPermissions.column.create,
1400
+ facts: async ({ input }) => {
1401
+ const board = await findBoard(adapter, input.boardId);
1402
+ if (!board) throw boardNotFoundError();
1403
+ const snapshot = boardSnapshot(board);
1404
+ boardSnapshots.set(input as object, snapshot);
1405
+ return boardFacts(snapshot) satisfies ColumnCreateFacts;
1406
+ },
1407
+ execute: async (context) => {
1408
+ const board = boardSnapshots.get(context.input as object);
1409
+ if (!board) throw staleStateError();
1410
+ requireAtomicTransactions(adapter);
1411
+ return adapter.transaction(async (tx) => {
1412
+ await verifyBoard(tx, board);
1413
+ const boardClaimedAt = await claimSnapshot(tx, "kanbanBoard", board);
1414
+ await runDomainHook(
1415
+ () =>
1416
+ hooks?.onBeforeCreateColumn?.(
1417
+ { ...context.input },
1418
+ hookContext(context, { body: context.input }),
1419
+ ),
1420
+ "CREATE_COLUMN_REJECTED",
1421
+ );
1422
+ const existing = await listColumns(tx, board.id);
1423
+ const nextOrder =
1424
+ existing.length === 0
1425
+ ? 0
1426
+ : Math.max(...existing.map((column) => column.order)) + 1;
1427
+ const now = new Date();
1428
+ const column = await tx.create<Column>({
1429
+ model: "kanbanColumn",
1430
+ data: {
1431
+ ...context.input,
1432
+ order: context.input.order ?? nextOrder,
1433
+ createdAt: now,
1434
+ updatedAt: now,
1435
+ },
1436
+ });
1437
+ await restoreSnapshot(tx, "kanbanBoard", board, boardClaimedAt);
1438
+ return serializeBareColumn(column);
1439
+ });
1440
+ },
1441
+ after: async (context) => {
1442
+ await hooks?.onAfterCreateColumn?.(
1443
+ context.result,
1444
+ hookContext(context, { body: context.input }),
1445
+ );
1446
+ },
1447
+ onError: ({ error }) => markMemoryRollback(adapter, error),
1448
+ });
1449
+
1450
+ const updateColumn = defineOperation({
1451
+ input: UpdateColumnOperationInputSchema,
1452
+ permission: kanbanPermissions.column.update,
1453
+ facts: async ({ input }) => {
1454
+ const snapshot = await loadColumnAuthorization(adapter, input.id);
1455
+ columnSnapshots.set(input as object, snapshot);
1456
+ return {
1457
+ ...boardFacts(snapshot.board),
1458
+ columnId: snapshot.column.id,
1459
+ } satisfies ColumnUpdateFacts;
1460
+ },
1461
+ additionalPermissions: ({ input }) => {
1462
+ if (input.data.order === undefined) return [];
1463
+ const snapshot = columnSnapshots.get(input as object);
1464
+ if (!snapshot) throw staleStateError();
1465
+ return [kanbanPermissions.column.reorder(boardFacts(snapshot.board))];
1466
+ },
1467
+ execute: async (context) => {
1468
+ const snapshot = columnSnapshots.get(context.input as object);
1469
+ if (!snapshot) throw staleStateError();
1470
+ if (
1471
+ context.input.data.boardId &&
1472
+ context.input.data.boardId !== snapshot.board.id
1473
+ ) {
1474
+ throw invalidTargetColumnError();
1475
+ }
1476
+ requireAtomicTransactions(adapter);
1477
+ return adapter.transaction(async (tx) => {
1478
+ await verifyColumnAuthorization(tx, snapshot);
1479
+ const claims = await claimColumnAuthorization(tx, snapshot);
1480
+ await runDomainHook(
1481
+ () =>
1482
+ hooks?.onBeforeUpdateColumn?.(
1483
+ context.input.id,
1484
+ { ...context.input.data, id: context.input.id },
1485
+ hookContext(context, {
1486
+ body: context.input.data,
1487
+ params: { id: context.input.id },
1488
+ }),
1489
+ ),
1490
+ "UPDATE_COLUMN_REJECTED",
1491
+ );
1492
+ const matched = await tx.updateMany({
1493
+ model: "kanbanColumn",
1494
+ where: columnWhere({
1495
+ ...snapshot.column,
1496
+ updatedAt: claims.columnClaimedAt,
1497
+ }),
1498
+ update: {
1499
+ ...context.input.data,
1500
+ boardId: snapshot.board.id,
1501
+ updatedAt: nextSnapshotDate(claims.columnClaimedAt),
1502
+ },
1503
+ });
1504
+ if (!didAffectRow(matched, snapshot.column.id)) {
1505
+ throw staleStateError();
1506
+ }
1507
+ await restoreSnapshot(
1508
+ tx,
1509
+ "kanbanBoard",
1510
+ snapshot.board,
1511
+ claims.boardClaimedAt,
1512
+ );
1513
+ const updated = await findColumn(tx, snapshot.column.id);
1514
+ if (!updated) throw columnNotFoundError();
1515
+ return serializeBareColumn(updated);
1516
+ });
1517
+ },
1518
+ after: async (context) => {
1519
+ await hooks?.onAfterUpdateColumn?.(
1520
+ context.result,
1521
+ hookContext(context, {
1522
+ body: context.input.data,
1523
+ params: { id: context.input.id },
1524
+ }),
1525
+ );
1526
+ },
1527
+ onError: ({ error }) => markMemoryRollback(adapter, error),
1528
+ });
1529
+
1530
+ const deleteColumn = defineOperation({
1531
+ input: ColumnIdOperationInputSchema,
1532
+ permission: kanbanPermissions.column.delete,
1533
+ facts: async ({ input }) => {
1534
+ const snapshot = await loadColumnAuthorization(adapter, input.id);
1535
+ columnSnapshots.set(input as object, snapshot);
1536
+ return {
1537
+ ...boardFacts(snapshot.board),
1538
+ columnId: snapshot.column.id,
1539
+ } satisfies ColumnDeleteFacts;
1540
+ },
1541
+ execute: async (context) => {
1542
+ const snapshot = columnSnapshots.get(context.input as object);
1543
+ if (!snapshot) throw staleStateError();
1544
+ requireAtomicTransactions(adapter);
1545
+ return adapter.transaction(async (tx) => {
1546
+ await verifyColumnAuthorization(tx, snapshot);
1547
+ const claims = await claimColumnAuthorization(tx, snapshot);
1548
+ await runDomainHook(
1549
+ () =>
1550
+ hooks?.onBeforeDeleteColumn?.(
1551
+ context.input.id,
1552
+ hookContext(context, {
1553
+ params: { id: context.input.id },
1554
+ }),
1555
+ ),
1556
+ "DELETE_COLUMN_REJECTED",
1557
+ );
1558
+ const deleted = await tx.deleteMany({
1559
+ model: "kanbanColumn",
1560
+ where: columnWhere({
1561
+ ...snapshot.column,
1562
+ updatedAt: claims.columnClaimedAt,
1563
+ }),
1564
+ });
1565
+ if (!didAffectRow(deleted, snapshot.column.id)) {
1566
+ throw staleStateError();
1567
+ }
1568
+ await restoreSnapshot(
1569
+ tx,
1570
+ "kanbanBoard",
1571
+ snapshot.board,
1572
+ claims.boardClaimedAt,
1573
+ );
1574
+ return { success: true } as const;
1575
+ });
1576
+ },
1577
+ after: async (context) => {
1578
+ await hooks?.onAfterDeleteColumn?.(
1579
+ context.input.id,
1580
+ hookContext(context, { params: { id: context.input.id } }),
1581
+ );
1582
+ },
1583
+ onError: ({ error }) => markMemoryRollback(adapter, error),
1584
+ });
1585
+
1586
+ const reorderColumns = defineOperation({
1587
+ input: reorderColumnsSchema,
1588
+ permission: kanbanPermissions.column.reorder,
1589
+ facts: async ({ input }) => {
1590
+ const board = await findBoard(adapter, input.boardId);
1591
+ if (!board) throw boardNotFoundError();
1592
+ const columns = await listColumns(adapter, board.id);
1593
+ if (
1594
+ !sameIdSet(
1595
+ input.columnIds,
1596
+ columns.map((column) => column.id),
1597
+ )
1598
+ ) {
1599
+ throw invalidColumnSetError();
1600
+ }
1601
+ const snapshot = {
1602
+ board: boardSnapshot(board),
1603
+ columns: columns.map(columnSnapshot),
1604
+ };
1605
+ columnReorderSnapshots.set(input as object, snapshot);
1606
+ return boardFacts(snapshot.board) satisfies ColumnReorderFacts;
1607
+ },
1608
+ execute: async (context) => {
1609
+ const snapshot = columnReorderSnapshots.get(context.input as object);
1610
+ if (!snapshot) throw staleStateError();
1611
+ requireAtomicTransactions(adapter);
1612
+ return adapter.transaction(async (tx) => {
1613
+ await verifyBoard(tx, snapshot.board);
1614
+ const current = (await listColumns(tx, snapshot.board.id)).map(
1615
+ columnSnapshot,
1616
+ );
1617
+ if (!sameSnapshotSet(current, snapshot.columns, sameColumnSnapshots)) {
1618
+ throw staleStateError();
1619
+ }
1620
+ const boardClaimedAt = await claimSnapshot(
1621
+ tx,
1622
+ "kanbanBoard",
1623
+ snapshot.board,
1624
+ );
1625
+ const claims = new Map<string, Date>();
1626
+ for (const column of snapshot.columns) {
1627
+ claims.set(
1628
+ column.id,
1629
+ await claimSnapshot(tx, "kanbanColumn", column),
1630
+ );
1631
+ }
1632
+ for (const [order, columnId] of context.input.columnIds.entries()) {
1633
+ await runDomainHook(
1634
+ () =>
1635
+ hooks?.onBeforeUpdateColumn?.(
1636
+ columnId,
1637
+ { id: columnId, order },
1638
+ hookContext(context, { body: context.input }),
1639
+ ),
1640
+ "REORDER_COLUMNS_REJECTED",
1641
+ );
1642
+ }
1643
+ const updated: SerializedColumn[] = [];
1644
+ for (const [order, columnId] of context.input.columnIds.entries()) {
1645
+ const original = snapshot.columns.find(
1646
+ (column) => column.id === columnId,
1647
+ );
1648
+ const claimedAt = claims.get(columnId);
1649
+ if (!original || !claimedAt) throw staleStateError();
1650
+ const matched = await tx.updateMany({
1651
+ model: "kanbanColumn",
1652
+ where: columnWhere({ ...original, updatedAt: claimedAt }),
1653
+ update: { order, updatedAt: nextSnapshotDate(claimedAt) },
1654
+ });
1655
+ if (!didAffectRow(matched, columnId)) throw staleStateError();
1656
+ const column = await findColumn(tx, columnId);
1657
+ if (!column) throw columnNotFoundError();
1658
+ updated.push(serializeBareColumn(column));
1659
+ }
1660
+ await restoreSnapshot(
1661
+ tx,
1662
+ "kanbanBoard",
1663
+ snapshot.board,
1664
+ boardClaimedAt,
1665
+ );
1666
+ const result = { success: true } as const;
1667
+ reorderedColumns.set(result, updated);
1668
+ return result;
1669
+ });
1670
+ },
1671
+ after: async (context) => {
1672
+ for (const column of reorderedColumns.get(context.result) ?? []) {
1673
+ await hooks?.onAfterUpdateColumn?.(
1674
+ column,
1675
+ hookContext<
1676
+ z.output<typeof reorderColumnsSchema>,
1677
+ ColumnReorderFacts,
1678
+ { readonly success: true }
1679
+ >(context, { body: context.input }),
1680
+ );
1681
+ }
1682
+ },
1683
+ onError: ({ error }) => markMemoryRollback(adapter, error),
1684
+ });
1685
+
1686
+ const createTask = defineOperation({
1687
+ input: createTaskSchema,
1688
+ permission: kanbanPermissions.task.create,
1689
+ facts: async ({ input }) => {
1690
+ const snapshot = await loadColumnAuthorization(adapter, input.columnId);
1691
+ columnSnapshots.set(input as object, snapshot);
1692
+ return {
1693
+ ...boardFacts(snapshot.board),
1694
+ columnId: snapshot.column.id,
1695
+ } satisfies TaskCreateFacts;
1696
+ },
1697
+ execute: async (context) => {
1698
+ const snapshot = columnSnapshots.get(context.input as object);
1699
+ if (!snapshot) throw staleStateError();
1700
+ requireAtomicTransactions(adapter);
1701
+ return adapter.transaction(async (tx) => {
1702
+ await verifyColumnAuthorization(tx, snapshot);
1703
+ const claims = await claimColumnAuthorization(tx, snapshot);
1704
+ await runDomainHook(
1705
+ () =>
1706
+ hooks?.onBeforeCreateTask?.(
1707
+ { ...context.input },
1708
+ hookContext(context, { body: context.input }),
1709
+ ),
1710
+ "CREATE_TASK_REJECTED",
1711
+ );
1712
+ const existing = await listTasks(tx, snapshot.column.id);
1713
+ const nextOrder =
1714
+ existing.length === 0
1715
+ ? 0
1716
+ : Math.max(...existing.map((task) => task.order)) + 1;
1717
+ const now = new Date();
1718
+ const { completedAt, ...persistenceInput } = context.input;
1719
+ const task = await tx.create<Task>({
1720
+ model: "kanbanTask",
1721
+ data: {
1722
+ ...persistenceInput,
1723
+ priority: context.input.priority ?? "MEDIUM",
1724
+ order: context.input.order ?? nextOrder,
1725
+ assigneeId: context.input.assigneeId ?? undefined,
1726
+ isArchived: context.input.isArchived ?? false,
1727
+ ...(completedAt ? { completedAt: new Date(completedAt) } : {}),
1728
+ createdAt: now,
1729
+ updatedAt: now,
1730
+ },
1731
+ });
1732
+ await restoreColumnAuthorization(tx, snapshot, claims);
1733
+ return operationTask(task);
1734
+ });
1735
+ },
1736
+ after: async (context) => {
1737
+ await hooks?.onAfterCreateTask?.(
1738
+ context.result,
1739
+ hookContext(context, { body: context.input }),
1740
+ );
1741
+ },
1742
+ onError: ({ error }) => markMemoryRollback(adapter, error),
1743
+ });
1744
+
1745
+ const updateTask = defineOperation({
1746
+ input: UpdateTaskOperationInputSchema,
1747
+ permission: kanbanPermissions.task.update,
1748
+ facts: async ({ input }) => {
1749
+ const snapshot = await loadTaskAuthorization(adapter, input.id);
1750
+ taskSnapshots.set(input as object, snapshot);
1751
+ return taskFacts(snapshot) satisfies TaskUpdateFacts;
1752
+ },
1753
+ additionalPermissions: async ({ input }) => {
1754
+ const snapshot = taskSnapshots.get(input as object);
1755
+ if (!snapshot) throw staleStateError();
1756
+ const targetColumnId = input.data.columnId ?? snapshot.column.id;
1757
+ const changesColumn = targetColumnId !== snapshot.column.id;
1758
+ const changesOrder = input.data.order !== undefined;
1759
+ if (!changesColumn && !changesOrder) return [];
1760
+ const moveSnapshot = await loadMoveSnapshot(
1761
+ adapter,
1762
+ snapshot,
1763
+ targetColumnId,
1764
+ input.data.order ?? Number.MAX_SAFE_INTEGER,
1765
+ );
1766
+ updateTaskMoveSnapshots.set(input as object, moveSnapshot);
1767
+ return [
1768
+ kanbanPermissions.task.move({
1769
+ ...taskFacts(moveSnapshot),
1770
+ targetColumnId: moveSnapshot.targetColumn.id,
1771
+ }),
1772
+ ];
1773
+ },
1774
+ execute: async (context) => {
1775
+ const snapshot = taskSnapshots.get(context.input as object);
1776
+ if (!snapshot) throw staleStateError();
1777
+ requireAtomicTransactions(adapter);
1778
+ const {
1779
+ columnId: _columnId,
1780
+ order: _order,
1781
+ completedAt,
1782
+ ...operationTaskData
1783
+ } = context.input.data;
1784
+ const taskData = {
1785
+ ...operationTaskData,
1786
+ ...(completedAt ? { completedAt: new Date(completedAt) } : {}),
1787
+ };
1788
+ const moveSnapshot = updateTaskMoveSnapshots.get(context.input as object);
1789
+ if (moveSnapshot) {
1790
+ return adapter.transaction(async (tx) => {
1791
+ await verifyMoveSnapshot(tx, moveSnapshot);
1792
+ const claims = await claimMoveSnapshot(tx, moveSnapshot);
1793
+ await runDomainHook(
1794
+ () =>
1795
+ hooks?.onBeforeUpdateTask?.(
1796
+ context.input.id,
1797
+ { ...context.input.data, id: context.input.id },
1798
+ hookContext(context, {
1799
+ body: context.input.data,
1800
+ params: { id: context.input.id },
1801
+ }),
1802
+ ),
1803
+ "UPDATE_TASK_REJECTED",
1804
+ );
1805
+ return operationTask(
1806
+ await applyTaskMove(tx, moveSnapshot, claims, taskData),
1807
+ );
1808
+ });
1809
+ }
1810
+ return adapter.transaction(async (tx) => {
1811
+ await verifyTaskAuthorization(tx, snapshot);
1812
+ const claims = await claimColumnAuthorization(tx, snapshot);
1813
+ const taskClaimedAt = await claimSnapshot(
1814
+ tx,
1815
+ "kanbanTask",
1816
+ snapshot.task,
1817
+ );
1818
+ await runDomainHook(
1819
+ () =>
1820
+ hooks?.onBeforeUpdateTask?.(
1821
+ context.input.id,
1822
+ { ...context.input.data, id: context.input.id },
1823
+ hookContext(context, {
1824
+ body: context.input.data,
1825
+ params: { id: context.input.id },
1826
+ }),
1827
+ ),
1828
+ "UPDATE_TASK_REJECTED",
1829
+ );
1830
+ const matched = await tx.updateMany({
1831
+ model: "kanbanTask",
1832
+ where: taskWhere({
1833
+ ...snapshot.task,
1834
+ updatedAt: taskClaimedAt,
1835
+ }),
1836
+ update: {
1837
+ ...taskData,
1838
+ updatedAt: nextSnapshotDate(taskClaimedAt),
1839
+ },
1840
+ });
1841
+ if (!didAffectRow(matched, snapshot.task.id)) {
1842
+ throw staleStateError();
1843
+ }
1844
+ await restoreColumnAuthorization(tx, snapshot, claims);
1845
+ const updated = await findTask(tx, snapshot.task.id);
1846
+ if (!updated) throw taskNotFoundError();
1847
+ return operationTask(updated);
1848
+ });
1849
+ },
1850
+ after: async (context) => {
1851
+ await hooks?.onAfterUpdateTask?.(
1852
+ context.result,
1853
+ hookContext(context, {
1854
+ body: context.input.data,
1855
+ params: { id: context.input.id },
1856
+ }),
1857
+ );
1858
+ },
1859
+ onError: ({ error }) => markMemoryRollback(adapter, error),
1860
+ });
1861
+
1862
+ const deleteTask = defineOperation({
1863
+ input: TaskIdOperationInputSchema,
1864
+ permission: kanbanPermissions.task.delete,
1865
+ facts: async ({ input }) => {
1866
+ const snapshot = await loadTaskAuthorization(adapter, input.id);
1867
+ taskSnapshots.set(input as object, snapshot);
1868
+ return taskFacts(snapshot) satisfies TaskDeleteFacts;
1869
+ },
1870
+ execute: async (context) => {
1871
+ const snapshot = taskSnapshots.get(context.input as object);
1872
+ if (!snapshot) throw staleStateError();
1873
+ requireAtomicTransactions(adapter);
1874
+ return adapter.transaction(async (tx) => {
1875
+ await verifyTaskAuthorization(tx, snapshot);
1876
+ const claims = await claimColumnAuthorization(tx, snapshot);
1877
+ const taskClaimedAt = await claimSnapshot(
1878
+ tx,
1879
+ "kanbanTask",
1880
+ snapshot.task,
1881
+ );
1882
+ await runDomainHook(
1883
+ () =>
1884
+ hooks?.onBeforeDeleteTask?.(
1885
+ context.input.id,
1886
+ hookContext(context, {
1887
+ params: { id: context.input.id },
1888
+ }),
1889
+ ),
1890
+ "DELETE_TASK_REJECTED",
1891
+ );
1892
+ const deleted = await tx.deleteMany({
1893
+ model: "kanbanTask",
1894
+ where: taskWhere({
1895
+ ...snapshot.task,
1896
+ updatedAt: taskClaimedAt,
1897
+ }),
1898
+ });
1899
+ if (!didAffectRow(deleted, snapshot.task.id)) {
1900
+ throw staleStateError();
1901
+ }
1902
+ await restoreColumnAuthorization(tx, snapshot, claims);
1903
+ return { success: true } as const;
1904
+ });
1905
+ },
1906
+ after: async (context) => {
1907
+ await hooks?.onAfterDeleteTask?.(
1908
+ context.input.id,
1909
+ hookContext(context, { params: { id: context.input.id } }),
1910
+ );
1911
+ },
1912
+ onError: ({ error }) => markMemoryRollback(adapter, error),
1913
+ });
1914
+
1915
+ const moveTask = defineOperation({
1916
+ input: moveTaskSchema,
1917
+ permission: kanbanPermissions.task.move,
1918
+ facts: async ({ input }) => {
1919
+ const primary = await loadTaskAuthorization(adapter, input.taskId);
1920
+ const snapshot = await loadMoveSnapshot(
1921
+ adapter,
1922
+ primary,
1923
+ input.targetColumnId,
1924
+ input.targetOrder,
1925
+ );
1926
+ taskMoveSnapshots.set(input as object, snapshot);
1927
+ return {
1928
+ ...taskFacts(snapshot),
1929
+ targetColumnId: snapshot.targetColumn.id,
1930
+ } satisfies TaskMoveFacts;
1931
+ },
1932
+ execute: async (context) => {
1933
+ const snapshot = taskMoveSnapshots.get(context.input as object);
1934
+ if (!snapshot) throw staleStateError();
1935
+ requireAtomicTransactions(adapter);
1936
+ return adapter.transaction(async (tx) => {
1937
+ await verifyMoveSnapshot(tx, snapshot);
1938
+ const claims = await claimMoveSnapshot(tx, snapshot);
1939
+ await runDomainHook(
1940
+ () =>
1941
+ hooks?.onBeforeUpdateTask?.(
1942
+ context.input.taskId,
1943
+ {
1944
+ id: context.input.taskId,
1945
+ columnId: context.input.targetColumnId,
1946
+ order: context.input.targetOrder,
1947
+ },
1948
+ hookContext(context, { body: context.input }),
1949
+ ),
1950
+ "MOVE_TASK_REJECTED",
1951
+ );
1952
+ return operationTask(await applyTaskMove(tx, snapshot, claims, {}));
1953
+ });
1954
+ },
1955
+ after: async (context) => {
1956
+ await hooks?.onAfterUpdateTask?.(
1957
+ context.result,
1958
+ hookContext(context, { body: context.input }),
1959
+ );
1960
+ },
1961
+ onError: ({ error }) => markMemoryRollback(adapter, error),
1962
+ });
1963
+
1964
+ const reorderTasks = defineOperation({
1965
+ input: reorderTasksSchema,
1966
+ permission: kanbanPermissions.task.reorder,
1967
+ facts: async ({ input }) => {
1968
+ const parent = await loadColumnAuthorization(adapter, input.columnId);
1969
+ const tasks = await listTasks(adapter, input.columnId);
1970
+ if (
1971
+ !sameIdSet(
1972
+ input.taskIds,
1973
+ tasks.map((task) => task.id),
1974
+ )
1975
+ ) {
1976
+ throw invalidTaskSetError();
1977
+ }
1978
+ const snapshot = { ...parent, tasks: tasks.map(taskSnapshot) };
1979
+ taskReorderSnapshots.set(input as object, snapshot);
1980
+ return {
1981
+ ...boardFacts(parent.board),
1982
+ columnId: parent.column.id,
1983
+ } satisfies TaskReorderFacts;
1984
+ },
1985
+ execute: async (context) => {
1986
+ const snapshot = taskReorderSnapshots.get(context.input as object);
1987
+ if (!snapshot) throw staleStateError();
1988
+ requireAtomicTransactions(adapter);
1989
+ return adapter.transaction(async (tx) => {
1990
+ await verifyColumnAuthorization(tx, snapshot);
1991
+ const current = (await listTasks(tx, snapshot.column.id)).map(
1992
+ taskSnapshot,
1993
+ );
1994
+ if (!sameSnapshotSet(current, snapshot.tasks, sameTaskSnapshots)) {
1995
+ throw staleStateError();
1996
+ }
1997
+ const parentClaims = await claimColumnAuthorization(tx, snapshot);
1998
+ const taskClaims = new Map<string, Date>();
1999
+ for (const task of snapshot.tasks) {
2000
+ taskClaims.set(task.id, await claimSnapshot(tx, "kanbanTask", task));
2001
+ }
2002
+ for (const [order, taskId] of context.input.taskIds.entries()) {
2003
+ await runDomainHook(
2004
+ () =>
2005
+ hooks?.onBeforeUpdateTask?.(
2006
+ taskId,
2007
+ { id: taskId, order },
2008
+ hookContext(context, { body: context.input }),
2009
+ ),
2010
+ "REORDER_TASKS_REJECTED",
2011
+ );
2012
+ }
2013
+ const updated: SerializedTask[] = [];
2014
+ for (const [order, taskId] of context.input.taskIds.entries()) {
2015
+ const original = snapshot.tasks.find((task) => task.id === taskId);
2016
+ const claimedAt = taskClaims.get(taskId);
2017
+ if (!original || !claimedAt) throw staleStateError();
2018
+ const matched = await tx.updateMany({
2019
+ model: "kanbanTask",
2020
+ where: taskWhere({ ...original, updatedAt: claimedAt }),
2021
+ update: { order, updatedAt: nextSnapshotDate(claimedAt) },
2022
+ });
2023
+ if (!didAffectRow(matched, taskId)) throw staleStateError();
2024
+ const task = await findTask(tx, taskId);
2025
+ if (!task) throw taskNotFoundError();
2026
+ updated.push(operationTask(task));
2027
+ }
2028
+ await restoreColumnAuthorization(tx, snapshot, parentClaims);
2029
+ const result = { success: true } as const;
2030
+ reorderedTasks.set(result, updated);
2031
+ return result;
2032
+ });
2033
+ },
2034
+ after: async (context) => {
2035
+ for (const task of reorderedTasks.get(context.result) ?? []) {
2036
+ await hooks?.onAfterUpdateTask?.(
2037
+ task,
2038
+ hookContext<
2039
+ z.output<typeof reorderTasksSchema>,
2040
+ TaskReorderFacts,
2041
+ { readonly success: true }
2042
+ >(context, { body: context.input }),
2043
+ );
2044
+ }
2045
+ },
2046
+ onError: ({ error }) => markMemoryRollback(adapter, error),
2047
+ });
2048
+
2049
+ return {
2050
+ listBoards,
2051
+ getBoard,
2052
+ createBoard,
2053
+ updateBoard,
2054
+ deleteBoard,
2055
+ createColumn,
2056
+ updateColumn,
2057
+ deleteColumn,
2058
+ reorderColumns,
2059
+ createTask,
2060
+ updateTask,
2061
+ deleteTask,
2062
+ moveTask,
2063
+ reorderTasks,
2064
+ };
2065
+ }