@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
@@ -10,10 +10,19 @@ import { createServerFn } from '@tanstack/react-start'
10
10
 
11
11
  import { getAdminBylineClient } from '@byline/client/server'
12
12
 
13
+ import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
14
+ import { withDocumentMutationErrors } from '../document-mutation-errors.js'
13
15
  import { serialise } from '../serialise.js'
14
16
 
15
17
  export const changeSingletonStatus = createServerFn({ method: 'POST' })
16
- .validator((input: { singleton: string; status: string }) => input)
17
- .handler(async ({ data }) =>
18
- serialise(await getAdminBylineClient().singleton(data.singleton).changeStatus(data.status))
18
+ .middleware([adminSessionMiddleware])
19
+ .validator((input: { expectedRevision: number; singleton: string; status: string }) => input)
20
+ .handler(
21
+ withDocumentMutationErrors(async ({ data }) =>
22
+ serialise(
23
+ await getAdminBylineClient()
24
+ .singleton(data.singleton)
25
+ .changeStatus(data.status, { expectedRevision: data.expectedRevision })
26
+ )
27
+ )
19
28
  )
@@ -10,14 +10,22 @@ import { createServerFn } from '@tanstack/react-start'
10
10
 
11
11
  import { getAdminBylineClient } from '@byline/client/server'
12
12
 
13
+ import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
14
+ import { withDocumentMutationErrors } from '../document-mutation-errors.js'
13
15
  import { serialise } from '../serialise.js'
14
16
 
15
17
  export const confirmSingletonScheduledPublish = createServerFn({ method: 'POST' })
16
- .validator((input: { singleton: string; expectedVersionId: string }) => input)
17
- .handler(async ({ data }) =>
18
- serialise(
19
- await getAdminBylineClient()
20
- .singleton(data.singleton)
21
- .confirmScheduledPublish({ expectedVersionId: data.expectedVersionId })
18
+ .middleware([adminSessionMiddleware])
19
+ .validator(
20
+ (input: { expectedRevision: number; singleton: string; expectedVersionId: string }) => input
21
+ )
22
+ .handler(
23
+ withDocumentMutationErrors(async ({ data }) =>
24
+ serialise(
25
+ await getAdminBylineClient().singleton(data.singleton).confirmScheduledPublish({
26
+ expectedRevision: data.expectedRevision,
27
+ expectedVersionId: data.expectedVersionId,
28
+ })
29
+ )
22
30
  )
23
31
  )
@@ -10,30 +10,37 @@ import { createServerFn } from '@tanstack/react-start'
10
10
 
11
11
  import { getAdminBylineClient } from '@byline/client/server'
12
12
 
13
+ import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
13
14
  import { toCommittedDocumentHookFailureResponse } from '../collections/save-outcome.js'
15
+ import { withDocumentMutationErrors } from '../document-mutation-errors.js'
14
16
  import { serialise } from '../serialise.js'
15
17
 
16
18
  export const copySingletonToLocale = createServerFn({ method: 'POST' })
19
+ .middleware([adminSessionMiddleware])
17
20
  .validator(
18
21
  (input: {
22
+ expectedRevision: number
19
23
  singleton: string
20
24
  sourceLocale: string
21
25
  targetLocale: string
22
26
  overwrite?: boolean
23
27
  }) => input
24
28
  )
25
- .handler(async ({ data }) => {
26
- try {
27
- return serialise(
28
- await getAdminBylineClient().singleton(data.singleton).copyToLocale({
29
- sourceLocale: data.sourceLocale,
30
- targetLocale: data.targetLocale,
31
- overwrite: data.overwrite,
32
- })
33
- )
34
- } catch (error) {
35
- const committedFailure = toCommittedDocumentHookFailureResponse(error)
36
- if (committedFailure != null) return committedFailure
37
- throw error
38
- }
39
- })
29
+ .handler(
30
+ withDocumentMutationErrors(async ({ data }) => {
31
+ try {
32
+ return serialise(
33
+ await getAdminBylineClient().singleton(data.singleton).copyToLocale({
34
+ expectedRevision: data.expectedRevision,
35
+ sourceLocale: data.sourceLocale,
36
+ targetLocale: data.targetLocale,
37
+ overwrite: data.overwrite,
38
+ })
39
+ )
40
+ } catch (error) {
41
+ const committedFailure = toCommittedDocumentHookFailureResponse(error)
42
+ if (committedFailure != null) return committedFailure
43
+ throw error
44
+ }
45
+ })
46
+ )
@@ -10,9 +10,11 @@ import { createServerFn } from '@tanstack/react-start'
10
10
 
11
11
  import { getAdminBylineClient } from '@byline/client/server'
12
12
 
13
+ import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
13
14
  import { serialise } from '../serialise.js'
14
15
 
15
16
  export const findSingletonByVersion = createServerFn({ method: 'GET' })
17
+ .middleware([adminSessionMiddleware])
16
18
  .validator((input: { singleton: string; versionId: string; locale?: string }) => input)
17
19
  .handler(async ({ data }) =>
18
20
  serialise(
@@ -10,9 +10,11 @@ import { createServerFn } from '@tanstack/react-start'
10
10
 
11
11
  import { getAdminBylineClient } from '@byline/client/server'
12
12
 
13
+ import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
13
14
  import { serialise } from '../serialise.js'
14
15
 
15
16
  export const getSingletonScheduledPublish = createServerFn({ method: 'GET' })
17
+ .middleware([adminSessionMiddleware])
16
18
  .validator((input: { singleton: string }) => input)
17
19
  .handler(async ({ data }) =>
18
20
  serialise(await getAdminBylineClient().singleton(data.singleton).getScheduledPublish())
@@ -8,6 +8,7 @@
8
8
 
9
9
  import { createServerFn } from '@tanstack/react-start'
10
10
 
11
+ import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
11
12
  import { readSingletonDocument } from '../singleton-document-read.js'
12
13
 
13
14
  export interface GetSingletonInput {
@@ -23,10 +24,11 @@ export interface GetSingletonInput {
23
24
  * - this shared host layer owns lenient reads, raw missing-locale values,
24
25
  * relation-summary population, transport serialization, and preservation of
25
26
  * restore warnings and locale metadata;
26
- * - the route loader composes published-version metadata plus scheduled-
27
- * publication enablement, capability, and schedule aliases because those
28
- * require host-only database and actor context beyond the singleton handle.
27
+ * - the route loader composes the informational published-version badge, feature
28
+ * enablement, capability, and schedule aliases. Schedule state itself comes
29
+ * from the same editable snapshot as the source document.
29
30
  */
30
31
  export const getSingleton = createServerFn({ method: 'GET' })
32
+ .middleware([adminSessionMiddleware])
31
33
  .validator((input: GetSingletonInput) => input)
32
34
  .handler(async ({ data }) => readSingletonDocument(data))
@@ -10,6 +10,7 @@ import { createServerFn } from '@tanstack/react-start'
10
10
 
11
11
  import { getAdminBylineClient } from '@byline/client/server'
12
12
 
13
+ import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
13
14
  import { type ActorLabelMap, resolveActorLabels } from '../actors.js'
14
15
  import { serialise } from '../serialise.js'
15
16
 
@@ -22,6 +23,7 @@ export interface SingletonHistorySearchParams {
22
23
  }
23
24
 
24
25
  export const getSingletonHistory = createServerFn({ method: 'GET' })
26
+ .middleware([adminSessionMiddleware])
25
27
  .validator((input: { singleton: string; params?: SingletonHistorySearchParams }) => input)
26
28
  .handler(async ({ data }) => {
27
29
  const params = data.params ?? {}
@@ -10,19 +10,26 @@ import { createServerFn } from '@tanstack/react-start'
10
10
 
11
11
  import { getAdminBylineClient } from '@byline/client/server'
12
12
 
13
+ import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
13
14
  import { toCommittedDocumentHookFailureResponse } from '../collections/save-outcome.js'
15
+ import { withDocumentMutationErrors } from '../document-mutation-errors.js'
14
16
  import { serialise } from '../serialise.js'
15
17
 
16
18
  export const restoreSingletonVersion = createServerFn({ method: 'POST' })
17
- .validator((input: { singleton: string; versionId: string }) => input)
18
- .handler(async ({ data }) => {
19
- try {
20
- return serialise(
21
- await getAdminBylineClient().singleton(data.singleton).restoreVersion(data.versionId)
22
- )
23
- } catch (error) {
24
- const committedFailure = toCommittedDocumentHookFailureResponse(error)
25
- if (committedFailure != null) return committedFailure
26
- throw error
27
- }
28
- })
19
+ .middleware([adminSessionMiddleware])
20
+ .validator((input: { expectedRevision: number; singleton: string; versionId: string }) => input)
21
+ .handler(
22
+ withDocumentMutationErrors(async ({ data }) => {
23
+ try {
24
+ return serialise(
25
+ await getAdminBylineClient()
26
+ .singleton(data.singleton)
27
+ .restoreVersion(data.versionId, { expectedRevision: data.expectedRevision })
28
+ )
29
+ } catch (error) {
30
+ const committedFailure = toCommittedDocumentHookFailureResponse(error)
31
+ if (committedFailure != null) return committedFailure
32
+ throw error
33
+ }
34
+ })
35
+ )
@@ -10,15 +10,28 @@ import { createServerFn } from '@tanstack/react-start'
10
10
 
11
11
  import { getAdminBylineClient } from '@byline/client/server'
12
12
 
13
+ import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
14
+ import { withDocumentMutationErrors } from '../document-mutation-errors.js'
13
15
  import { serialise } from '../serialise.js'
14
16
 
15
17
  export const scheduleSingletonPublish = createServerFn({ method: 'POST' })
16
- .validator((input: { singleton: string; publishAt: string; expectedVersionId: string }) => input)
17
- .handler(async ({ data }) =>
18
- serialise(
19
- await getAdminBylineClient().singleton(data.singleton).schedulePublish({
20
- publishAt: data.publishAt,
21
- expectedVersionId: data.expectedVersionId,
22
- })
18
+ .middleware([adminSessionMiddleware])
19
+ .validator(
20
+ (input: {
21
+ expectedRevision: number
22
+ singleton: string
23
+ publishAt: string
24
+ expectedVersionId: string
25
+ }) => input
26
+ )
27
+ .handler(
28
+ withDocumentMutationErrors(async ({ data }) =>
29
+ serialise(
30
+ await getAdminBylineClient().singleton(data.singleton).schedulePublish({
31
+ publishAt: data.publishAt,
32
+ expectedRevision: data.expectedRevision,
33
+ expectedVersionId: data.expectedVersionId,
34
+ })
35
+ )
23
36
  )
24
37
  )
@@ -6,7 +6,12 @@
6
6
  * Copyright (c) Infonomic Company Limited
7
7
  */
8
8
 
9
- import { ERR_DOCUMENT_HOOK_COMMITTED, ErrorCodes } from '@byline/core'
9
+ import {
10
+ ERR_DOCUMENT_HOOK_COMMITTED,
11
+ ERR_LOCK_CONFLICT,
12
+ ErrorCodes,
13
+ getLockConflictDetails,
14
+ } from '@byline/core'
10
15
  import { beforeEach, describe, expect, it, vi } from 'vitest'
11
16
 
12
17
  const mocks = vi.hoisted(() => ({
@@ -18,6 +23,9 @@ vi.mock('@tanstack/react-start', () => ({
18
23
  createServerFn: () => {
19
24
  let validate = (input: unknown) => input
20
25
  const chain = {
26
+ middleware() {
27
+ return chain
28
+ },
21
29
  validator(validator: (input: unknown) => unknown) {
22
30
  validate = validator
23
31
  return chain
@@ -51,13 +59,16 @@ import { scheduleSingletonPublish } from './schedule-publish.js'
51
59
  import { unpublishSingleton } from './unpublish.js'
52
60
  import { updateSingleton } from './update.js'
53
61
 
62
+ // Handler-level tests isolate transport; session-middleware tests exercise the real pre-handler boundary.
63
+ vi.mock('../../integrations/session-middleware.js', () => ({ adminSessionMiddleware: {} }))
64
+
54
65
  type ServerFunction = (options: { data: any }) => Promise<any>
55
66
 
56
67
  const invoke = (serverFunction: unknown, data: Record<string, any>) =>
57
68
  (serverFunction as ServerFunction)({ data })
58
69
 
59
70
  const handle = {
60
- get: vi.fn(),
71
+ getForEdit: vi.fn(),
61
72
  update: vi.fn(),
62
73
  changeStatus: vi.fn(),
63
74
  unpublish: vi.fn(),
@@ -84,14 +95,13 @@ describe('singleton server functions', () => {
84
95
  const cases = [
85
96
  {
86
97
  serverFunction: getSingleton,
87
- method: 'get',
98
+ method: 'getForEdit',
88
99
  input: { singleton: 'site-settings', locale: 'fr' },
89
100
  args: [
90
101
  {
91
102
  locale: 'fr',
92
103
  populate: undefined,
93
104
  depth: undefined,
94
- status: 'any',
95
105
  onMissingLocale: 'empty',
96
106
  lenient: true,
97
107
  },
@@ -105,23 +115,23 @@ describe('singleton server functions', () => {
105
115
  singleton: 'site-settings',
106
116
  data: { title: 'Changed' },
107
117
  locale: 'fr',
108
- expectedVersionId: 'version-1',
118
+ expectedRevision: 7,
109
119
  },
110
- args: [{ title: 'Changed' }, { locale: 'fr', expectedVersionId: 'version-1' }],
120
+ args: [{ title: 'Changed' }, { locale: 'fr', expectedRevision: 7 }],
111
121
  result: { versionId: 'version-2' },
112
122
  },
113
123
  {
114
124
  serverFunction: changeSingletonStatus,
115
125
  method: 'changeStatus',
116
- input: { singleton: 'site-settings', status: 'published' },
117
- args: ['published'],
126
+ input: { singleton: 'site-settings', status: 'published', expectedRevision: 7 },
127
+ args: ['published', { expectedRevision: 7 }],
118
128
  result: { newStatus: 'published' },
119
129
  },
120
130
  {
121
131
  serverFunction: unpublishSingleton,
122
132
  method: 'unpublish',
123
- input: { singleton: 'site-settings' },
124
- args: [],
133
+ input: { singleton: 'site-settings', expectedRevision: 7 },
134
+ args: [{ expectedRevision: 7 }],
125
135
  result: { archivedCount: 1 },
126
136
  },
127
137
  {
@@ -130,11 +140,13 @@ describe('singleton server functions', () => {
130
140
  input: {
131
141
  singleton: 'site-settings',
132
142
  publishAt: '2026-09-01T10:00:00Z',
143
+ expectedRevision: 7,
133
144
  expectedVersionId: 'version-2',
134
145
  },
135
146
  args: [
136
147
  {
137
148
  publishAt: '2026-09-01T10:00:00Z',
149
+ expectedRevision: 7,
138
150
  expectedVersionId: 'version-2',
139
151
  },
140
152
  ],
@@ -143,15 +155,15 @@ describe('singleton server functions', () => {
143
155
  {
144
156
  serverFunction: confirmSingletonScheduledPublish,
145
157
  method: 'confirmScheduledPublish',
146
- input: { singleton: 'site-settings', expectedVersionId: 'version-3' },
147
- args: [{ expectedVersionId: 'version-3' }],
158
+ input: { singleton: 'site-settings', expectedRevision: 7, expectedVersionId: 'version-3' },
159
+ args: [{ expectedRevision: 7, expectedVersionId: 'version-3' }],
148
160
  result: { state: 'armed' },
149
161
  },
150
162
  {
151
163
  serverFunction: cancelSingletonScheduledPublish,
152
164
  method: 'cancelScheduledPublish',
153
- input: { singleton: 'site-settings' },
154
- args: [],
165
+ input: { singleton: 'site-settings', expectedRevision: 7 },
166
+ args: [{ expectedRevision: 7 }],
155
167
  result: { state: 'cancelled' },
156
168
  },
157
169
  {
@@ -185,8 +197,8 @@ describe('singleton server functions', () => {
185
197
  {
186
198
  serverFunction: restoreSingletonVersion,
187
199
  method: 'restoreVersion',
188
- input: { singleton: 'site-settings', versionId: 'version-1' },
189
- args: ['version-1'],
200
+ input: { singleton: 'site-settings', versionId: 'version-1', expectedRevision: 7 },
201
+ args: ['version-1', { expectedRevision: 7 }],
190
202
  result: { versionId: 'version-4' },
191
203
  },
192
204
  {
@@ -194,18 +206,23 @@ describe('singleton server functions', () => {
194
206
  method: 'copyToLocale',
195
207
  input: {
196
208
  singleton: 'site-settings',
209
+ expectedRevision: 7,
197
210
  sourceLocale: 'en',
198
211
  targetLocale: 'fr',
199
212
  overwrite: true,
200
213
  },
201
- args: [{ sourceLocale: 'en', targetLocale: 'fr', overwrite: true }],
214
+ args: [{ expectedRevision: 7, sourceLocale: 'en', targetLocale: 'fr', overwrite: true }],
202
215
  result: { versionId: 'version-5' },
203
216
  },
204
217
  ] as const
205
218
 
206
219
  for (const testCase of cases) {
207
220
  const method = handle[testCase.method]
208
- method.mockResolvedValueOnce(testCase.result)
221
+ method.mockResolvedValueOnce(
222
+ testCase.method === 'getForEdit'
223
+ ? { state: 'document', document: testCase.result }
224
+ : testCase.result
225
+ )
209
226
 
210
227
  const result = await invoke(testCase.serverFunction, testCase.input)
211
228
 
@@ -219,18 +236,18 @@ describe('singleton server functions', () => {
219
236
  }
220
237
  })
221
238
 
222
- it('forwards the optimistic version to update unchanged', async () => {
239
+ it('forwards the observed revision to update unchanged', async () => {
223
240
  handle.update.mockResolvedValueOnce({ versionId: 'version-next' })
224
241
 
225
242
  await invoke(updateSingleton, {
226
243
  singleton: 'site-settings',
227
244
  data: { title: 'Changed' },
228
- expectedVersionId: 'version-current',
245
+ expectedRevision: 7,
229
246
  })
230
247
 
231
248
  expect(handle.update).toHaveBeenCalledWith(
232
249
  { title: 'Changed' },
233
- { locale: undefined, expectedVersionId: 'version-current' }
250
+ { locale: undefined, expectedRevision: 7 }
234
251
  )
235
252
  })
236
253
 
@@ -254,6 +271,7 @@ describe('singleton server functions', () => {
254
271
  phase: 'afterSave',
255
272
  documentId: 'doc-settings',
256
273
  documentVersionId: 'version-next',
274
+ revision: 8,
257
275
  sideEffectCode: ErrorCodes.UNHANDLED,
258
276
  },
259
277
  })
@@ -263,13 +281,14 @@ describe('singleton server functions', () => {
263
281
  status: 'committed-hook-failed',
264
282
  documentId: 'doc-settings',
265
283
  documentVersionId: 'version-next',
284
+ revision: 8,
266
285
  sideEffectFailure: { phase: 'afterSave', code: ErrorCodes.UNHANDLED },
267
286
  })
268
287
  })
269
288
 
270
289
  it('preserves authentication failures from the handle', async () => {
271
290
  const error = { code: 'ERR_UNAUTHENTICATED', message: 'sign in required' }
272
- handle.get.mockRejectedValueOnce(error)
291
+ handle.getForEdit.mockRejectedValueOnce(error)
273
292
 
274
293
  await expect(invoke(getSingleton, { singleton: 'site-settings' })).rejects.toBe(error)
275
294
  })
@@ -301,9 +320,34 @@ describe('singleton server functions', () => {
301
320
  expect(mocks.resolveActorLabels).toHaveBeenCalledWith([])
302
321
  })
303
322
 
323
+ it('preserves confirmed lock failures as safe wire data through the host', async () => {
324
+ const conflict = ERR_LOCK_CONFLICT({
325
+ message: 'private diagnostic',
326
+ cause: new Error('driver SQL'),
327
+ })
328
+ handle.update.mockRejectedValueOnce(conflict)
329
+ let received: unknown
330
+ try {
331
+ await invoke(updateSingleton, {
332
+ singleton: 'site-settings',
333
+ data: { title: 'Changed' },
334
+ expectedRevision: 6,
335
+ })
336
+ } catch (error) {
337
+ received = error
338
+ }
339
+ expect(received).not.toHaveProperty('cause')
340
+ expect(getLockConflictDetails(JSON.parse(JSON.stringify(received)))).toEqual({
341
+ reason: 'lock_conflict',
342
+ rolledBack: true,
343
+ retryable: true,
344
+ })
345
+ expect(handle.update).toHaveBeenCalledTimes(1)
346
+ })
347
+
304
348
  it('keeps missing-slot and stale-write failures distinguishable', async () => {
305
349
  const missing = { code: 'ERR_NOT_FOUND', message: 'slot is empty' }
306
- const stale = { code: 'ERR_CONFLICT', message: 'version changed' }
350
+ const stale = { code: 'ERR_DOCUMENT_STALE', message: 'document changed' }
307
351
  handle.changeStatus.mockRejectedValueOnce(missing)
308
352
  handle.update.mockRejectedValueOnce(stale)
309
353
 
@@ -314,9 +358,9 @@ describe('singleton server functions', () => {
314
358
  invoke(updateSingleton, {
315
359
  singleton: 'site-settings',
316
360
  data: { title: 'Changed' },
317
- expectedVersionId: 'stale',
361
+ expectedRevision: 6,
318
362
  })
319
- ).rejects.toMatchObject({ code: 'ERR_CONFLICT' })
363
+ ).rejects.toMatchObject({ code: 'ERR_DOCUMENT_STALE' })
320
364
  })
321
365
 
322
366
  it('serialises schedule dates at the transport boundary', async () => {
@@ -10,10 +10,19 @@ import { createServerFn } from '@tanstack/react-start'
10
10
 
11
11
  import { getAdminBylineClient } from '@byline/client/server'
12
12
 
13
+ import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
14
+ import { withDocumentMutationErrors } from '../document-mutation-errors.js'
13
15
  import { serialise } from '../serialise.js'
14
16
 
15
17
  export const unpublishSingleton = createServerFn({ method: 'POST' })
16
- .validator((input: { singleton: string }) => input)
17
- .handler(async ({ data }) =>
18
- serialise(await getAdminBylineClient().singleton(data.singleton).unpublish())
18
+ .middleware([adminSessionMiddleware])
19
+ .validator((input: { expectedRevision: number; singleton: string }) => input)
20
+ .handler(
21
+ withDocumentMutationErrors(async ({ data }) =>
22
+ serialise(
23
+ await getAdminBylineClient()
24
+ .singleton(data.singleton)
25
+ .unpublish({ expectedRevision: data.expectedRevision })
26
+ )
27
+ )
19
28
  )
@@ -9,30 +9,37 @@
9
9
  import { createServerFn } from '@tanstack/react-start'
10
10
 
11
11
  import { getAdminBylineClient } from '@byline/client/server'
12
+ import type { SingletonSavePrecondition } from '@byline/core'
12
13
 
14
+ import { adminSessionMiddleware } from '../../integrations/session-middleware.js'
13
15
  import { toCommittedDocumentHookFailureResponse } from '../collections/save-outcome.js'
16
+ import { withDocumentMutationErrors } from '../document-mutation-errors.js'
14
17
  import { serialise } from '../serialise.js'
15
18
 
16
- export interface UpdateSingletonInput {
19
+ export type UpdateSingletonInput = SingletonSavePrecondition & {
17
20
  singleton: string
18
21
  data: Record<string, any>
19
22
  locale?: string
20
- expectedVersionId?: string
21
23
  }
22
24
 
23
25
  export const updateSingleton = createServerFn({ method: 'POST' })
26
+ .middleware([adminSessionMiddleware])
24
27
  .validator((input: UpdateSingletonInput) => input)
25
- .handler(async ({ data }) => {
26
- const { singleton, data: fields, locale, expectedVersionId } = data
27
- try {
28
- const result = await getAdminBylineClient().singleton(singleton).update(fields, {
29
- locale,
30
- expectedVersionId,
31
- })
32
- return serialise(result)
33
- } catch (error) {
34
- const committedFailure = toCommittedDocumentHookFailureResponse(error)
35
- if (committedFailure != null) return committedFailure
36
- throw error
37
- }
38
- })
28
+ .handler(
29
+ withDocumentMutationErrors(async ({ data }) => {
30
+ const { singleton, data: fields, locale, ...precondition } = data
31
+ try {
32
+ const result = await getAdminBylineClient()
33
+ .singleton(singleton)
34
+ .update(fields, {
35
+ locale,
36
+ ...precondition,
37
+ })
38
+ return serialise(result)
39
+ } catch (error) {
40
+ const committedFailure = toCommittedDocumentHookFailureResponse(error)
41
+ if (committedFailure != null) return committedFailure
42
+ throw error
43
+ }
44
+ })
45
+ )
@@ -0,0 +1,41 @@
1
+ import { ErrorCodes, isDocumentRevision, type StructuralMutationReceipt } from '@byline/core'
2
+
3
+ /** Copy only the authorized receipt fields emitted by the lifecycle service. */
4
+ export function structuralReceipt(value: unknown): StructuralMutationReceipt | null {
5
+ if (typeof value !== 'object' || value === null) return null
6
+ const documentId = Reflect.get(value, 'documentId')
7
+ const revision = Reflect.get(value, 'revision')
8
+ const suspended = Reflect.get(value, 'scheduledPublicationsNeedReconfirmation')
9
+ const affected = Reflect.get(value, 'affectedDocuments')
10
+ if (
11
+ typeof documentId !== 'string' ||
12
+ !isDocumentRevision(revision) ||
13
+ typeof suspended !== 'boolean' ||
14
+ !Array.isArray(affected)
15
+ )
16
+ return null
17
+ const affectedDocuments = []
18
+ for (const row of affected) {
19
+ if (typeof row !== 'object' || row === null) return null
20
+ const id = Reflect.get(row, 'documentId')
21
+ const token = Reflect.get(row, 'revision')
22
+ if (typeof id !== 'string' || !isDocumentRevision(token)) return null
23
+ affectedDocuments.push({ documentId: id, revision: token })
24
+ }
25
+ return {
26
+ documentId,
27
+ revision,
28
+ affectedDocuments,
29
+ scheduledPublicationsNeedReconfirmation: suspended,
30
+ }
31
+ }
32
+
33
+ export function committedTreeReceipt(error: unknown): StructuralMutationReceipt | null {
34
+ if (
35
+ typeof error !== 'object' ||
36
+ error === null ||
37
+ Reflect.get(error, 'code') !== ErrorCodes.TREE_HOOK_COMMITTED
38
+ )
39
+ return null
40
+ return structuralReceipt(Reflect.get(error, 'details'))
41
+ }