@byline/host-tanstack-start 4.18.0 → 5.0.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 (148) hide show
  1. package/dist/admin-shell/collections/create.js +22 -5
  2. package/dist/admin-shell/collections/edit.d.ts +1 -1
  3. package/dist/admin-shell/collections/edit.js +222 -34
  4. package/dist/admin-shell/collections/history.js +4 -2
  5. package/dist/admin-shell/collections/list.js +30 -6
  6. package/dist/admin-shell/collections/restore-version-modal.d.ts +3 -1
  7. package/dist/admin-shell/collections/restore-version-modal.js +23 -6
  8. package/dist/admin-shell/collections/save-navigation.test.d.ts +8 -0
  9. package/dist/admin-shell/collections/tree-list.js +16 -6
  10. package/dist/admin-shell/collections/version-history.d.ts +2 -1
  11. package/dist/admin-shell/collections/version-history.js +11 -2
  12. package/dist/admin-shell/document-mutation-notice.d.ts +6 -0
  13. package/dist/admin-shell/document-mutation-notice.js +53 -0
  14. package/dist/admin-shell/document-mutation-state.d.ts +22 -0
  15. package/dist/admin-shell/document-mutation-state.js +71 -0
  16. package/dist/admin-shell/document-mutation-state.test.d.ts +1 -0
  17. package/dist/admin-shell/scheduled-publications/list.js +15 -5
  18. package/dist/admin-shell/singletons/history.js +4 -2
  19. package/dist/admin-shell/singletons/view.d.ts +1 -1
  20. package/dist/admin-shell/singletons/view.js +108 -23
  21. package/dist/integrations/byline-field-services.js +2 -9
  22. package/dist/routes/create-collection-edit-route.js +1 -1
  23. package/dist/routes/create-collection-list-route.d.ts +0 -7
  24. package/dist/routes/create-collection-list-route.js +7 -9
  25. package/dist/routes/create-singleton-route.d.ts +2 -0
  26. package/dist/routes/create-singleton-route.js +4 -3
  27. package/dist/server-fns/collections/copy-to-locale.d.ts +4 -8
  28. package/dist/server-fns/collections/copy-to-locale.js +17 -8
  29. package/dist/server-fns/collections/create.d.ts +2 -8
  30. package/dist/server-fns/collections/create.js +20 -10
  31. package/dist/server-fns/collections/delete-locale.d.ts +4 -8
  32. package/dist/server-fns/collections/delete-locale.js +15 -6
  33. package/dist/server-fns/collections/delete-outcome.d.ts +7 -3
  34. package/dist/server-fns/collections/delete-outcome.js +4 -0
  35. package/dist/server-fns/collections/delete.d.ts +2 -7
  36. package/dist/server-fns/collections/delete.js +5 -3
  37. package/dist/server-fns/collections/duplicate.d.ts +4 -8
  38. package/dist/server-fns/collections/duplicate.js +14 -5
  39. package/dist/server-fns/collections/get.d.ts +1 -7
  40. package/dist/server-fns/collections/get.js +5 -4
  41. package/dist/server-fns/collections/index.d.ts +1 -0
  42. package/dist/server-fns/collections/index.js +1 -0
  43. package/dist/server-fns/collections/list.d.ts +0 -7
  44. package/dist/server-fns/collections/list.js +11 -3
  45. package/dist/server-fns/collections/reorder.d.ts +7 -8
  46. package/dist/server-fns/collections/reorder.js +20 -76
  47. package/dist/server-fns/collections/restore-version.d.ts +4 -8
  48. package/dist/server-fns/collections/restore-version.js +22 -12
  49. package/dist/server-fns/collections/save-outcome.d.ts +24 -0
  50. package/dist/server-fns/collections/save-outcome.js +19 -0
  51. package/dist/server-fns/collections/scheduled-publication.d.ts +49 -9
  52. package/dist/server-fns/collections/scheduled-publication.js +31 -21
  53. package/dist/server-fns/collections/status.d.ts +8 -7
  54. package/dist/server-fns/collections/status.js +13 -5
  55. package/dist/server-fns/collections/tree-mutation.d.ts +7 -6
  56. package/dist/server-fns/collections/tree-mutation.js +17 -5
  57. package/dist/server-fns/collections/tree.d.ts +14 -8
  58. package/dist/server-fns/collections/tree.js +21 -17
  59. package/dist/server-fns/collections/update.d.ts +14 -11
  60. package/dist/server-fns/collections/update.js +48 -24
  61. package/dist/server-fns/collections/upload.d.ts +1 -0
  62. package/dist/server-fns/collections/upload.js +3 -2
  63. package/dist/server-fns/document-mutation-errors.d.ts +5 -0
  64. package/dist/server-fns/document-mutation-errors.js +50 -0
  65. package/dist/server-fns/editable-revision.d.ts +9 -0
  66. package/dist/server-fns/editable-revision.js +11 -0
  67. package/dist/server-fns/singleton-document-read.d.ts +1 -1
  68. package/dist/server-fns/singleton-document-read.js +7 -3
  69. package/dist/server-fns/singletons/cancel-scheduled-publish.d.ts +6 -8
  70. package/dist/server-fns/singletons/cancel-scheduled-publish.js +4 -1
  71. package/dist/server-fns/singletons/change-status.d.ts +2 -7
  72. package/dist/server-fns/singletons/change-status.js +4 -1
  73. package/dist/server-fns/singletons/confirm-scheduled-publish.d.ts +5 -8
  74. package/dist/server-fns/singletons/confirm-scheduled-publish.js +4 -2
  75. package/dist/server-fns/singletons/copy-to-locale.d.ts +2 -7
  76. package/dist/server-fns/singletons/copy-to-locale.js +16 -5
  77. package/dist/server-fns/singletons/get.d.ts +4 -4
  78. package/dist/server-fns/singletons/restore-version.d.ts +2 -7
  79. package/dist/server-fns/singletons/restore-version.js +13 -1
  80. package/dist/server-fns/singletons/schedule-publish.d.ts +5 -8
  81. package/dist/server-fns/singletons/schedule-publish.js +4 -2
  82. package/dist/server-fns/singletons/unpublish.d.ts +2 -7
  83. package/dist/server-fns/singletons/unpublish.js +4 -1
  84. package/dist/server-fns/singletons/update.d.ts +4 -11
  85. package/dist/server-fns/singletons/update.js +16 -8
  86. package/dist/server-fns/structural-receipt.d.ts +4 -0
  87. package/dist/server-fns/structural-receipt.js +31 -0
  88. package/package.json +11 -11
  89. package/src/admin-shell/collections/create.tsx +22 -3
  90. package/src/admin-shell/collections/edit.tsx +233 -61
  91. package/src/admin-shell/collections/history.tsx +11 -2
  92. package/src/admin-shell/collections/list.tsx +22 -6
  93. package/src/admin-shell/collections/restore-version-modal.tsx +23 -5
  94. package/src/admin-shell/collections/save-navigation.test.tsx +532 -0
  95. package/src/admin-shell/collections/tree-list.tsx +16 -6
  96. package/src/admin-shell/collections/version-history.tsx +13 -3
  97. package/src/admin-shell/document-mutation-notice.tsx +45 -0
  98. package/src/admin-shell/document-mutation-state.test.tsx +146 -0
  99. package/src/admin-shell/document-mutation-state.ts +95 -0
  100. package/src/admin-shell/scheduled-publications/list.tsx +16 -6
  101. package/src/admin-shell/singletons/history.test.tsx +46 -1
  102. package/src/admin-shell/singletons/history.tsx +10 -2
  103. package/src/admin-shell/singletons/view.test.tsx +86 -18
  104. package/src/admin-shell/singletons/view.tsx +145 -45
  105. package/src/integrations/byline-field-services.ts +2 -3
  106. package/src/routes/create-collection-edit-route.tsx +1 -0
  107. package/src/routes/create-collection-list-route.tsx +13 -2
  108. package/src/routes/create-singleton-route.test.node.ts +10 -2
  109. package/src/routes/create-singleton-route.tsx +6 -4
  110. package/src/server-fns/collections/copy-to-locale.ts +48 -29
  111. package/src/server-fns/collections/create.ts +56 -44
  112. package/src/server-fns/collections/delete-locale.ts +48 -25
  113. package/src/server-fns/collections/delete-outcome.test.node.ts +16 -0
  114. package/src/server-fns/collections/delete-outcome.ts +27 -16
  115. package/src/server-fns/collections/delete.ts +44 -38
  116. package/src/server-fns/collections/duplicate.ts +45 -25
  117. package/src/server-fns/collections/get.ts +8 -4
  118. package/src/server-fns/collections/index.ts +1 -0
  119. package/src/server-fns/collections/list.ts +8 -3
  120. package/src/server-fns/collections/reorder.ts +32 -124
  121. package/src/server-fns/collections/restore-version.ts +51 -36
  122. package/src/server-fns/collections/revision-input.test.node.ts +108 -0
  123. package/src/server-fns/collections/save-outcome.test.node.ts +80 -0
  124. package/src/server-fns/collections/save-outcome.ts +57 -0
  125. package/src/server-fns/collections/scheduled-publication.ts +138 -111
  126. package/src/server-fns/collections/status.ts +86 -64
  127. package/src/server-fns/collections/tree-mutation.test.node.ts +87 -29
  128. package/src/server-fns/collections/tree-mutation.ts +28 -11
  129. package/src/server-fns/collections/tree.ts +150 -122
  130. package/src/server-fns/collections/update.ts +110 -71
  131. package/src/server-fns/collections/upload-singleton.test.node.ts +3 -7
  132. package/src/server-fns/collections/upload.ts +81 -77
  133. package/src/server-fns/document-mutation-errors.ts +67 -0
  134. package/src/server-fns/document-mutation-transport.test.node.ts +211 -0
  135. package/src/server-fns/editable-revision.test.node.ts +23 -0
  136. package/src/server-fns/editable-revision.ts +18 -0
  137. package/src/server-fns/singleton-document-read.ts +7 -3
  138. package/src/server-fns/singletons/cancel-scheduled-publish.ts +10 -3
  139. package/src/server-fns/singletons/change-status.ts +10 -3
  140. package/src/server-fns/singletons/confirm-scheduled-publish.ts +12 -6
  141. package/src/server-fns/singletons/copy-to-locale.ts +20 -8
  142. package/src/server-fns/singletons/get.ts +3 -3
  143. package/src/server-fns/singletons/restore-version.ts +17 -3
  144. package/src/server-fns/singletons/schedule-publish.ts +18 -7
  145. package/src/server-fns/singletons/singletons.test.node.ts +96 -24
  146. package/src/server-fns/singletons/unpublish.ts +10 -3
  147. package/src/server-fns/singletons/update.ts +21 -9
  148. package/src/server-fns/structural-receipt.ts +41 -0
