@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
@@ -5,26 +5,30 @@
5
5
  *
6
6
  * Copyright (c) Infonomic Company Limited
7
7
  */
8
- import type { DeleteDocumentResult, DeleteDocumentSideEffectCode, DeleteDocumentSideEffectPhase } from '@byline/core';
8
+ import type { DeleteDocumentResult, DeleteDocumentSideEffectCode, DeleteDocumentSideEffectPhase, StructuralMutationReceipt } from '@byline/core';
9
9
  export type DeleteDocumentPublicSideEffectPhase = DeleteDocumentSideEffectPhase | 'unknown';
10
10
  export type DeleteDocumentPublicSideEffectCode = DeleteDocumentSideEffectCode;
11
11
  export interface DeleteDocumentPublicSideEffectFailure {
12
12
  phase: DeleteDocumentPublicSideEffectPhase;
13
13
  code: DeleteDocumentPublicSideEffectCode;
14
14
  }
15
- export type DeleteDocumentResponse = {
15
+ export type DeleteDocumentResponse = StructuralMutationReceipt & ({
16
16
  status: 'ok';
17
+ documentId: string;
18
+ revision: number;
17
19
  deletedVersionCount: number;
18
20
  outcome: 'committed';
19
21
  sideEffectFailures: [];
20
22
  } | {
21
23
  status: 'ok';
24
+ documentId: string;
25
+ revision: number;
22
26
  deletedVersionCount: number;
23
27
  outcome: 'committed-with-side-effect-failures';
24
28
  sideEffectFailures: [
25
29
  DeleteDocumentPublicSideEffectFailure,
26
30
  ...DeleteDocumentPublicSideEffectFailure[]
27
31
  ];
28
- };
32
+ });
29
33
  export declare function toDeleteDocumentResponse(result: DeleteDocumentResult): DeleteDocumentResponse;
30
34
  export declare function hasDeleteSideEffectFailures(result: Pick<DeleteDocumentResult, 'outcome'>): boolean;
@@ -20,6 +20,10 @@ function toDeleteDocumentResponse(result) {
20
20
  const [firstFailure, ...remainingFailures] = result.sideEffectFailures;
21
21
  return {
22
22
  status: 'ok',
23
+ documentId: result.documentId,
24
+ revision: result.revision,
25
+ affectedDocuments: result.affectedDocuments,
26
+ scheduledPublicationsNeedReconfirmation: result.scheduledPublicationsNeedReconfirmation,
23
27
  deletedVersionCount: result.deletedVersionCount,
24
28
  outcome: result.outcome,
25
29
  sideEffectFailures: [
@@ -1,14 +1,9 @@
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 deleteDocument: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
2
+ expectedRevision: number;
9
3
  collection: string;
10
4
  id: string;
11
5
  }) => {
6
+ expectedRevision: number;
12
7
  collection: string;
13
8
  id: string;
14
9
  }, Promise<import("./delete-outcome.js").DeleteDocumentResponse>>;
@@ -1,3 +1,4 @@
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, getUploadFields } from "@byline/core";
@@ -6,7 +7,7 @@ import { ensureCollection } from "../../integrations/api-utils.js";
6
7
  import { toDeleteDocumentResponse } from "./delete-outcome.js";
7
8
  const delete_deleteDocument = createServerFn({
8
9
  method: 'POST'
9
- }).validator((input)=>input).handler(async ({ data: input })=>{
10
+ }).validator((input)=>input).handler(withDocumentMutationErrors(async ({ data: input })=>{
10
11
  const { collection: path, id } = input;
11
12
  const logger = getLogger();
12
13
  const config = await ensureCollection(path);
@@ -35,8 +36,9 @@ const delete_deleteDocument = createServerFn({
35
36
  requestContext: await getAdminRequestContext()
36
37
  };
37
38
  const result = await deleteDocument(ctx, {
38
- documentId: id
39
+ documentId: id,
40
+ expectedRevision: input.expectedRevision
39
41
  });
40
42
  return toDeleteDocumentResponse(result);
41
- });
43
+ }));
42
44
  export { delete_deleteDocument as deleteDocument };
