@btst/stack 3.0.0-rc.1 → 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 (1172) hide show
  1. package/README.md +122 -46
  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 +30 -12
  67. package/dist/next/index.d.mts +30 -12
  68. package/dist/next/index.d.ts +30 -12
  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/next/page.cjs +8 -6
  91. package/dist/packages/stack/src/next/page.mjs +8 -6
  92. package/dist/packages/stack/src/plugin-registration.cjs +59 -0
  93. package/dist/packages/stack/src/plugin-registration.mjs +57 -0
  94. package/dist/packages/stack/src/plugins/ai-chat/api/lifecycle-migrations.cjs +18 -0
  95. package/dist/packages/stack/src/plugins/ai-chat/api/lifecycle-migrations.mjs +16 -0
  96. package/dist/packages/stack/src/plugins/ai-chat/api/operations.cjs +1341 -0
  97. package/dist/packages/stack/src/plugins/ai-chat/api/operations.mjs +1333 -0
  98. package/dist/packages/stack/src/plugins/ai-chat/api/plugin.cjs +82 -640
  99. package/dist/packages/stack/src/plugins/ai-chat/api/plugin.mjs +78 -641
  100. package/dist/packages/stack/src/plugins/ai-chat/api/serializers.cjs +23 -0
  101. package/dist/packages/stack/src/plugins/ai-chat/api/serializers.mjs +20 -0
  102. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-input.cjs +183 -111
  103. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-input.mjs +187 -115
  104. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.cjs +562 -116
  105. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.mjs +567 -121
  106. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.cjs +6 -6
  107. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.mjs +6 -6
  108. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-message.cjs +1 -1
  109. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-message.mjs +1 -1
  110. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-sidebar.cjs +45 -29
  111. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-sidebar.mjs +46 -30
  112. package/dist/packages/stack/src/plugins/ai-chat/client/components/pages/404-page.cjs +1 -1
  113. package/dist/packages/stack/src/plugins/ai-chat/client/components/pages/404-page.mjs +1 -1
  114. package/dist/packages/stack/src/plugins/ai-chat/client/components/pages/chat-page.cjs +46 -7
  115. package/dist/packages/stack/src/plugins/ai-chat/client/components/pages/chat-page.internal.cjs +5 -3
  116. package/dist/packages/stack/src/plugins/ai-chat/client/components/pages/chat-page.internal.mjs +5 -3
  117. package/dist/packages/stack/src/plugins/ai-chat/client/components/pages/chat-page.mjs +48 -9
  118. package/dist/packages/stack/src/plugins/ai-chat/client/components/shared/default-error.cjs +1 -1
  119. package/dist/packages/stack/src/plugins/ai-chat/client/components/shared/default-error.mjs +1 -1
  120. package/dist/packages/stack/src/plugins/ai-chat/client/components/tool-call-display.cjs +1 -1
  121. package/dist/packages/stack/src/plugins/ai-chat/client/components/tool-call-display.mjs +1 -1
  122. package/dist/packages/stack/src/plugins/ai-chat/client/hooks/ai-chat-resource.cjs +1 -1
  123. package/dist/packages/stack/src/plugins/ai-chat/client/hooks/ai-chat-resource.mjs +1 -1
  124. package/dist/packages/stack/src/plugins/ai-chat/client/hooks/chat-hooks.cjs +179 -19
  125. package/dist/packages/stack/src/plugins/ai-chat/client/hooks/chat-hooks.mjs +180 -21
  126. package/dist/packages/stack/src/plugins/ai-chat/client/navigation.cjs +11 -0
  127. package/dist/packages/stack/src/plugins/ai-chat/client/navigation.mjs +9 -0
  128. package/dist/packages/stack/src/plugins/ai-chat/client/overrides.cjs +24 -0
  129. package/dist/packages/stack/src/plugins/ai-chat/client/overrides.mjs +21 -1
  130. package/dist/packages/stack/src/plugins/ai-chat/client/plugin.cjs +152 -82
  131. package/dist/packages/stack/src/plugins/ai-chat/client/plugin.mjs +152 -82
  132. package/dist/packages/stack/src/plugins/api/create-endpoint.cjs +27 -9
  133. package/dist/packages/stack/src/plugins/api/create-endpoint.mjs +27 -10
  134. package/dist/packages/stack/src/plugins/api/endpoint-inventory.cjs +125 -0
  135. package/dist/packages/stack/src/plugins/api/endpoint-inventory.mjs +123 -0
  136. package/dist/packages/stack/src/plugins/api/operation.cjs +208 -0
  137. package/dist/packages/stack/src/plugins/api/operation.mjs +198 -0
  138. package/dist/packages/stack/src/plugins/blog/api/lifecycle-migrations.cjs +17 -0
  139. package/dist/packages/stack/src/plugins/blog/api/lifecycle-migrations.mjs +15 -0
  140. package/dist/packages/stack/src/plugins/blog/api/mutations.cjs +10 -2
  141. package/dist/packages/stack/src/plugins/blog/api/mutations.mjs +10 -2
  142. package/dist/packages/stack/src/plugins/blog/api/operations.cjs +455 -0
  143. package/dist/packages/stack/src/plugins/blog/api/operations.mjs +448 -0
  144. package/dist/packages/stack/src/plugins/blog/api/plugin.cjs +51 -298
  145. package/dist/packages/stack/src/plugins/blog/api/plugin.mjs +55 -304
  146. package/dist/packages/stack/src/plugins/blog/client/components/forms/image-field.cjs +2 -1
  147. package/dist/packages/stack/src/plugins/blog/client/components/forms/image-field.mjs +2 -1
  148. package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.cjs +2 -1
  149. package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.mjs +2 -1
  150. package/dist/packages/stack/src/plugins/blog/client/components/forms/post-forms.cjs +82 -67
  151. package/dist/packages/stack/src/plugins/blog/client/components/forms/post-forms.mjs +84 -69
  152. package/dist/packages/stack/src/plugins/blog/client/components/pages/404-page.cjs +2 -1
  153. package/dist/packages/stack/src/plugins/blog/client/components/pages/404-page.mjs +2 -1
  154. package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.cjs +22 -4
  155. package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.internal.cjs +2 -1
  156. package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.internal.mjs +2 -1
  157. package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.mjs +23 -5
  158. package/dist/packages/stack/src/plugins/blog/client/components/pages/home-page.cjs +6 -2
  159. package/dist/packages/stack/src/plugins/blog/client/components/pages/home-page.internal.cjs +2 -1
  160. package/dist/packages/stack/src/plugins/blog/client/components/pages/home-page.internal.mjs +2 -1
  161. package/dist/packages/stack/src/plugins/blog/client/components/pages/home-page.mjs +6 -2
  162. package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.cjs +4 -2
  163. package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.internal.cjs +2 -1
  164. package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.internal.mjs +2 -1
  165. package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.mjs +4 -2
  166. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.cjs +30 -3
  167. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.cjs +2 -1
  168. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.mjs +2 -1
  169. package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.mjs +31 -4
  170. package/dist/packages/stack/src/plugins/blog/client/components/pages/tag-page.cjs +4 -1
  171. package/dist/packages/stack/src/plugins/blog/client/components/pages/tag-page.internal.cjs +2 -1
  172. package/dist/packages/stack/src/plugins/blog/client/components/pages/tag-page.internal.mjs +2 -1
  173. package/dist/packages/stack/src/plugins/blog/client/components/pages/tag-page.mjs +4 -1
  174. package/dist/packages/stack/src/plugins/blog/client/components/shared/collapsible-tag-list.cjs +2 -1
  175. package/dist/packages/stack/src/plugins/blog/client/components/shared/collapsible-tag-list.mjs +2 -1
  176. package/dist/packages/stack/src/plugins/blog/client/components/shared/default-error.cjs +2 -1
  177. package/dist/packages/stack/src/plugins/blog/client/components/shared/default-error.mjs +2 -1
  178. package/dist/packages/stack/src/plugins/blog/client/components/shared/on-this-page.cjs +3 -2
  179. package/dist/packages/stack/src/plugins/blog/client/components/shared/on-this-page.mjs +3 -2
  180. package/dist/packages/stack/src/plugins/blog/client/components/shared/page-wrapper.cjs +2 -1
  181. package/dist/packages/stack/src/plugins/blog/client/components/shared/page-wrapper.mjs +2 -1
  182. package/dist/packages/stack/src/plugins/blog/client/components/shared/post-card.cjs +2 -1
  183. package/dist/packages/stack/src/plugins/blog/client/components/shared/post-card.mjs +2 -1
  184. package/dist/packages/stack/src/plugins/blog/client/components/shared/posts-list.cjs +2 -1
  185. package/dist/packages/stack/src/plugins/blog/client/components/shared/posts-list.mjs +2 -1
  186. package/dist/packages/stack/src/plugins/blog/client/components/shared/recent-posts-carousel.cjs +2 -1
  187. package/dist/packages/stack/src/plugins/blog/client/components/shared/recent-posts-carousel.mjs +2 -1
  188. package/dist/packages/stack/src/plugins/blog/client/components/shared/search-input.cjs +2 -2
  189. package/dist/packages/stack/src/plugins/blog/client/components/shared/search-input.mjs +1 -1
  190. package/dist/packages/stack/src/plugins/blog/client/constants.cjs +5 -0
  191. package/dist/packages/stack/src/plugins/blog/client/constants.mjs +3 -0
  192. package/dist/packages/stack/src/plugins/blog/client/hooks/blog-resource.cjs +2 -1
  193. package/dist/packages/stack/src/plugins/blog/client/hooks/blog-resource.mjs +2 -1
  194. package/dist/packages/stack/src/plugins/blog/client/plugin.cjs +169 -163
  195. package/dist/packages/stack/src/plugins/blog/client/plugin.mjs +169 -163
  196. package/dist/packages/stack/src/plugins/client/resource/hooks.cjs +31 -16
  197. package/dist/packages/stack/src/plugins/client/resource/hooks.mjs +31 -16
  198. package/dist/packages/stack/src/plugins/client/resource/internal.cjs +66 -52
  199. package/dist/packages/stack/src/plugins/client/resource/internal.mjs +66 -52
  200. package/dist/packages/stack/src/plugins/client/resource/use-form.cjs +20 -17
  201. package/dist/packages/stack/src/plugins/client/resource/use-form.mjs +20 -17
  202. package/dist/packages/stack/src/plugins/client/resource/use-select.cjs +39 -33
  203. package/dist/packages/stack/src/plugins/client/resource/use-select.mjs +39 -33
  204. package/dist/packages/stack/src/plugins/cms/api/lifecycle-migrations.cjs +13 -0
  205. package/dist/packages/stack/src/plugins/cms/api/lifecycle-migrations.mjs +11 -0
  206. package/dist/packages/stack/src/plugins/cms/api/operations.cjs +1763 -0
  207. package/dist/packages/stack/src/plugins/cms/api/operations.mjs +1756 -0
  208. package/dist/packages/stack/src/plugins/cms/api/plugin.cjs +165 -752
  209. package/dist/packages/stack/src/plugins/cms/api/plugin.mjs +164 -755
  210. package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.cjs +2 -1
  211. package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.mjs +2 -1
  212. package/dist/packages/stack/src/plugins/cms/client/components/forms/file-upload.cjs +2 -1
  213. package/dist/packages/stack/src/plugins/cms/client/components/forms/file-upload.mjs +2 -1
  214. package/dist/packages/stack/src/plugins/cms/client/components/forms/relation-field.cjs +2 -1
  215. package/dist/packages/stack/src/plugins/cms/client/components/forms/relation-field.mjs +2 -1
  216. package/dist/packages/stack/src/plugins/cms/client/components/inverse-relations-panel.cjs +23 -13
  217. package/dist/packages/stack/src/plugins/cms/client/components/inverse-relations-panel.mjs +24 -14
  218. package/dist/packages/stack/src/plugins/cms/client/components/pages/404-page.cjs +5 -4
  219. package/dist/packages/stack/src/plugins/cms/client/components/pages/404-page.mjs +6 -5
  220. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-editor-page.cjs +40 -7
  221. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-editor-page.internal.cjs +15 -9
  222. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-editor-page.internal.mjs +14 -8
  223. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-editor-page.mjs +41 -8
  224. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-list-page.cjs +7 -1
  225. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-list-page.internal.cjs +42 -29
  226. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-list-page.internal.mjs +40 -27
  227. package/dist/packages/stack/src/plugins/cms/client/components/pages/content-list-page.mjs +7 -1
  228. package/dist/packages/stack/src/plugins/cms/client/components/pages/dashboard-page.cjs +4 -1
  229. package/dist/packages/stack/src/plugins/cms/client/components/pages/dashboard-page.internal.cjs +13 -7
  230. package/dist/packages/stack/src/plugins/cms/client/components/pages/dashboard-page.internal.mjs +12 -6
  231. package/dist/packages/stack/src/plugins/cms/client/components/pages/dashboard-page.mjs +4 -1
  232. package/dist/packages/stack/src/plugins/cms/client/components/shared/page-wrapper.cjs +2 -1
  233. package/dist/packages/stack/src/plugins/cms/client/components/shared/page-wrapper.mjs +2 -1
  234. package/dist/packages/stack/src/plugins/cms/client/constants.cjs +5 -0
  235. package/dist/packages/stack/src/plugins/cms/client/constants.mjs +3 -0
  236. package/dist/packages/stack/src/plugins/cms/client/hooks/cms-resource.cjs +2 -1
  237. package/dist/packages/stack/src/plugins/cms/client/hooks/cms-resource.mjs +2 -1
  238. package/dist/packages/stack/src/plugins/cms/client/overrides.cjs +26 -0
  239. package/dist/packages/stack/src/plugins/cms/client/overrides.mjs +24 -0
  240. package/dist/packages/stack/src/plugins/cms/client/plugin.cjs +102 -81
  241. package/dist/packages/stack/src/plugins/cms/client/plugin.mjs +103 -82
  242. package/dist/packages/stack/src/plugins/comments/api/getters.cjs +6 -2
  243. package/dist/packages/stack/src/plugins/comments/api/getters.mjs +6 -3
  244. package/dist/packages/stack/src/plugins/comments/api/lifecycle-migrations.cjs +18 -0
  245. package/dist/packages/stack/src/plugins/comments/api/lifecycle-migrations.mjs +16 -0
  246. package/dist/packages/stack/src/plugins/comments/api/mutations.cjs +179 -68
  247. package/dist/packages/stack/src/plugins/comments/api/mutations.mjs +179 -68
  248. package/dist/packages/stack/src/plugins/comments/api/operations.cjs +453 -0
  249. package/dist/packages/stack/src/plugins/comments/api/operations.mjs +445 -0
  250. package/dist/packages/stack/src/plugins/comments/api/plugin.cjs +51 -276
  251. package/dist/packages/stack/src/plugins/comments/api/plugin.mjs +48 -277
  252. package/dist/packages/stack/src/plugins/comments/client/components/comment-count.cjs +32 -5
  253. package/dist/packages/stack/src/plugins/comments/client/components/comment-count.mjs +34 -7
  254. package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.cjs +161 -105
  255. package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.mjs +162 -106
  256. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.cjs +14 -2
  257. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.internal.cjs +141 -71
  258. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.internal.mjs +142 -72
  259. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-page.mjs +14 -2
  260. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-state.cjs +12 -0
  261. package/dist/packages/stack/src/plugins/comments/client/components/pages/moderation-state.mjs +9 -0
  262. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.cjs +2 -1
  263. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.internal.cjs +40 -26
  264. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.internal.mjs +40 -26
  265. package/dist/packages/stack/src/plugins/comments/client/components/pages/my-comments-page.mjs +2 -1
  266. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.cjs +9 -1
  267. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.internal.cjs +25 -20
  268. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.internal.mjs +26 -21
  269. package/dist/packages/stack/src/plugins/comments/client/components/pages/resource-comments-page.mjs +9 -1
  270. package/dist/packages/stack/src/plugins/comments/client/components/shared/page-wrapper.cjs +2 -1
  271. package/dist/packages/stack/src/plugins/comments/client/components/shared/page-wrapper.mjs +2 -1
  272. package/dist/packages/stack/src/plugins/comments/client/components/shared/pagination.cjs +2 -1
  273. package/dist/packages/stack/src/plugins/comments/client/components/shared/pagination.mjs +2 -1
  274. package/dist/packages/stack/src/plugins/comments/client/constants.cjs +5 -0
  275. package/dist/packages/stack/src/plugins/comments/client/constants.mjs +3 -0
  276. package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.cjs +323 -290
  277. package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.mjs +325 -292
  278. package/dist/packages/stack/src/plugins/comments/client/localization/comments-thread.cjs +1 -0
  279. package/dist/packages/stack/src/plugins/comments/client/localization/comments-thread.mjs +1 -0
  280. package/dist/packages/stack/src/plugins/comments/client/plugin.cjs +78 -46
  281. package/dist/packages/stack/src/plugins/comments/client/plugin.mjs +80 -48
  282. package/dist/packages/stack/src/plugins/form-builder/api/getters.cjs +9 -0
  283. package/dist/packages/stack/src/plugins/form-builder/api/getters.mjs +9 -1
  284. package/dist/packages/stack/src/plugins/form-builder/api/lifecycle-migrations.cjs +15 -0
  285. package/dist/packages/stack/src/plugins/form-builder/api/lifecycle-migrations.mjs +13 -0
  286. package/dist/packages/stack/src/plugins/form-builder/api/operations.cjs +1093 -0
  287. package/dist/packages/stack/src/plugins/form-builder/api/operations.mjs +1082 -0
  288. package/dist/packages/stack/src/plugins/form-builder/api/plugin.cjs +97 -466
  289. package/dist/packages/stack/src/plugins/form-builder/api/plugin.mjs +92 -468
  290. package/dist/packages/stack/src/plugins/form-builder/api/query-key-defs.cjs +5 -0
  291. package/dist/packages/stack/src/plugins/form-builder/api/query-key-defs.mjs +5 -0
  292. package/dist/packages/stack/src/plugins/form-builder/client/components/forms/form-renderer.cjs +41 -11
  293. package/dist/packages/stack/src/plugins/form-builder/client/components/forms/form-renderer.mjs +42 -12
  294. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/404-page.cjs +8 -5
  295. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/404-page.mjs +9 -6
  296. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-builder-page.cjs +36 -3
  297. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-builder-page.internal.cjs +32 -11
  298. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-builder-page.internal.mjs +35 -14
  299. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-builder-page.mjs +37 -4
  300. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-list-page.cjs +6 -1
  301. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-list-page.internal.cjs +34 -26
  302. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-list-page.internal.mjs +34 -26
  303. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/form-list-page.mjs +6 -1
  304. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.cjs +24 -7
  305. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.cjs +72 -40
  306. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.mjs +76 -44
  307. package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.mjs +25 -8
  308. package/dist/packages/stack/src/plugins/form-builder/client/components/shared/page-wrapper.cjs +2 -1
  309. package/dist/packages/stack/src/plugins/form-builder/client/components/shared/page-wrapper.mjs +2 -1
  310. package/dist/packages/stack/src/plugins/form-builder/client/constants.cjs +5 -0
  311. package/dist/packages/stack/src/plugins/form-builder/client/constants.mjs +3 -0
  312. package/dist/packages/stack/src/plugins/form-builder/client/hooks/form-builder-hooks.cjs +29 -0
  313. package/dist/packages/stack/src/plugins/form-builder/client/hooks/form-builder-hooks.mjs +28 -1
  314. package/dist/packages/stack/src/plugins/form-builder/client/hooks/form-builder-resource.cjs +2 -1
  315. package/dist/packages/stack/src/plugins/form-builder/client/hooks/form-builder-resource.mjs +2 -1
  316. package/dist/packages/stack/src/plugins/form-builder/client/plugin.cjs +113 -86
  317. package/dist/packages/stack/src/plugins/form-builder/client/plugin.mjs +114 -87
  318. package/dist/packages/stack/src/plugins/form-builder/schemas.cjs +9 -3
  319. package/dist/packages/stack/src/plugins/form-builder/schemas.mjs +9 -3
  320. package/dist/packages/stack/src/plugins/kanban/api/getters.cjs +47 -36
  321. package/dist/packages/stack/src/plugins/kanban/api/getters.mjs +47 -37
  322. package/dist/packages/stack/src/plugins/kanban/api/lifecycle-migrations.cjs +23 -0
  323. package/dist/packages/stack/src/plugins/kanban/api/lifecycle-migrations.mjs +21 -0
  324. package/dist/packages/stack/src/plugins/kanban/api/operations.cjs +1552 -0
  325. package/dist/packages/stack/src/plugins/kanban/api/operations.mjs +1543 -0
  326. package/dist/packages/stack/src/plugins/kanban/api/plugin.cjs +64 -631
  327. package/dist/packages/stack/src/plugins/kanban/api/plugin.mjs +62 -634
  328. package/dist/packages/stack/src/plugins/kanban/api/serializers.cjs +21 -0
  329. package/dist/packages/stack/src/plugins/kanban/api/serializers.mjs +21 -1
  330. package/dist/packages/stack/src/plugins/kanban/client/components/forms/board-form.cjs +2 -1
  331. package/dist/packages/stack/src/plugins/kanban/client/components/forms/board-form.mjs +2 -1
  332. package/dist/packages/stack/src/plugins/kanban/client/components/forms/column-form.cjs +2 -1
  333. package/dist/packages/stack/src/plugins/kanban/client/components/forms/column-form.mjs +2 -1
  334. package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.cjs +72 -54
  335. package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.mjs +74 -56
  336. package/dist/packages/stack/src/plugins/kanban/client/components/pages/404-page.cjs +4 -6
  337. package/dist/packages/stack/src/plugins/kanban/client/components/pages/404-page.mjs +5 -7
  338. package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.cjs +22 -6
  339. package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.internal.cjs +82 -77
  340. package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.internal.mjs +83 -78
  341. package/dist/packages/stack/src/plugins/kanban/client/components/pages/board-page.mjs +23 -7
  342. package/dist/packages/stack/src/plugins/kanban/client/components/pages/boards-list-page.cjs +2 -1
  343. package/dist/packages/stack/src/plugins/kanban/client/components/pages/boards-list-page.internal.cjs +34 -24
  344. package/dist/packages/stack/src/plugins/kanban/client/components/pages/boards-list-page.internal.mjs +35 -25
  345. package/dist/packages/stack/src/plugins/kanban/client/components/pages/boards-list-page.mjs +2 -1
  346. package/dist/packages/stack/src/plugins/kanban/client/components/pages/new-board-page.cjs +2 -1
  347. package/dist/packages/stack/src/plugins/kanban/client/components/pages/new-board-page.internal.cjs +6 -9
  348. package/dist/packages/stack/src/plugins/kanban/client/components/pages/new-board-page.internal.mjs +7 -10
  349. package/dist/packages/stack/src/plugins/kanban/client/components/pages/new-board-page.mjs +2 -1
  350. package/dist/packages/stack/src/plugins/kanban/client/components/shared/column-content.cjs +72 -45
  351. package/dist/packages/stack/src/plugins/kanban/client/components/shared/column-content.mjs +73 -46
  352. package/dist/packages/stack/src/plugins/kanban/client/components/shared/default-error.cjs +2 -1
  353. package/dist/packages/stack/src/plugins/kanban/client/components/shared/default-error.mjs +2 -1
  354. package/dist/packages/stack/src/plugins/kanban/client/components/shared/kanban-board.cjs +4 -4
  355. package/dist/packages/stack/src/plugins/kanban/client/components/shared/kanban-board.mjs +4 -4
  356. package/dist/packages/stack/src/plugins/kanban/client/components/shared/permission-access-any.cjs +23 -0
  357. package/dist/packages/stack/src/plugins/kanban/client/components/shared/permission-access-any.mjs +21 -0
  358. package/dist/packages/stack/src/plugins/kanban/client/components/shared/task-card.cjs +54 -17
  359. package/dist/packages/stack/src/plugins/kanban/client/components/shared/task-card.mjs +55 -18
  360. package/dist/packages/stack/src/plugins/kanban/client/components/shared/user-avatar.cjs +2 -1
  361. package/dist/packages/stack/src/plugins/kanban/client/components/shared/user-avatar.mjs +2 -1
  362. package/dist/packages/stack/src/plugins/kanban/client/constants.cjs +5 -0
  363. package/dist/packages/stack/src/plugins/kanban/client/constants.mjs +3 -0
  364. package/dist/packages/stack/src/plugins/kanban/client/hooks/kanban-hooks.cjs +38 -9
  365. package/dist/packages/stack/src/plugins/kanban/client/hooks/kanban-hooks.mjs +39 -10
  366. package/dist/packages/stack/src/plugins/kanban/client/hooks/kanban-resource.cjs +2 -1
  367. package/dist/packages/stack/src/plugins/kanban/client/hooks/kanban-resource.mjs +2 -1
  368. package/dist/packages/stack/src/plugins/kanban/client/plugin.cjs +177 -104
  369. package/dist/packages/stack/src/plugins/kanban/client/plugin.mjs +178 -105
  370. package/dist/packages/stack/src/plugins/kanban/schemas.cjs +32 -23
  371. package/dist/packages/stack/src/plugins/kanban/schemas.mjs +33 -21
  372. package/dist/packages/stack/src/plugins/media/api/lifecycle-migrations.cjs +9 -0
  373. package/dist/packages/stack/src/plugins/media/api/lifecycle-migrations.mjs +7 -0
  374. package/dist/packages/stack/src/plugins/media/api/mutations.cjs +7 -2
  375. package/dist/packages/stack/src/plugins/media/api/mutations.mjs +7 -2
  376. package/dist/packages/stack/src/plugins/media/api/operations.cjs +1299 -0
  377. package/dist/packages/stack/src/plugins/media/api/operations.mjs +1290 -0
  378. package/dist/packages/stack/src/plugins/media/api/plugin.cjs +131 -508
  379. package/dist/packages/stack/src/plugins/media/api/plugin.mjs +127 -510
  380. package/dist/packages/stack/src/plugins/media/api/query-key-defs.cjs +26 -2
  381. package/dist/packages/stack/src/plugins/media/api/query-key-defs.mjs +25 -3
  382. package/dist/packages/stack/src/plugins/media/api/serializers.cjs +4 -2
  383. package/dist/packages/stack/src/plugins/media/api/serializers.mjs +4 -2
  384. package/dist/packages/stack/src/plugins/media/asset-url.cjs +13 -0
  385. package/dist/packages/stack/src/plugins/media/asset-url.mjs +11 -0
  386. package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-card.cjs +10 -10
  387. package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-card.mjs +12 -12
  388. package/dist/packages/stack/src/plugins/media/client/components/media-picker/folder-tree.cjs +47 -27
  389. package/dist/packages/stack/src/plugins/media/client/components/media-picker/folder-tree.mjs +49 -29
  390. package/dist/packages/stack/src/plugins/media/client/components/media-picker/index.cjs +225 -180
  391. package/dist/packages/stack/src/plugins/media/client/components/media-picker/index.mjs +228 -183
  392. package/dist/packages/stack/src/plugins/media/client/components/media-picker/upload-tab.cjs +1 -8
  393. package/dist/packages/stack/src/plugins/media/client/components/media-picker/upload-tab.mjs +2 -9
  394. package/dist/packages/stack/src/plugins/media/client/components/media-picker/url-tab.cjs +0 -6
  395. package/dist/packages/stack/src/plugins/media/client/components/media-picker/url-tab.mjs +1 -7
  396. package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.cjs +4 -2
  397. package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.internal.cjs +92 -75
  398. package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.internal.mjs +93 -76
  399. package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.mjs +4 -2
  400. package/dist/packages/stack/src/plugins/media/client/components/upload-permission-check.cjs +52 -0
  401. package/dist/packages/stack/src/plugins/media/client/components/upload-permission-check.mjs +49 -0
  402. package/dist/packages/stack/src/plugins/media/client/constants.cjs +5 -0
  403. package/dist/packages/stack/src/plugins/media/client/constants.mjs +3 -0
  404. package/dist/packages/stack/src/plugins/media/client/hooks/media-resource.cjs +2 -1
  405. package/dist/packages/stack/src/plugins/media/client/hooks/media-resource.mjs +2 -1
  406. package/dist/packages/stack/src/plugins/media/client/hooks/use-media.cjs +214 -43
  407. package/dist/packages/stack/src/plugins/media/client/hooks/use-media.mjs +217 -46
  408. package/dist/packages/stack/src/plugins/media/client/plugin.cjs +122 -62
  409. package/dist/packages/stack/src/plugins/media/client/plugin.mjs +123 -63
  410. package/dist/packages/stack/src/plugins/media/client/upload.cjs +121 -52
  411. package/dist/packages/stack/src/plugins/media/client/upload.mjs +121 -53
  412. package/dist/packages/stack/src/plugins/media/db.cjs +8 -0
  413. package/dist/packages/stack/src/plugins/media/db.mjs +8 -0
  414. package/dist/packages/stack/src/plugins/open-api/api/generator.cjs +36 -5
  415. package/dist/packages/stack/src/plugins/open-api/api/generator.mjs +36 -5
  416. package/dist/packages/stack/src/plugins/open-api/api/plugin.cjs +22 -11
  417. package/dist/packages/stack/src/plugins/open-api/api/plugin.mjs +22 -11
  418. package/dist/packages/stack/src/plugins/route-docs/client/components/pages/docs-page.cjs +65 -34
  419. package/dist/packages/stack/src/plugins/route-docs/client/components/pages/docs-page.mjs +56 -25
  420. package/dist/packages/stack/src/plugins/route-docs/client/constants.cjs +5 -0
  421. package/dist/packages/stack/src/plugins/route-docs/client/constants.mjs +3 -0
  422. package/dist/packages/stack/src/plugins/route-docs/client/hooks.cjs +9 -8
  423. package/dist/packages/stack/src/plugins/route-docs/client/hooks.mjs +10 -9
  424. package/dist/packages/stack/src/plugins/route-docs/client/plugin.cjs +143 -75
  425. package/dist/packages/stack/src/plugins/route-docs/client/plugin.mjs +144 -74
  426. package/dist/packages/stack/src/plugins/route-docs/client/schema.cjs +26 -0
  427. package/dist/packages/stack/src/plugins/route-docs/client/schema.mjs +23 -0
  428. package/dist/packages/stack/src/plugins/route-docs/generator.cjs +7 -4
  429. package/dist/packages/stack/src/plugins/route-docs/generator.mjs +7 -4
  430. package/dist/packages/stack/src/plugins/ui-builder/client/components/page-renderer.cjs +41 -12
  431. package/dist/packages/stack/src/plugins/ui-builder/client/components/page-renderer.mjs +42 -13
  432. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.cjs +37 -3
  433. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.cjs +32 -20
  434. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.mjs +34 -22
  435. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.mjs +38 -4
  436. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-list-page.cjs +8 -2
  437. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-list-page.internal.cjs +37 -19
  438. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-list-page.internal.mjs +38 -20
  439. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-list-page.mjs +8 -2
  440. package/dist/packages/stack/src/plugins/ui-builder/client/components/shared/default-error.cjs +2 -1
  441. package/dist/packages/stack/src/plugins/ui-builder/client/components/shared/default-error.mjs +2 -1
  442. package/dist/packages/stack/src/plugins/ui-builder/client/components/shared/page-wrapper.cjs +2 -1
  443. package/dist/packages/stack/src/plugins/ui-builder/client/components/shared/page-wrapper.mjs +2 -1
  444. package/dist/packages/stack/src/plugins/ui-builder/client/constants.cjs +5 -0
  445. package/dist/packages/stack/src/plugins/ui-builder/client/constants.mjs +3 -0
  446. package/dist/packages/stack/src/plugins/ui-builder/client/hooks/ui-builder-resource.cjs +2 -1
  447. package/dist/packages/stack/src/plugins/ui-builder/client/hooks/ui-builder-resource.mjs +2 -1
  448. package/dist/packages/stack/src/plugins/ui-builder/client/overrides.cjs +8 -0
  449. package/dist/packages/stack/src/plugins/ui-builder/client/overrides.mjs +6 -0
  450. package/dist/packages/stack/src/plugins/ui-builder/client/plugin.cjs +93 -53
  451. package/dist/packages/stack/src/plugins/ui-builder/client/plugin.mjs +94 -54
  452. package/dist/packages/stack/src/plugins/utils.cjs +4 -12
  453. package/dist/packages/stack/src/plugins/utils.mjs +5 -12
  454. package/dist/packages/stack/src/react-router/layout.cjs +14 -0
  455. package/dist/packages/stack/src/react-router/layout.mjs +12 -0
  456. package/dist/packages/stack/src/react-router/page.cjs +27 -16
  457. package/dist/packages/stack/src/react-router/page.mjs +28 -17
  458. package/dist/packages/stack/src/shared/initial-identity.cjs +27 -0
  459. package/dist/packages/stack/src/shared/initial-identity.mjs +23 -0
  460. package/dist/packages/stack/src/tanstack/layout.cjs +10 -0
  461. package/dist/packages/stack/src/tanstack/layout.mjs +8 -0
  462. package/dist/packages/stack/src/tanstack/page.cjs +11 -5
  463. package/dist/packages/stack/src/tanstack/page.mjs +11 -5
  464. package/dist/plugins/ai-chat/api/index.cjs +10 -0
  465. package/dist/plugins/ai-chat/api/index.d.cts +31 -5
  466. package/dist/plugins/ai-chat/api/index.d.mts +31 -5
  467. package/dist/plugins/ai-chat/api/index.d.ts +31 -5
  468. package/dist/plugins/ai-chat/api/index.mjs +3 -1
  469. package/dist/plugins/ai-chat/client/components/index.d.cts +1 -1
  470. package/dist/plugins/ai-chat/client/components/index.d.mts +1 -1
  471. package/dist/plugins/ai-chat/client/components/index.d.ts +1 -1
  472. package/dist/plugins/ai-chat/client/hooks/index.cjs +1 -0
  473. package/dist/plugins/ai-chat/client/hooks/index.d.cts +14 -8
  474. package/dist/plugins/ai-chat/client/hooks/index.d.mts +14 -8
  475. package/dist/plugins/ai-chat/client/hooks/index.d.ts +14 -8
  476. package/dist/plugins/ai-chat/client/hooks/index.mjs +1 -1
  477. package/dist/plugins/ai-chat/client/index.d.cts +21 -25
  478. package/dist/plugins/ai-chat/client/index.d.mts +21 -25
  479. package/dist/plugins/ai-chat/client/index.d.ts +21 -25
  480. package/dist/plugins/ai-chat/permissions.cjs +90 -0
  481. package/dist/plugins/ai-chat/permissions.d.cts +84 -0
  482. package/dist/plugins/ai-chat/permissions.d.mts +84 -0
  483. package/dist/plugins/ai-chat/permissions.d.ts +84 -0
  484. package/dist/plugins/ai-chat/permissions.mjs +88 -0
  485. package/dist/plugins/ai-chat/query-keys.cjs +116 -18
  486. package/dist/plugins/ai-chat/query-keys.d.cts +6 -2
  487. package/dist/plugins/ai-chat/query-keys.d.mts +6 -2
  488. package/dist/plugins/ai-chat/query-keys.d.ts +6 -2
  489. package/dist/plugins/ai-chat/query-keys.mjs +116 -19
  490. package/dist/plugins/api/index.cjs +4 -0
  491. package/dist/plugins/api/index.d.cts +13 -18
  492. package/dist/plugins/api/index.d.mts +13 -18
  493. package/dist/plugins/api/index.d.ts +13 -18
  494. package/dist/plugins/api/index.mjs +1 -0
  495. package/dist/plugins/blog/api/index.cjs +9 -2
  496. package/dist/plugins/blog/api/index.d.cts +118 -4
  497. package/dist/plugins/blog/api/index.d.mts +118 -4
  498. package/dist/plugins/blog/api/index.d.ts +118 -4
  499. package/dist/plugins/blog/api/index.mjs +4 -1
  500. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  501. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  502. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  503. package/dist/plugins/blog/client/index.d.cts +210 -226
  504. package/dist/plugins/blog/client/index.d.mts +210 -226
  505. package/dist/plugins/blog/client/index.d.ts +210 -226
  506. package/dist/plugins/blog/permissions.cjs +47 -0
  507. package/dist/plugins/blog/permissions.d.cts +49 -0
  508. package/dist/plugins/blog/permissions.d.mts +49 -0
  509. package/dist/plugins/blog/permissions.d.ts +49 -0
  510. package/dist/plugins/blog/permissions.mjs +45 -0
  511. package/dist/plugins/blog/query-keys.d.cts +7 -3
  512. package/dist/plugins/blog/query-keys.d.mts +7 -3
  513. package/dist/plugins/blog/query-keys.d.ts +7 -3
  514. package/dist/plugins/client/hooks/index.d.cts +2 -2
  515. package/dist/plugins/client/hooks/index.d.mts +2 -2
  516. package/dist/plugins/client/hooks/index.d.ts +2 -2
  517. package/dist/plugins/client/index.cjs +3 -0
  518. package/dist/plugins/client/index.d.cts +30 -10
  519. package/dist/plugins/client/index.d.mts +30 -10
  520. package/dist/plugins/client/index.d.ts +30 -10
  521. package/dist/plugins/client/index.mjs +3 -0
  522. package/dist/plugins/cms/api/index.cjs +7 -0
  523. package/dist/plugins/cms/api/index.d.cts +173 -4
  524. package/dist/plugins/cms/api/index.d.mts +173 -4
  525. package/dist/plugins/cms/api/index.d.ts +173 -4
  526. package/dist/plugins/cms/api/index.mjs +2 -0
  527. package/dist/plugins/cms/client/hooks/index.d.cts +1 -1
  528. package/dist/plugins/cms/client/hooks/index.d.mts +1 -1
  529. package/dist/plugins/cms/client/hooks/index.d.ts +1 -1
  530. package/dist/plugins/cms/client/index.d.cts +188 -184
  531. package/dist/plugins/cms/client/index.d.mts +188 -184
  532. package/dist/plugins/cms/client/index.d.ts +188 -184
  533. package/dist/plugins/cms/permissions.cjs +46 -0
  534. package/dist/plugins/cms/permissions.d.cts +42 -0
  535. package/dist/plugins/cms/permissions.d.mts +42 -0
  536. package/dist/plugins/cms/permissions.d.ts +42 -0
  537. package/dist/plugins/cms/permissions.mjs +44 -0
  538. package/dist/plugins/cms/query-keys.d.cts +4 -4
  539. package/dist/plugins/cms/query-keys.d.mts +4 -4
  540. package/dist/plugins/cms/query-keys.d.ts +4 -4
  541. package/dist/plugins/comments/api/index.cjs +10 -0
  542. package/dist/plugins/comments/api/index.d.cts +54 -18
  543. package/dist/plugins/comments/api/index.d.mts +54 -18
  544. package/dist/plugins/comments/api/index.d.ts +54 -18
  545. package/dist/plugins/comments/api/index.mjs +3 -0
  546. package/dist/plugins/comments/client/components/index.d.cts +7 -2
  547. package/dist/plugins/comments/client/components/index.d.mts +7 -2
  548. package/dist/plugins/comments/client/components/index.d.ts +7 -2
  549. package/dist/plugins/comments/client/hooks/index.d.cts +12 -18
  550. package/dist/plugins/comments/client/hooks/index.d.mts +12 -18
  551. package/dist/plugins/comments/client/hooks/index.d.ts +12 -18
  552. package/dist/plugins/comments/client/index.d.cts +96 -114
  553. package/dist/plugins/comments/client/index.d.mts +96 -114
  554. package/dist/plugins/comments/client/index.d.ts +96 -114
  555. package/dist/plugins/comments/permissions.cjs +70 -0
  556. package/dist/plugins/comments/permissions.d.cts +76 -0
  557. package/dist/plugins/comments/permissions.d.mts +76 -0
  558. package/dist/plugins/comments/permissions.d.ts +76 -0
  559. package/dist/plugins/comments/permissions.mjs +68 -0
  560. package/dist/plugins/comments/query-keys.cjs +1 -1
  561. package/dist/plugins/comments/query-keys.d.cts +16 -19
  562. package/dist/plugins/comments/query-keys.d.mts +16 -19
  563. package/dist/plugins/comments/query-keys.d.ts +16 -19
  564. package/dist/plugins/comments/query-keys.mjs +1 -1
  565. package/dist/plugins/form-builder/api/index.cjs +8 -0
  566. package/dist/plugins/form-builder/api/index.d.cts +115 -4
  567. package/dist/plugins/form-builder/api/index.d.mts +115 -4
  568. package/dist/plugins/form-builder/api/index.d.ts +115 -4
  569. package/dist/plugins/form-builder/api/index.mjs +4 -1
  570. package/dist/plugins/form-builder/client/components/index.d.cts +6 -2
  571. package/dist/plugins/form-builder/client/components/index.d.mts +6 -2
  572. package/dist/plugins/form-builder/client/components/index.d.ts +6 -2
  573. package/dist/plugins/form-builder/client/hooks/index.cjs +2 -0
  574. package/dist/plugins/form-builder/client/hooks/index.d.cts +34 -8
  575. package/dist/plugins/form-builder/client/hooks/index.d.mts +34 -8
  576. package/dist/plugins/form-builder/client/hooks/index.d.ts +34 -8
  577. package/dist/plugins/form-builder/client/hooks/index.mjs +1 -1
  578. package/dist/plugins/form-builder/client/index.d.cts +153 -174
  579. package/dist/plugins/form-builder/client/index.d.mts +153 -174
  580. package/dist/plugins/form-builder/client/index.d.ts +153 -174
  581. package/dist/plugins/form-builder/permissions.cjs +95 -0
  582. package/dist/plugins/form-builder/permissions.d.cts +94 -0
  583. package/dist/plugins/form-builder/permissions.d.mts +94 -0
  584. package/dist/plugins/form-builder/permissions.d.ts +94 -0
  585. package/dist/plugins/form-builder/permissions.mjs +93 -0
  586. package/dist/plugins/form-builder/query-keys.cjs +11 -4
  587. package/dist/plugins/form-builder/query-keys.d.cts +38 -12
  588. package/dist/plugins/form-builder/query-keys.d.mts +38 -12
  589. package/dist/plugins/form-builder/query-keys.d.ts +38 -12
  590. package/dist/plugins/form-builder/query-keys.mjs +11 -4
  591. package/dist/plugins/kanban/api/index.cjs +9 -0
  592. package/dist/plugins/kanban/api/index.d.cts +137 -5
  593. package/dist/plugins/kanban/api/index.d.mts +137 -5
  594. package/dist/plugins/kanban/api/index.d.ts +137 -5
  595. package/dist/plugins/kanban/api/index.mjs +5 -2
  596. package/dist/plugins/kanban/client/components/index.d.cts +18 -10
  597. package/dist/plugins/kanban/client/components/index.d.mts +18 -10
  598. package/dist/plugins/kanban/client/components/index.d.ts +18 -10
  599. package/dist/plugins/kanban/client/hooks/index.d.cts +15 -7
  600. package/dist/plugins/kanban/client/hooks/index.d.mts +15 -7
  601. package/dist/plugins/kanban/client/hooks/index.d.ts +15 -7
  602. package/dist/plugins/kanban/client/index.d.cts +32 -28
  603. package/dist/plugins/kanban/client/index.d.mts +32 -28
  604. package/dist/plugins/kanban/client/index.d.ts +32 -28
  605. package/dist/plugins/kanban/permissions.cjs +101 -0
  606. package/dist/plugins/kanban/permissions.d.cts +109 -0
  607. package/dist/plugins/kanban/permissions.d.mts +109 -0
  608. package/dist/plugins/kanban/permissions.d.ts +109 -0
  609. package/dist/plugins/kanban/permissions.mjs +99 -0
  610. package/dist/plugins/kanban/query-keys.cjs +14 -15
  611. package/dist/plugins/kanban/query-keys.d.cts +63 -42
  612. package/dist/plugins/kanban/query-keys.d.mts +63 -42
  613. package/dist/plugins/kanban/query-keys.d.ts +63 -42
  614. package/dist/plugins/kanban/query-keys.mjs +14 -15
  615. package/dist/plugins/media/api/adapters/local.d.cts +1 -1
  616. package/dist/plugins/media/api/adapters/local.d.mts +1 -1
  617. package/dist/plugins/media/api/adapters/local.d.ts +1 -1
  618. package/dist/plugins/media/api/adapters/s3.d.cts +1 -1
  619. package/dist/plugins/media/api/adapters/s3.d.mts +1 -1
  620. package/dist/plugins/media/api/adapters/s3.d.ts +1 -1
  621. package/dist/plugins/media/api/adapters/vercel-blob.cjs +46 -0
  622. package/dist/plugins/media/api/adapters/vercel-blob.d.cts +6 -9
  623. package/dist/plugins/media/api/adapters/vercel-blob.d.mts +6 -9
  624. package/dist/plugins/media/api/adapters/vercel-blob.d.ts +6 -9
  625. package/dist/plugins/media/api/adapters/vercel-blob.mjs +46 -0
  626. package/dist/plugins/media/api/index.cjs +10 -0
  627. package/dist/plugins/media/api/index.d.cts +93 -6
  628. package/dist/plugins/media/api/index.d.mts +93 -6
  629. package/dist/plugins/media/api/index.d.ts +93 -6
  630. package/dist/plugins/media/api/index.mjs +2 -0
  631. package/dist/plugins/media/client/components/index.d.cts +1 -1
  632. package/dist/plugins/media/client/components/index.d.mts +1 -1
  633. package/dist/plugins/media/client/components/index.d.ts +1 -1
  634. package/dist/plugins/media/client/hooks/index.d.cts +20 -10
  635. package/dist/plugins/media/client/hooks/index.d.mts +20 -10
  636. package/dist/plugins/media/client/hooks/index.d.ts +20 -10
  637. package/dist/plugins/media/client/index.cjs +1 -0
  638. package/dist/plugins/media/client/index.d.cts +112 -100
  639. package/dist/plugins/media/client/index.d.mts +112 -100
  640. package/dist/plugins/media/client/index.d.ts +112 -100
  641. package/dist/plugins/media/client/index.mjs +1 -1
  642. package/dist/plugins/media/permissions.cjs +63 -0
  643. package/dist/plugins/media/permissions.d.cts +64 -0
  644. package/dist/plugins/media/permissions.d.mts +64 -0
  645. package/dist/plugins/media/permissions.d.ts +64 -0
  646. package/dist/plugins/media/permissions.mjs +61 -0
  647. package/dist/plugins/media/query-keys.cjs +29 -17
  648. package/dist/plugins/media/query-keys.d.cts +24 -31
  649. package/dist/plugins/media/query-keys.d.mts +24 -31
  650. package/dist/plugins/media/query-keys.d.ts +24 -31
  651. package/dist/plugins/media/query-keys.mjs +30 -18
  652. package/dist/plugins/open-api/api/index.d.cts +9 -4
  653. package/dist/plugins/open-api/api/index.d.mts +9 -4
  654. package/dist/plugins/open-api/api/index.d.ts +9 -4
  655. package/dist/plugins/route-docs/client/index.cjs +2 -2
  656. package/dist/plugins/route-docs/client/index.d.cts +54 -53
  657. package/dist/plugins/route-docs/client/index.d.mts +54 -53
  658. package/dist/plugins/route-docs/client/index.d.ts +54 -53
  659. package/dist/plugins/route-docs/client/index.mjs +2 -1
  660. package/dist/plugins/ui-builder/client/components/index.d.cts +8 -5
  661. package/dist/plugins/ui-builder/client/components/index.d.mts +8 -5
  662. package/dist/plugins/ui-builder/client/components/index.d.ts +8 -5
  663. package/dist/plugins/ui-builder/client/hooks/index.d.cts +5 -5
  664. package/dist/plugins/ui-builder/client/hooks/index.d.mts +5 -5
  665. package/dist/plugins/ui-builder/client/hooks/index.d.ts +5 -5
  666. package/dist/plugins/ui-builder/client/index.d.cts +151 -199
  667. package/dist/plugins/ui-builder/client/index.d.mts +151 -199
  668. package/dist/plugins/ui-builder/client/index.d.ts +151 -199
  669. package/dist/plugins/ui-builder/index.d.cts +11 -8
  670. package/dist/plugins/ui-builder/index.d.mts +11 -8
  671. package/dist/plugins/ui-builder/index.d.ts +11 -8
  672. package/dist/plugins/ui-builder/query-keys.d.cts +6 -6
  673. package/dist/plugins/ui-builder/query-keys.d.mts +6 -6
  674. package/dist/plugins/ui-builder/query-keys.d.ts +6 -6
  675. package/dist/react-router/index.cjs +2 -0
  676. package/dist/react-router/index.d.cts +41 -7
  677. package/dist/react-router/index.d.mts +41 -7
  678. package/dist/react-router/index.d.ts +41 -7
  679. package/dist/react-router/index.mjs +1 -0
  680. package/dist/shared/stack.0O62uXqP.d.ts +613 -0
  681. package/dist/shared/stack.0y6iTP03.d.ts +533 -0
  682. package/dist/shared/{stack.CbZenbej.d.cts → stack.1ukVJym7.d.cts} +3 -4
  683. package/dist/shared/{stack.CbZenbej.d.mts → stack.1ukVJym7.d.mts} +3 -4
  684. package/dist/shared/{stack.CbZenbej.d.ts → stack.1ukVJym7.d.ts} +3 -4
  685. package/dist/shared/stack.3Sr6qRtT.d.ts +507 -0
  686. package/dist/shared/{stack.9BAOaSqE.d.mts → stack.5_zzCejP.d.cts} +16 -14
  687. package/dist/shared/{stack.9BAOaSqE.d.ts → stack.5_zzCejP.d.mts} +16 -14
  688. package/dist/shared/{stack.9BAOaSqE.d.cts → stack.5_zzCejP.d.ts} +16 -14
  689. package/dist/shared/stack.B1UrNoVl.d.mts +533 -0
  690. package/dist/shared/{stack.B2xi4jj7.d.cts → stack.B1srlBud.d.mts} +2 -2
  691. package/dist/shared/stack.B3_aDcYL.d.cts +281 -0
  692. package/dist/shared/stack.B3_aDcYL.d.mts +281 -0
  693. package/dist/shared/stack.B3_aDcYL.d.ts +281 -0
  694. package/dist/shared/stack.B5yPgHsp.d.mts +507 -0
  695. package/dist/shared/stack.B8PG21zX.d.mts +764 -0
  696. package/dist/shared/stack.BFP_9pRX.d.cts +707 -0
  697. package/dist/shared/{stack.INBa9-TO.d.cts → stack.BGzgFx3R.d.cts} +5 -5
  698. package/dist/shared/stack.BJx1ZXrN.d.cts +118 -0
  699. package/dist/shared/{stack.7b3vsM8_.d.ts → stack.BK9Z2dcL.d.ts} +2 -2
  700. package/dist/shared/stack.BMHoxAn7.d.mts +707 -0
  701. package/dist/shared/stack.BMmvEBH_.d.ts +118 -0
  702. package/dist/shared/stack.BOZRaVpu.d.cts +1184 -0
  703. package/dist/shared/stack.BPLykw4s.d.ts +436 -0
  704. package/dist/shared/{stack.aqVZ6PEW.d.mts → stack.BRvqQWGt.d.cts} +40 -1
  705. package/dist/shared/{stack.aqVZ6PEW.d.cts → stack.BRvqQWGt.d.mts} +40 -1
  706. package/dist/shared/{stack.aqVZ6PEW.d.ts → stack.BRvqQWGt.d.ts} +40 -1
  707. package/dist/shared/stack.BSACE667.d.cts +23 -0
  708. package/dist/shared/stack.BYMentwB.d.cts +47 -0
  709. package/dist/shared/stack.BYMentwB.d.mts +47 -0
  710. package/dist/shared/stack.BYMentwB.d.ts +47 -0
  711. package/dist/shared/stack.Bt8hfw9o.d.ts +707 -0
  712. package/dist/shared/{stack.D7bewlgt.d.mts → stack.Bu2IPOLi.d.cts} +1 -0
  713. package/dist/shared/{stack.D7bewlgt.d.ts → stack.Bu2IPOLi.d.mts} +1 -0
  714. package/dist/shared/{stack.D7bewlgt.d.cts → stack.Bu2IPOLi.d.ts} +1 -0
  715. package/dist/shared/stack.BuxUQqL8.d.ts +408 -0
  716. package/dist/shared/stack.BzWGB5MG.d.cts +436 -0
  717. package/dist/shared/stack.C3bU9w6L.d.ts +351 -0
  718. package/dist/shared/stack.C4oVW2qK.d.cts +507 -0
  719. package/dist/shared/stack.CKXzdGiJ.d.cts +3 -0
  720. package/dist/shared/stack.CKXzdGiJ.d.mts +3 -0
  721. package/dist/shared/stack.CKXzdGiJ.d.ts +3 -0
  722. package/dist/shared/{stack.C3GBG8in.d.ts → stack.COrJrztD.d.cts} +1 -1
  723. package/dist/shared/{stack.C3GBG8in.d.cts → stack.COrJrztD.d.mts} +1 -1
  724. package/dist/shared/{stack.C3GBG8in.d.mts → stack.COrJrztD.d.ts} +1 -1
  725. package/dist/shared/stack.CX83YNuS.d.cts +408 -0
  726. package/dist/shared/stack.Cbd7sD5M.d.mts +118 -0
  727. package/dist/shared/{stack.B9NtGpAR.d.ts → stack.CcjENrr4.d.cts} +1 -5
  728. package/dist/shared/stack.CfK5NKlK.d.mts +613 -0
  729. package/dist/shared/{stack.3di7vQs6.d.mts → stack.CjCSDfWy.d.mts} +5 -5
  730. package/dist/shared/stack.Cmg7lzzy.d.cts +44 -0
  731. package/dist/shared/stack.Cmg7lzzy.d.mts +44 -0
  732. package/dist/shared/stack.Cmg7lzzy.d.ts +44 -0
  733. package/dist/shared/stack.CpE1mS3p.d.ts +331 -0
  734. package/dist/shared/stack.CskhFw3z.d.mts +23 -0
  735. package/dist/shared/stack.CvlC5kqe.d.cts +331 -0
  736. package/dist/shared/stack.CxKc9evq.d.ts +23 -0
  737. package/dist/shared/stack.CyalJ0j6.d.ts +764 -0
  738. package/dist/shared/stack.D6Pg-ufX.d.cts +764 -0
  739. package/dist/shared/{stack.lkebw2nj.d.mts → stack.DVEjWKCT.d.cts} +19 -5
  740. package/dist/shared/{stack.lkebw2nj.d.ts → stack.DVEjWKCT.d.mts} +19 -5
  741. package/dist/shared/{stack.lkebw2nj.d.cts → stack.DVEjWKCT.d.ts} +19 -5
  742. package/dist/shared/{stack.Cp_6mO5s.d.mts → stack.DbbvHbhn.d.mts} +1 -5
  743. package/dist/shared/stack.DiuRuzIs.d.cts +613 -0
  744. package/dist/shared/{stack.BMlLOMED.d.mts → stack.DjGixVx3.d.cts} +6 -2
  745. package/dist/shared/{stack.BMlLOMED.d.ts → stack.DjGixVx3.d.mts} +6 -2
  746. package/dist/shared/{stack.BMlLOMED.d.cts → stack.DjGixVx3.d.ts} +6 -2
  747. package/dist/shared/stack.DlBoNqYu.d.ts +1184 -0
  748. package/dist/shared/stack.DlCycSM-.d.mts +331 -0
  749. package/dist/shared/{stack.DycBcTeg.d.mts → stack.DmpPDPxA.d.cts} +2 -2
  750. package/dist/shared/{stack.CRelKJfH.d.ts → stack.Dpg_JjIo.d.ts} +5 -5
  751. package/dist/shared/stack.Dvy7FmaA.d.mts +1184 -0
  752. package/dist/shared/stack.DxHPKr2R.d.mts +436 -0
  753. package/dist/shared/stack.Egr3RWhA.d.mts +408 -0
  754. package/dist/shared/stack.Mak7pzIV.d.cts +533 -0
  755. package/dist/shared/stack.OYT8WU4n.d.mts +351 -0
  756. package/dist/shared/{stack.CPsYC2-Z.d.cts → stack.fdi94T4S.d.cts} +7 -7
  757. package/dist/shared/{stack.CPsYC2-Z.d.mts → stack.fdi94T4S.d.mts} +7 -7
  758. package/dist/shared/{stack.CPsYC2-Z.d.ts → stack.fdi94T4S.d.ts} +7 -7
  759. package/dist/shared/{stack.Dlgtumru.d.cts → stack.kaCV7vEL.d.ts} +1 -5
  760. package/dist/shared/stack.v2O65JqF.d.cts +351 -0
  761. package/dist/tanstack/index.cjs +2 -0
  762. package/dist/tanstack/index.d.cts +44 -14
  763. package/dist/tanstack/index.d.mts +44 -14
  764. package/dist/tanstack/index.d.ts +44 -14
  765. package/dist/tanstack/index.mjs +1 -0
  766. package/dist/tanstack/server.cjs +9 -0
  767. package/dist/tanstack/server.d.cts +15 -0
  768. package/dist/tanstack/server.d.mts +15 -0
  769. package/dist/tanstack/server.d.ts +15 -0
  770. package/dist/tanstack/server.mjs +7 -0
  771. package/package.json +203 -8
  772. package/src/__tests__/ai-chat-client-runtime.test.ts +481 -0
  773. package/src/__tests__/ai-chat-client-runtime.typecheck.tsx +130 -0
  774. package/src/__tests__/ai-chat-lifecycle-migrations.typecheck.ts +61 -0
  775. package/src/__tests__/ai-chat-query-keys.test.ts +125 -22
  776. package/src/__tests__/auth-provider.test.tsx +41 -314
  777. package/src/__tests__/auth-test-utils.ts +24 -0
  778. package/src/__tests__/authorization-client.test.tsx +767 -0
  779. package/src/__tests__/authorization-contraction.test.ts +182 -0
  780. package/src/__tests__/authorization-remote.test.ts +222 -0
  781. package/src/__tests__/authorization-server.test.ts +90 -0
  782. package/src/__tests__/authorization.test.ts +652 -0
  783. package/src/__tests__/authorization.typecheck.ts +671 -0
  784. package/src/__tests__/backend-plugin-factories.test.ts +54 -0
  785. package/src/__tests__/backend-plugin-factories.typecheck.ts +133 -0
  786. package/src/__tests__/client-plugin-ssr-loaders.test.ts +304 -138
  787. package/src/__tests__/client-runtime.test.ts +1021 -0
  788. package/src/__tests__/client-runtime.typecheck.ts +107 -0
  789. package/src/__tests__/client-stack-test-utils.ts +36 -0
  790. package/src/__tests__/content-lifecycle-migrations.typecheck.ts +122 -0
  791. package/src/__tests__/endpoint-inventory.test.ts +768 -0
  792. package/src/__tests__/entry-factories.test.tsx +104 -4
  793. package/src/__tests__/form-builder-query-keys.test.ts +32 -0
  794. package/src/__tests__/i18n-provider.test.tsx +4 -3
  795. package/src/__tests__/initial-identity-hydration.test.tsx +156 -0
  796. package/src/__tests__/initial-identity-layouts.test.tsx +204 -0
  797. package/src/__tests__/kanban-query-keys.test.ts +189 -2
  798. package/src/__tests__/managed-origin-hydration.browser.test.tsx +163 -0
  799. package/src/__tests__/media-query-keys.test.ts +91 -17
  800. package/src/__tests__/notify-provider.test.tsx +3 -2
  801. package/src/__tests__/package-metadata.test.ts +315 -0
  802. package/src/__tests__/page-component-overrides.test.tsx +3 -4
  803. package/src/__tests__/plugin-registration.test.tsx +183 -0
  804. package/src/__tests__/plugin-registration.typecheck.tsx +139 -0
  805. package/src/__tests__/plugins.test.tsx +72 -42
  806. package/src/__tests__/resource-factory.test.tsx +79 -2
  807. package/src/__tests__/resource-use-form.test.tsx +2 -2
  808. package/src/__tests__/resource-use-select.test.tsx +2 -4
  809. package/src/__tests__/router-overrides.test.tsx +20 -8
  810. package/src/__tests__/sitemap.test.ts +38 -22
  811. package/src/__tests__/stack-api.test.ts +39 -34
  812. package/src/__tests__/stack-auth.test.ts +44 -140
  813. package/src/__tests__/stack-constructors.test.ts +96 -0
  814. package/src/__tests__/stack-constructors.typecheck.ts +115 -0
  815. package/src/__tests__/use-list-state.test.tsx +7 -6
  816. package/src/__tests__/workflow-lifecycle-migrations.typecheck.ts +150 -0
  817. package/src/api/index.ts +204 -99
  818. package/src/authorization/client.tsx +310 -0
  819. package/src/authorization/index.ts +873 -0
  820. package/src/authorization/json.ts +70 -0
  821. package/src/authorization/remote.ts +234 -0
  822. package/src/authorization/server.ts +171 -0
  823. package/src/client/__tests__/server-origin.test.ts +119 -0
  824. package/src/client/components/compose.tsx +126 -18
  825. package/src/client/hooks/index.ts +12 -0
  826. package/src/client/hooks/use-list-state.ts +7 -13
  827. package/src/client/index.ts +105 -50
  828. package/src/client/runtime.ts +414 -0
  829. package/src/client/server.ts +174 -0
  830. package/src/context/auth.tsx +205 -154
  831. package/src/context/index.ts +10 -4
  832. package/src/context/provider.tsx +96 -57
  833. package/src/context/router.tsx +7 -0
  834. package/src/context/site-navigation.ts +52 -0
  835. package/src/next/index.tsx +2 -0
  836. package/src/next/layout.tsx +81 -0
  837. package/src/next/page.tsx +33 -12
  838. package/src/next/router.tsx +1 -1
  839. package/src/next/server.ts +5 -0
  840. package/src/plugin-registration.ts +70 -0
  841. package/src/plugins/ai-chat/__tests__/authorization.test.ts +2051 -0
  842. package/src/plugins/ai-chat/__tests__/chat-hooks.test.tsx +147 -0
  843. package/src/plugins/ai-chat/__tests__/chat-input-authorization.test.tsx +188 -0
  844. package/src/plugins/ai-chat/__tests__/client-sweep.test.tsx +1506 -34
  845. package/src/plugins/ai-chat/__tests__/hooks.test.tsx +131 -23
  846. package/src/plugins/ai-chat/api/getters.ts +2 -2
  847. package/src/plugins/ai-chat/api/index.ts +6 -1
  848. package/src/plugins/ai-chat/api/lifecycle-migrations.ts +20 -0
  849. package/src/plugins/ai-chat/api/operations.ts +1865 -0
  850. package/src/plugins/ai-chat/api/plugin.ts +78 -1168
  851. package/src/plugins/ai-chat/api/serializers.ts +30 -0
  852. package/src/plugins/ai-chat/client/components/chat-input.tsx +228 -129
  853. package/src/plugins/ai-chat/client/components/chat-interface.tsx +776 -183
  854. package/src/plugins/ai-chat/client/components/chat-layout.tsx +6 -7
  855. package/src/plugins/ai-chat/client/components/chat-message.tsx +1 -1
  856. package/src/plugins/ai-chat/client/components/chat-sidebar.tsx +57 -37
  857. package/src/plugins/ai-chat/client/components/pages/404-page.tsx +1 -1
  858. package/src/plugins/ai-chat/client/components/pages/chat-page.internal.tsx +5 -3
  859. package/src/plugins/ai-chat/client/components/pages/chat-page.tsx +56 -15
  860. package/src/plugins/ai-chat/client/components/shared/default-error.tsx +1 -1
  861. package/src/plugins/ai-chat/client/components/tool-call-display.tsx +1 -1
  862. package/src/plugins/ai-chat/client/hooks/ai-chat-resource.ts +1 -1
  863. package/src/plugins/ai-chat/client/hooks/chat-hooks.tsx +253 -20
  864. package/src/plugins/ai-chat/client/index.ts +1 -0
  865. package/src/plugins/ai-chat/client/navigation.ts +11 -0
  866. package/src/plugins/ai-chat/client/overrides.ts +47 -11
  867. package/src/plugins/ai-chat/client/plugin.tsx +208 -111
  868. package/src/plugins/ai-chat/permissions.ts +89 -0
  869. package/src/plugins/ai-chat/query-keys.ts +183 -25
  870. package/src/plugins/ai-chat/types.ts +7 -5
  871. package/src/plugins/api/create-endpoint.ts +40 -22
  872. package/src/plugins/api/endpoint-inventory.ts +206 -0
  873. package/src/plugins/api/index.ts +38 -4
  874. package/src/plugins/api/operation.ts +600 -0
  875. package/src/plugins/blog/__tests__/authorization.test.ts +1366 -0
  876. package/src/plugins/blog/__tests__/client-runtime.browser.test.tsx +257 -0
  877. package/src/plugins/blog/__tests__/client-runtime.test.ts +283 -0
  878. package/src/plugins/blog/__tests__/client-runtime.typecheck.tsx +98 -0
  879. package/src/plugins/blog/__tests__/client-sweep.test.tsx +212 -37
  880. package/src/plugins/blog/api/getters.ts +3 -3
  881. package/src/plugins/blog/api/index.ts +2 -0
  882. package/src/plugins/blog/api/lifecycle-migrations.ts +19 -0
  883. package/src/plugins/blog/api/mutations.ts +26 -5
  884. package/src/plugins/blog/api/operations.ts +727 -0
  885. package/src/plugins/blog/api/plugin.ts +99 -504
  886. package/src/plugins/blog/client/components/forms/image-field.tsx +2 -1
  887. package/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.tsx +2 -1
  888. package/src/plugins/blog/client/components/forms/post-forms.tsx +61 -33
  889. package/src/plugins/blog/client/components/pages/404-page.tsx +3 -1
  890. package/src/plugins/blog/client/components/pages/edit-post-page.internal.tsx +2 -1
  891. package/src/plugins/blog/client/components/pages/edit-post-page.tsx +28 -5
  892. package/src/plugins/blog/client/components/pages/home-page.internal.tsx +2 -1
  893. package/src/plugins/blog/client/components/pages/home-page.tsx +7 -4
  894. package/src/plugins/blog/client/components/pages/new-post-page.internal.tsx +2 -1
  895. package/src/plugins/blog/client/components/pages/new-post-page.tsx +5 -2
  896. package/src/plugins/blog/client/components/pages/post-page.internal.tsx +2 -1
  897. package/src/plugins/blog/client/components/pages/post-page.tsx +38 -4
  898. package/src/plugins/blog/client/components/pages/tag-page.internal.tsx +2 -1
  899. package/src/plugins/blog/client/components/pages/tag-page.tsx +5 -1
  900. package/src/plugins/blog/client/components/shared/collapsible-tag-list.tsx +2 -1
  901. package/src/plugins/blog/client/components/shared/default-error.tsx +3 -1
  902. package/src/plugins/blog/client/components/shared/on-this-page.tsx +5 -2
  903. package/src/plugins/blog/client/components/shared/page-wrapper.tsx +2 -1
  904. package/src/plugins/blog/client/components/shared/post-card.tsx +2 -1
  905. package/src/plugins/blog/client/components/shared/posts-list.tsx +2 -1
  906. package/src/plugins/blog/client/components/shared/recent-posts-carousel.tsx +2 -1
  907. package/src/plugins/blog/client/components/shared/search-input.tsx +1 -1
  908. package/src/plugins/blog/client/constants.ts +2 -0
  909. package/src/plugins/blog/client/hooks/blog-resource.ts +2 -1
  910. package/src/plugins/blog/client/overrides.ts +1 -6
  911. package/src/plugins/blog/client/plugin.tsx +145 -129
  912. package/src/plugins/blog/permissions.ts +44 -0
  913. package/src/plugins/client/index.ts +108 -7
  914. package/src/plugins/client/resource/hooks.tsx +41 -18
  915. package/src/plugins/client/resource/internal.ts +77 -61
  916. package/src/plugins/client/resource/queries.ts +3 -4
  917. package/src/plugins/client/resource/use-form.ts +23 -20
  918. package/src/plugins/client/resource/use-select.ts +40 -34
  919. package/src/plugins/cms/__tests__/authorization.test.ts +2188 -0
  920. package/src/plugins/cms/__tests__/client-runtime.browser.test.tsx +245 -0
  921. package/src/plugins/cms/__tests__/client-runtime.test.ts +320 -0
  922. package/src/plugins/cms/__tests__/client-runtime.typecheck.tsx +208 -0
  923. package/src/plugins/cms/__tests__/client-sweep.test.tsx +202 -50
  924. package/src/plugins/cms/api/getters.ts +4 -4
  925. package/src/plugins/cms/api/index.ts +20 -0
  926. package/src/plugins/cms/api/lifecycle-migrations.ts +15 -0
  927. package/src/plugins/cms/api/mutations.ts +2 -2
  928. package/src/plugins/cms/api/operations.ts +2218 -0
  929. package/src/plugins/cms/api/plugin.ts +169 -1047
  930. package/src/plugins/cms/api/relations.ts +3 -1
  931. package/src/plugins/cms/client/components/forms/content-form.tsx +2 -1
  932. package/src/plugins/cms/client/components/forms/file-upload.tsx +3 -1
  933. package/src/plugins/cms/client/components/forms/relation-field.tsx +3 -1
  934. package/src/plugins/cms/client/components/inverse-relations-panel.tsx +29 -17
  935. package/src/plugins/cms/client/components/pages/404-page.tsx +7 -5
  936. package/src/plugins/cms/client/components/pages/content-editor-page.internal.tsx +19 -10
  937. package/src/plugins/cms/client/components/pages/content-editor-page.tsx +50 -12
  938. package/src/plugins/cms/client/components/pages/content-list-page.internal.tsx +48 -34
  939. package/src/plugins/cms/client/components/pages/content-list-page.tsx +8 -1
  940. package/src/plugins/cms/client/components/pages/dashboard-page.internal.tsx +15 -8
  941. package/src/plugins/cms/client/components/pages/dashboard-page.tsx +5 -1
  942. package/src/plugins/cms/client/components/shared/page-wrapper.tsx +2 -1
  943. package/src/plugins/cms/client/constants.ts +2 -0
  944. package/src/plugins/cms/client/hooks/cms-resource.ts +2 -1
  945. package/src/plugins/cms/client/index.ts +6 -1
  946. package/src/plugins/cms/client/overrides.ts +46 -6
  947. package/src/plugins/cms/client/plugin.tsx +135 -80
  948. package/src/plugins/cms/permissions.ts +43 -0
  949. package/src/plugins/cms/types.ts +174 -46
  950. package/src/plugins/comments/__tests__/authorization.test.ts +1570 -0
  951. package/src/plugins/comments/__tests__/client-runtime.browser.test.tsx +207 -0
  952. package/src/plugins/comments/__tests__/client-runtime.test.ts +224 -0
  953. package/src/plugins/comments/__tests__/client-runtime.typecheck.tsx +85 -0
  954. package/src/plugins/comments/__tests__/client-sweep.test.tsx +465 -79
  955. package/src/plugins/comments/api/getters.ts +19 -3
  956. package/src/plugins/comments/api/index.ts +2 -6
  957. package/src/plugins/comments/api/lifecycle-migrations.ts +20 -0
  958. package/src/plugins/comments/api/mutations.ts +261 -88
  959. package/src/plugins/comments/api/operations.ts +742 -0
  960. package/src/plugins/comments/api/plugin.ts +76 -548
  961. package/src/plugins/comments/client/components/comment-count.tsx +38 -5
  962. package/src/plugins/comments/client/components/comment-thread.tsx +154 -112
  963. package/src/plugins/comments/client/components/pages/moderation-page.internal.tsx +144 -78
  964. package/src/plugins/comments/client/components/pages/moderation-page.tsx +18 -2
  965. package/src/plugins/comments/client/components/pages/moderation-state.ts +13 -0
  966. package/src/plugins/comments/client/components/pages/my-comments-page.internal.tsx +38 -30
  967. package/src/plugins/comments/client/components/pages/my-comments-page.tsx +3 -1
  968. package/src/plugins/comments/client/components/pages/resource-comments-page.internal.tsx +29 -30
  969. package/src/plugins/comments/client/components/pages/resource-comments-page.tsx +10 -1
  970. package/src/plugins/comments/client/components/shared/page-wrapper.tsx +2 -1
  971. package/src/plugins/comments/client/components/shared/pagination.tsx +2 -1
  972. package/src/plugins/comments/client/constants.ts +2 -0
  973. package/src/plugins/comments/client/hooks/use-comments.tsx +430 -421
  974. package/src/plugins/comments/client/localization/comments-thread.ts +1 -0
  975. package/src/plugins/comments/client/plugin.tsx +113 -72
  976. package/src/plugins/comments/permissions.ts +68 -0
  977. package/src/plugins/comments/query-keys.ts +9 -10
  978. package/src/plugins/comments/schemas.ts +7 -8
  979. package/src/plugins/form-builder/__tests__/authorization.test.ts +1600 -0
  980. package/src/plugins/form-builder/__tests__/client-runtime.browser.test.tsx +255 -0
  981. package/src/plugins/form-builder/__tests__/client-runtime.test.ts +132 -0
  982. package/src/plugins/form-builder/__tests__/client-runtime.typecheck.tsx +77 -0
  983. package/src/plugins/form-builder/__tests__/client-sweep.test.tsx +350 -54
  984. package/src/plugins/form-builder/__tests__/form-renderer-authorization.test.tsx +162 -0
  985. package/src/plugins/form-builder/api/getters.ts +21 -5
  986. package/src/plugins/form-builder/api/index.ts +31 -0
  987. package/src/plugins/form-builder/api/lifecycle-migrations.ts +17 -0
  988. package/src/plugins/form-builder/api/operations.ts +1493 -0
  989. package/src/plugins/form-builder/api/plugin.ts +125 -632
  990. package/src/plugins/form-builder/api/query-key-defs.ts +7 -0
  991. package/src/plugins/form-builder/api/serializers.ts +1 -0
  992. package/src/plugins/form-builder/client/components/forms/form-renderer.tsx +49 -19
  993. package/src/plugins/form-builder/client/components/pages/404-page.tsx +9 -9
  994. package/src/plugins/form-builder/client/components/pages/form-builder-page.internal.tsx +54 -30
  995. package/src/plugins/form-builder/client/components/pages/form-builder-page.tsx +42 -9
  996. package/src/plugins/form-builder/client/components/pages/form-list-page.internal.tsx +46 -34
  997. package/src/plugins/form-builder/client/components/pages/form-list-page.tsx +6 -2
  998. package/src/plugins/form-builder/client/components/pages/submissions-page.internal.tsx +98 -64
  999. package/src/plugins/form-builder/client/components/pages/submissions-page.tsx +29 -9
  1000. package/src/plugins/form-builder/client/components/shared/page-wrapper.tsx +2 -1
  1001. package/src/plugins/form-builder/client/constants.ts +2 -0
  1002. package/src/plugins/form-builder/client/hooks/form-builder-hooks.tsx +68 -4
  1003. package/src/plugins/form-builder/client/hooks/form-builder-resource.ts +2 -1
  1004. package/src/plugins/form-builder/client/hooks/index.tsx +1 -0
  1005. package/src/plugins/form-builder/client/overrides.ts +1 -6
  1006. package/src/plugins/form-builder/client/plugin.tsx +128 -96
  1007. package/src/plugins/form-builder/permissions.ts +94 -0
  1008. package/src/plugins/form-builder/query-keys.ts +28 -3
  1009. package/src/plugins/form-builder/schemas.ts +9 -1
  1010. package/src/plugins/form-builder/types.ts +173 -41
  1011. package/src/plugins/kanban/__tests__/authorization.test.ts +1382 -0
  1012. package/src/plugins/kanban/__tests__/client-runtime.browser.test.tsx +302 -0
  1013. package/src/plugins/kanban/__tests__/client-runtime.test.ts +130 -0
  1014. package/src/plugins/kanban/__tests__/client-runtime.typecheck.tsx +81 -0
  1015. package/src/plugins/kanban/__tests__/client-sweep.test.tsx +418 -33
  1016. package/src/plugins/kanban/__tests__/identity-partition.test.tsx +306 -0
  1017. package/src/plugins/kanban/api/getters.ts +65 -50
  1018. package/src/plugins/kanban/api/index.ts +55 -4
  1019. package/src/plugins/kanban/api/lifecycle-migrations.ts +25 -0
  1020. package/src/plugins/kanban/api/mutations.ts +3 -3
  1021. package/src/plugins/kanban/api/operations.ts +2065 -0
  1022. package/src/plugins/kanban/api/plugin.ts +83 -1013
  1023. package/src/plugins/kanban/api/serializers.ts +26 -0
  1024. package/src/plugins/kanban/client/components/forms/board-form.tsx +3 -1
  1025. package/src/plugins/kanban/client/components/forms/column-form.tsx +3 -1
  1026. package/src/plugins/kanban/client/components/forms/task-form.tsx +92 -74
  1027. package/src/plugins/kanban/client/components/pages/404-page.tsx +6 -9
  1028. package/src/plugins/kanban/client/components/pages/board-page.internal.tsx +57 -74
  1029. package/src/plugins/kanban/client/components/pages/board-page.tsx +31 -7
  1030. package/src/plugins/kanban/client/components/pages/boards-list-page.internal.tsx +54 -45
  1031. package/src/plugins/kanban/client/components/pages/boards-list-page.tsx +2 -1
  1032. package/src/plugins/kanban/client/components/pages/new-board-page.internal.tsx +8 -12
  1033. package/src/plugins/kanban/client/components/pages/new-board-page.tsx +2 -1
  1034. package/src/plugins/kanban/client/components/shared/column-content.tsx +61 -48
  1035. package/src/plugins/kanban/client/components/shared/default-error.tsx +3 -1
  1036. package/src/plugins/kanban/client/components/shared/kanban-board.tsx +6 -6
  1037. package/src/plugins/kanban/client/components/shared/permission-access-any.tsx +32 -0
  1038. package/src/plugins/kanban/client/components/shared/task-card.tsx +64 -20
  1039. package/src/plugins/kanban/client/components/shared/user-avatar.tsx +3 -1
  1040. package/src/plugins/kanban/client/constants.ts +2 -0
  1041. package/src/plugins/kanban/client/hooks/kanban-hooks.tsx +55 -10
  1042. package/src/plugins/kanban/client/hooks/kanban-resource.ts +2 -1
  1043. package/src/plugins/kanban/client/overrides.ts +0 -5
  1044. package/src/plugins/kanban/client/plugin.tsx +201 -113
  1045. package/src/plugins/kanban/permissions.ts +100 -0
  1046. package/src/plugins/kanban/query-keys.ts +61 -21
  1047. package/src/plugins/kanban/schemas.ts +44 -18
  1048. package/src/plugins/kanban/types.ts +381 -1
  1049. package/src/plugins/media/__tests__/authorization.test.ts +1769 -0
  1050. package/src/plugins/media/__tests__/client-runtime.browser.test.tsx +975 -0
  1051. package/src/plugins/media/__tests__/client-runtime.test.ts +539 -0
  1052. package/src/plugins/media/__tests__/client-runtime.typecheck.tsx +135 -0
  1053. package/src/plugins/media/__tests__/client-sweep.test.tsx +292 -24
  1054. package/src/plugins/media/__tests__/identity-partition.test.tsx +741 -0
  1055. package/src/plugins/media/__tests__/plugin.test.ts +68 -28
  1056. package/src/plugins/media/__tests__/query-key-defs.test.ts +32 -2
  1057. package/src/plugins/media/api/adapters/vercel-blob.ts +84 -8
  1058. package/src/plugins/media/api/getters.ts +6 -6
  1059. package/src/plugins/media/api/index.ts +3 -0
  1060. package/src/plugins/media/api/lifecycle-migrations.ts +11 -0
  1061. package/src/plugins/media/api/mutations.ts +19 -13
  1062. package/src/plugins/media/api/operations.ts +1873 -0
  1063. package/src/plugins/media/api/plugin.ts +211 -899
  1064. package/src/plugins/media/api/query-key-defs.ts +66 -4
  1065. package/src/plugins/media/api/serializers.ts +4 -2
  1066. package/src/plugins/media/api/storage-adapter.ts +22 -4
  1067. package/src/plugins/media/asset-url.ts +14 -0
  1068. package/src/plugins/media/client/components/media-picker/asset-card.tsx +105 -98
  1069. package/src/plugins/media/client/components/media-picker/folder-tree.tsx +82 -60
  1070. package/src/plugins/media/client/components/media-picker/index.tsx +235 -171
  1071. package/src/plugins/media/client/components/media-picker/upload-tab.tsx +2 -10
  1072. package/src/plugins/media/client/components/media-picker/url-tab.tsx +1 -8
  1073. package/src/plugins/media/client/components/pages/library-page.internal.tsx +103 -82
  1074. package/src/plugins/media/client/components/pages/library-page.tsx +4 -2
  1075. package/src/plugins/media/client/components/upload-permission-check.tsx +75 -0
  1076. package/src/plugins/media/client/constants.ts +2 -0
  1077. package/src/plugins/media/client/hooks/media-resource.ts +2 -1
  1078. package/src/plugins/media/client/hooks/use-media.tsx +292 -49
  1079. package/src/plugins/media/client/index.ts +16 -3
  1080. package/src/plugins/media/client/overrides.ts +6 -17
  1081. package/src/plugins/media/client/plugin.tsx +188 -123
  1082. package/src/plugins/media/client/upload.ts +170 -53
  1083. package/src/plugins/media/db.ts +8 -0
  1084. package/src/plugins/media/permissions.ts +61 -0
  1085. package/src/plugins/media/query-keys.ts +63 -19
  1086. package/src/plugins/media/types.ts +8 -2
  1087. package/src/plugins/open-api/__tests__/route-introspection.test.ts +277 -0
  1088. package/src/plugins/open-api/api/generator.ts +49 -8
  1089. package/src/plugins/open-api/api/plugin.ts +36 -14
  1090. package/src/plugins/route-docs/client/components/pages/docs-page.tsx +63 -23
  1091. package/src/plugins/route-docs/client/constants.ts +2 -0
  1092. package/src/plugins/route-docs/client/hooks.ts +23 -13
  1093. package/src/plugins/route-docs/client/index.ts +7 -1
  1094. package/src/plugins/route-docs/client/plugin.tsx +222 -145
  1095. package/src/plugins/route-docs/client/schema.ts +31 -0
  1096. package/src/plugins/route-docs/generator.ts +9 -5
  1097. package/src/plugins/ui-builder/__tests__/client-sweep.test.tsx +427 -48
  1098. package/src/plugins/ui-builder/client/components/page-renderer.tsx +58 -16
  1099. package/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.tsx +47 -26
  1100. package/src/plugins/ui-builder/client/components/pages/page-builder-page.tsx +43 -8
  1101. package/src/plugins/ui-builder/client/components/pages/page-list-page.internal.tsx +38 -19
  1102. package/src/plugins/ui-builder/client/components/pages/page-list-page.tsx +8 -2
  1103. package/src/plugins/ui-builder/client/components/shared/default-error.tsx +2 -1
  1104. package/src/plugins/ui-builder/client/components/shared/page-wrapper.tsx +2 -1
  1105. package/src/plugins/ui-builder/client/constants.ts +2 -0
  1106. package/src/plugins/ui-builder/client/hooks/ui-builder-resource.ts +2 -1
  1107. package/src/plugins/ui-builder/client/index.ts +4 -1
  1108. package/src/plugins/ui-builder/client/overrides.ts +16 -21
  1109. package/src/plugins/ui-builder/client/plugin.tsx +119 -66
  1110. package/src/plugins/ui-builder/client/registry.ts +3 -5
  1111. package/src/plugins/ui-builder/types.ts +7 -4
  1112. package/src/plugins/utils.ts +5 -1
  1113. package/src/react-router/index.tsx +6 -0
  1114. package/src/react-router/layout.ts +43 -0
  1115. package/src/react-router/page.tsx +46 -17
  1116. package/src/react-router/router.tsx +1 -1
  1117. package/src/shared/auth-types.ts +19 -70
  1118. package/src/shared/entry-factories.ts +12 -3
  1119. package/src/shared/initial-identity.ts +73 -0
  1120. package/src/shared/list-state.ts +12 -0
  1121. package/src/shared/types.ts +1 -0
  1122. package/src/tanstack/index.tsx +7 -0
  1123. package/src/tanstack/layout.ts +36 -0
  1124. package/src/tanstack/page.tsx +35 -13
  1125. package/src/tanstack/router.tsx +1 -1
  1126. package/src/tanstack/server.ts +18 -0
  1127. package/src/types.ts +557 -65
  1128. package/dist/shared/stack.80-y2TTR.d.mts +0 -840
  1129. package/dist/shared/stack.B-hEIoUw.d.ts +0 -406
  1130. package/dist/shared/stack.B8vdOMnQ.d.cts +0 -329
  1131. package/dist/shared/stack.BISAZ5LX.d.mts +0 -492
  1132. package/dist/shared/stack.BL5PvShn.d.mts +0 -193
  1133. package/dist/shared/stack.Be1QIHEn.d.cts +0 -23
  1134. package/dist/shared/stack.Be1QIHEn.d.mts +0 -23
  1135. package/dist/shared/stack.Be1QIHEn.d.ts +0 -23
  1136. package/dist/shared/stack.BjvOk_Bs.d.cts +0 -492
  1137. package/dist/shared/stack.Bk5UB_BT.d.ts +0 -840
  1138. package/dist/shared/stack.BozPgbrZ.d.cts +0 -201
  1139. package/dist/shared/stack.BozPgbrZ.d.mts +0 -201
  1140. package/dist/shared/stack.BozPgbrZ.d.ts +0 -201
  1141. package/dist/shared/stack.BqTR6qi2.d.ts +0 -193
  1142. package/dist/shared/stack.BvxHSZYT.d.cts +0 -406
  1143. package/dist/shared/stack.C1ATQ3pd.d.ts +0 -416
  1144. package/dist/shared/stack.C4_gEENS.d.cts +0 -97
  1145. package/dist/shared/stack.C4_gEENS.d.mts +0 -97
  1146. package/dist/shared/stack.C4_gEENS.d.ts +0 -97
  1147. package/dist/shared/stack.C5Y5lViI.d.cts +0 -461
  1148. package/dist/shared/stack.C8SwdUz8.d.ts +0 -461
  1149. package/dist/shared/stack.CBsp9rDM.d.cts +0 -193
  1150. package/dist/shared/stack.CMbX8Q5C.d.ts +0 -560
  1151. package/dist/shared/stack.Cf-DbL1A.d.ts +0 -329
  1152. package/dist/shared/stack.DHTvwoNG.d.mts +0 -329
  1153. package/dist/shared/stack.DJaKVY7v.d.cts +0 -76
  1154. package/dist/shared/stack.DJaKVY7v.d.mts +0 -76
  1155. package/dist/shared/stack.DJaKVY7v.d.ts +0 -76
  1156. package/dist/shared/stack.DKZMaVr6.d.cts +0 -416
  1157. package/dist/shared/stack.DQq8DEIv.d.mts +0 -461
  1158. package/dist/shared/stack.Dd-8shBE.d.ts +0 -492
  1159. package/dist/shared/stack.Dj04W2c3.d.mts +0 -560
  1160. package/dist/shared/stack.DrUBX-QE.d.cts +0 -34
  1161. package/dist/shared/stack.DrUBX-QE.d.mts +0 -34
  1162. package/dist/shared/stack.DrUBX-QE.d.ts +0 -34
  1163. package/dist/shared/stack.H5B8miAV.d.cts +0 -840
  1164. package/dist/shared/stack.aXdBOJAR.d.cts +0 -173
  1165. package/dist/shared/stack.aXdBOJAR.d.mts +0 -173
  1166. package/dist/shared/stack.aXdBOJAR.d.ts +0 -173
  1167. package/dist/shared/stack.eq5eg1yt.d.cts +0 -560
  1168. package/dist/shared/stack.f8Vke-eH.d.mts +0 -406
  1169. package/dist/shared/stack.oBokjN_J.d.mts +0 -416
  1170. package/dist/shared/{stack.B0muOwUF.d.cts → stack.Csi-4Mgz.d.cts} +8 -8
  1171. package/dist/shared/{stack.B0muOwUF.d.mts → stack.Csi-4Mgz.d.mts} +8 -8
  1172. package/dist/shared/{stack.B0muOwUF.d.ts → stack.Csi-4Mgz.d.ts} +8 -8
@@ -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
+ }