@byline/host-tanstack-start 4.19.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/dist/admin-shell/chrome/admin-app-bar.d.ts +7 -0
  2. package/dist/admin-shell/chrome/admin-app-bar.js +10 -2
  3. package/dist/admin-shell/chrome/session-change-boundary.d.ts +14 -0
  4. package/dist/admin-shell/chrome/session-change-boundary.js +87 -0
  5. package/dist/admin-shell/chrome/session-change-boundary.test.d.ts +8 -0
  6. package/dist/admin-shell/chrome/sign-in-page.d.ts +2 -1
  7. package/dist/admin-shell/chrome/sign-in-page.js +60 -3
  8. package/dist/admin-shell/collections/edit.d.ts +1 -1
  9. package/dist/admin-shell/collections/edit.js +151 -31
  10. package/dist/admin-shell/collections/history.js +4 -2
  11. package/dist/admin-shell/collections/list.js +30 -6
  12. package/dist/admin-shell/collections/restore-version-modal.d.ts +3 -1
  13. package/dist/admin-shell/collections/restore-version-modal.js +9 -4
  14. package/dist/admin-shell/collections/tree-list.js +16 -6
  15. package/dist/admin-shell/collections/version-history.d.ts +2 -1
  16. package/dist/admin-shell/collections/version-history.js +11 -2
  17. package/dist/admin-shell/document-mutation-notice.d.ts +6 -0
  18. package/dist/admin-shell/document-mutation-notice.js +53 -0
  19. package/dist/admin-shell/document-mutation-state.d.ts +22 -0
  20. package/dist/admin-shell/document-mutation-state.js +71 -0
  21. package/dist/admin-shell/document-mutation-state.test.d.ts +1 -0
  22. package/dist/admin-shell/scheduled-publications/list.js +15 -5
  23. package/dist/admin-shell/singletons/history.js +4 -2
  24. package/dist/admin-shell/singletons/view.d.ts +1 -1
  25. package/dist/admin-shell/singletons/view.js +98 -19
  26. package/dist/integrations/byline-admin-services.js +7 -1
  27. package/dist/integrations/byline-field-services.js +2 -9
  28. package/dist/integrations/client-ip.d.ts +19 -0
  29. package/dist/integrations/client-ip.js +34 -0
  30. package/dist/integrations/session-coordination.d.ts +24 -0
  31. package/dist/integrations/session-coordination.js +85 -0
  32. package/dist/integrations/session-middleware.d.ts +11 -0
  33. package/dist/integrations/session-middleware.js +94 -0
  34. package/dist/integrations/session-request-kind.d.ts +9 -0
  35. package/dist/integrations/session-request-kind.js +10 -0
  36. package/dist/integrations/session-request-middleware.d.ts +9 -0
  37. package/dist/integrations/session-request-middleware.js +7 -0
  38. package/dist/integrations/sign-in-body.d.ts +12 -0
  39. package/dist/integrations/sign-in-body.js +52 -0
  40. package/dist/integrations/sign-in-middleware.d.ts +9 -0
  41. package/dist/integrations/sign-in-middleware.js +10 -0
  42. package/dist/routes/create-admin-layout-route.js +46 -31
  43. package/dist/routes/create-collection-edit-route.js +1 -1
  44. package/dist/routes/create-collection-list-route.d.ts +0 -7
  45. package/dist/routes/create-collection-list-route.js +7 -9
  46. package/dist/routes/create-sign-in-route.js +4 -2
  47. package/dist/routes/create-singleton-route.d.ts +2 -0
  48. package/dist/routes/create-singleton-route.js +4 -3
  49. package/dist/server-fns/admin-account/change-password.d.ts +3 -1
  50. package/dist/server-fns/admin-account/change-password.js +9 -3
  51. package/dist/server-fns/admin-account/get.d.ts +3 -1
  52. package/dist/server-fns/admin-account/get.js +4 -1
  53. package/dist/server-fns/admin-account/index.d.ts +5 -5
  54. package/dist/server-fns/admin-account/update.d.ts +3 -1
  55. package/dist/server-fns/admin-account/update.js +4 -1
  56. package/dist/server-fns/admin-activity/get.d.ts +3 -1
  57. package/dist/server-fns/admin-activity/get.js +4 -1
  58. package/dist/server-fns/admin-permissions/get-role-abilities.d.ts +3 -1
  59. package/dist/server-fns/admin-permissions/get-role-abilities.js +4 -1
  60. package/dist/server-fns/admin-permissions/index.d.ts +4 -4
  61. package/dist/server-fns/admin-permissions/list-registered.d.ts +3 -1
  62. package/dist/server-fns/admin-permissions/list-registered.js +4 -1
  63. package/dist/server-fns/admin-permissions/set-role-abilities.d.ts +3 -1
  64. package/dist/server-fns/admin-permissions/set-role-abilities.js +4 -1
  65. package/dist/server-fns/admin-permissions/who-has.d.ts +3 -1
  66. package/dist/server-fns/admin-permissions/who-has.js +4 -1
  67. package/dist/server-fns/admin-roles/create.d.ts +3 -1
  68. package/dist/server-fns/admin-roles/create.js +4 -1
  69. package/dist/server-fns/admin-roles/delete.d.ts +3 -1
  70. package/dist/server-fns/admin-roles/delete.js +4 -1
  71. package/dist/server-fns/admin-roles/get.d.ts +3 -1
  72. package/dist/server-fns/admin-roles/get.js +4 -1
  73. package/dist/server-fns/admin-roles/index.d.ts +6 -6
  74. package/dist/server-fns/admin-roles/list.d.ts +3 -1
  75. package/dist/server-fns/admin-roles/list.js +4 -1
  76. package/dist/server-fns/admin-roles/reorder.d.ts +3 -1
  77. package/dist/server-fns/admin-roles/reorder.js +4 -1
  78. package/dist/server-fns/admin-roles/update.d.ts +3 -1
  79. package/dist/server-fns/admin-roles/update.js +4 -1
  80. package/dist/server-fns/admin-users/create.d.ts +3 -1
  81. package/dist/server-fns/admin-users/create.js +4 -1
  82. package/dist/server-fns/admin-users/delete.d.ts +3 -1
  83. package/dist/server-fns/admin-users/delete.js +4 -1
  84. package/dist/server-fns/admin-users/disable.d.ts +3 -1
  85. package/dist/server-fns/admin-users/disable.js +4 -1
  86. package/dist/server-fns/admin-users/enable.d.ts +3 -1
  87. package/dist/server-fns/admin-users/enable.js +4 -1
  88. package/dist/server-fns/admin-users/get-user-roles.d.ts +3 -1
  89. package/dist/server-fns/admin-users/get-user-roles.js +4 -1
  90. package/dist/server-fns/admin-users/get.d.ts +3 -1
  91. package/dist/server-fns/admin-users/get.js +4 -1
  92. package/dist/server-fns/admin-users/index.d.ts +10 -10
  93. package/dist/server-fns/admin-users/list.d.ts +3 -1
  94. package/dist/server-fns/admin-users/list.js +4 -1
  95. package/dist/server-fns/admin-users/set-password.d.ts +3 -1
  96. package/dist/server-fns/admin-users/set-password.js +4 -1
  97. package/dist/server-fns/admin-users/set-user-roles.d.ts +3 -1
  98. package/dist/server-fns/admin-users/set-user-roles.js +4 -1
  99. package/dist/server-fns/admin-users/update.d.ts +3 -1
  100. package/dist/server-fns/admin-users/update.js +4 -1
  101. package/dist/server-fns/ai/execute.d.ts +3 -1
  102. package/dist/server-fns/ai/execute.js +4 -1
  103. package/dist/server-fns/ai/index.d.ts +1 -1
  104. package/dist/server-fns/analytics/maintenance.d.ts +6 -2
  105. package/dist/server-fns/analytics/maintenance.js +7 -2
  106. package/dist/server-fns/analytics/queries.d.ts +18 -6
  107. package/dist/server-fns/analytics/queries.js +19 -6
  108. package/dist/server-fns/auth/current-user.d.ts +1 -0
  109. package/dist/server-fns/auth/current-user.js +5 -2
  110. package/dist/server-fns/auth/renew.d.ts +13 -0
  111. package/dist/server-fns/auth/renew.js +45 -0
  112. package/dist/server-fns/auth/sign-in.d.ts +2 -1
  113. package/dist/server-fns/auth/sign-in.js +88 -17
  114. package/dist/server-fns/auth/sign-out.d.ts +3 -1
  115. package/dist/server-fns/auth/sign-out.js +38 -6
  116. package/dist/server-fns/collections/audit.d.ts +3 -1
  117. package/dist/server-fns/collections/audit.js +4 -1
  118. package/dist/server-fns/collections/copy-to-locale.d.ts +5 -1
  119. package/dist/server-fns/collections/copy-to-locale.js +7 -2
  120. package/dist/server-fns/collections/create.d.ts +4 -1
  121. package/dist/server-fns/collections/create.js +8 -3
  122. package/dist/server-fns/collections/delete-locale.d.ts +5 -1
  123. package/dist/server-fns/collections/delete-locale.js +7 -2
  124. package/dist/server-fns/collections/delete-outcome.d.ts +7 -3
  125. package/dist/server-fns/collections/delete-outcome.js +4 -0
  126. package/dist/server-fns/collections/delete.d.ts +5 -1
  127. package/dist/server-fns/collections/delete.js +8 -3
  128. package/dist/server-fns/collections/duplicate.d.ts +5 -1
  129. package/dist/server-fns/collections/duplicate.js +8 -3
  130. package/dist/server-fns/collections/get.d.ts +1 -0
  131. package/dist/server-fns/collections/get.js +12 -6
  132. package/dist/server-fns/collections/history.d.ts +3 -1
  133. package/dist/server-fns/collections/history.js +4 -1
  134. package/dist/server-fns/collections/index.d.ts +21 -21
  135. package/dist/server-fns/collections/list.d.ts +3 -1
  136. package/dist/server-fns/collections/list.js +15 -4
  137. package/dist/server-fns/collections/reindex.d.ts +3 -1
  138. package/dist/server-fns/collections/reindex.js +4 -1
  139. package/dist/server-fns/collections/reorder.d.ts +10 -2
  140. package/dist/server-fns/collections/reorder.js +23 -76
  141. package/dist/server-fns/collections/restore-version.d.ts +6 -1
  142. package/dist/server-fns/collections/restore-version.js +8 -2
  143. package/dist/server-fns/collections/save-outcome.d.ts +1 -0
  144. package/dist/server-fns/collections/save-outcome.js +1 -0
  145. package/dist/server-fns/collections/scheduled-publication.d.ts +64 -7
  146. package/dist/server-fns/collections/scheduled-publication.js +42 -21
  147. package/dist/server-fns/collections/set-list-view-preference.d.ts +3 -1
  148. package/dist/server-fns/collections/set-list-view-preference.js +4 -1
  149. package/dist/server-fns/collections/stats.js +4 -1
  150. package/dist/server-fns/collections/status.d.ts +14 -2
  151. package/dist/server-fns/collections/status.js +18 -5
  152. package/dist/server-fns/collections/tree-mutation.d.ts +7 -6
  153. package/dist/server-fns/collections/tree-mutation.js +17 -5
  154. package/dist/server-fns/collections/tree.d.ts +29 -6
  155. package/dist/server-fns/collections/tree.js +32 -17
  156. package/dist/server-fns/collections/update.d.ts +18 -6
  157. package/dist/server-fns/collections/update.js +39 -20
  158. package/dist/server-fns/collections/upload.d.ts +11 -1
  159. package/dist/server-fns/collections/upload.js +6 -2
  160. package/dist/server-fns/document-mutation-errors.d.ts +5 -0
  161. package/dist/server-fns/document-mutation-errors.js +50 -0
  162. package/dist/server-fns/editable-revision.d.ts +9 -0
  163. package/dist/server-fns/editable-revision.js +11 -0
  164. package/dist/server-fns/i18n/set-locale.d.ts +3 -1
  165. package/dist/server-fns/i18n/set-locale.js +18 -3
  166. package/dist/server-fns/preview/disable.d.ts +3 -1
  167. package/dist/server-fns/preview/disable.js +4 -1
  168. package/dist/server-fns/preview/enable.d.ts +3 -1
  169. package/dist/server-fns/preview/enable.js +4 -1
  170. package/dist/server-fns/singleton-document-read.d.ts +1 -1
  171. package/dist/server-fns/singleton-document-read.js +7 -3
  172. package/dist/server-fns/singletons/cancel-scheduled-publish.d.ts +9 -2
  173. package/dist/server-fns/singletons/cancel-scheduled-publish.js +7 -1
  174. package/dist/server-fns/singletons/change-status.d.ts +5 -1
  175. package/dist/server-fns/singletons/change-status.js +7 -1
  176. package/dist/server-fns/singletons/confirm-scheduled-publish.d.ts +8 -2
  177. package/dist/server-fns/singletons/confirm-scheduled-publish.js +7 -2
  178. package/dist/server-fns/singletons/copy-to-locale.d.ts +5 -1
  179. package/dist/server-fns/singletons/copy-to-locale.js +7 -2
  180. package/dist/server-fns/singletons/find-by-version.d.ts +3 -1
  181. package/dist/server-fns/singletons/find-by-version.js +4 -1
  182. package/dist/server-fns/singletons/get-scheduled-publish.d.ts +3 -1
  183. package/dist/server-fns/singletons/get-scheduled-publish.js +4 -1
  184. package/dist/server-fns/singletons/get.d.ts +6 -4
  185. package/dist/server-fns/singletons/get.js +4 -1
  186. package/dist/server-fns/singletons/history.d.ts +3 -1
  187. package/dist/server-fns/singletons/history.js +4 -1
  188. package/dist/server-fns/singletons/restore-version.d.ts +5 -1
  189. package/dist/server-fns/singletons/restore-version.js +9 -3
  190. package/dist/server-fns/singletons/schedule-publish.d.ts +8 -2
  191. package/dist/server-fns/singletons/schedule-publish.js +7 -2
  192. package/dist/server-fns/singletons/unpublish.d.ts +5 -1
  193. package/dist/server-fns/singletons/unpublish.js +7 -1
  194. package/dist/server-fns/singletons/update.d.ts +6 -4
  195. package/dist/server-fns/singletons/update.js +8 -4
  196. package/dist/server-fns/structural-receipt.d.ts +4 -0
  197. package/dist/server-fns/structural-receipt.js +31 -0
  198. package/package.json +20 -20
  199. package/src/admin-shell/chrome/admin-app-bar.tsx +11 -6
  200. package/src/admin-shell/chrome/session-change-boundary.test.tsx +78 -0
  201. package/src/admin-shell/chrome/session-change-boundary.tsx +94 -0
  202. package/src/admin-shell/chrome/sign-in-page.tsx +68 -3
  203. package/src/admin-shell/collections/edit.tsx +173 -54
  204. package/src/admin-shell/collections/history.tsx +11 -2
  205. package/src/admin-shell/collections/list.tsx +22 -6
  206. package/src/admin-shell/collections/restore-version-modal.tsx +12 -3
  207. package/src/admin-shell/collections/save-navigation.test.tsx +156 -8
  208. package/src/admin-shell/collections/tree-list.tsx +16 -6
  209. package/src/admin-shell/collections/version-history.tsx +13 -3
  210. package/src/admin-shell/document-mutation-notice.tsx +45 -0
  211. package/src/admin-shell/document-mutation-state.test.tsx +146 -0
  212. package/src/admin-shell/document-mutation-state.ts +95 -0
  213. package/src/admin-shell/scheduled-publications/list.tsx +16 -6
  214. package/src/admin-shell/singletons/history.test.tsx +3 -1
  215. package/src/admin-shell/singletons/history.tsx +10 -2
  216. package/src/admin-shell/singletons/view.test.tsx +59 -18
  217. package/src/admin-shell/singletons/view.tsx +110 -29
  218. package/src/integrations/byline-admin-services.ts +7 -1
  219. package/src/integrations/byline-field-services.ts +2 -3
  220. package/src/integrations/client-ip.test.node.ts +45 -0
  221. package/src/integrations/client-ip.ts +54 -0
  222. package/src/integrations/framework-fetch-shape.test.node.ts +56 -0
  223. package/src/integrations/session-client.test.node.ts +121 -0
  224. package/src/integrations/session-coordination.test.node.ts +77 -0
  225. package/src/integrations/session-coordination.ts +96 -0
  226. package/src/integrations/session-middleware.test.node.ts +87 -0
  227. package/src/integrations/session-middleware.ts +107 -0
  228. package/src/integrations/session-request-kind.ts +18 -0
  229. package/src/integrations/session-request-middleware.ts +17 -0
  230. package/src/integrations/sign-in-body.test.node.ts +59 -0
  231. package/src/integrations/sign-in-body.ts +50 -0
  232. package/src/integrations/sign-in-middleware.ts +24 -0
  233. package/src/routes/create-admin-layout-route.tsx +48 -30
  234. package/src/routes/create-collection-edit-route.tsx +1 -0
  235. package/src/routes/create-collection-list-route.tsx +13 -2
  236. package/src/routes/create-sign-in-route.tsx +10 -2
  237. package/src/routes/create-singleton-route.test.node.ts +10 -2
  238. package/src/routes/create-singleton-route.tsx +6 -4
  239. package/src/server-fns/admin-account/change-password.test.node.ts +69 -0
  240. package/src/server-fns/admin-account/change-password.ts +13 -2
  241. package/src/server-fns/admin-account/get.ts +2 -0
  242. package/src/server-fns/admin-account/update.ts +2 -0
  243. package/src/server-fns/admin-activity/get.ts +2 -0
  244. package/src/server-fns/admin-permissions/get-role-abilities.ts +2 -0
  245. package/src/server-fns/admin-permissions/list-registered.ts +5 -4
  246. package/src/server-fns/admin-permissions/set-role-abilities.ts +2 -0
  247. package/src/server-fns/admin-permissions/who-has.ts +2 -0
  248. package/src/server-fns/admin-roles/create.ts +2 -0
  249. package/src/server-fns/admin-roles/delete.ts +2 -0
  250. package/src/server-fns/admin-roles/get.ts +2 -0
  251. package/src/server-fns/admin-roles/list.ts +5 -4
  252. package/src/server-fns/admin-roles/reorder.ts +2 -0
  253. package/src/server-fns/admin-roles/update.ts +2 -0
  254. package/src/server-fns/admin-users/create.ts +2 -0
  255. package/src/server-fns/admin-users/delete.ts +2 -0
  256. package/src/server-fns/admin-users/disable.ts +2 -0
  257. package/src/server-fns/admin-users/enable.ts +2 -0
  258. package/src/server-fns/admin-users/get-user-roles.ts +2 -0
  259. package/src/server-fns/admin-users/get.ts +2 -0
  260. package/src/server-fns/admin-users/list.ts +2 -0
  261. package/src/server-fns/admin-users/set-password.ts +2 -0
  262. package/src/server-fns/admin-users/set-user-roles.ts +2 -0
  263. package/src/server-fns/admin-users/update.ts +2 -0
  264. package/src/server-fns/ai/execute.ts +3 -0
  265. package/src/server-fns/analytics/maintenance.ts +4 -0
  266. package/src/server-fns/analytics/queries.ts +14 -8
  267. package/src/server-fns/auth/current-user.ts +7 -3
  268. package/src/server-fns/auth/renew.test.node.ts +85 -0
  269. package/src/server-fns/auth/renew.ts +54 -0
  270. package/src/server-fns/auth/sign-in.test.node.ts +114 -0
  271. package/src/server-fns/auth/sign-in.ts +80 -27
  272. package/src/server-fns/auth/sign-out.test.node.ts +85 -0
  273. package/src/server-fns/auth/sign-out.ts +44 -19
  274. package/src/server-fns/collections/audit.ts +2 -0
  275. package/src/server-fns/collections/copy-to-locale.ts +43 -36
  276. package/src/server-fns/collections/create.ts +56 -50
  277. package/src/server-fns/collections/delete-locale.ts +43 -32
  278. package/src/server-fns/collections/delete-outcome.test.node.ts +16 -0
  279. package/src/server-fns/collections/delete-outcome.ts +27 -16
  280. package/src/server-fns/collections/delete.ts +46 -38
  281. package/src/server-fns/collections/duplicate.ts +40 -32
  282. package/src/server-fns/collections/get.ts +11 -4
  283. package/src/server-fns/collections/history.ts +2 -0
  284. package/src/server-fns/collections/list.ts +10 -3
  285. package/src/server-fns/collections/reindex.ts +2 -0
  286. package/src/server-fns/collections/reorder.ts +34 -124
  287. package/src/server-fns/collections/restore-version.ts +49 -39
  288. package/src/server-fns/collections/revision-input.test.node.ts +122 -0
  289. package/src/server-fns/collections/save-outcome.test.node.ts +3 -0
  290. package/src/server-fns/collections/save-outcome.ts +2 -0
  291. package/src/server-fns/collections/scheduled-publication.ts +149 -115
  292. package/src/server-fns/collections/set-list-view-preference.ts +2 -0
  293. package/src/server-fns/collections/stats.ts +2 -0
  294. package/src/server-fns/collections/status.ts +89 -64
  295. package/src/server-fns/collections/tree-mutation.test.node.ts +87 -29
  296. package/src/server-fns/collections/tree-mutation.ts +28 -11
  297. package/src/server-fns/collections/tree.ts +156 -122
  298. package/src/server-fns/collections/update.ts +113 -86
  299. package/src/server-fns/collections/upload-singleton.test.node.ts +10 -7
  300. package/src/server-fns/collections/upload.ts +91 -77
  301. package/src/server-fns/document-mutation-errors.ts +67 -0
  302. package/src/server-fns/document-mutation-transport.test.node.ts +211 -0
  303. package/src/server-fns/editable-revision.test.node.ts +23 -0
  304. package/src/server-fns/editable-revision.ts +18 -0
  305. package/src/server-fns/i18n/set-locale.ts +19 -3
  306. package/src/server-fns/preview/disable.ts +8 -4
  307. package/src/server-fns/preview/enable.ts +9 -5
  308. package/src/server-fns/singleton-document-read.ts +7 -3
  309. package/src/server-fns/singletons/cancel-scheduled-publish.ts +12 -3
  310. package/src/server-fns/singletons/change-status.ts +12 -3
  311. package/src/server-fns/singletons/confirm-scheduled-publish.ts +14 -6
  312. package/src/server-fns/singletons/copy-to-locale.ts +22 -15
  313. package/src/server-fns/singletons/find-by-version.ts +2 -0
  314. package/src/server-fns/singletons/get-scheduled-publish.ts +2 -0
  315. package/src/server-fns/singletons/get.ts +5 -3
  316. package/src/server-fns/singletons/history.ts +2 -0
  317. package/src/server-fns/singletons/restore-version.ts +19 -12
  318. package/src/server-fns/singletons/schedule-publish.ts +20 -7
  319. package/src/server-fns/singletons/singletons.test.node.ts +69 -25
  320. package/src/server-fns/singletons/unpublish.ts +12 -3
  321. package/src/server-fns/singletons/update.ts +23 -16
  322. package/src/server-fns/structural-receipt.ts +41 -0
