@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,2218 @@
1
+ import type { DBAdapter as Adapter } from "@btst/db";
2
+ import type { PermissionFactsFor } from "@btst/stack/authorization";
3
+ import {
4
+ defineOperation,
5
+ type DeepReadonly,
6
+ OperationHttpError,
7
+ type OperationContext,
8
+ type OperationData,
9
+ } from "@btst/stack/plugins/api";
10
+ import { z } from "zod";
11
+ import { formSchemaToZod } from "@workspace/ui/lib/schema-converter";
12
+ import { cmsPermissions } from "../permissions";
13
+ import {
14
+ createListContentQuerySchema,
15
+ DEFAULT_MAX_PAGE_SIZE,
16
+ } from "../schemas";
17
+ import type {
18
+ CMSBackendHooks,
19
+ CMSCreateOperationContext,
20
+ CMSDeleteOperationContext,
21
+ CMSOperationData,
22
+ CMSOperationLifecycleContext,
23
+ CMSUpdateOperationContext,
24
+ ContentItem,
25
+ ContentItemWithType,
26
+ ContentRelation,
27
+ ContentType,
28
+ InverseRelation,
29
+ RelationValue,
30
+ SerializedContentItemWithType,
31
+ } from "../types";
32
+ import { slugify } from "../utils";
33
+ import {
34
+ getAllContentItems,
35
+ getContentItemBySlug,
36
+ serializeContentItemWithType,
37
+ serializeContentType,
38
+ } from "./getters";
39
+ import {
40
+ extractRelationFields,
41
+ isExistingRelationValue,
42
+ isNewRelationValue,
43
+ syncRelations,
44
+ } from "./relations";
45
+
46
+ type DataAdapter = Omit<Adapter, "transaction">;
47
+
48
+ function operationContentType(
49
+ contentType: ReturnType<typeof serializeContentType>,
50
+ ) {
51
+ return { ...contentType };
52
+ }
53
+
54
+ function operationContentItem(item: SerializedContentItemWithType) {
55
+ const {
56
+ parsedData,
57
+ contentType,
58
+ _relations: _relations,
59
+ ...serializedItem
60
+ } = item;
61
+ return {
62
+ ...serializedItem,
63
+ parsedData: parsedData as Record<string, OperationData>,
64
+ ...(contentType ? { contentType: operationContentType(contentType) } : {}),
65
+ };
66
+ }
67
+
68
+ function operationContentList(
69
+ result: Awaited<ReturnType<typeof getAllContentItems>>,
70
+ ) {
71
+ return {
72
+ items: result.items.map(operationContentItem),
73
+ total: result.total,
74
+ ...(result.limit !== undefined ? { limit: result.limit } : {}),
75
+ ...(result.offset !== undefined ? { offset: result.offset } : {}),
76
+ };
77
+ }
78
+
79
+ const JSONValueSchema: z.ZodType<OperationData> = z.lazy(() =>
80
+ z.union([
81
+ z.string(),
82
+ z.number(),
83
+ z.boolean(),
84
+ z.null(),
85
+ z.array(JSONValueSchema),
86
+ z.record(z.string(), JSONValueSchema),
87
+ ]),
88
+ );
89
+
90
+ // Runtime validation remains recursive and JSON-safe. The intentionally broad
91
+ // static record avoids TypeScript's recursive-instantiation ceiling for CMS's
92
+ // schema-defined payloads; the operation boundary deep-freezes the parsed value.
93
+ const CMSDataSchema: z.ZodType<Record<string, any>> = z.record(
94
+ z.string(),
95
+ JSONValueSchema,
96
+ );
97
+ const EmptyInputSchema = z.object({});
98
+
99
+ export const CMSContentTypeParamsSchema = z.object({
100
+ slug: z.string().min(1),
101
+ });
102
+ export const CMSContentItemParamsSchema = z.object({
103
+ typeSlug: z.string().min(1),
104
+ id: z.string().min(1),
105
+ });
106
+ export const CMSCreateContentItemBodySchema = z.object({
107
+ slug: z.string().min(1),
108
+ data: CMSDataSchema,
109
+ });
110
+ export const CMSUpdateContentItemBodySchema = z.object({
111
+ slug: z.string().min(1).optional(),
112
+ data: CMSDataSchema.optional(),
113
+ });
114
+
115
+ const ListContentTypesInputSchema = EmptyInputSchema;
116
+ const GetContentTypeInputSchema = CMSContentTypeParamsSchema;
117
+ const GetContentItemInputSchema = CMSContentItemParamsSchema;
118
+ const CreateContentItemInputSchema = z.object({
119
+ typeSlug: z.string().min(1),
120
+ body: CMSCreateContentItemBodySchema,
121
+ });
122
+ const UpdateContentItemInputSchema = z.object({
123
+ typeSlug: z.string().min(1),
124
+ id: z.string().min(1),
125
+ body: CMSUpdateContentItemBodySchema,
126
+ });
127
+ const DeleteContentItemInputSchema = CMSContentItemParamsSchema;
128
+
129
+ type RecordReadFacts = PermissionFactsFor<typeof cmsPermissions.record.read>;
130
+ type RecordCreateFacts = PermissionFactsFor<
131
+ typeof cmsPermissions.record.create
132
+ >;
133
+ type RecordUpdateFacts = PermissionFactsFor<
134
+ typeof cmsPermissions.record.update
135
+ >;
136
+ type RecordDeleteFacts = PermissionFactsFor<
137
+ typeof cmsPermissions.record.delete
138
+ >;
139
+
140
+ type RequestFields = {
141
+ readonly request?: Request;
142
+ readonly headers?: Headers;
143
+ readonly userId?: string;
144
+ };
145
+
146
+ type CMSLifecycleContext<TInput, TFacts> = OperationContext<TInput, TFacts> &
147
+ RequestFields & {
148
+ readonly typeSlug?: string;
149
+ };
150
+
151
+ /** A domain/HTTP error raised by a CMS operation. */
152
+ export class CMSOperationError extends OperationHttpError {
153
+ constructor(
154
+ statusCode: number,
155
+ message: string,
156
+ code = "CMS_OPERATION_ERROR",
157
+ issues?: readonly unknown[],
158
+ ) {
159
+ super(statusCode, message, code, issues);
160
+ this.name = "CMSOperationError";
161
+ }
162
+ }
163
+
164
+ function normalizeOperationError(error: unknown, fallback: string): Error {
165
+ if (error instanceof Error) return error;
166
+ return new Error(typeof error === "string" ? error : fallback, {
167
+ cause: error,
168
+ });
169
+ }
170
+
171
+ async function runBeforeHook(
172
+ hook: (() => Promise<void> | void) | undefined,
173
+ defaultMessage: string,
174
+ ) {
175
+ if (!hook) return;
176
+ try {
177
+ await hook();
178
+ } catch (error) {
179
+ throw new CMSOperationError(
180
+ 403,
181
+ error instanceof Error ? error.message : defaultMessage,
182
+ "HOOK_DENIED",
183
+ );
184
+ }
185
+ }
186
+
187
+ function requestFields(
188
+ context: OperationContext<unknown, unknown>,
189
+ ): RequestFields {
190
+ return {
191
+ ...(context.request
192
+ ? { request: context.request, headers: context.request.headers }
193
+ : {}),
194
+ ...(context.identity ? { userId: context.identity.id } : {}),
195
+ };
196
+ }
197
+
198
+ function createContext(
199
+ context: OperationContext<
200
+ z.output<typeof CreateContentItemInputSchema>,
201
+ RecordCreateFacts
202
+ >,
203
+ ): CMSCreateOperationContext {
204
+ return Object.freeze({
205
+ ...context,
206
+ typeSlug: context.facts.contentType,
207
+ body: context.input.body,
208
+ ...requestFields(context),
209
+ });
210
+ }
211
+
212
+ function updateContext(
213
+ context: OperationContext<
214
+ z.output<typeof UpdateContentItemInputSchema>,
215
+ RecordUpdateFacts
216
+ >,
217
+ ): CMSUpdateOperationContext {
218
+ return Object.freeze({
219
+ ...context,
220
+ typeSlug: context.facts.contentType,
221
+ params: Object.freeze({
222
+ typeSlug: context.facts.contentType,
223
+ id: context.facts.recordId,
224
+ }),
225
+ body: context.input.body,
226
+ ...requestFields(context),
227
+ });
228
+ }
229
+
230
+ function deleteContext(
231
+ context: OperationContext<
232
+ z.output<typeof DeleteContentItemInputSchema>,
233
+ RecordDeleteFacts
234
+ >,
235
+ ): CMSDeleteOperationContext {
236
+ return Object.freeze({
237
+ ...context,
238
+ typeSlug: context.facts.contentType,
239
+ params: Object.freeze({
240
+ typeSlug: context.facts.contentType,
241
+ id: context.facts.recordId,
242
+ }),
243
+ ...requestFields(context),
244
+ });
245
+ }
246
+
247
+ function genericContext<TInput, TFacts>(
248
+ context: OperationContext<TInput, TFacts>,
249
+ typeSlug?: string,
250
+ ): CMSLifecycleContext<TInput, TFacts> {
251
+ return Object.freeze({
252
+ ...context,
253
+ ...(typeSlug ? { typeSlug } : {}),
254
+ ...requestFields(context),
255
+ });
256
+ }
257
+
258
+ function operationErrorContext<T extends CMSOperationLifecycleContext>(
259
+ context: T,
260
+ error: unknown,
261
+ ): T & { readonly error: unknown } {
262
+ return Object.freeze({ ...context, error });
263
+ }
264
+
265
+ function getContentTypeZodSchema(contentType: ContentType): z.ZodTypeAny {
266
+ return formSchemaToZod(JSON.parse(contentType.jsonSchema));
267
+ }
268
+
269
+ async function getContentTypeOrThrow(
270
+ adapter: DataAdapter,
271
+ ensureSynced: () => Promise<void>,
272
+ slug: string,
273
+ message = "Content type not found",
274
+ ): Promise<ContentType> {
275
+ await ensureSynced();
276
+ const contentType = await adapter.findOne<ContentType>({
277
+ model: "contentType",
278
+ where: [{ field: "slug", value: slug, operator: "eq" as const }],
279
+ });
280
+ if (!contentType) {
281
+ throw new CMSOperationError(404, message, "CONTENT_TYPE_NOT_FOUND");
282
+ }
283
+ return contentType;
284
+ }
285
+
286
+ async function getRecordOrThrow(
287
+ adapter: DataAdapter,
288
+ ensureSynced: () => Promise<void>,
289
+ typeSlug: string,
290
+ id: string,
291
+ ): Promise<ContentItemWithType> {
292
+ await ensureSynced();
293
+ const item = await adapter.findOne<ContentItemWithType>({
294
+ model: "contentItem",
295
+ where: [{ field: "id", value: id, operator: "eq" as const }],
296
+ join: { contentType: true },
297
+ });
298
+ if (!item || item.contentType?.slug !== typeSlug) {
299
+ throw new CMSOperationError(
300
+ 404,
301
+ "Content item not found",
302
+ "RECORD_NOT_FOUND",
303
+ );
304
+ }
305
+ return item;
306
+ }
307
+
308
+ function recordFacts(item: ContentItemWithType): RecordReadFacts {
309
+ if (!item.contentType) {
310
+ throw new CMSOperationError(
311
+ 500,
312
+ "Content item is missing its content type",
313
+ "CONTENT_TYPE_JOIN_MISSING",
314
+ );
315
+ }
316
+ return {
317
+ contentType: item.contentType.slug,
318
+ scope: "record",
319
+ recordId: item.id,
320
+ ...(item.authorId ? { authorId: item.authorId } : {}),
321
+ };
322
+ }
323
+
324
+ function collectionFacts(contentType: string): RecordReadFacts {
325
+ return { contentType, scope: "collection" };
326
+ }
327
+
328
+ function assertRecordFacts(
329
+ item: Pick<ContentItemWithType, "id" | "authorId"> & {
330
+ contentType?: Pick<ContentType, "slug">;
331
+ },
332
+ facts: DeepReadonly<RecordReadFacts | RecordUpdateFacts | RecordDeleteFacts>,
333
+ ) {
334
+ if (
335
+ !facts.recordId ||
336
+ item.id !== facts.recordId ||
337
+ item.contentType?.slug !== facts.contentType ||
338
+ (item.authorId ?? undefined) !== facts.authorId
339
+ ) {
340
+ throw staleRecordError();
341
+ }
342
+ }
343
+
344
+ const AFFECTED_ROW_KEYS = [
345
+ "rowCount",
346
+ "affectedRows",
347
+ "rowsAffected",
348
+ "changes",
349
+ "numUpdatedRows",
350
+ ] as const;
351
+
352
+ function hasPositiveCount(value: unknown): boolean {
353
+ if (typeof value === "number") return Number.isFinite(value) && value > 0;
354
+ if (typeof value === "bigint") return value > 0n;
355
+ return false;
356
+ }
357
+
358
+ function didUpdateContentItem(result: unknown, expectedId: string): boolean {
359
+ if (typeof result === "number" || typeof result === "bigint") {
360
+ return hasPositiveCount(result);
361
+ }
362
+ if (!result || typeof result !== "object") return false;
363
+ const record = result as Record<string, unknown>;
364
+ if ("count" in record) return hasPositiveCount(record.count);
365
+ if (Array.isArray(result)) {
366
+ return result.length > 0 && didUpdateContentItem(result[0], expectedId);
367
+ }
368
+ for (const key of AFFECTED_ROW_KEYS) {
369
+ if (key in record) return hasPositiveCount(record[key]);
370
+ }
371
+ if ("meta" in record) {
372
+ const meta = record.meta;
373
+ return Boolean(
374
+ meta &&
375
+ typeof meta === "object" &&
376
+ "changes" in meta &&
377
+ hasPositiveCount((meta as Record<string, unknown>).changes),
378
+ );
379
+ }
380
+ return record.id === expectedId;
381
+ }
382
+
383
+ function staleRecordError() {
384
+ return new CMSOperationError(
385
+ 409,
386
+ "Content item changed while authorization was being evaluated. Retry the operation.",
387
+ "RECORD_STATE_CHANGED",
388
+ );
389
+ }
390
+
391
+ function assertRecordSnapshot(
392
+ current: ContentItemWithType,
393
+ expected: ContentItemWithType,
394
+ ) {
395
+ if (
396
+ current.id !== expected.id ||
397
+ current.contentTypeId !== expected.contentTypeId ||
398
+ current.contentType?.slug !== expected.contentType?.slug ||
399
+ (current.authorId ?? undefined) !== (expected.authorId ?? undefined) ||
400
+ current.slug !== expected.slug ||
401
+ current.data !== expected.data ||
402
+ current.updatedAt.getTime() !== expected.updatedAt.getTime()
403
+ ) {
404
+ throw staleRecordError();
405
+ }
406
+ }
407
+
408
+ interface RelationWritePlan {
409
+ readonly processedData: Record<string, OperationData>;
410
+ readonly relationIds: Record<string, string[]>;
411
+ readonly newItems: readonly ContentItem[];
412
+ }
413
+
414
+ async function planRelatedItem(
415
+ adapter: DataAdapter,
416
+ targetContentType: ContentType,
417
+ slug: string,
418
+ data: Record<string, OperationData>,
419
+ authorId?: string,
420
+ ): Promise<ContentItem> {
421
+ const validation = getContentTypeZodSchema(targetContentType).safeParse(data);
422
+ if (!validation.success) {
423
+ throw new CMSOperationError(
424
+ 400,
425
+ `Validation failed for new ${targetContentType.slug}`,
426
+ "RELATED_RECORD_VALIDATION_FAILED",
427
+ validation.error.issues,
428
+ );
429
+ }
430
+ const existing = await adapter.findOne<ContentItem>({
431
+ model: "contentItem",
432
+ where: [
433
+ {
434
+ field: "contentTypeId",
435
+ value: targetContentType.id,
436
+ operator: "eq" as const,
437
+ },
438
+ { field: "slug", value: slug, operator: "eq" as const },
439
+ ],
440
+ });
441
+ if (existing) {
442
+ throw relatedRecordSlugConflictError();
443
+ }
444
+ const now = new Date();
445
+ return {
446
+ id: globalThis.crypto.randomUUID(),
447
+ contentTypeId: targetContentType.id,
448
+ slug,
449
+ data: JSON.stringify(validation.data),
450
+ ...(authorId ? { authorId } : {}),
451
+ createdAt: now,
452
+ updatedAt: now,
453
+ };
454
+ }
455
+
456
+ function relatedRecordSlugConflictError() {
457
+ return new CMSOperationError(
458
+ 409,
459
+ "Inline related record could not be created because its slug is unavailable.",
460
+ "RELATED_RECORD_SLUG_CONFLICT",
461
+ );
462
+ }
463
+
464
+ async function planRelationsInData(
465
+ adapter: DataAdapter,
466
+ contentType: ContentType,
467
+ data: Record<string, OperationData>,
468
+ ensureSynced: () => Promise<void>,
469
+ authorId?: string,
470
+ ): Promise<RelationWritePlan> {
471
+ const relationFields = extractRelationFields(contentType);
472
+ const processedData = { ...data };
473
+ const relationIds: Record<string, string[]> = {};
474
+ const newItems: ContentItem[] = [];
475
+ const plannedSlugs = new Set<string>();
476
+
477
+ for (const [fieldName, relationConfig] of Object.entries(relationFields)) {
478
+ if (!(fieldName in data)) continue;
479
+ const fieldValue = data[fieldName];
480
+ if (!fieldValue) {
481
+ relationIds[fieldName] = [];
482
+ continue;
483
+ }
484
+ const targetContentType = await getContentTypeOrThrow(
485
+ adapter,
486
+ ensureSynced,
487
+ relationConfig.targetType,
488
+ `Target content type "${relationConfig.targetType}" not found for relation field "${fieldName}"`,
489
+ );
490
+ const ids: string[] = [];
491
+ if (relationConfig.type === "belongsTo") {
492
+ const value = fieldValue as RelationValue;
493
+ if (isNewRelationValue(value)) {
494
+ const item = await resolvePlannedRelatedItem(
495
+ adapter,
496
+ targetContentType,
497
+ value.data as Record<string, OperationData>,
498
+ authorId,
499
+ plannedSlugs,
500
+ newItems,
501
+ );
502
+ ids.push(item.id);
503
+ processedData[fieldName] = { id: item.id };
504
+ } else if (isExistingRelationValue(value)) {
505
+ ids.push(value.id);
506
+ }
507
+ } else {
508
+ const values = (
509
+ Array.isArray(fieldValue) ? fieldValue : []
510
+ ) as RelationValue[];
511
+ const processedValues: Array<{ id: string }> = [];
512
+ for (const value of values) {
513
+ if (isNewRelationValue(value)) {
514
+ const item = await resolvePlannedRelatedItem(
515
+ adapter,
516
+ targetContentType,
517
+ value.data as Record<string, OperationData>,
518
+ authorId,
519
+ plannedSlugs,
520
+ newItems,
521
+ );
522
+ ids.push(item.id);
523
+ processedValues.push({ id: item.id });
524
+ } else if (isExistingRelationValue(value)) {
525
+ ids.push(value.id);
526
+ processedValues.push({ id: value.id });
527
+ }
528
+ }
529
+ processedData[fieldName] = processedValues;
530
+ }
531
+ relationIds[fieldName] = ids;
532
+ }
533
+ return { processedData, relationIds, newItems };
534
+ }
535
+
536
+ async function resolvePlannedRelatedItem(
537
+ adapter: DataAdapter,
538
+ targetContentType: ContentType,
539
+ data: Record<string, OperationData>,
540
+ authorId: string | undefined,
541
+ plannedSlugs: Set<string>,
542
+ newItems: ContentItem[],
543
+ ) {
544
+ const slug = slugify(
545
+ (data.slug as string) ||
546
+ (data.name as string) ||
547
+ (data.title as string) ||
548
+ `item-${Date.now()}`,
549
+ );
550
+ const key = `${targetContentType.id}\u0000${slug}`;
551
+ if (plannedSlugs.has(key)) throw relatedRecordSlugConflictError();
552
+ const planned = await planRelatedItem(
553
+ adapter,
554
+ targetContentType,
555
+ slug,
556
+ data,
557
+ authorId,
558
+ );
559
+ plannedSlugs.add(key);
560
+ newItems.push(planned);
561
+ return planned;
562
+ }
563
+
564
+ async function applyRelationWritePlan(
565
+ adapter: DataAdapter,
566
+ plan: RelationWritePlan,
567
+ ) {
568
+ for (const item of plan.newItems) {
569
+ const existing = await adapter.findOne<ContentItem>({
570
+ model: "contentItem",
571
+ where: [
572
+ {
573
+ field: "contentTypeId",
574
+ value: item.contentTypeId,
575
+ operator: "eq" as const,
576
+ },
577
+ { field: "slug", value: item.slug, operator: "eq" as const },
578
+ ],
579
+ });
580
+ if (existing) throw staleRecordError();
581
+ await adapter.create<ContentItem>({
582
+ model: "contentItem",
583
+ data: item,
584
+ });
585
+ }
586
+ }
587
+
588
+ interface RelationWriteAuthorization {
589
+ readonly permissions: readonly (
590
+ | ReturnType<typeof cmsPermissions.record.create>
591
+ | ReturnType<typeof cmsPermissions.record.read>
592
+ )[];
593
+ readonly snapshot: readonly string[];
594
+ readonly targets: readonly RecordReadFacts[];
595
+ }
596
+
597
+ interface RelationFilterAuthorization {
598
+ readonly permission: ReturnType<typeof cmsPermissions.record.read>;
599
+ readonly snapshot: string;
600
+ readonly targetFacts: RecordReadFacts;
601
+ }
602
+
603
+ function relationFilterSnapshot(
604
+ sourceContentType: ContentType,
605
+ fieldName: string,
606
+ relation: ReturnType<typeof extractRelationFields>[string],
607
+ targetContentType: ContentType,
608
+ ) {
609
+ return JSON.stringify([
610
+ sourceContentType.id,
611
+ sourceContentType.slug,
612
+ fieldName,
613
+ relation.type,
614
+ targetContentType.id,
615
+ targetContentType.slug,
616
+ ]);
617
+ }
618
+
619
+ async function deriveRelationFilterAuthorization(
620
+ adapter: Adapter,
621
+ ensureSynced: () => Promise<void>,
622
+ sourceContentType: ContentType,
623
+ fieldName: string,
624
+ targetId: string,
625
+ ): Promise<RelationFilterAuthorization> {
626
+ const relation = extractRelationFields(sourceContentType)[fieldName];
627
+ if (!relation) {
628
+ throw new CMSOperationError(
629
+ 404,
630
+ `Relation field "${fieldName}" was not found on content type "${sourceContentType.slug}".`,
631
+ "RELATION_FIELD_NOT_FOUND",
632
+ );
633
+ }
634
+ const targetContentType = await getContentTypeOrThrow(
635
+ adapter,
636
+ ensureSynced,
637
+ relation.targetType,
638
+ `Target content type "${relation.targetType}" not found for relation field "${fieldName}"`,
639
+ );
640
+ const target = await getRecordOrThrow(
641
+ adapter,
642
+ ensureSynced,
643
+ targetContentType.slug,
644
+ targetId,
645
+ );
646
+ const targetFacts = recordFacts(target);
647
+ return {
648
+ permission: cmsPermissions.record.read(targetFacts),
649
+ snapshot: relationFilterSnapshot(
650
+ sourceContentType,
651
+ fieldName,
652
+ relation,
653
+ targetContentType,
654
+ ),
655
+ targetFacts,
656
+ };
657
+ }
658
+
659
+ function relationTargetSnapshot(facts: RecordReadFacts) {
660
+ return JSON.stringify([
661
+ facts.contentType,
662
+ facts.recordId,
663
+ facts.authorId ?? null,
664
+ ]);
665
+ }
666
+
667
+ async function deriveRelationWriteAuthorization(
668
+ adapter: DataAdapter,
669
+ contentType: ContentType,
670
+ data: Readonly<Record<string, unknown>>,
671
+ ensureSynced: () => Promise<void>,
672
+ expectedSchemaSnapshot?: readonly string[],
673
+ ): Promise<RelationWriteAuthorization> {
674
+ const permissions: Array<
675
+ | ReturnType<typeof cmsPermissions.record.create>
676
+ | ReturnType<typeof cmsPermissions.record.read>
677
+ > = [];
678
+ const snapshot: string[] = [];
679
+ const targets: RecordReadFacts[] = [];
680
+ const seenCreateTargetTypes = new Set<string>();
681
+ const seenReadTargets = new Set<string>();
682
+ const resolvedRelations: Array<{
683
+ readonly values: readonly unknown[];
684
+ readonly targetContentType: ContentType;
685
+ }> = [];
686
+
687
+ for (const [fieldName, relationConfig] of Object.entries(
688
+ extractRelationFields(contentType),
689
+ )) {
690
+ if (!(fieldName in data)) continue;
691
+ const value = data[fieldName];
692
+ const values =
693
+ relationConfig.type === "belongsTo"
694
+ ? [value]
695
+ : Array.isArray(value)
696
+ ? value
697
+ : [];
698
+ const targetContentType = await getContentTypeOrThrow(
699
+ adapter,
700
+ ensureSynced,
701
+ relationConfig.targetType,
702
+ `Target content type "${relationConfig.targetType}" not found for relation field "${fieldName}"`,
703
+ );
704
+ snapshot.push(
705
+ JSON.stringify([
706
+ fieldName,
707
+ relationConfig.type,
708
+ targetContentType.id,
709
+ targetContentType.slug,
710
+ ]),
711
+ );
712
+ resolvedRelations.push({ values, targetContentType });
713
+ }
714
+
715
+ snapshot.sort();
716
+ if (expectedSchemaSnapshot) {
717
+ assertRelationSchemaSnapshot(expectedSchemaSnapshot, snapshot);
718
+ }
719
+ for (const { values, targetContentType } of resolvedRelations) {
720
+ if (
721
+ values.some(isNewRelationValue) &&
722
+ !seenCreateTargetTypes.has(targetContentType.slug)
723
+ ) {
724
+ seenCreateTargetTypes.add(targetContentType.slug);
725
+ permissions.push(
726
+ cmsPermissions.record.create({ contentType: targetContentType.slug }),
727
+ );
728
+ }
729
+ for (const reference of values) {
730
+ if (
731
+ isNewRelationValue(reference) ||
732
+ !isExistingRelationValue(reference)
733
+ ) {
734
+ continue;
735
+ }
736
+ const target = await getRecordOrThrow(
737
+ adapter,
738
+ ensureSynced,
739
+ targetContentType.slug,
740
+ reference.id,
741
+ );
742
+ const facts = recordFacts(target);
743
+ const targetKey = relationTargetSnapshot(facts);
744
+ if (seenReadTargets.has(targetKey)) continue;
745
+ seenReadTargets.add(targetKey);
746
+ targets.push(facts);
747
+ permissions.push(cmsPermissions.record.read(facts));
748
+ }
749
+ }
750
+
751
+ return {
752
+ permissions,
753
+ snapshot,
754
+ targets: targets.sort((left, right) =>
755
+ relationTargetSnapshot(left).localeCompare(relationTargetSnapshot(right)),
756
+ ),
757
+ };
758
+ }
759
+
760
+ function assertRelationSchemaSnapshot(
761
+ authorized: readonly string[],
762
+ current: readonly string[],
763
+ ) {
764
+ if (JSON.stringify(authorized) !== JSON.stringify(current)) {
765
+ throw relationSchemaChangedError();
766
+ }
767
+ }
768
+
769
+ function assertRelationWriteAuthorization(
770
+ authorized: RelationWriteAuthorization,
771
+ current: RelationWriteAuthorization,
772
+ ) {
773
+ assertRelationSchemaSnapshot(authorized.snapshot, current.snapshot);
774
+ if (
775
+ JSON.stringify(authorized.targets.map(relationTargetSnapshot)) !==
776
+ JSON.stringify(current.targets.map(relationTargetSnapshot))
777
+ ) {
778
+ throw staleRecordError();
779
+ }
780
+ }
781
+
782
+ function relationSchemaChangedError() {
783
+ return new CMSOperationError(
784
+ 409,
785
+ "Content type relations changed while authorization was being evaluated. Retry the operation.",
786
+ "RELATION_SCHEMA_CHANGED",
787
+ );
788
+ }
789
+
790
+ interface PopulatedRelationSnapshot {
791
+ readonly relationKeys: readonly string[];
792
+ readonly targets: readonly {
793
+ readonly item: SerializedContentItemWithType;
794
+ readonly facts: RecordReadFacts;
795
+ }[];
796
+ }
797
+
798
+ async function getPopulatedRelationSnapshot(
799
+ adapter: Adapter,
800
+ sourceId: string,
801
+ ): Promise<PopulatedRelationSnapshot> {
802
+ const relations = await adapter.findMany<ContentRelation>({
803
+ model: "contentRelation",
804
+ where: [{ field: "sourceId", value: sourceId, operator: "eq" as const }],
805
+ });
806
+ const targets: PopulatedRelationSnapshot["targets"][number][] = [];
807
+ const seenTargets = new Set<string>();
808
+ for (const relation of relations) {
809
+ if (seenTargets.has(relation.targetId)) continue;
810
+ const target = await adapter.findOne<ContentItemWithType>({
811
+ model: "contentItem",
812
+ where: [
813
+ { field: "id", value: relation.targetId, operator: "eq" as const },
814
+ ],
815
+ join: { contentType: true },
816
+ });
817
+ if (!target) continue;
818
+ seenTargets.add(target.id);
819
+ targets.push({
820
+ item: serializeContentItemWithType(target),
821
+ facts: recordFacts(target),
822
+ });
823
+ }
824
+ return {
825
+ relationKeys: relations
826
+ .map((relation) => `${relation.fieldName}\u0000${relation.targetId}`)
827
+ .sort(),
828
+ targets: targets.sort((left, right) =>
829
+ (left.facts.recordId ?? "").localeCompare(right.facts.recordId ?? ""),
830
+ ),
831
+ };
832
+ }
833
+
834
+ function assertPopulatedRelationSnapshot(
835
+ authorized: PopulatedRelationSnapshot,
836
+ current: PopulatedRelationSnapshot,
837
+ ) {
838
+ const factsKey = (facts: RecordReadFacts) =>
839
+ JSON.stringify([facts.contentType, facts.recordId, facts.authorId ?? null]);
840
+ if (
841
+ JSON.stringify(authorized.relationKeys) !==
842
+ JSON.stringify(current.relationKeys) ||
843
+ JSON.stringify(authorized.targets.map(({ facts }) => factsKey(facts))) !==
844
+ JSON.stringify(current.targets.map(({ facts }) => factsKey(facts)))
845
+ ) {
846
+ throw new CMSOperationError(
847
+ 409,
848
+ "Related content changed while authorization was being evaluated. Retry the operation.",
849
+ "RECORD_STATE_CHANGED",
850
+ );
851
+ }
852
+ }
853
+
854
+ function populatedRelationsFromSnapshot(snapshot: PopulatedRelationSnapshot) {
855
+ const targetsById = new Map(
856
+ snapshot.targets.map((target) => [target.facts.recordId, target.item]),
857
+ );
858
+ const populated: Record<string, SerializedContentItemWithType[]> = {};
859
+ for (const relationKey of snapshot.relationKeys) {
860
+ const [fieldName, targetId] = relationKey.split("\u0000");
861
+ if (!fieldName || !targetId) continue;
862
+ const target = targetsById.get(targetId);
863
+ if (target) (populated[fieldName] ??= []).push(target);
864
+ }
865
+ return populated;
866
+ }
867
+
868
+ interface InverseSourceDescriptor {
869
+ readonly contentTypeId: string;
870
+ readonly contentTypeSlug: string;
871
+ readonly contentTypeName: string;
872
+ readonly fieldName: string;
873
+ }
874
+
875
+ interface InverseSourceAuthorization {
876
+ readonly permissions: readonly ReturnType<
877
+ typeof cmsPermissions.contentType.read
878
+ >[];
879
+ readonly snapshot: readonly string[];
880
+ readonly sources: readonly InverseSourceDescriptor[];
881
+ }
882
+
883
+ function inverseSourceKey(source: InverseSourceDescriptor) {
884
+ return JSON.stringify([
885
+ source.contentTypeId,
886
+ source.contentTypeSlug,
887
+ source.contentTypeName,
888
+ source.fieldName,
889
+ ]);
890
+ }
891
+
892
+ async function deriveInverseSourceAuthorization(
893
+ adapter: Adapter,
894
+ ensureSynced: () => Promise<void>,
895
+ targetType: string,
896
+ ): Promise<InverseSourceAuthorization> {
897
+ await ensureSynced();
898
+ const permissions = [];
899
+ const sources: InverseSourceDescriptor[] = [];
900
+ const seenSourceTypes = new Set<string>();
901
+ for (const contentType of await adapter.findMany<ContentType>({
902
+ model: "contentType",
903
+ })) {
904
+ for (const [fieldName, relation] of Object.entries(
905
+ extractRelationFields(contentType),
906
+ )) {
907
+ if (relation.type !== "belongsTo" || relation.targetType !== targetType) {
908
+ continue;
909
+ }
910
+ sources.push({
911
+ contentTypeId: contentType.id,
912
+ contentTypeSlug: contentType.slug,
913
+ contentTypeName: contentType.name,
914
+ fieldName,
915
+ });
916
+ if (seenSourceTypes.has(contentType.slug)) continue;
917
+ seenSourceTypes.add(contentType.slug);
918
+ permissions.push(
919
+ cmsPermissions.contentType.read({ contentType: contentType.slug }),
920
+ );
921
+ }
922
+ }
923
+ sources.sort((left, right) =>
924
+ inverseSourceKey(left).localeCompare(inverseSourceKey(right)),
925
+ );
926
+ return {
927
+ permissions,
928
+ snapshot: sources.map(inverseSourceKey),
929
+ sources,
930
+ };
931
+ }
932
+
933
+ async function listRecordsForRelation(
934
+ adapter: Adapter,
935
+ contentType: ContentType,
936
+ fieldName: string,
937
+ targetId: string,
938
+ limit: number,
939
+ offset: number,
940
+ ) {
941
+ const relations = await adapter.findMany<ContentRelation>({
942
+ model: "contentRelation",
943
+ where: [
944
+ { field: "targetId", value: targetId, operator: "eq" as const },
945
+ { field: "fieldName", value: fieldName, operator: "eq" as const },
946
+ ],
947
+ });
948
+ const ids = [...new Set(relations.map((relation) => relation.sourceId))];
949
+ const items: ContentItemWithType[] = [];
950
+ for (const id of ids) {
951
+ const item = await adapter.findOne<ContentItemWithType>({
952
+ model: "contentItem",
953
+ where: [
954
+ { field: "id", value: id, operator: "eq" as const },
955
+ {
956
+ field: "contentTypeId",
957
+ value: contentType.id,
958
+ operator: "eq" as const,
959
+ },
960
+ ],
961
+ join: { contentType: true },
962
+ });
963
+ if (item) items.push(item);
964
+ }
965
+ items.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
966
+ return {
967
+ items: items
968
+ .slice(offset, offset + limit)
969
+ .map(serializeContentItemWithType),
970
+ total: items.length,
971
+ limit,
972
+ offset,
973
+ };
974
+ }
975
+
976
+ type EnsureSynced = () => Promise<void>;
977
+ type GetContentTypesWithCounts = () => Promise<
978
+ Array<ReturnType<typeof serializeContentType> & { itemCount: number }>
979
+ >;
980
+
981
+ export function createCMSOperations(
982
+ adapter: Adapter,
983
+ options: {
984
+ ensureSynced: EnsureSynced;
985
+ getContentTypesWithCounts: GetContentTypesWithCounts;
986
+ maxPageSize?: number;
987
+ hooks?: CMSBackendHooks;
988
+ },
989
+ ) {
990
+ const { ensureSynced, getContentTypesWithCounts, hooks } = options;
991
+ const populatedRelationSnapshots = new WeakMap<
992
+ object,
993
+ PopulatedRelationSnapshot
994
+ >();
995
+ const relationWriteAuthorizationSnapshots = new WeakMap<
996
+ object,
997
+ RelationWriteAuthorization
998
+ >();
999
+ const relationFilterAuthorizationSnapshots = new WeakMap<
1000
+ object,
1001
+ RelationFilterAuthorization
1002
+ >();
1003
+ const inverseSourceAuthorizationSnapshots = new WeakMap<
1004
+ object,
1005
+ readonly string[]
1006
+ >();
1007
+ const inverseTargetAuthorizationSnapshots = new WeakMap<
1008
+ object,
1009
+ RecordReadFacts
1010
+ >();
1011
+ const contentTypeListSnapshots = new WeakMap<
1012
+ object,
1013
+ Awaited<ReturnType<GetContentTypesWithCounts>>
1014
+ >();
1015
+ const assertCurrentRelationWriteAuthorization = async (
1016
+ currentAdapter: DataAdapter,
1017
+ authorized: RelationWriteAuthorization,
1018
+ contentTypeSlug: string,
1019
+ data: Readonly<Record<string, unknown>>,
1020
+ ) => {
1021
+ let current: RelationWriteAuthorization;
1022
+ try {
1023
+ const contentType = await getContentTypeOrThrow(
1024
+ currentAdapter,
1025
+ ensureSynced,
1026
+ contentTypeSlug,
1027
+ );
1028
+ current = await deriveRelationWriteAuthorization(
1029
+ currentAdapter,
1030
+ contentType,
1031
+ data,
1032
+ ensureSynced,
1033
+ authorized.snapshot,
1034
+ );
1035
+ } catch (error) {
1036
+ if (error instanceof CMSOperationError) {
1037
+ if (error.code === "CONTENT_TYPE_NOT_FOUND") {
1038
+ throw relationSchemaChangedError();
1039
+ }
1040
+ if (error.code === "RECORD_NOT_FOUND") throw staleRecordError();
1041
+ }
1042
+ throw error;
1043
+ }
1044
+ assertRelationWriteAuthorization(authorized, current);
1045
+ };
1046
+ const recheckRelationWriteAuthorization = async (
1047
+ input: object,
1048
+ contentTypeSlug: string,
1049
+ data: Readonly<Record<string, unknown>>,
1050
+ ) => {
1051
+ const authorized = relationWriteAuthorizationSnapshots.get(input);
1052
+ relationWriteAuthorizationSnapshots.delete(input);
1053
+ if (!authorized) {
1054
+ throw new CMSOperationError(
1055
+ 500,
1056
+ "Relation authorization snapshot is unavailable.",
1057
+ "AUTHORIZATION_SNAPSHOT_MISSING",
1058
+ );
1059
+ }
1060
+ await assertCurrentRelationWriteAuthorization(
1061
+ adapter,
1062
+ authorized,
1063
+ contentTypeSlug,
1064
+ data,
1065
+ );
1066
+ return authorized;
1067
+ };
1068
+ const recheckInverseSourceAuthorization = async (
1069
+ input: object,
1070
+ targetType: string,
1071
+ ) => {
1072
+ const authorized = inverseSourceAuthorizationSnapshots.get(input);
1073
+ inverseSourceAuthorizationSnapshots.delete(input);
1074
+ if (!authorized) {
1075
+ throw new CMSOperationError(
1076
+ 500,
1077
+ "Inverse relation authorization snapshot is unavailable.",
1078
+ "AUTHORIZATION_SNAPSHOT_MISSING",
1079
+ );
1080
+ }
1081
+ const current = await deriveInverseSourceAuthorization(
1082
+ adapter,
1083
+ ensureSynced,
1084
+ targetType,
1085
+ );
1086
+ assertRelationSchemaSnapshot(authorized, current.snapshot);
1087
+ return current.sources;
1088
+ };
1089
+ const recheckRelationFilterAuthorization = async (
1090
+ input: object,
1091
+ sourceType: string,
1092
+ fieldName: string,
1093
+ targetId: string,
1094
+ ) => {
1095
+ const authorized = relationFilterAuthorizationSnapshots.get(input);
1096
+ relationFilterAuthorizationSnapshots.delete(input);
1097
+ if (!authorized) {
1098
+ throw new CMSOperationError(
1099
+ 500,
1100
+ "Relation-filter authorization snapshot is unavailable.",
1101
+ "AUTHORIZATION_SNAPSHOT_MISSING",
1102
+ );
1103
+ }
1104
+ let sourceContentType: ContentType;
1105
+ try {
1106
+ sourceContentType = await getContentTypeOrThrow(
1107
+ adapter,
1108
+ ensureSynced,
1109
+ sourceType,
1110
+ );
1111
+ } catch (error) {
1112
+ if (
1113
+ error instanceof CMSOperationError &&
1114
+ error.code === "CONTENT_TYPE_NOT_FOUND"
1115
+ ) {
1116
+ throw relationSchemaChangedError();
1117
+ }
1118
+ throw error;
1119
+ }
1120
+ const relation = extractRelationFields(sourceContentType)[fieldName];
1121
+ if (!relation) throw relationSchemaChangedError();
1122
+ let targetContentType: ContentType;
1123
+ try {
1124
+ targetContentType = await getContentTypeOrThrow(
1125
+ adapter,
1126
+ ensureSynced,
1127
+ relation.targetType,
1128
+ );
1129
+ } catch (error) {
1130
+ if (
1131
+ error instanceof CMSOperationError &&
1132
+ error.code === "CONTENT_TYPE_NOT_FOUND"
1133
+ ) {
1134
+ throw relationSchemaChangedError();
1135
+ }
1136
+ throw error;
1137
+ }
1138
+ const currentSnapshot = relationFilterSnapshot(
1139
+ sourceContentType,
1140
+ fieldName,
1141
+ relation,
1142
+ targetContentType,
1143
+ );
1144
+ if (authorized.snapshot !== currentSnapshot) {
1145
+ throw relationSchemaChangedError();
1146
+ }
1147
+ let target: ContentItemWithType;
1148
+ try {
1149
+ target = await getRecordOrThrow(
1150
+ adapter,
1151
+ ensureSynced,
1152
+ targetContentType.slug,
1153
+ targetId,
1154
+ );
1155
+ } catch (error) {
1156
+ if (
1157
+ error instanceof CMSOperationError &&
1158
+ error.code === "RECORD_NOT_FOUND"
1159
+ ) {
1160
+ throw staleRecordError();
1161
+ }
1162
+ throw error;
1163
+ }
1164
+ const currentFacts = recordFacts(target);
1165
+ if (
1166
+ authorized.targetFacts.contentType !== currentFacts.contentType ||
1167
+ authorized.targetFacts.recordId !== currentFacts.recordId ||
1168
+ authorized.targetFacts.authorId !== currentFacts.authorId
1169
+ ) {
1170
+ throw staleRecordError();
1171
+ }
1172
+ return sourceContentType;
1173
+ };
1174
+ const recheckInverseTargetAuthorization = async (
1175
+ input: object,
1176
+ targetType: string,
1177
+ targetId: string,
1178
+ ) => {
1179
+ const authorized = inverseTargetAuthorizationSnapshots.get(input);
1180
+ if (
1181
+ !authorized ||
1182
+ authorized.contentType !== targetType ||
1183
+ authorized.recordId !== targetId
1184
+ ) {
1185
+ throw new CMSOperationError(
1186
+ 500,
1187
+ "Inverse-relation target authorization snapshot is unavailable.",
1188
+ "AUTHORIZATION_SNAPSHOT_MISSING",
1189
+ );
1190
+ }
1191
+ const current = await adapter.findOne<ContentItemWithType>({
1192
+ model: "contentItem",
1193
+ where: [{ field: "id", value: targetId, operator: "eq" as const }],
1194
+ join: { contentType: true },
1195
+ });
1196
+ if (!current) throw staleRecordError();
1197
+ assertRecordFacts(current, authorized);
1198
+ };
1199
+ const listQuerySchema = createListContentQuerySchema(options.maxPageSize);
1200
+ const paginationSchema = z.object({
1201
+ limit: z.coerce
1202
+ .number()
1203
+ .min(1)
1204
+ .max(options.maxPageSize ?? DEFAULT_MAX_PAGE_SIZE)
1205
+ .optional()
1206
+ .default(20),
1207
+ offset: z.coerce.number().min(0).optional().default(0),
1208
+ });
1209
+ const ListContentItemsInputSchema = z.object({
1210
+ typeSlug: z.string().min(1),
1211
+ query: listQuerySchema,
1212
+ });
1213
+ const ListContentByRelationInputSchema = z.object({
1214
+ typeSlug: z.string().min(1),
1215
+ query: z
1216
+ .object({ field: z.string().min(1), targetId: z.string().min(1) })
1217
+ .merge(paginationSchema),
1218
+ });
1219
+ const GetInverseRelationsInputSchema = z.object({
1220
+ slug: z.string().min(1),
1221
+ query: z.object({ itemId: z.string().min(1).optional() }),
1222
+ });
1223
+ const ListInverseRelationItemsInputSchema = z.object({
1224
+ slug: z.string().min(1),
1225
+ sourceType: z.string().min(1),
1226
+ query: z
1227
+ .object({
1228
+ itemId: z.string().min(1),
1229
+ fieldName: z.string().min(1),
1230
+ })
1231
+ .merge(paginationSchema),
1232
+ });
1233
+
1234
+ const notifyError = async (
1235
+ error: unknown,
1236
+ operation: "create" | "update" | "delete" | "list" | "get",
1237
+ context: CMSOperationLifecycleContext,
1238
+ ) => {
1239
+ await hooks?.onErrorExecuteContentOperation?.(
1240
+ normalizeOperationError(error, `CMS ${operation} operation failed.`),
1241
+ operation,
1242
+ operationErrorContext(context, error),
1243
+ );
1244
+ };
1245
+
1246
+ const listContentTypes = defineOperation({
1247
+ input: ListContentTypesInputSchema,
1248
+ permission: cmsPermissions.contentType.read,
1249
+ facts: async () => {
1250
+ await ensureSynced();
1251
+ return {};
1252
+ },
1253
+ additionalPermissions: async ({ input }) => {
1254
+ const contentTypes = await getContentTypesWithCounts();
1255
+ contentTypeListSnapshots.set(input as object, contentTypes);
1256
+ return contentTypes.map((contentType) =>
1257
+ cmsPermissions.record.read(collectionFacts(contentType.slug)),
1258
+ );
1259
+ },
1260
+ execute: ({ input }) => {
1261
+ const contentTypes = contentTypeListSnapshots.get(input as object);
1262
+ contentTypeListSnapshots.delete(input as object);
1263
+ if (!contentTypes) {
1264
+ throw new CMSOperationError(
1265
+ 500,
1266
+ "Content-type catalog authorization snapshot is unavailable.",
1267
+ "AUTHORIZATION_SNAPSHOT_MISSING",
1268
+ );
1269
+ }
1270
+ return contentTypes.map((contentType) => ({
1271
+ ...operationContentType(contentType),
1272
+ itemCount: contentType.itemCount,
1273
+ }));
1274
+ },
1275
+ onError: ({ error, ...context }) =>
1276
+ notifyError(error, "list", genericContext(context)),
1277
+ });
1278
+
1279
+ const getContentTypeBySlug = defineOperation({
1280
+ input: GetContentTypeInputSchema,
1281
+ permission: cmsPermissions.contentType.read,
1282
+ facts: async ({ input }) => {
1283
+ const contentType = await getContentTypeOrThrow(
1284
+ adapter,
1285
+ ensureSynced,
1286
+ input.slug,
1287
+ );
1288
+ return { contentType: contentType.slug };
1289
+ },
1290
+ execute: async ({ facts }) =>
1291
+ operationContentType(
1292
+ serializeContentType(
1293
+ await getContentTypeOrThrow(
1294
+ adapter,
1295
+ ensureSynced,
1296
+ facts.contentType ?? "",
1297
+ ),
1298
+ ),
1299
+ ),
1300
+ onError: ({ error, ...context }) =>
1301
+ notifyError(
1302
+ error,
1303
+ "get",
1304
+ genericContext(context, context.facts.contentType),
1305
+ ),
1306
+ });
1307
+
1308
+ const listContentItems = defineOperation({
1309
+ input: ListContentItemsInputSchema,
1310
+ permission: cmsPermissions.record.read,
1311
+ facts: async ({ input }) => {
1312
+ const contentType = await getContentTypeOrThrow(
1313
+ adapter,
1314
+ ensureSynced,
1315
+ input.typeSlug,
1316
+ );
1317
+ if (!input.query.slug) {
1318
+ return collectionFacts(contentType.slug);
1319
+ }
1320
+ const item = await getContentItemBySlug(
1321
+ adapter,
1322
+ contentType.slug,
1323
+ input.query.slug,
1324
+ );
1325
+ const facts: RecordReadFacts = {
1326
+ contentType: contentType.slug,
1327
+ scope: "record",
1328
+ ...(item
1329
+ ? {
1330
+ recordId: item.id,
1331
+ ...(item.authorId ? { authorId: item.authorId } : {}),
1332
+ }
1333
+ : {}),
1334
+ };
1335
+ return facts;
1336
+ },
1337
+ additionalPermissions: ({ facts }) => [
1338
+ cmsPermissions.contentType.read({ contentType: facts.contentType }),
1339
+ ],
1340
+ execute: async ({ input, facts }) => {
1341
+ const result = await getAllContentItems(
1342
+ adapter,
1343
+ facts.contentType,
1344
+ input.query,
1345
+ );
1346
+ if (facts.scope === "record") {
1347
+ if (!facts.recordId) {
1348
+ if (result.items.length > 0) throw staleRecordError();
1349
+ if (
1350
+ await getContentItemBySlug(
1351
+ adapter,
1352
+ facts.contentType,
1353
+ input.query.slug ?? "",
1354
+ )
1355
+ ) {
1356
+ throw staleRecordError();
1357
+ }
1358
+ return operationContentList(result);
1359
+ }
1360
+ if (result.items.length > 1) throw staleRecordError();
1361
+ if (result.items.length === 0) {
1362
+ const current = await getContentItemBySlug(
1363
+ adapter,
1364
+ facts.contentType,
1365
+ input.query.slug ?? "",
1366
+ );
1367
+ if (!current) throw staleRecordError();
1368
+ assertRecordFacts(current, facts);
1369
+ } else {
1370
+ assertRecordFacts(result.items[0]!, facts);
1371
+ }
1372
+ }
1373
+ return operationContentList(result);
1374
+ },
1375
+ onError: ({ error, ...context }) =>
1376
+ notifyError(
1377
+ error,
1378
+ "list",
1379
+ genericContext(context, context.facts.contentType),
1380
+ ),
1381
+ });
1382
+
1383
+ const getContentItem = defineOperation({
1384
+ input: GetContentItemInputSchema,
1385
+ permission: cmsPermissions.record.read,
1386
+ facts: async ({ input }) =>
1387
+ recordFacts(
1388
+ await getRecordOrThrow(adapter, ensureSynced, input.typeSlug, input.id),
1389
+ ),
1390
+ additionalPermissions: ({ facts }) => [
1391
+ cmsPermissions.contentType.read({ contentType: facts.contentType }),
1392
+ ],
1393
+ execute: async ({ facts }) => {
1394
+ const item = await getRecordOrThrow(
1395
+ adapter,
1396
+ ensureSynced,
1397
+ facts.contentType,
1398
+ facts.recordId ?? "",
1399
+ );
1400
+ assertRecordFacts(item, facts);
1401
+ return operationContentItem(serializeContentItemWithType(item));
1402
+ },
1403
+ onError: ({ error, ...context }) =>
1404
+ notifyError(
1405
+ error,
1406
+ "get",
1407
+ genericContext(context, context.facts.contentType),
1408
+ ),
1409
+ });
1410
+
1411
+ const createContentItem = defineOperation({
1412
+ input: CreateContentItemInputSchema,
1413
+ permission: cmsPermissions.record.create,
1414
+ facts: async ({ input }) => ({
1415
+ contentType: (
1416
+ await getContentTypeOrThrow(adapter, ensureSynced, input.typeSlug)
1417
+ ).slug,
1418
+ }),
1419
+ additionalPermissions: async ({ input, facts }) => {
1420
+ const authorization = await deriveRelationWriteAuthorization(
1421
+ adapter,
1422
+ await getContentTypeOrThrow(adapter, ensureSynced, facts.contentType),
1423
+ input.body.data as unknown as Readonly<Record<string, unknown>>,
1424
+ ensureSynced,
1425
+ );
1426
+ relationWriteAuthorizationSnapshots.set(input as object, authorization);
1427
+ return [
1428
+ cmsPermissions.contentType.read({ contentType: facts.contentType }),
1429
+ ...authorization.permissions,
1430
+ ];
1431
+ },
1432
+ execute: async (context) => {
1433
+ const lifecycleContext = createContext(context);
1434
+ const contentType = await getContentTypeOrThrow(
1435
+ adapter,
1436
+ ensureSynced,
1437
+ context.facts.contentType,
1438
+ );
1439
+ const slug = slugify(context.input.body.slug);
1440
+ if (!slug) {
1441
+ throw new CMSOperationError(
1442
+ 400,
1443
+ "Invalid slug: must contain at least one alphanumeric character",
1444
+ "INVALID_SLUG",
1445
+ );
1446
+ }
1447
+ const relationAuthorization = await recheckRelationWriteAuthorization(
1448
+ context.input as object,
1449
+ contentType.slug,
1450
+ context.input.body.data,
1451
+ );
1452
+ const relationPlan = await planRelationsInData(
1453
+ adapter,
1454
+ contentType,
1455
+ context.input.body.data,
1456
+ ensureSynced,
1457
+ context.identity?.id,
1458
+ );
1459
+ const validation = getContentTypeZodSchema(contentType).safeParse(
1460
+ relationPlan.processedData,
1461
+ );
1462
+ if (!validation.success) {
1463
+ throw new CMSOperationError(
1464
+ 400,
1465
+ "Validation failed",
1466
+ "RECORD_VALIDATION_FAILED",
1467
+ validation.error.issues,
1468
+ );
1469
+ }
1470
+ const existing = await adapter.findOne<ContentItem>({
1471
+ model: "contentItem",
1472
+ where: [
1473
+ {
1474
+ field: "contentTypeId",
1475
+ value: contentType.id,
1476
+ operator: "eq" as const,
1477
+ },
1478
+ { field: "slug", value: slug, operator: "eq" as const },
1479
+ ],
1480
+ });
1481
+ if (existing) {
1482
+ throw new CMSOperationError(
1483
+ 409,
1484
+ "Content item with this slug already exists",
1485
+ "DUPLICATE_SLUG",
1486
+ );
1487
+ }
1488
+ const validatedData = validation.data as Record<string, CMSOperationData>;
1489
+ await runBeforeHook(
1490
+ () => hooks?.onBeforeCreateContent?.(validatedData, lifecycleContext),
1491
+ "Create operation denied",
1492
+ );
1493
+ return adapter.transaction(async (tx) => {
1494
+ await assertCurrentRelationWriteAuthorization(
1495
+ tx,
1496
+ relationAuthorization,
1497
+ contentType.slug,
1498
+ context.input.body.data,
1499
+ );
1500
+ const currentDuplicate = await tx.findOne<ContentItem>({
1501
+ model: "contentItem",
1502
+ where: [
1503
+ {
1504
+ field: "contentTypeId",
1505
+ value: contentType.id,
1506
+ operator: "eq" as const,
1507
+ },
1508
+ { field: "slug", value: slug, operator: "eq" as const },
1509
+ ],
1510
+ });
1511
+ if (currentDuplicate) {
1512
+ throw new CMSOperationError(
1513
+ 409,
1514
+ "Content item with this slug already exists",
1515
+ "DUPLICATE_SLUG",
1516
+ );
1517
+ }
1518
+ await applyRelationWritePlan(tx, relationPlan);
1519
+ const item = await tx.create<ContentItem>({
1520
+ model: "contentItem",
1521
+ data: {
1522
+ contentTypeId: contentType.id,
1523
+ slug,
1524
+ data: JSON.stringify(validatedData),
1525
+ ...(context.identity ? { authorId: context.identity.id } : {}),
1526
+ createdAt: new Date(),
1527
+ updatedAt: new Date(),
1528
+ },
1529
+ });
1530
+ await syncRelations(tx, item.id, relationPlan.relationIds);
1531
+ const joined = await getRecordOrThrow(
1532
+ tx,
1533
+ ensureSynced,
1534
+ contentType.slug,
1535
+ item.id,
1536
+ );
1537
+ return operationContentItem(serializeContentItemWithType(joined));
1538
+ });
1539
+ },
1540
+ after: async (context) => {
1541
+ const base = createContext(context);
1542
+ // The serialized item type recursively contains populated relations. Keep
1543
+ // that useful public hook type while bridging the already-frozen result
1544
+ // without asking TypeScript to recursively compare every relation level.
1545
+ const afterCreateContent = hooks?.onAfterCreateContent as
1546
+ | ((item: unknown, context: unknown) => Promise<void> | void)
1547
+ | undefined;
1548
+ await afterCreateContent?.(
1549
+ context.result,
1550
+ Object.freeze({ ...base, result: context.result }),
1551
+ );
1552
+ },
1553
+ onError: async ({ error, ...context }) => {
1554
+ await notifyError(error, "create", createContext(context));
1555
+ },
1556
+ });
1557
+
1558
+ const updateContentItem = defineOperation({
1559
+ input: UpdateContentItemInputSchema,
1560
+ permission: cmsPermissions.record.update,
1561
+ facts: async ({ input }) => {
1562
+ const item = await getRecordOrThrow(
1563
+ adapter,
1564
+ ensureSynced,
1565
+ input.typeSlug,
1566
+ input.id,
1567
+ );
1568
+ const facts = recordFacts(item);
1569
+ return {
1570
+ contentType: facts.contentType,
1571
+ recordId: facts.recordId ?? input.id,
1572
+ ...(facts.authorId ? { authorId: facts.authorId } : {}),
1573
+ };
1574
+ },
1575
+ additionalPermissions: async ({ input, facts }) => {
1576
+ const contentTypePermission = cmsPermissions.contentType.read({
1577
+ contentType: facts.contentType,
1578
+ });
1579
+ if (!input.body.data) return [contentTypePermission];
1580
+ const authorization = await deriveRelationWriteAuthorization(
1581
+ adapter,
1582
+ await getContentTypeOrThrow(adapter, ensureSynced, facts.contentType),
1583
+ input.body.data as unknown as Readonly<Record<string, unknown>>,
1584
+ ensureSynced,
1585
+ );
1586
+ relationWriteAuthorizationSnapshots.set(input as object, authorization);
1587
+ return [contentTypePermission, ...authorization.permissions];
1588
+ },
1589
+ execute: async (context) => {
1590
+ const lifecycleContext = updateContext(context);
1591
+ const contentType = await getContentTypeOrThrow(
1592
+ adapter,
1593
+ ensureSynced,
1594
+ context.facts.contentType,
1595
+ );
1596
+ const rawSlug = context.input.body.slug;
1597
+ const slug = rawSlug ? slugify(rawSlug) : undefined;
1598
+ if (rawSlug && !slug) {
1599
+ throw new CMSOperationError(
1600
+ 400,
1601
+ "Invalid slug: must contain at least one alphanumeric character",
1602
+ "INVALID_SLUG",
1603
+ );
1604
+ }
1605
+ const bodyData = context.input.body.data;
1606
+ const relationAuthorization = bodyData
1607
+ ? await recheckRelationWriteAuthorization(
1608
+ context.input as object,
1609
+ contentType.slug,
1610
+ bodyData,
1611
+ )
1612
+ : undefined;
1613
+ const authorizedRecord = await getRecordOrThrow(
1614
+ adapter,
1615
+ ensureSynced,
1616
+ context.facts.contentType,
1617
+ context.facts.recordId,
1618
+ );
1619
+ assertRecordFacts(authorizedRecord, context.facts);
1620
+ if (slug && slug !== authorizedRecord.slug) {
1621
+ const duplicate = await adapter.findOne<ContentItem>({
1622
+ model: "contentItem",
1623
+ where: [
1624
+ {
1625
+ field: "contentTypeId",
1626
+ value: contentType.id,
1627
+ operator: "eq" as const,
1628
+ },
1629
+ { field: "slug", value: slug, operator: "eq" as const },
1630
+ ],
1631
+ });
1632
+ if (duplicate) {
1633
+ throw new CMSOperationError(
1634
+ 409,
1635
+ "Content item with this slug already exists",
1636
+ "DUPLICATE_SLUG",
1637
+ );
1638
+ }
1639
+ }
1640
+ const relationPlan = bodyData
1641
+ ? await planRelationsInData(
1642
+ adapter,
1643
+ contentType,
1644
+ bodyData,
1645
+ ensureSynced,
1646
+ context.identity?.id,
1647
+ )
1648
+ : undefined;
1649
+ const currentData = authorizedRecord.data
1650
+ ? (JSON.parse(authorizedRecord.data) as Record<string, OperationData>)
1651
+ : {};
1652
+ const validation = getContentTypeZodSchema(contentType).safeParse({
1653
+ ...currentData,
1654
+ ...(relationPlan?.processedData ?? {}),
1655
+ });
1656
+ if (!validation.success) {
1657
+ throw new CMSOperationError(
1658
+ 400,
1659
+ "Validation failed",
1660
+ "RECORD_VALIDATION_FAILED",
1661
+ validation.error.issues,
1662
+ );
1663
+ }
1664
+ const mergedData = validation.data as Record<string, CMSOperationData>;
1665
+ await runBeforeHook(
1666
+ () =>
1667
+ hooks?.onBeforeUpdateContent?.(
1668
+ context.facts.recordId,
1669
+ mergedData,
1670
+ lifecycleContext,
1671
+ ),
1672
+ "Update operation denied",
1673
+ );
1674
+ return adapter.transaction(async (tx) => {
1675
+ if (bodyData && relationAuthorization) {
1676
+ await assertCurrentRelationWriteAuthorization(
1677
+ tx,
1678
+ relationAuthorization,
1679
+ contentType.slug,
1680
+ bodyData,
1681
+ );
1682
+ }
1683
+ const current = await getRecordOrThrow(
1684
+ tx,
1685
+ ensureSynced,
1686
+ context.facts.contentType,
1687
+ context.facts.recordId,
1688
+ );
1689
+ assertRecordFacts(current, context.facts);
1690
+ assertRecordSnapshot(current, authorizedRecord);
1691
+ if (slug && slug !== current.slug) {
1692
+ const duplicate = await tx.findOne<ContentItem>({
1693
+ model: "contentItem",
1694
+ where: [
1695
+ {
1696
+ field: "contentTypeId",
1697
+ value: contentType.id,
1698
+ operator: "eq" as const,
1699
+ },
1700
+ { field: "slug", value: slug, operator: "eq" as const },
1701
+ ],
1702
+ });
1703
+ if (duplicate) {
1704
+ throw new CMSOperationError(
1705
+ 409,
1706
+ "Content item with this slug already exists",
1707
+ "DUPLICATE_SLUG",
1708
+ );
1709
+ }
1710
+ }
1711
+ if (relationPlan) await applyRelationWritePlan(tx, relationPlan);
1712
+ const update: Partial<ContentItem> = { updatedAt: new Date() };
1713
+ if (slug) update.slug = slug;
1714
+ if (bodyData) update.data = JSON.stringify(mergedData);
1715
+ const matched = await tx.updateMany({
1716
+ model: "contentItem",
1717
+ where: [
1718
+ {
1719
+ field: "id",
1720
+ value: context.facts.recordId,
1721
+ operator: "eq" as const,
1722
+ },
1723
+ {
1724
+ field: "contentTypeId",
1725
+ value: contentType.id,
1726
+ operator: "eq" as const,
1727
+ },
1728
+ {
1729
+ field: "authorId",
1730
+ value: context.facts.authorId ?? null,
1731
+ operator: "eq" as const,
1732
+ },
1733
+ {
1734
+ field: "updatedAt",
1735
+ value: current.updatedAt,
1736
+ operator: "eq" as const,
1737
+ },
1738
+ ],
1739
+ update,
1740
+ });
1741
+ if (!didUpdateContentItem(matched, context.facts.recordId)) {
1742
+ throw staleRecordError();
1743
+ }
1744
+ if (relationPlan) {
1745
+ await syncRelations(
1746
+ tx,
1747
+ context.facts.recordId,
1748
+ relationPlan.relationIds,
1749
+ );
1750
+ }
1751
+ const updated = await getRecordOrThrow(
1752
+ tx,
1753
+ ensureSynced,
1754
+ context.facts.contentType,
1755
+ context.facts.recordId,
1756
+ );
1757
+ return operationContentItem(serializeContentItemWithType(updated));
1758
+ });
1759
+ },
1760
+ after: async (context) => {
1761
+ const base = updateContext(context);
1762
+ const afterUpdateContent = hooks?.onAfterUpdateContent as
1763
+ | ((item: unknown, context: unknown) => Promise<void> | void)
1764
+ | undefined;
1765
+ await afterUpdateContent?.(
1766
+ context.result,
1767
+ Object.freeze({ ...base, result: context.result }),
1768
+ );
1769
+ },
1770
+ onError: async ({ error, ...context }) => {
1771
+ await notifyError(error, "update", updateContext(context));
1772
+ },
1773
+ });
1774
+
1775
+ const deleteContentItem = defineOperation({
1776
+ input: DeleteContentItemInputSchema,
1777
+ permission: cmsPermissions.record.delete,
1778
+ facts: async ({ input }) => {
1779
+ const item = await getRecordOrThrow(
1780
+ adapter,
1781
+ ensureSynced,
1782
+ input.typeSlug,
1783
+ input.id,
1784
+ );
1785
+ const facts = recordFacts(item);
1786
+ return {
1787
+ contentType: facts.contentType,
1788
+ recordId: facts.recordId ?? input.id,
1789
+ ...(facts.authorId ? { authorId: facts.authorId } : {}),
1790
+ };
1791
+ },
1792
+ before: async (context) => {
1793
+ const current = await getRecordOrThrow(
1794
+ adapter,
1795
+ ensureSynced,
1796
+ context.facts.contentType,
1797
+ context.facts.recordId,
1798
+ );
1799
+ assertRecordFacts(current, context.facts);
1800
+ await runBeforeHook(
1801
+ () =>
1802
+ hooks?.onBeforeDeleteContent?.(
1803
+ context.facts.recordId,
1804
+ deleteContext(context),
1805
+ ),
1806
+ "Delete operation denied",
1807
+ );
1808
+ },
1809
+ execute: async ({ facts }) =>
1810
+ adapter.transaction(async (tx) => {
1811
+ const existing = await getRecordOrThrow(
1812
+ tx,
1813
+ ensureSynced,
1814
+ facts.contentType,
1815
+ facts.recordId,
1816
+ );
1817
+ assertRecordFacts(existing, facts);
1818
+ const matched = await tx.updateMany({
1819
+ model: "contentItem",
1820
+ where: [
1821
+ { field: "id", value: facts.recordId, operator: "eq" as const },
1822
+ {
1823
+ field: "contentTypeId",
1824
+ value: existing.contentTypeId,
1825
+ operator: "eq" as const,
1826
+ },
1827
+ {
1828
+ field: "authorId",
1829
+ value: facts.authorId ?? null,
1830
+ operator: "eq" as const,
1831
+ },
1832
+ {
1833
+ field: "updatedAt",
1834
+ value: existing.updatedAt,
1835
+ operator: "eq" as const,
1836
+ },
1837
+ ],
1838
+ update: { updatedAt: new Date() },
1839
+ });
1840
+ if (!didUpdateContentItem(matched, facts.recordId)) {
1841
+ throw staleRecordError();
1842
+ }
1843
+ await tx.delete({
1844
+ model: "contentItem",
1845
+ where: [
1846
+ { field: "id", value: facts.recordId, operator: "eq" as const },
1847
+ {
1848
+ field: "contentTypeId",
1849
+ value: existing.contentTypeId,
1850
+ operator: "eq" as const,
1851
+ },
1852
+ ],
1853
+ });
1854
+ return { success: true } as const;
1855
+ }),
1856
+ after: async (context) => {
1857
+ const base = deleteContext(context);
1858
+ await hooks?.onAfterDeleteContent?.(
1859
+ context.facts.recordId,
1860
+ Object.freeze({ ...base, result: context.result }),
1861
+ );
1862
+ },
1863
+ onError: async ({ error, ...context }) => {
1864
+ await notifyError(error, "delete", deleteContext(context));
1865
+ },
1866
+ });
1867
+
1868
+ const getContentItemPopulated = defineOperation({
1869
+ input: GetContentItemInputSchema,
1870
+ permission: cmsPermissions.record.read,
1871
+ facts: async ({ input }) =>
1872
+ recordFacts(
1873
+ await getRecordOrThrow(adapter, ensureSynced, input.typeSlug, input.id),
1874
+ ),
1875
+ additionalPermissions: async ({ input, facts }) => {
1876
+ const snapshot = await getPopulatedRelationSnapshot(
1877
+ adapter,
1878
+ facts.recordId ?? "",
1879
+ );
1880
+ populatedRelationSnapshots.set(input as object, snapshot);
1881
+ const contentTypes = new Set([
1882
+ facts.contentType,
1883
+ ...snapshot.targets.map(
1884
+ ({ facts: targetFacts }) => targetFacts.contentType,
1885
+ ),
1886
+ ]);
1887
+ return [
1888
+ ...[...contentTypes].map((contentType) =>
1889
+ cmsPermissions.contentType.read({ contentType }),
1890
+ ),
1891
+ ...snapshot.targets.map(({ facts: targetFacts }) =>
1892
+ cmsPermissions.record.read(targetFacts),
1893
+ ),
1894
+ ];
1895
+ },
1896
+ execute: async ({ input, facts }) => {
1897
+ try {
1898
+ const item = await getRecordOrThrow(
1899
+ adapter,
1900
+ ensureSynced,
1901
+ facts.contentType,
1902
+ facts.recordId ?? "",
1903
+ );
1904
+ assertRecordFacts(item, facts);
1905
+ const authorizedSnapshot = populatedRelationSnapshots.get(
1906
+ input as object,
1907
+ );
1908
+ if (!authorizedSnapshot) {
1909
+ throw new CMSOperationError(
1910
+ 500,
1911
+ "Related-content authorization snapshot is unavailable.",
1912
+ "AUTHORIZATION_SNAPSHOT_MISSING",
1913
+ );
1914
+ }
1915
+ const currentSnapshot = await getPopulatedRelationSnapshot(
1916
+ adapter,
1917
+ facts.recordId ?? "",
1918
+ );
1919
+ assertPopulatedRelationSnapshot(authorizedSnapshot, currentSnapshot);
1920
+ const relations = populatedRelationsFromSnapshot(currentSnapshot);
1921
+ return {
1922
+ ...operationContentItem(serializeContentItemWithType(item)),
1923
+ _relations: Object.fromEntries(
1924
+ Object.entries(relations).map(([field, records]) => [
1925
+ field,
1926
+ records.map(operationContentItem),
1927
+ ]),
1928
+ ),
1929
+ };
1930
+ } finally {
1931
+ populatedRelationSnapshots.delete(input as object);
1932
+ }
1933
+ },
1934
+ onError: ({ error, ...context }) =>
1935
+ notifyError(
1936
+ error,
1937
+ "get",
1938
+ genericContext(context, context.facts.contentType),
1939
+ ),
1940
+ });
1941
+
1942
+ const listContentByRelation = defineOperation({
1943
+ input: ListContentByRelationInputSchema,
1944
+ permission: cmsPermissions.record.read,
1945
+ facts: async ({ input }) =>
1946
+ collectionFacts(
1947
+ (await getContentTypeOrThrow(adapter, ensureSynced, input.typeSlug))
1948
+ .slug,
1949
+ ),
1950
+ additionalPermissions: async ({ input, facts }) => {
1951
+ const sourceContentType = await getContentTypeOrThrow(
1952
+ adapter,
1953
+ ensureSynced,
1954
+ facts.contentType,
1955
+ );
1956
+ const authorization = await deriveRelationFilterAuthorization(
1957
+ adapter,
1958
+ ensureSynced,
1959
+ sourceContentType,
1960
+ input.query.field,
1961
+ input.query.targetId,
1962
+ );
1963
+ relationFilterAuthorizationSnapshots.set(input as object, authorization);
1964
+ return [
1965
+ cmsPermissions.contentType.read({ contentType: facts.contentType }),
1966
+ authorization.permission,
1967
+ ];
1968
+ },
1969
+ execute: async ({ input, facts }) => {
1970
+ const sourceContentType = await recheckRelationFilterAuthorization(
1971
+ input as object,
1972
+ facts.contentType,
1973
+ input.query.field,
1974
+ input.query.targetId,
1975
+ );
1976
+ const result = await listRecordsForRelation(
1977
+ adapter,
1978
+ sourceContentType,
1979
+ input.query.field,
1980
+ input.query.targetId,
1981
+ input.query.limit,
1982
+ input.query.offset,
1983
+ );
1984
+ return {
1985
+ ...result,
1986
+ items: result.items.map(operationContentItem),
1987
+ };
1988
+ },
1989
+ onError: ({ error, ...context }) =>
1990
+ notifyError(
1991
+ error,
1992
+ "list",
1993
+ genericContext(context, context.facts.contentType),
1994
+ ),
1995
+ });
1996
+
1997
+ const getInverseRelations = defineOperation({
1998
+ input: GetInverseRelationsInputSchema,
1999
+ permission: cmsPermissions.contentType.read,
2000
+ facts: async ({ input }) => ({
2001
+ contentType: (
2002
+ await getContentTypeOrThrow(adapter, ensureSynced, input.slug)
2003
+ ).slug,
2004
+ }),
2005
+ additionalPermissions: async ({ input, facts }) => {
2006
+ const authorization = await deriveInverseSourceAuthorization(
2007
+ adapter,
2008
+ ensureSynced,
2009
+ facts.contentType ?? "",
2010
+ );
2011
+ inverseSourceAuthorizationSnapshots.set(
2012
+ input as object,
2013
+ authorization.snapshot,
2014
+ );
2015
+ const permissions: Array<
2016
+ | ReturnType<typeof cmsPermissions.contentType.read>
2017
+ | ReturnType<typeof cmsPermissions.record.read>
2018
+ > = [...authorization.permissions];
2019
+ if (input.query.itemId) {
2020
+ const targetFacts = recordFacts(
2021
+ await getRecordOrThrow(
2022
+ adapter,
2023
+ ensureSynced,
2024
+ facts.contentType ?? "",
2025
+ input.query.itemId,
2026
+ ),
2027
+ );
2028
+ inverseTargetAuthorizationSnapshots.set(input as object, targetFacts);
2029
+ permissions.push(cmsPermissions.record.read(targetFacts));
2030
+ const authorizedSourceTypes = new Set<string>();
2031
+ for (const source of authorization.sources) {
2032
+ if (authorizedSourceTypes.has(source.contentTypeSlug)) continue;
2033
+ authorizedSourceTypes.add(source.contentTypeSlug);
2034
+ permissions.push(
2035
+ cmsPermissions.record.read(collectionFacts(source.contentTypeSlug)),
2036
+ );
2037
+ }
2038
+ }
2039
+ return permissions;
2040
+ },
2041
+ execute: async ({ input, facts }) => {
2042
+ try {
2043
+ const sources = await recheckInverseSourceAuthorization(
2044
+ input as object,
2045
+ facts.contentType ?? "",
2046
+ );
2047
+ if (input.query.itemId) {
2048
+ await recheckInverseTargetAuthorization(
2049
+ input as object,
2050
+ facts.contentType ?? "",
2051
+ input.query.itemId,
2052
+ );
2053
+ }
2054
+ const inverseRelations: InverseRelation[] = [];
2055
+ for (const source of sources) {
2056
+ let count = 0;
2057
+ if (input.query.itemId) {
2058
+ const relations = await adapter.findMany<ContentRelation>({
2059
+ model: "contentRelation",
2060
+ where: [
2061
+ {
2062
+ field: "targetId",
2063
+ value: input.query.itemId,
2064
+ operator: "eq" as const,
2065
+ },
2066
+ {
2067
+ field: "fieldName",
2068
+ value: source.fieldName,
2069
+ operator: "eq" as const,
2070
+ },
2071
+ ],
2072
+ });
2073
+ for (const sourceId of relations.map((value) => value.sourceId)) {
2074
+ const item = await adapter.findOne<ContentItem>({
2075
+ model: "contentItem",
2076
+ where: [
2077
+ {
2078
+ field: "id",
2079
+ value: sourceId,
2080
+ operator: "eq" as const,
2081
+ },
2082
+ {
2083
+ field: "contentTypeId",
2084
+ value: source.contentTypeId,
2085
+ operator: "eq" as const,
2086
+ },
2087
+ ],
2088
+ });
2089
+ if (item) count += 1;
2090
+ }
2091
+ }
2092
+ inverseRelations.push({
2093
+ sourceType: source.contentTypeSlug,
2094
+ sourceTypeName: source.contentTypeName,
2095
+ fieldName: source.fieldName,
2096
+ count,
2097
+ });
2098
+ }
2099
+ return {
2100
+ inverseRelations: inverseRelations.map((relation) => ({
2101
+ ...relation,
2102
+ })),
2103
+ };
2104
+ } finally {
2105
+ inverseTargetAuthorizationSnapshots.delete(input as object);
2106
+ }
2107
+ },
2108
+ onError: ({ error, ...context }) =>
2109
+ notifyError(
2110
+ error,
2111
+ "get",
2112
+ genericContext(context, context.facts.contentType),
2113
+ ),
2114
+ });
2115
+
2116
+ const listInverseRelationItems = defineOperation({
2117
+ input: ListInverseRelationItemsInputSchema,
2118
+ permission: cmsPermissions.record.read,
2119
+ facts: async ({ input }) => {
2120
+ await getContentTypeOrThrow(adapter, ensureSynced, input.slug);
2121
+ const sourceType = await getContentTypeOrThrow(
2122
+ adapter,
2123
+ ensureSynced,
2124
+ input.sourceType,
2125
+ );
2126
+ return collectionFacts(sourceType.slug);
2127
+ },
2128
+ additionalPermissions: async ({ input, facts }) => {
2129
+ const authorization = await deriveInverseSourceAuthorization(
2130
+ adapter,
2131
+ ensureSynced,
2132
+ input.slug,
2133
+ );
2134
+ const source = authorization.sources.find(
2135
+ (value) =>
2136
+ value.contentTypeSlug === facts.contentType &&
2137
+ value.fieldName === input.query.fieldName,
2138
+ );
2139
+ if (!source) {
2140
+ throw new CMSOperationError(
2141
+ 404,
2142
+ "Inverse relation not found",
2143
+ "INVERSE_RELATION_NOT_FOUND",
2144
+ );
2145
+ }
2146
+ inverseSourceAuthorizationSnapshots.set(
2147
+ input as object,
2148
+ authorization.snapshot,
2149
+ );
2150
+ const targetFacts = recordFacts(
2151
+ await getRecordOrThrow(
2152
+ adapter,
2153
+ ensureSynced,
2154
+ input.slug,
2155
+ input.query.itemId,
2156
+ ),
2157
+ );
2158
+ inverseTargetAuthorizationSnapshots.set(input as object, targetFacts);
2159
+ return [
2160
+ cmsPermissions.contentType.read({ contentType: facts.contentType }),
2161
+ cmsPermissions.record.read(targetFacts),
2162
+ ];
2163
+ },
2164
+ execute: async ({ input, facts }) => {
2165
+ try {
2166
+ const sources = await recheckInverseSourceAuthorization(
2167
+ input as object,
2168
+ input.slug,
2169
+ );
2170
+ const source = sources.find(
2171
+ (value) =>
2172
+ value.contentTypeSlug === facts.contentType &&
2173
+ value.fieldName === input.query.fieldName,
2174
+ );
2175
+ if (!source) throw relationSchemaChangedError();
2176
+ await recheckInverseTargetAuthorization(
2177
+ input as object,
2178
+ input.slug,
2179
+ input.query.itemId,
2180
+ );
2181
+ const result = await listRecordsForRelation(
2182
+ adapter,
2183
+ await getContentTypeOrThrow(adapter, ensureSynced, facts.contentType),
2184
+ source.fieldName,
2185
+ input.query.itemId,
2186
+ input.query.limit,
2187
+ input.query.offset,
2188
+ );
2189
+ return {
2190
+ ...result,
2191
+ items: result.items.map(operationContentItem),
2192
+ };
2193
+ } finally {
2194
+ inverseTargetAuthorizationSnapshots.delete(input as object);
2195
+ }
2196
+ },
2197
+ onError: ({ error, ...context }) =>
2198
+ notifyError(
2199
+ error,
2200
+ "list",
2201
+ genericContext(context, context.facts.contentType),
2202
+ ),
2203
+ });
2204
+
2205
+ return {
2206
+ listContentTypes,
2207
+ getContentTypeBySlug,
2208
+ listContentItems,
2209
+ getContentItem,
2210
+ createContentItem,
2211
+ updateContentItem,
2212
+ deleteContentItem,
2213
+ getContentItemPopulated,
2214
+ listContentByRelation,
2215
+ getInverseRelations,
2216
+ listInverseRelationItems,
2217
+ } as const;
2218
+ }