@@ -2,18 +2,19 @@
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { useRouter } from "@tanstack/react-router";
4
4
  import { FormRenderer } from "@byline/admin/react";
5
- import { ErrorCodes, getDefaultStatus, getWorkflow, getWorkflowStatuses, validateStatusTransition } from "@byline/core";
5
+ import { getDefaultStatus, getWorkflow, getWorkflowStatuses, validateStatusTransition } from "@byline/core";
6
6
  import { useTranslation } from "@byline/i18n/react";
7
7
  import { Container, Section, useToastManager } from "@byline/ui/react";
8
8
  import { getAdminRoutePath } from "../../routes/admin-path.js";
9
+ import { hasCommittedDocumentHookFailure } from "../../server-fns/collections/index.js";
9
10
  import { cancelSingletonScheduledPublish, changeSingletonStatus, confirmSingletonScheduledPublish, copySingletonToLocale, scheduleSingletonPublish, unpublishSingleton, updateSingleton } from "../../server-fns/singletons/index.js";
10
11
  import { useNavigate } from "../chrome/loose-router.js";
11
12
  import { useTanStackNavigationGuard } from "../collections/tanstack-navigation-guard.js";
13
+ import { useDocumentMutationState } from "../document-mutation-state.js";
12
14
  import { SingletonViewMenu } from "./view-menu.js";