@@ -17,86 +17,111 @@ import {
17
17
  } from '@byline/core/services'
18
18
 
19
19
  import { ensureCollection } from '../../integrations/api-utils.js'
20
+ import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
21
+ import { withDocumentMutationErrors } from '../document-mutation-errors.js'
20
22
 
21
23
  // ---------------------------------------------------------------------------
22
24
  // Change document workflow status
23
25
  // ---------------------------------------------------------------------------
24
26
 
25
27
  export const updateDocumentStatus = createServerFn({ method: 'POST' })
26
- .validator((input: { collection: string; id: string; status: string }) => input)
27
- .handler(async ({ data: input }) => {
28
- const { collection: path, id, status: nextStatus } = input
29
- const logger = getLogger()
30
- const config = await ensureCollection(path)
31
- if (!config) {
32
- throw ERR_NOT_FOUND({
33
- message: 'Collection not found',
34
- details: { collectionPath: path },
35
- }).log(logger)
36
- }
28
+ .middleware([adminSessionMiddleware])
29
+ .validator(
30
+ (input: { expectedRevision: number; collection: string; id: string; status: string }) => input
31
+ )
32
+ .handler(
33
+ withDocumentMutationErrors(async ({ data: input }) => {
34
+ const { collection: path, id, status: nextStatus } = input
35
+ const logger = getLogger()
36
+ const config = await ensureCollection(path)
37
+ if (!config) {
38
+ throw ERR_NOT_FOUND({
39
+ message: 'Collection not found',
40
+ details: { collectionPath: path },
41
+ }).log(logger)
42
+ }
37
43
 
38
- const serverConfig = getServerConfig()
39
- const ctx: DocumentLifecycleContext = {
40
- db: serverConfig.db,
41
- definition: config.definition,
42
- collectionId: config.collection.id,
43
- collectionVersion: config.collection.version,
44
- collectionPath: path,
45
- logger,
46
- defaultLocale: serverConfig.i18n.content.defaultLocale,
47
- slugifier: serverConfig.slugifier,
48
- requestContext: await getAdminRequestContext(),
49
- }
44
+ const serverConfig = getServerConfig()
45
+ const ctx: DocumentLifecycleContext = {
46
+ db: serverConfig.db,
47
+ definition: config.definition,
48
+ collectionId: config.collection.id,
49
+ collectionVersion: config.collection.version,
50
+ collectionPath: path,
51
+ logger,
52
+ defaultLocale: serverConfig.i18n.content.defaultLocale,
53
+ slugifier: serverConfig.slugifier,
54
+ requestContext: await getAdminRequestContext(),
55
+ }
50
56
 
51
- const result = await changeDocumentStatus(ctx, {
52
- documentId: id,
53
- nextStatus,
57
+ const result = await changeDocumentStatus(ctx, {
58
+ documentId: id,
59
+ expectedRevision: input.expectedRevision,
60
+ nextStatus,
61
+ })
62
+ return {
63
+ status: 'ok' as const,
64
+ documentId: result.documentId,
65
+ revision: result.revision,
66
+ previousStatus: result.previousStatus,
67
+ newStatus: result.newStatus,
68
+ }
54
69
  })
55
- return {
56
- status: 'ok' as const,
57
- previousStatus: result.previousStatus,
58
- newStatus: result.newStatus,
59
- }
60
- })
70
+ )
61
71
 
62
72
  // ---------------------------------------------------------------------------
63
73
  // Unpublish document (archive the live published version)
64
74
  // ---------------------------------------------------------------------------
65
75
 
66
76
  export const unpublishDocument = createServerFn({ method: 'POST' })
67
- .validator((input: { collection: string; id: string }) => input)
68
- .handler(async ({ data: input }) => {
69
- const { collection: path, id } = input
70
- const logger = getLogger()
71
- const config = await ensureCollection(path)
72
- if (!config) {
73
- throw ERR_NOT_FOUND({
74
- message: 'Collection not found',
75
- details: { collectionPath: path },
76
- }).log(logger)
77
- }
77
+ .middleware([adminSessionMiddleware])
78
+ .validator((input: { expectedRevision: number; collection: string; id: string }) => input)
79
+ .handler(
80
+ withDocumentMutationErrors(async ({ data: input }) => {
81
+ const { collection: path, id } = input
82
+ const logger = getLogger()
83
+ const config = await ensureCollection(path)
84
+ if (!config) {
85
+ throw ERR_NOT_FOUND({
86
+ message: 'Collection not found',
87
+ details: { collectionPath: path },
88
+ }).log(logger)
89
+ }
78
90
 
79
- const serverConfig = getServerConfig()
80
- const ctx: DocumentLifecycleContext = {
81
- db: serverConfig.db,
82
- definition: config.definition,
83
- collectionId: config.collection.id,
84
- collectionVersion: config.collection.version,
85
- collectionPath: path,
86
- logger,
87
- defaultLocale: serverConfig.i18n.content.defaultLocale,
88
- slugifier: serverConfig.slugifier,
89
- requestContext: await getAdminRequestContext(),
90
- }
91
+ const serverConfig = getServerConfig()
92
+ const ctx: DocumentLifecycleContext = {
93
+ db: serverConfig.db,
94
+ definition: config.definition,
95
+ collectionId: config.collection.id,
96
+ collectionVersion: config.collection.version,
97
+ collectionPath: path,
98
+ logger,
99
+ defaultLocale: serverConfig.i18n.content.defaultLocale,
100
+ slugifier: serverConfig.slugifier,
101
+ requestContext: await getAdminRequestContext(),
102
+ }
91
103
 
92
- const result = await unpublishDocumentService(ctx, { documentId: id })
104
+ const result = await unpublishDocumentService(ctx, {
105
+ documentId: id,
106
+ expectedRevision: input.expectedRevision,
107
+ })
93
108
 
94
- if (result.archivedCount === 0) {
95
- throw ERR_NOT_FOUND({
96
- message: 'No published version found for this document',
97
- details: { documentId: id, collectionPath: path },
98
- }).log(logger)
99
- }
109
+ if (result.archivedCount === 0) {
110
+ throw ERR_NOT_FOUND({
111
+ message: 'No published version found for this document',
112
+ details: {
113
+ documentId: id,
114
+ expectedRevision: input.expectedRevision,
115
+ collectionPath: path,
116
+ },
117
+ }).log(logger)
118
+ }
100
119
 
101
- return { status: 'ok' as const, archivedCount: result.archivedCount }
102
- })
120
+ return {
121
+ status: 'ok' as const,
122
+ documentId: result.documentId,
123
+ revision: result.revision,
124
+ archivedCount: result.archivedCount,
125
+ }
126
+ })
127
+ )
@@ -175,51 +175,109 @@ describe('admin tree mutations', () => {
175
175
  expect(controller.isMoving()).toBe(false)
176
176
  })
177
177
 
178
- it('opts ordinary placement retries into lifecycle reconciliation', async () => {
178
+ const receipt = {
179
+ documentId: 'doc-1',
180
+ revision: 2,
181
+ affectedDocuments: [{ documentId: 'doc-1', revision: 2 }],
182
+ scheduledPublicationsNeedReconfirmation: false,
183
+ }
184
+
185
+ it('forwards each caller observation and requests reconciliation without retrying', async () => {
179
186
  const placeTreeNode = vi
180
- .fn()
187
+ .fn<Parameters<typeof placeAdminTreeNode>[0]['placeTreeNode']>()
181
188
  .mockRejectedValueOnce(new Error('post-commit hook failed'))
182
- .mockResolvedValue({ orderKey: 'key-1' })
183
-
184
- const retry = () =>
185
- placeAdminTreeNode({ placeTreeNode } as any, 'doc-1', {
189
+ .mockResolvedValue({ orderKey: 'key-1', ...receipt })
190
+ const handle = { placeTreeNode }
191
+ await expect(
192
+ placeAdminTreeNode(handle, 'doc-1', {
193
+ expectedRevision: 1,
186
194
  parentDocumentId: 'parent-1',
187
195
  beforeDocumentId: 'left-1',
188
196
  })
189
-
190
- await expect(retry()).rejects.toThrow('post-commit hook failed')
191
- await expect(retry()).resolves.toEqual({ orderKey: 'key-1' })
192
-
193
- expect(placeTreeNode).toHaveBeenCalledTimes(2)
194
- expect(placeTreeNode).toHaveBeenNthCalledWith(
195
- 2,
196
- 'doc-1',
197
- expect.objectContaining({ reconcile: true })
198
- )
197
+ ).rejects.toThrow('post-commit hook failed')
198
+ expect(placeTreeNode).toHaveBeenCalledTimes(1)
199
+ // A separate caller action carries its newly observed receipt, never an implicit retry.
200
+ await expect(
201
+ placeAdminTreeNode(handle, 'doc-1', {
202
+ expectedRevision: 2,
203
+ parentDocumentId: 'parent-1',
204
+ beforeDocumentId: 'left-1',
205
+ })
206
+ ).resolves.toEqual({ orderKey: 'key-1', ...receipt })
207
+ expect(placeTreeNode).toHaveBeenNthCalledWith(2, 'doc-1', {
208
+ expectedRevision: 2,
209
+ parentDocumentId: 'parent-1',
210
+ beforeDocumentId: 'left-1',
211
+ reconcile: true,
212
+ })
199
213
  })
200
214
 
201
- it('opts ordinary removal retries into lifecycle reconciliation', async () => {
202
- const removeFromTree = vi.fn().mockResolvedValue(undefined)
203
-
204
- await removeAdminTreeNode({ removeFromTree } as any, 'doc-1')
205
-
206
- expect(removeFromTree).toHaveBeenCalledWith('doc-1', { reconcile: true })
215
+ it('requests reconciliation for removal and preserves the caller revision', async () => {
216
+ const removeFromTree = vi
217
+ .fn<Parameters<typeof removeAdminTreeNode>[0]['removeFromTree']>()
218
+ .mockResolvedValue(receipt)
219
+ await expect(
220
+ removeAdminTreeNode({ removeFromTree }, 'doc-1', { expectedRevision: 1 })
221
+ ).resolves.toEqual(receipt)
222
+ expect(removeFromTree).toHaveBeenCalledWith('doc-1', { expectedRevision: 1, reconcile: true })
207
223
  })
208
224
 
209
225
  it('preserves an explicit reconciliation override', async () => {
210
- const placeTreeNode = vi.fn().mockResolvedValue({ orderKey: 'key-1' })
211
- const removeFromTree = vi.fn().mockResolvedValue(undefined)
212
-
213
- await placeAdminTreeNode({ placeTreeNode } as any, 'doc-1', {
226
+ const placeTreeNode = vi
227
+ .fn<Parameters<typeof placeAdminTreeNode>[0]['placeTreeNode']>()
228
+ .mockResolvedValue({ orderKey: 'key-1', ...receipt })
229
+ const removeFromTree = vi
230
+ .fn<Parameters<typeof removeAdminTreeNode>[0]['removeFromTree']>()
231
+ .mockResolvedValue(receipt)
232
+ await placeAdminTreeNode({ placeTreeNode }, 'doc-1', {
233
+ expectedRevision: 1,
214
234
  parentDocumentId: null,
215
235
  reconcile: false,
216
236
  })
217
- await removeAdminTreeNode({ removeFromTree } as any, 'doc-1', { reconcile: false })
218
-
237
+ await removeAdminTreeNode({ removeFromTree }, 'doc-1', {
238
+ expectedRevision: 2,
239
+ reconcile: false,
240
+ })
219
241
  expect(placeTreeNode).toHaveBeenCalledWith('doc-1', {
242
+ expectedRevision: 1,
220
243
  parentDocumentId: null,
221
244
  reconcile: false,
222
245
  })
223
- expect(removeFromTree).toHaveBeenCalledWith('doc-1', { reconcile: false })
246
+ expect(removeFromTree).toHaveBeenCalledWith('doc-1', { expectedRevision: 2, reconcile: false })
224
247
  })
225
248
  })
249
+
250
+ it('does not refresh or replace the observation after a typed stale tree move', async () => {
251
+ const error = {
252
+ code: 'ERR_DOCUMENT_STALE',
253
+ details: {
254
+ reason: 'revision_mismatch',
255
+ documentId: 'doc',
256
+ expectedRevision: 2,
257
+ currentRevision: 3,
258
+ },
259
+ }
260
+ const refresh = vi.fn()
261
+ const mutate = vi.fn().mockRejectedValue(error)
262
+ expect(await executeAdminTreeMove(mutate, refresh)).toEqual({ status: 'mutation-failed', error })
263
+ expect(mutate).toHaveBeenCalledTimes(1)
264
+ expect(refresh).not.toHaveBeenCalled()
265
+ })
266
+
267
+ it('retains the authorized schedule summary when refresh fails after a committed move', async () => {
268
+ const receipt = {
269
+ documentId: 'doc',
270
+ revision: 3,
271
+ affectedDocuments: [{ documentId: 'sibling', revision: 4 }],
272
+ scheduledPublicationsNeedReconfirmation: true,
273
+ }
274
+ const error = new Error('refresh unavailable')
275
+ expect(
276
+ await executeAdminTreeMove(
277
+ async () => receipt,
278
+ async () => {
279
+ throw error
280
+ }
281
+ )
282
+ ).toEqual({ status: 'refresh-failed', error, receipt })
283
+ })
@@ -1,5 +1,14 @@
1
1
  import type { CollectionHandle, PlaceTreeNodeOptions, RemoveFromTreeOptions } from '@byline/client'
2
- import { ErrorCodes, TREE_HOOK_COMMITTED_MARKER, TREE_PLACEMENT_STALE_MARKER } from '@byline/core'
2
+ import {
3
+ ErrorCodes,
4
+ getDocumentRevisionValidationDetails,
5
+ getDocumentStaleDetails,
6
+ getLockConflictDetails,
7
+ TREE_HOOK_COMMITTED_MARKER,
8
+ TREE_PLACEMENT_STALE_MARKER,
9
+ } from '@byline/core'
10
+
11
+ import { structuralReceipt } from '../structural-receipt.js'
3
12
 
4
13
  function hasErrorCode(error: unknown, code: string): boolean {
5
14
  return (
@@ -27,11 +36,11 @@ export function isCommittedTreeHookFailure(error: unknown): boolean {
27
36
  }
28
37
 
29
38
  export type AdminTreeMoveOutcome =
30
- | { status: 'ok' }
39
+ | { status: 'ok'; receipt?: ReturnType<typeof structuralReceipt> }
31
40
  | { status: 'suppressed' }
32
41
  | { status: 'mutation-failed'; error: unknown; refreshError?: unknown }
33
42
  | { status: 'committed-hook-failed'; error: unknown; refreshError?: unknown }
34
- | { status: 'refresh-failed'; error: unknown }
43
+ | { status: 'refresh-failed'; error: unknown; receipt?: ReturnType<typeof structuralReceipt> }
35
44
 
36
45
  export interface AdminTreeMoveUiEffects {
37
46
  rollback: boolean
@@ -93,9 +102,17 @@ export async function executeAdminTreeMove(
93
102
  mutate: () => Promise<unknown>,
94
103
  refresh: () => Promise<unknown>
95
104
  ): Promise<AdminTreeMoveOutcome> {
105
+ let receipt: ReturnType<typeof structuralReceipt>
96
106
  try {
97
- await mutate()
107
+ receipt = structuralReceipt(await mutate())
98
108
  } catch (error) {
109
+ if (
110
+ getDocumentStaleDetails(error) ||
111
+ getDocumentRevisionValidationDetails(error) ||
112
+ getLockConflictDetails(error) ||
113
+ hasErrorCode(error, ErrorCodes.NOT_FOUND)
114
+ )
115
+ return { status: 'mutation-failed', error }
99
116
  const status = isCommittedTreeHookFailure(error)
100
117
  ? ('committed-hook-failed' as const)
101
118
  : ('mutation-failed' as const)
@@ -109,9 +126,9 @@ export async function executeAdminTreeMove(
109
126
 
110
127
  try {
111
128
  await refresh()
112
- return { status: 'ok' }
129
+ return { status: 'ok', ...(receipt ? { receipt } : {}) }
113
130
  } catch (error) {
114
- return { status: 'refresh-failed', error }
131
+ return { status: 'refresh-failed', error, ...(receipt ? { receipt } : {}) }
115
132
  }
116
133
  }
117
134
 
@@ -140,24 +157,24 @@ export function createAdminTreeMoveController(): AdminTreeMoveController {
140
157
  }
141
158
  }
142
159
 
143
- /** Admin retries reconcile post-commit tree hooks unless explicitly disabled. */
160
+ /** Request reconciliation for explicit caller actions; never retry or replace their revision. */
144
161
  export function placeAdminTreeNode(
145
162
  handle: Pick<CollectionHandle, 'placeTreeNode'>,
146
163
  documentId: string,
147
164
  options: PlaceTreeNodeOptions
148
- ): Promise<{ orderKey: string }> {
165
+ ): ReturnType<CollectionHandle['placeTreeNode']> {
149
166
  return handle.placeTreeNode(documentId, {
150
167
  ...options,
151
168
  reconcile: options.reconcile ?? true,
152
169
  })
153
170
  }
154
171
 
155
- /** Admin retries reconcile post-commit tree hooks unless explicitly disabled. */
172
+ /** Request reconciliation for explicit caller actions; never retry or replace their revision. */
156
173
  export function removeAdminTreeNode(
157
174
  handle: Pick<CollectionHandle, 'removeFromTree'>,
158
175
  documentId: string,
159
- options: RemoveFromTreeOptions = {}
160
- ): Promise<void> {
176
+ options: RemoveFromTreeOptions
177
+ ): ReturnType<CollectionHandle['removeFromTree']> {
161
178
  return handle.removeFromTree(documentId, {
162
179
  ...options,
163
180
  reconcile: options.reconcile ?? true,