@@ -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 { DuplicateDocumentResult } from '@byline/core/services';
2
+ import { type CollectionDocumentCommittedHookFailureResponse } from './save-outcome.js';
9
3
  /**
10
4
  * Duplicate an existing document — clones all locales into a brand-new
11
5
  * document in one atomic write. Returns the new document's id so the UI
@@ -18,9 +12,11 @@ import type { DuplicateDocumentResult } from '@byline/core/services';
18
12
  * TanStack Start's transport layer for the client to branch on.
19
13
  */
20
14
  export declare const duplicateCollectionDocument: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
15
+ expectedRevision: number;
21
16
  collection: string;
22
17
  id: string;
23
18
  }) => {
19
+ expectedRevision: number;
24
20
  collection: string;
25
21
  id: string;
26
- }, Promise<DuplicateDocumentResult>>;
22
+ }, Promise<CollectionDocumentCommittedHookFailureResponse | DuplicateDocumentResult>>;
@@ -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 { duplicateDocument } from "@byline/core/services";
5
6
  import { ensureCollection } from "../../integrations/api-utils.js";
7
+ import { toCommittedDocumentHookFailureResponse } from "./save-outcome.js";
6
8
  const duplicateCollectionDocument = 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: sourceDocumentId } = input;
10
12
  const logger = getLogger();
11
13
  const config = await ensureCollection(path);
@@ -27,8 +29,15 @@ const duplicateCollectionDocument = createServerFn({
27
29
  slugifier: serverConfig.slugifier,
28
30
  requestContext: await getAdminRequestContext()
29
31
  };
30
- return duplicateDocument(ctx, {
31
- sourceDocumentId
32
- });
33
- });
32
+ try {
33
+ return await duplicateDocument(ctx, {
34
+ sourceDocumentId,
35
+ expectedRevision: input.expectedRevision
36
+ });
37
+ } catch (error) {
38
+ const committedFailure = toCommittedDocumentHookFailureResponse(error);
39
+ if (null != committedFailure) return committedFailure;
40
+ throw error;
41
+ }
42
+ }));
34
43
  export { duplicateCollectionDocument };
@@ -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
  /**
9
2
  * Fetch a single document by ID. Returns `null` when the document is not found
10
3
  * so callers can degrade gracefully (e.g. `notFound()`).
@@ -16,6 +9,7 @@
16
9
  * `{target_document_id, target_collection_id}` refs.
17
10
  */