13
- function errorCode(error) {
14
- return 'string' == typeof error?.code ? error.code : null;
15
- }
16
- function SingletonView({ singletonDefinition, adminConfig, document, initialData, locale, contentLocales, defaultContentLocale }) {
15
+ function SingletonView({ singletonDefinition, adminConfig, document: loadedDocument, initialData, locale, contentLocales, defaultContentLocale }) {
16
+ const mutation = useDocumentMutationState(loadedDocument);
17
+ const document = mutation.document ?? null;
17
18
  const router = useRouter();
18
19
  const navigate = useNavigate();
19
20
  const toastManager = useToastManager();
@@ -49,26 +50,42 @@ function SingletonView({ singletonDefinition, adminConfig, document, initialData
49
50
  if (scheduledPublication?.state !== 'armed') return;
50
51
  toast(t('scheduledPublication.toast.suspendedTitle'), t("scheduledPublication.toast.suspendedDescription"), 'warning');
51
52
  };
53
+ const notifyCommittedHookFailure = ()=>{
54
+ toast(t('collections.save.hookFailedToast'), t("collections.save.hookFailedDescription"), 'warning');
55
+ };
52
56
  const handleSubmit = async ({ data })=>{
53
57
  try {
54
- await updateSingleton({
58
+ mutation.assertWritable();
59
+ const result = await updateSingleton({
55
60
  data: {
56
61
  singleton: path,
57
62
  data,
58
63
  locale: locale ?? defaultContentLocale,
59
- expectedVersionId: document?.versionId
64
+ ...null == document ? {
65
+ expectedState: 'empty'
66
+ } : {
67
+ expectedRevision: mutation.expectedRevision()
68
+ }
60
69
  }
61
70
  });
71
+ mutation.adopt(result);
62
72
  if (null != document) notifyScheduleSuspended();
63
- toast(t('singletons.edit.updateTitle', {
73
+ if (hasCommittedDocumentHookFailure(result)) notifyCommittedHookFailure();
74
+ else toast(t('singletons.edit.updateTitle', {
64
75
  label
65
76
  }), t("singletons.edit.updatedDescription", {
66
77
  label: label.toLowerCase()
67
78
  }), 'success');
68
79
  await reload();
69
80
  } catch (err) {
70
- const code = errorCode(err);
71
- const description = code === ErrorCodes.CONFLICT ? t("singletons.edit.conflictDescription") : code === ErrorCodes.NOT_FOUND ? t("singletons.edit.notConfiguredDescription") : t("singletons.edit.updateFailedDescription", {
81
+ const handled = mutation.report(err);
82
+ if ('blocked' === handled) throw err;
83
+ if ('committed' === handled) {
84
+ notifyCommittedHookFailure();
85
+ await reload();
86
+ return;
87
+ }
88
+ const description = t("singletons.edit.updateFailedDescription", {
72
89
  label: label.toLowerCase()
73
90
  });
74
91
  toast(t('singletons.edit.updateTitle', {
@@ -79,12 +96,15 @@ function SingletonView({ singletonDefinition, adminConfig, document, initialData
79
96
  };
80
97
  const handleStatusChange = async (status)=>{
81
98
  try {
82
- await changeSingletonStatus({
99
+ mutation.assertWritable();
100
+ const result = await changeSingletonStatus({
83
101
  data: {
102
+ expectedRevision: mutation.expectedRevision(),
84
103
  singleton: path,
85
104
  status
86
105
  }
87
106
  });
107
+ mutation.adopt(result);
88
108
  toast(t('collections.edit.statusUpdateTitle', {
89
109
  label
90
110
  }), t("collections.edit.statusChangedDescription", {
@@ -92,48 +112,69 @@ function SingletonView({ singletonDefinition, adminConfig, document, initialData
92
112
  }), 'success');
93
113
  await reload();
94
114
  } catch (err) {
115
+ const handled = mutation.report(err);
116
+ if ('blocked' === handled) throw err;
117
+ if ('committed' === handled) {
118
+ notifyCommittedHookFailure();
119
+ await reload();
120
+ return;
121
+ }
95
122
  toast(t('collections.edit.statusUpdateTitle', {
96
123
  label
97
124
  }), t("collections.edit.statusChangeFailedDescription", {
98
- message: err.message
125
+ message: t('documentConcurrency.failed')
99
126
  }), 'danger');
100
127
  throw err;
101
128
  }
102
129
  };
103
130
  const handleUnpublish = async ()=>{
104
131
  try {
105
- await unpublishSingleton({
132
+ mutation.assertWritable();
133
+ const result = await unpublishSingleton({
106
134
  data: {
135
+ expectedRevision: mutation.expectedRevision(),
107
136
  singleton: path
108
137
  }
109
138
  });
139
+ mutation.adopt(result);
110
140
  toast(t('collections.edit.unpublishTitle', {
111
141
  label
112
142
  }), t("collections.edit.unpublishedDescription"), 'success');
113
143
  await reload();
114
144
  } catch (err) {
145
+ const handled = mutation.report(err);
146
+ if ('blocked' === handled) throw err;
147
+ if ('committed' === handled) {
148
+ notifyCommittedHookFailure();
149
+ await reload();
150
+ return;
151
+ }
115
152
  toast(t('collections.edit.unpublishTitle', {
116
153
  label
117
154
  }), t("collections.edit.unpublishFailedDescription", {
118
- message: err.message
155
+ message: t('documentConcurrency.failed')
119
156
  }), 'danger');
120
157
  throw err;
121
158
  }
122
159
  };
123
160
  const handleCopyToLocale = async ({ targetLocale, overwrite })=>{
124
161
  try {
125
- await copySingletonToLocale({
162
+ mutation.assertWritable();
163
+ const result = await copySingletonToLocale({
126
164
  data: {
165
+ expectedRevision: mutation.expectedRevision(),
127
166
  singleton: path,
128
167
  sourceLocale: locale ?? defaultContentLocale,
129
168
  targetLocale,
130
169
  overwrite
131
170
  }
132
171
  });
172
+ mutation.adopt(result);
133
173
  const sourceLocale = locale ?? defaultContentLocale;
134
174
  const sourceLabel = contentLocales.find((entry)=>entry.code === sourceLocale)?.label ?? sourceLocale;
135
175
  const targetLabel = contentLocales.find((entry)=>entry.code === targetLocale)?.label ?? targetLocale;
136
- toast(t('collections.edit.copyToLocaleTitle', {
176
+ if (hasCommittedDocumentHookFailure(result)) notifyCommittedHookFailure();
177
+ else toast(t('collections.edit.copyToLocaleTitle', {
137
178
  label
138
179
  }), t('collections.edit.copiedSuccessMessage', {
139
180
  source: sourceLabel,
@@ -150,62 +191,106 @@ function SingletonView({ singletonDefinition, adminConfig, document, initialData
150
191
  }
151
192
  });
152
193
  } catch (err) {
194
+ const handled = mutation.report(err);
195
+ if ('blocked' === handled) throw err;
196
+ if ('committed' === handled) {
197
+ notifyCommittedHookFailure();
198
+ await reload();
199
+ return;
200
+ }
153
201
  toast(t('collections.edit.copyToLocaleTitle', {
154
202
  label
155
203
  }), t("collections.edit.copyFailedDescription", {
156
- message: err.message
204
+ message: t('documentConcurrency.failed')
157
205
  }), 'danger');
158
206
  throw err;
159
207
  }
160
208
  };
161
209
  const handleSchedulePublication = async ({ publishAt })=>{
162
210
  try {
163
- await scheduleSingletonPublish({
211
+ mutation.assertWritable();
212
+ const result = await scheduleSingletonPublish({
164
213
  data: {
214
+ expectedRevision: mutation.expectedRevision(),
165
215
  singleton: path,
166
216
  publishAt,
167
217
  expectedVersionId: String(document?.versionId)
168
218
  }
169
219
  });
220
+ mutation.adopt(result);
170
221
  toast(t('scheduledPublication.toast.scheduledTitle'), t("scheduledPublication.toast.scheduledDescription"), 'success');
171
222
  await reload();
172
223
  } catch (err) {
173
- toast(t('scheduledPublication.toast.failedTitle'), err.message, 'danger');
224
+ const handled = mutation.report(err);
225
+ if ('blocked' === handled) throw err;
226
+ if ('committed' === handled) {
227
+ notifyCommittedHookFailure();
228
+ await reload();
229
+ return;
230
+ }
231
+ toast(t('scheduledPublication.toast.failedTitle'), t('documentConcurrency.failed'), 'danger');
174
232
  throw err;
175
233
  }
176
234
  };
177
235
  const handleConfirmScheduledPublication = async ()=>{
178
236
  try {
179
- await confirmSingletonScheduledPublish({
237
+ mutation.assertWritable();
238
+ const result = await confirmSingletonScheduledPublish({
180
239
  data: {
240
+ expectedRevision: mutation.expectedRevision(),
181
241
  singleton: path,
182
242
  expectedVersionId: String(document?.versionId)
183
243
  }
184
244
  });
245
+ mutation.adopt(result);
185
246
  toast(t('scheduledPublication.toast.confirmedTitle'), t("scheduledPublication.toast.confirmedDescription"), 'success');
186
247
  await reload();
187
248
  } catch (err) {
188
- toast(t('scheduledPublication.toast.failedTitle'), err.message, 'danger');
249
+ const handled = mutation.report(err);
250
+ if ('blocked' === handled) throw err;
251
+ if ('committed' === handled) {
252
+ notifyCommittedHookFailure();
253
+ await reload();
254
+ return;
255
+ }
256
+ toast(t('scheduledPublication.toast.failedTitle'), t('documentConcurrency.failed'), 'danger');
189
257
  throw err;
190
258
  }
191
259
  };
192
260
  const handleCancelScheduledPublication = async ()=>{
193
261
  try {
194
- await cancelSingletonScheduledPublish({
262
+ mutation.assertWritable();
263
+ const result = await cancelSingletonScheduledPublish({
195
264
  data: {
265
+ expectedRevision: mutation.expectedRevision(),
196
266
  singleton: path
197
267
  }
198
268
  });
269
+ mutation.adopt({
270
+ ...result,
271
+ documentId: String(document?.id)
272
+ });
199
273
  toast(t('scheduledPublication.toast.cancelledTitle'), t("scheduledPublication.toast.cancelledDescription"), 'success');
200
274
  await reload();
201
275
  } catch (err) {
202
- toast(t('scheduledPublication.toast.failedTitle'), err.message, 'danger');
276
+ const handled = mutation.report(err);
277
+ if ('blocked' === handled) throw err;
278
+ if ('committed' === handled) {
279
+ notifyCommittedHookFailure();
280
+ await reload();
281
+ return;
282
+ }
283
+ toast(t('scheduledPublication.toast.failedTitle'), t('documentConcurrency.failed'), 'danger');
203
284
  throw err;
204
285
  }
205
286
  };
206
287
  return /*#__PURE__*/ jsx(Section, {
207
288
  children: /*#__PURE__*/ jsx(Container, {
208
289
  children: /*#__PURE__*/ jsx(FormRenderer, {
290
+ mutationIssue: mutation.issue,
291
+ mutationsBlocked: mutation.blocked,
292
+ observedRevision: mutation.revision,
293
+ onMutationError: mutation.report,
209
294
  mode: null == document ? 'create' : 'edit',
210
295
  fields: fields,
211
296
  onSubmit: handleSubmit,
@@ -8,19 +8,12 @@ const byline_field_services_getCollectionDocuments = ({ collection, params })=>g
8
8
  }
9
9
  });
10
10
  const byline_field_services_uploadField = (collection, formData, createDocument)=>uploadField(collection, formData, createDocument);
11
- const byline_field_services_placeTreeNode = async (input)=>{
12
- const { orderKey } = await placeTreeNode({
11
+ const byline_field_services_placeTreeNode = async (input)=>placeTreeNode({
13
12
  data: input
14
13
  });
15
- return {
16
- orderKey
17
- };
18
- };
19
- const byline_field_services_removeFromTree = async (input)=>{
20
- await removeFromTree({
14
+ const byline_field_services_removeFromTree = async (input)=>removeFromTree({
21
15
  data: input
22
16
  });
23
- };
24
17
  const byline_field_services_getTreeAncestors = (input)=>getTreeAncestors({
25
18
  data: input
26
19
  });
@@ -122,7 +122,7 @@ function createCollectionEditRoute(path) {
122
122
  contentLocales: contentLocales,
123
123
  defaultContentLocale: defaultContentLocale,
124
124
  returnSearch: returnSearch
125
- })
125
+ }, `${collection}/${id}/${locale}`)
126
126
  ]
127
127
  });
128
128
  }
@@ -1,8 +1 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
1
  export declare function createCollectionListRoute(path: string): any;
@@ -1,8 +1,8 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { getCollectionAdminConfig, getWorkflowStatuses, parseDocumentRevision } from "@byline/core";
2
3
  import { useEffect, useRef } from "react";
3
4
  import { createFileRoute, notFound } from "@tanstack/react-router";
4
5
  import { applyRelationColumnFormatters } from "@byline/admin/react";
5
- import { getCollectionAdminConfig, getWorkflowStatuses } from "@byline/core";
6
6
  import { useTranslation } from "@byline/i18n/react";
7
7
  import { useToastManager } from "@byline/ui/react";
8
8
  import { BreadcrumbsClient } from "../admin-shell/chrome/breadcrumbs/breadcrumbs-client.js";
@@ -124,17 +124,16 @@ function createCollectionListRoute(path) {
124
124
  collection: collection,
125
125
  collectionLabels: data.included.collection.labels,
126
126
  listActions: adminConfig?.listActions,
127
- onMove: async ({ documentId, parentDocumentId, beforeDocumentId, afterDocumentId })=>{
128
- await placeTreeNode({
127
+ onMove: async ({ documentId, parentDocumentId, beforeDocumentId, afterDocumentId })=>await placeTreeNode({
129
128
  data: {
129
+ expectedRevision: parseDocumentRevision(data.rows.find((row)=>row.id === documentId)?.revision),
130
130
  collection,
131
131
  documentId,
132
132
  parentDocumentId,
133
133
  beforeDocumentId,
134
134
  afterDocumentId
135
135
  }
136
- });
137
- }
136
+ })
138
137
  }) : CustomListView ? /*#__PURE__*/ jsx(CustomListView, {
139
138
  data: data,
140
139
  workflowStatuses: workflowStatuses
@@ -145,16 +144,15 @@ function createCollectionListRoute(path) {
145
144
  useAsTitle: collectionDef.useAsTitle,
146
145
  orderable: true === collectionDef.orderable,
147
146
  listActions: adminConfig?.listActions,
148
- onReorder: async ({ documentId, beforeDocumentId, afterDocumentId })=>{
149
- await reorderCollectionDocument({
147
+ onReorder: async ({ documentId, beforeDocumentId, afterDocumentId })=>await reorderCollectionDocument({
150
148
  data: {
149
+ expectedRevision: parseDocumentRevision(data.docs.find((row)=>row.id === documentId)?.revision),
151
150
  collection,
152
151
  documentId,
153
152
  beforeDocumentId,
154
153
  afterDocumentId
155
154
  }
156
- });
157
- }
155
+ })
158
156
  })
159
157
  ]
160
158
  });
@@ -20,8 +20,10 @@ export declare const getSingletonRouteDocument: import("@tanstack/react-start").
20
20
  locale?: string;
21
21
  }, Promise<{
22
22
  document: null;
23
+ expectedEmpty: true;
23
24
  initialData: Record<string, any>;
24
25
  } | {
26
+ expectedEmpty?: undefined;
25
27
  document: {
26
28
  _publishedVersion: Record<string, any> | null;
27
29
  _scheduledPublicationEnabled: boolean;
@@ -1,7 +1,7 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { createFileRoute, notFound } from "@tanstack/react-router";
3
3
  import { createServerFn } from "@tanstack/react-start";
4
- import { getAdminBylineClient, getAdminRequestContext } from "@byline/client/server";
4
+ import { getAdminRequestContext } from "@byline/client/server";
5
5
  import { buildInitialDataFromFields, documentAbilityKey, getServerConfig, getSingletonAdminConfig } from "@byline/core";
6
6
  import { useTranslation } from "@byline/i18n/react";
7
7
  import { z } from "zod";
@@ -28,6 +28,7 @@ const getSingletonRouteDocument = createServerFn({
28
28
  });
29
29
  if (null == document) return {
30
30
  document: null,
31
+ expectedEmpty: true,
31
32
  initialData: serialise(await buildInitialDataFromFields(definition.fields, {
32
33
  locale: data.locale ?? getServerConfig().i18n.content.defaultLocale
33
34
  }))
@@ -54,7 +55,7 @@ const getSingletonRouteDocument = createServerFn({
54
55
  if (scheduledPublicationEnabled) {
55
56
  const actor = (await getAdminRequestContext()).actor;
56
57
  canSchedulePublication = actor?.hasAbility(documentAbilityKey(definition, 'changeStatus')) === true && actor.hasAbility(documentAbilityKey(definition, 'publish'));
57
- if (canSchedulePublication) scheduledPublish = await getAdminBylineClient().singleton(data.singleton).getScheduledPublish();
58
+ if (canSchedulePublication) scheduledPublish = document.scheduledPublication;
58
59
  }
59
60
  const serializedSchedule = null == scheduledPublish ? null : serialise(scheduledPublish);
60
61
  return {
@@ -120,7 +121,7 @@ function createSingletonRoute(path) {
120
121
  locale: locale,
121
122
  contentLocales: contentLocales,
122
123
  defaultContentLocale: defaultContentLocale
123
- })
124
+ }, `${singleton}/${locale}`)
124
125
  ]
125
126
  });
126
127
  }
@@ -1,11 +1,5 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
1
  import type { CopyToLocaleResult } from '@byline/core/services';
2
+ import { type CollectionDocumentCommittedHookFailureResponse } from './save-outcome.js';
9
3
  /**
10
4
  * Copy a document's content from `sourceLocale` into `targetLocale` on
11
5
  * the same document. Mirrors the thin-wrapper pattern of the other
@@ -16,15 +10,17 @@ import type { CopyToLocaleResult } from '@byline/core/services';
16
10
  * failures propagate to TanStack Start's transport layer.
17
11
  */
18
12
  export declare const copyDocumentToLocale: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
13
+ expectedRevision: number;
19
14
  collection: string;
20
15
  id: string;
21
16
  sourceLocale: string;
22
17
  targetLocale: string;
23
18
  overwrite: boolean;
24
19
  }) => {
20
+ expectedRevision: number;
25
21
  collection: string;
26
22
  id: string;
27
23
  sourceLocale: string;
28
24
  targetLocale: string;
29
25
  overwrite: boolean;
30
- }, Promise<CopyToLocaleResult>>;
26
+ }, Promise<CollectionDocumentCommittedHookFailureResponse | CopyToLocaleResult>>;
@@ -1,11 +1,13 @@
1
+ import { withDocumentMutationErrors } from "../document-mutation-errors.js";
1
2
  import { createServerFn } from "@tanstack/react-start";
2
3
  import { getAdminRequestContext } from "@byline/client/server";
3
4
  import { ERR_NOT_FOUND, getLogger, getServerConfig } from "@byline/core";
4
5
  import { copyToLocale } from "@byline/core/services";
5
6
  import { ensureCollection } from "../../integrations/api-utils.js";
7
+ import { toCommittedDocumentHookFailureResponse } from "./save-outcome.js";
6
8
  const copyDocumentToLocale = createServerFn({
7
9
  method: 'POST'
8
- }).validator((input)=>input).handler(async ({ data: input })=>{
10
+ }).validator((input)=>input).handler(withDocumentMutationErrors(async ({ data: input })=>{
9
11
  const { collection: path, id, sourceLocale, targetLocale, overwrite } = input;
10
12
  const logger = getLogger();
11
13
  const config = await ensureCollection(path);
@@ -27,11 +29,18 @@ const copyDocumentToLocale = createServerFn({
27
29
  slugifier: serverConfig.slugifier,
28
30
  requestContext: await getAdminRequestContext()
29
31
  };
30
- return copyToLocale(ctx, {
31
- documentId: id,
32
- sourceLocale,
33
- targetLocale,
34
- overwrite
35
- });
36
- });
32
+ try {
33
+ return await copyToLocale(ctx, {
34
+ documentId: id,
35
+ expectedRevision: input.expectedRevision,
36
+ sourceLocale,
37
+ targetLocale,
38
+ overwrite
39
+ });
40
+ } catch (error) {
41
+ const committedFailure = toCommittedDocumentHookFailureResponse(error);
42
+ if (null != committedFailure) return committedFailure;
43
+ throw error;
44
+ }
45
+ }));
37
46
  export { copyDocumentToLocale };
@@ -1,10 +1,3 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
1
  export declare const createCollectionDocument: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
9
2
  collection: string;
10
3
  data: any;
@@ -17,8 +10,9 @@ export declare const createCollectionDocument: import("@tanstack/react-start").R
17
10
  locale?: string;
18
11
  path?: string;
19
12
  availableLocales?: string[];
20
- }, Promise<{
13
+ }, Promise<import("./save-outcome.js").CollectionDocumentCommittedHookFailureResponse | {
21
14
  status: 'ok';
22
15
  documentId: string;
23
16
  documentVersionId: string;
17
+ revision: number;
24
18
  }>>;
@@ -1,11 +1,13 @@
1
+ import { withDocumentMutationErrors } from "../document-mutation-errors.js";
1
2
  import { createServerFn } from "@tanstack/react-start";
2
3
  import { getAdminRequestContext } from "@byline/client/server";
3
4
  import { ERR_NOT_FOUND, getLogger, getServerConfig } from "@byline/core";
4
5
  import { createDocument } from "@byline/core/services";
5
6
  import { ensureCollection } from "../../integrations/api-utils.js";
7
+ import { toCommittedDocumentHookFailureResponse } from "./save-outcome.js";
6
8
  const createCollectionDocument = createServerFn({
7
9
  method: 'POST'
8
- }).validator((input)=>input).handler(async ({ data: input })=>{
10
+ }).validator((input)=>input).handler(withDocumentMutationErrors(async ({ data: input })=>{
9
11
  const { collection: path, data: documentData, locale, path: explicitPath, availableLocales } = input;
10
12
  const logger = getLogger();
11
13
  const config = await ensureCollection(path);
@@ -27,17 +29,25 @@ const createCollectionDocument = createServerFn({
27
29
  slugifier: serverConfig.slugifier,
28
30
  requestContext: await getAdminRequestContext()
29
31
  };
30
- const result = await createDocument(ctx, {
31
- data: structuredClone(documentData),
32
- status: documentData.status,
33
- locale: locale ?? serverConfig.i18n.content.defaultLocale,
34
- path: explicitPath,
35
- availableLocales
36
- });
32
+ let result;
33
+ try {
34
+ result = await createDocument(ctx, {
35
+ data: structuredClone(documentData),
36
+ status: documentData.status,
37
+ locale: locale ?? serverConfig.i18n.content.defaultLocale,
38
+ path: explicitPath,
39
+ availableLocales
40
+ });
41
+ } catch (error) {
42
+ const committedFailure = toCommittedDocumentHookFailureResponse(error);
43
+ if (null != committedFailure) return committedFailure;
44
+ throw error;
45
+ }
37
46
  return {
38
47
  status: 'ok',
39
48
  documentId: result.documentId,
40
- documentVersionId: result.documentVersionId
49
+ documentVersionId: result.documentVersionId,
50
+ revision: result.revision
41
51
  };
42
- });
52
+ }));
43
53
  export { createCollectionDocument };
@@ -1,11 +1,5 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
1
  import type { DeleteLocaleResult } from '@byline/core/services';
2
+ import { type CollectionDocumentCommittedHookFailureResponse } from './save-outcome.js';
9
3
  /**
10
4
  * Remove a single (non-default) content locale from a document, leaving every
11
5
  * other locale untouched. Mirrors the thin-wrapper pattern of the other
@@ -17,11 +11,13 @@ import type { DeleteLocaleResult } from '@byline/core/services';
17
11
  * transport layer.
18
12
  */
19
13
  export declare const deleteDocumentLocale: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
14
+ expectedRevision: number;
20
15
  collection: string;
21
16
  id: string;
22
17
  locale: string;
23
18
  }) => {
19
+ expectedRevision: number;
24
20
  collection: string;
25
21
  id: string;
26
22
  locale: string;
27
- }, Promise<DeleteLocaleResult>>;
23
+ }, Promise<CollectionDocumentCommittedHookFailureResponse | DeleteLocaleResult>>;
@@ -1,11 +1,13 @@
1
+ import { withDocumentMutationErrors } from "../document-mutation-errors.js";
1
2
  import { createServerFn } from "@tanstack/react-start";
2
3
  import { getAdminRequestContext } from "@byline/client/server";
3
4
  import { ERR_NOT_FOUND, getLogger, getServerConfig } from "@byline/core";
4
5
  import { deleteLocale } from "@byline/core/services";
5
6
  import { ensureCollection } from "../../integrations/api-utils.js";
7
+ import { toCommittedDocumentHookFailureResponse } from "./save-outcome.js";
6
8
  const deleteDocumentLocale = createServerFn({
7
9
  method: 'POST'
8
- }).validator((input)=>input).handler(async ({ data: input })=>{
10
+ }).validator((input)=>input).handler(withDocumentMutationErrors(async ({ data: input })=>{
9
11
  const { collection: path, id, locale } = input;
10
12
  const logger = getLogger();
11
13
  const config = await ensureCollection(path);
@@ -27,9 +29,16 @@ const deleteDocumentLocale = createServerFn({
27
29
  slugifier: serverConfig.slugifier,
28
30
  requestContext: await getAdminRequestContext()
29
31
  };
30
- return deleteLocale(ctx, {
31
- documentId: id,
32
- locale
33
- });
34
- });
32
+ try {
33
+ return await deleteLocale(ctx, {
34
+ documentId: id,
35
+ expectedRevision: input.expectedRevision,
36
+ locale
37
+ });
38
+ } catch (error) {
39
+ const committedFailure = toCommittedDocumentHookFailureResponse(error);
40
+ if (null != committedFailure) return committedFailure;
41
+ throw error;
42
+ }
43
+ }));
35
44
  export { deleteDocumentLocale };