18
11
  export declare function getCollectionDocument(collection: string, id: string, locale?: string, depth?: number, populateRelations?: boolean): Promise<{
12
+ revision: number;
19
13
  _publishedVersion: Record<string, any> | null;
20
14
  _restoreWarnings?: string[] | undefined;
21
15
  availableLocales?: string[] | undefined;
@@ -1,3 +1,4 @@
1
+ import { withEditableRevision } from "../editable-revision.js";
1
2
  import { createServerFn } from "@tanstack/react-start";
2
3
  import { getAdminBylineClient, getAdminRequestContext } from "@byline/client/server";
3
4
  import { BylineError, ERR_NOT_FOUND, ErrorCodes, documentAbilityKey, getCollectionSchemasForPath, getLogger, getServerConfig } from "@byline/core";
@@ -18,12 +19,12 @@ const getDocumentFn = createServerFn({
18
19
  }).log(logger);
19
20
  const client = getAdminBylineClient();
20
21
  const handle = client.collection(path);
21
- const { options: readOptions, populatedTree } = resolveAdminDocumentRead(config.definition, {
22
+ const { options: { status: _status, ...readOptions }, populatedTree } = resolveAdminDocumentRead(config.definition, {
22
23
  locale,
23
24
  depth,
24
25
  populateRelations
25
26
  });
26
- const document = await handle.findById(id, {
27
+ const document = await handle.findByIdForEdit(id, {
27
28
  ...readOptions
28
29
  });
29
30
  if (!document) throw ERR_NOT_FOUND({
@@ -61,11 +62,11 @@ const getDocumentFn = createServerFn({
61
62
  if (scheduledPublicationEnabled) {
62
63
  const actor = (await getAdminRequestContext()).actor;
63
64
  canSchedulePublication = actor?.hasAbility(documentAbilityKey(config.definition, 'changeStatus')) === true && actor.hasAbility(documentAbilityKey(config.definition, 'publish'));
64
- if (canSchedulePublication) scheduledPublish = await handle.getScheduledPublish(id);
65
+ if (canSchedulePublication) scheduledPublish = document.scheduledPublication;
65
66
  }
66
67
  const serializedSchedule = null == scheduledPublish ? null : serialise(scheduledPublish);
67
68
  return {
68
- ...parsed,
69
+ ...withEditableRevision(parsed, document),
69
70
  _publishedVersion: publishedVersion,
70
71
  ...restoreWarnings && restoreWarnings.length > 0 ? {
71
72
  _restoreWarnings: restoreWarnings
@@ -17,6 +17,7 @@ export * from './list';
17
17
  export * from './reindex';
18
18
  export * from './reorder';
19
19
  export * from './restore-version';
20
+ export * from './save-outcome';
20
21
  export * from './scheduled-publication';
21
22
  export * from './set-list-view-preference';
22
23
  export * from './stats';
@@ -11,6 +11,7 @@ export * from "./list.js";
11
11
  export * from "./reindex.js";
12
12
  export * from "./reorder.js";
13
13
  export * from "./restore-version.js";
14
+ export * from "./save-outcome.js";
14
15
  export * from "./scheduled-publication.js";
15
16
  export * from "./set-list-view-preference.js";
16
17
  export * from "./stats.js";
@@ -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 interface CollectionSearchParams {
9
2
  page?: number;
10
3
  page_size?: number;
@@ -1,3 +1,4 @@
1
+ import { withEditableRevision } from "../editable-revision.js";
1
2
  import { createServerFn } from "@tanstack/react-start";
2
3
  import { getPreferenceCommand } from "@byline/admin/admin-preferences";
3
4
  import { getAdminBylineClient, getAdminRequestContext } from "@byline/client/server";
@@ -61,7 +62,7 @@ const getCollectionDocuments = createServerFn({
61
62
  }));
62
63
  const hasRelations = Object.keys(populateMap).length > 0;
63
64
  const populate = hasRelations ? populateMap : void 0;
64
- const result = await handle.find({
65
+ const result = await handle.findForEdit({
65
66
  where: Object.keys(where).length > 0 ? where : void 0,
66
67
  sort: viewState.sort,
67
68
  locale: params.locale ?? 'en',
@@ -70,7 +71,6 @@ const getCollectionDocuments = createServerFn({
70
71
  select: params.fields,
71
72
  populate,
72
73
  depth: hasRelations ? 1 : void 0,
73
- status: 'any',
74
74
  onMissingLocale: 'empty'
75
75
  });
76
76
  const documentIds = result.docs.map((d)=>d.id);
@@ -104,6 +104,14 @@ const getCollectionDocuments = createServerFn({
104
104
  const serialised = serialise(response);
105
105
  const { list } = getCollectionSchemasForPath(path);
106
106
  if (hasRelations) return serialised;
107
- return list.parse(serialised);
107
+ const parsed = list.parse(serialised);
108
+ const observations = new Map(result.docs.map((doc)=>[
109
+ doc.id,
110
+ doc
111
+ ]));
112
+ return {
113
+ ...parsed,
114
+ docs: parsed.docs.map((doc)=>withEditableRevision(doc, observations.get(doc.id)))
115
+ };
108
116
  });
109
117
  export { getCollectionDocuments };
@@ -1,21 +1,20 @@
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 reorderCollectionDocument: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
2
+ expectedRevision: number;
9
3
  collection: string;
10
4
  documentId: string;
11
5
  beforeDocumentId?: string | null;
12
6
  afterDocumentId?: string | null;
13
7
  }) => {
8
+ expectedRevision: number;
14
9
  collection: string;
15
10
  documentId: string;
16
11
  beforeDocumentId?: string | null;
17
12
  afterDocumentId?: string | null;
18
13
  }, Promise<{
19
- status: 'ok';
14
+ documentId: string;
15
+ revision: number;
16
+ affectedDocuments: import("@byline/core").DocumentRevisionReceipt[];
17
+ scheduledPublicationsNeedReconfirmation: boolean;
20
18
  orderKey: string;
19
+ status: 'ok';
21
20
  }>>;
@@ -1,10 +1,11 @@
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
- import { ERR_NOT_FOUND, ERR_VALIDATION, assertActorCanPerform, generateKeyBetween, generateNKeysBetween, getLogger, getServerConfig } from "@byline/core";
4
+ import { ERR_NOT_FOUND, getLogger, getServerConfig, reorderDocument } from "@byline/core";
4
5
  import { ensureCollection } from "../../integrations/api-utils.js";
5
6
  const reorderCollectionDocument = createServerFn({
6
7
  method: 'POST'
7
- }).validator((input)=>input).handler(async ({ data: input })=>{
8
+ }).validator((input)=>input).handler(withDocumentMutationErrors(async ({ data: input })=>{
8
9
  const { collection: path, documentId, beforeDocumentId, afterDocumentId } = input;
9
10
  const logger = getLogger();
10
11
  const config = await ensureCollection(path);
@@ -14,83 +15,26 @@ const reorderCollectionDocument = createServerFn({
14
15
  collectionPath: path
15
16
  }
16
17
  }).log(logger);
17
- if (true !== config.definition.orderable) throw ERR_VALIDATION({
18
- message: `collection '${path}' is not orderable; set \`orderable: true\` on its collection definition to enable reordering`,
19
- details: {
20
- collectionPath: path
21
- }
22
- }).log(logger);
23
- const requestContext = await getAdminRequestContext();
24
- assertActorCanPerform(requestContext, config.definition, 'update');
25
18
  const serverConfig = getServerConfig();
26
- const collectionId = config.collection.id;
27
- const canonical = await serverConfig.db.queries.documents.getCanonicalDocumentOrder({
28
- collection_id: collectionId
29
- });
30
- let corrupted = false;
31
- {
32
- const seen = new Set();
33
- let lastKey = null;
34
- for (const doc of canonical)if (null != doc.order_key) {
35
- if (seen.has(doc.order_key)) {
36
- corrupted = true;
37
- break;
38
- }
39
- if (null != lastKey && doc.order_key <= lastKey) {
40
- corrupted = true;
41
- break;
42
- }
43
- seen.add(doc.order_key);
44
- lastKey = doc.order_key;
45
- }
46
- }
47
- if (corrupted) {
48
- const allKeys = generateNKeysBetween(null, null, canonical.length);
49
- for(let i = 0; i < canonical.length; i++)await serverConfig.db.commands.documents.setOrderKey({
50
- document_id: canonical[i]?.id,
51
- order_key: allKeys[i]
52
- });
53
- } else {
54
- const firstNullIdx = canonical.findIndex((d)=>null == d.order_key);
55
- if (-1 !== firstNullIdx) {
56
- const nullDocs = canonical.slice(firstNullIdx);
57
- const lastExistingKey = 0 === firstNullIdx ? null : canonical[firstNullIdx - 1]?.order_key;
58
- const newKeys = generateNKeysBetween(lastExistingKey, null, nullDocs.length);
59
- for(let i = 0; i < nullDocs.length; i++)await serverConfig.db.commands.documents.setOrderKey({
60
- document_id: nullDocs[i]?.id,
61
- order_key: newKeys[i]
62
- });
63
- }
64
- }
65
- const { left, right } = await serverConfig.db.queries.documents.getNeighborOrderKeys({
66
- collection_id: collectionId,
67
- before_document_id: beforeDocumentId ?? null,
68
- after_document_id: afterDocumentId ?? null
69
- });
70
- let newKey;
71
- try {
72
- newKey = generateKeyBetween(left, right);
73
- } catch (err) {
74
- throw ERR_VALIDATION({
75
- message: 'cannot generate order_key between supplied neighbors',
76
- details: {
77
- collectionPath: path,
78
- documentId,
79
- beforeDocumentId,
80
- afterDocumentId,
81
- left,
82
- right,
83
- cause: err instanceof Error ? err.message : String(err)
84
- }
85
- }).log(logger);
86
- }
87
- await serverConfig.db.commands.documents.setOrderKey({
88
- document_id: documentId,
89
- order_key: newKey
19
+ const result = await reorderDocument({
20
+ db: serverConfig.db,
21
+ definition: config.definition,
22
+ collectionId: config.collection.id,
23
+ collectionVersion: config.collection.version,
24
+ collectionPath: path,
25
+ requestContext: await getAdminRequestContext(),
26
+ logger,
27
+ defaultLocale: serverConfig.i18n.content.defaultLocale,
28
+ slugifier: serverConfig.slugifier
29
+ }, {
30
+ documentId,
31
+ expectedRevision: input.expectedRevision,
32
+ beforeDocumentId,
33
+ afterDocumentId
90
34
  });
91
35
  return {
92
36
  status: 'ok',
93
- orderKey: newKey
37
+ ...result
94
38
  };
95
- });
39
+ }));
96
40
  export { reorderCollectionDocument };
@@ -1,21 +1,17 @@
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 restoreDocumentVersion: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
2
+ expectedRevision: number;
9
3
  collection: string;
10
4
  id: string;
11
5
  versionId: string;
12
6
  }) => {
7
+ expectedRevision: number;
13
8
  collection: string;
14
9
  id: string;
15
10
  versionId: string;
16
- }, Promise<{
11
+ }, Promise<import("./save-outcome.js").CollectionDocumentCommittedHookFailureResponse | {
17
12
  status: 'ok';
18
13
  documentId: string;
14
+ revision: number;
19
15
  documentVersionId: string;
20
16
  sourceVersionId: string;
21
17
  }>>;
@@ -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 { restoreDocumentVersion } from "@byline/core/services";
5
6
  import { ensureCollection } from "../../integrations/api-utils.js";
7
+ import { toCommittedDocumentHookFailureResponse } from "./save-outcome.js";
6
8
  const restore_version_restoreDocumentVersion = 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, versionId } = input;
10
12
  const logger = getLogger();
11
13
  const config = await ensureCollection(path);
@@ -27,15 +29,23 @@ const restore_version_restoreDocumentVersion = createServerFn({
27
29
  slugifier: serverConfig.slugifier,
28
30
  requestContext: await getAdminRequestContext()
29
31
  };
30
- const result = await restoreDocumentVersion(ctx, {
31
- documentId: id,
32
- sourceVersionId: versionId
33
- });
34
- return {
35
- status: 'ok',
36
- documentId: result.documentId,
37
- documentVersionId: result.documentVersionId,
38
- sourceVersionId: result.sourceVersionId
39
- };
40
- });
32
+ try {
33
+ const result = await restoreDocumentVersion(ctx, {
34
+ documentId: id,
35
+ expectedRevision: input.expectedRevision,
36
+ sourceVersionId: versionId
37
+ });
38
+ return {
39
+ status: 'ok',
40
+ documentId: result.documentId,
41
+ revision: result.revision,
42
+ documentVersionId: result.documentVersionId,
43
+ sourceVersionId: result.sourceVersionId
44
+ };
45
+ } catch (error) {
46
+ const committedFailure = toCommittedDocumentHookFailureResponse(error);
47
+ if (null != committedFailure) return committedFailure;
48
+ throw error;
49
+ }
50
+ }));
41
51
  export { restore_version_restoreDocumentVersion as restoreDocumentVersion };
@@ -0,0 +1,24 @@
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
+ import { type DocumentHookCommittedPhase, type DocumentHookSideEffectCode } from '@byline/core/services';
9
+ export interface CollectionDocumentCommittedHookFailureResponse {
10
+ status: 'committed-hook-failed';
11
+ documentId: string;
12
+ documentVersionId: string;
13
+ revision: number;
14
+ sideEffectFailure: {
15
+ phase: DocumentHookCommittedPhase;
16
+ code: DocumentHookSideEffectCode;
17
+ };
18
+ }
19
+ /**
20
+ * Convert only validated core committed-hook errors into safe wire data. Raw
21
+ * hook errors, messages, stack traces, and storage paths remain server-side.
22
+ */
23
+ export declare function toCommittedDocumentHookFailureResponse(error: unknown): CollectionDocumentCommittedHookFailureResponse | null;
24
+ export declare function hasCommittedDocumentHookFailure(result: unknown): result is CollectionDocumentCommittedHookFailureResponse;
@@ -0,0 +1,19 @@
1
+ import { getDocumentHookCommittedDetails } from "@byline/core/services";
2
+ function toCommittedDocumentHookFailureResponse(error) {
3
+ const details = getDocumentHookCommittedDetails(error);
4
+ if (null == details) return null;
5
+ return {
6
+ status: 'committed-hook-failed',
7
+ documentId: details.documentId,
8
+ documentVersionId: details.documentVersionId,
9
+ revision: details.revision,
10
+ sideEffectFailure: {
11
+ phase: details.phase,
12
+ code: details.sideEffectCode
13
+ }
14
+ };
15
+ }
16
+ function hasCommittedDocumentHookFailure(result) {
17
+ return 'object' == typeof result && null != result && 'status' in result && 'committed-hook-failed' === result.status;
18
+ }
19
+ export { hasCommittedDocumentHookFailure, toCommittedDocumentHookFailureResponse };
@@ -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
  import type { DocumentPublishScheduleInfo } from '@byline/client';
9
2
  import { type DocumentPublishScheduleState, type RecurringTaskHealth } from '@byline/core';
10
3
  export interface ScheduledPublicationRuntime {
@@ -31,6 +24,7 @@ export type SerializedDocumentPublishSchedule = Omit<DocumentPublishScheduleInfo
31
24
  nextAttemptAt: string;
32
25
  };
33
26
  export interface ScheduledPublicationListItem extends SerializedDocumentPublishSchedule {
27
+ revision: number;
34
28
  collectionPath: string;
35
29
  collectionLabel: string;
36
30
  documentPath: string | null;
@@ -56,37 +50,83 @@ export interface ScheduledPublicationListResponse {
56
50
  /** Authenticated feature discovery for the admin shell and health warning. */
57
51
  export declare const getScheduledPublicationRuntime: import("@tanstack/react-start").OptionalFetcher<undefined, undefined, Promise<ScheduledPublicationRuntime>>;
58
52
  export declare const scheduleCollectionDocumentPublish: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
53
+ expectedRevision: number;
59
54
  collection: string;
60
55
  id: string;
61
56
  publishAt: string;
62
57
  expectedVersionId: string;
63
58
  }) => {
59
+ expectedRevision: number;
64
60
  collection: string;
65
61
  id: string;
66
62
  publishAt: string;
67
63
  expectedVersionId: string;
68
- }, Promise<SerializedDocumentPublishSchedule>>;
64
+ }, Promise<{
65
+ authorizedRevision: number | null;
66
+ documentId: string;
67
+ collectionId: string;
68
+ targetVersionId: string;
69
+ state: DocumentPublishScheduleState;
70
+ suspendedReason: ("content_edited" | "document_metadata_changed" | "upgrade_invalidated") | null;
71
+ scheduledBy: string | null;
72
+ lastAuthorizedBy: string | null;
73
+ attemptCount: number;
74
+ lastError: string | null;
75
+ publishAt: string;
76
+ suspendedAt: string | null;
77
+ lastAuthorizedAt: string;
78
+ scheduledAt: string;
79
+ updatedAt: string;
80
+ lastAttemptAt: string | null;
81
+ nextAttemptAt: string;
82
+ revision: number;
83
+ }>>;
69
84
  export declare const confirmCollectionDocumentScheduledPublish: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
85
+ expectedRevision: number;
70
86
  collection: string;
71
87
  id: string;
72
88
  expectedVersionId: string;
73
89
  }) => {
90
+ expectedRevision: number;
74
91
  collection: string;
75
92
  id: string;
76
93
  expectedVersionId: string;
77
- }, Promise<SerializedDocumentPublishSchedule>>;
94
+ }, Promise<{
95
+ authorizedRevision: number | null;
96
+ documentId: string;
97
+ collectionId: string;
98
+ targetVersionId: string;
99
+ state: DocumentPublishScheduleState;
100
+ suspendedReason: ("content_edited" | "document_metadata_changed" | "upgrade_invalidated") | null;
101
+ scheduledBy: string | null;
102
+ lastAuthorizedBy: string | null;
103
+ attemptCount: number;
104
+ lastError: string | null;
105
+ publishAt: string;
106
+ suspendedAt: string | null;
107
+ lastAuthorizedAt: string;
108
+ scheduledAt: string;
109
+ updatedAt: string;
110
+ lastAttemptAt: string | null;
111
+ nextAttemptAt: string;
112
+ revision: number;
113
+ }>>;
78
114
  export declare const cancelCollectionDocumentScheduledPublish: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
115
+ expectedRevision: number;
79
116
  collection: string;
80
117
  id: string;
81
118
  }) => {
119
+ expectedRevision: number;
82
120
  collection: string;
83
121
  id: string;
84
122
  }, Promise<{
85
123
  status: 'not_found';
124
+ revision: number;
86
125
  schedule?: undefined;
87
126
  } | {
88
127
  status: 'cancelled';
89
128
  schedule: SerializedDocumentPublishSchedule;
129
+ revision: number;
90
130
  }>>;
91
131
  export declare const listScheduledPublications: import("@tanstack/react-start").RequiredFetcher<undefined, (input: {
92
132
  states?: readonly DocumentPublishScheduleState[];