@byline/host-tanstack-start 4.19.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.
- package/dist/admin-shell/collections/edit.d.ts +1 -1
- package/dist/admin-shell/collections/edit.js +151 -31
- package/dist/admin-shell/collections/history.js +4 -2
- package/dist/admin-shell/collections/list.js +30 -6
- package/dist/admin-shell/collections/restore-version-modal.d.ts +3 -1
- package/dist/admin-shell/collections/restore-version-modal.js +9 -4
- package/dist/admin-shell/collections/tree-list.js +16 -6
- package/dist/admin-shell/collections/version-history.d.ts +2 -1
- package/dist/admin-shell/collections/version-history.js +11 -2
- package/dist/admin-shell/document-mutation-notice.d.ts +6 -0
- package/dist/admin-shell/document-mutation-notice.js +53 -0
- package/dist/admin-shell/document-mutation-state.d.ts +22 -0
- package/dist/admin-shell/document-mutation-state.js +71 -0
- package/dist/admin-shell/document-mutation-state.test.d.ts +1 -0
- package/dist/admin-shell/scheduled-publications/list.js +15 -5
- package/dist/admin-shell/singletons/history.js +4 -2
- package/dist/admin-shell/singletons/view.d.ts +1 -1
- package/dist/admin-shell/singletons/view.js +98 -19
- package/dist/integrations/byline-field-services.js +2 -9
- package/dist/routes/create-collection-edit-route.js +1 -1
- package/dist/routes/create-collection-list-route.d.ts +0 -7
- package/dist/routes/create-collection-list-route.js +7 -9
- package/dist/routes/create-singleton-route.d.ts +2 -0
- package/dist/routes/create-singleton-route.js +4 -3
- package/dist/server-fns/collections/copy-to-locale.d.ts +2 -7
- package/dist/server-fns/collections/copy-to-locale.js +4 -2
- package/dist/server-fns/collections/create.d.ts +1 -7
- package/dist/server-fns/collections/create.js +5 -3
- package/dist/server-fns/collections/delete-locale.d.ts +2 -7
- package/dist/server-fns/collections/delete-locale.js +4 -2
- package/dist/server-fns/collections/delete-outcome.d.ts +7 -3
- package/dist/server-fns/collections/delete-outcome.js +4 -0
- package/dist/server-fns/collections/delete.d.ts +2 -7
- package/dist/server-fns/collections/delete.js +5 -3
- package/dist/server-fns/collections/duplicate.d.ts +2 -7
- package/dist/server-fns/collections/duplicate.js +5 -3
- package/dist/server-fns/collections/get.d.ts +1 -7
- package/dist/server-fns/collections/get.js +5 -4
- package/dist/server-fns/collections/list.d.ts +0 -7
- package/dist/server-fns/collections/list.js +11 -3
- package/dist/server-fns/collections/reorder.d.ts +7 -8
- package/dist/server-fns/collections/reorder.js +20 -76
- package/dist/server-fns/collections/restore-version.d.ts +3 -7
- package/dist/server-fns/collections/restore-version.js +5 -2
- package/dist/server-fns/collections/save-outcome.d.ts +1 -0
- package/dist/server-fns/collections/save-outcome.js +1 -0
- package/dist/server-fns/collections/scheduled-publication.d.ts +49 -9
- package/dist/server-fns/collections/scheduled-publication.js +31 -21
- package/dist/server-fns/collections/status.d.ts +8 -7
- package/dist/server-fns/collections/status.js +13 -5
- package/dist/server-fns/collections/tree-mutation.d.ts +7 -6
- package/dist/server-fns/collections/tree-mutation.js +17 -5
- package/dist/server-fns/collections/tree.d.ts +14 -8
- package/dist/server-fns/collections/tree.js +21 -17
- package/dist/server-fns/collections/update.d.ts +12 -11
- package/dist/server-fns/collections/update.js +34 -20
- package/dist/server-fns/collections/upload.d.ts +1 -0
- package/dist/server-fns/collections/upload.js +3 -2
- package/dist/server-fns/document-mutation-errors.d.ts +5 -0
- package/dist/server-fns/document-mutation-errors.js +50 -0
- package/dist/server-fns/editable-revision.d.ts +9 -0
- package/dist/server-fns/editable-revision.js +11 -0
- package/dist/server-fns/singleton-document-read.d.ts +1 -1
- package/dist/server-fns/singleton-document-read.js +7 -3
- package/dist/server-fns/singletons/cancel-scheduled-publish.d.ts +6 -8
- package/dist/server-fns/singletons/cancel-scheduled-publish.js +4 -1
- package/dist/server-fns/singletons/change-status.d.ts +2 -7
- package/dist/server-fns/singletons/change-status.js +4 -1
- package/dist/server-fns/singletons/confirm-scheduled-publish.d.ts +5 -8
- package/dist/server-fns/singletons/confirm-scheduled-publish.js +4 -2
- package/dist/server-fns/singletons/copy-to-locale.d.ts +2 -7
- package/dist/server-fns/singletons/copy-to-locale.js +4 -2
- package/dist/server-fns/singletons/get.d.ts +4 -4
- package/dist/server-fns/singletons/restore-version.d.ts +2 -7
- package/dist/server-fns/singletons/restore-version.js +6 -3
- package/dist/server-fns/singletons/schedule-publish.d.ts +5 -8
- package/dist/server-fns/singletons/schedule-publish.js +4 -2
- package/dist/server-fns/singletons/unpublish.d.ts +2 -7
- package/dist/server-fns/singletons/unpublish.js +4 -1
- package/dist/server-fns/singletons/update.d.ts +4 -11
- package/dist/server-fns/singletons/update.js +5 -4
- package/dist/server-fns/structural-receipt.d.ts +4 -0
- package/dist/server-fns/structural-receipt.js +31 -0
- package/package.json +11 -11
- package/src/admin-shell/collections/edit.tsx +173 -54
- package/src/admin-shell/collections/history.tsx +11 -2
- package/src/admin-shell/collections/list.tsx +22 -6
- package/src/admin-shell/collections/restore-version-modal.tsx +12 -3
- package/src/admin-shell/collections/save-navigation.test.tsx +156 -8
- package/src/admin-shell/collections/tree-list.tsx +16 -6
- package/src/admin-shell/collections/version-history.tsx +13 -3
- package/src/admin-shell/document-mutation-notice.tsx +45 -0
- package/src/admin-shell/document-mutation-state.test.tsx +146 -0
- package/src/admin-shell/document-mutation-state.ts +95 -0
- package/src/admin-shell/scheduled-publications/list.tsx +16 -6
- package/src/admin-shell/singletons/history.test.tsx +3 -1
- package/src/admin-shell/singletons/history.tsx +10 -2
- package/src/admin-shell/singletons/view.test.tsx +59 -18
- package/src/admin-shell/singletons/view.tsx +110 -29
- package/src/integrations/byline-field-services.ts +2 -3
- package/src/routes/create-collection-edit-route.tsx +1 -0
- package/src/routes/create-collection-list-route.tsx +13 -2
- package/src/routes/create-singleton-route.test.node.ts +10 -2
- package/src/routes/create-singleton-route.tsx +6 -4
- package/src/server-fns/collections/copy-to-locale.ts +41 -36
- package/src/server-fns/collections/create.ts +54 -50
- package/src/server-fns/collections/delete-locale.ts +41 -32
- package/src/server-fns/collections/delete-outcome.test.node.ts +16 -0
- package/src/server-fns/collections/delete-outcome.ts +27 -16
- package/src/server-fns/collections/delete.ts +44 -38
- package/src/server-fns/collections/duplicate.ts +38 -32
- package/src/server-fns/collections/get.ts +8 -4
- package/src/server-fns/collections/list.ts +8 -3
- package/src/server-fns/collections/reorder.ts +32 -124
- package/src/server-fns/collections/restore-version.ts +47 -39
- package/src/server-fns/collections/revision-input.test.node.ts +108 -0
- package/src/server-fns/collections/save-outcome.test.node.ts +3 -0
- package/src/server-fns/collections/save-outcome.ts +2 -0
- package/src/server-fns/collections/scheduled-publication.ts +138 -111
- package/src/server-fns/collections/status.ts +86 -64
- package/src/server-fns/collections/tree-mutation.test.node.ts +87 -29
- package/src/server-fns/collections/tree-mutation.ts +28 -11
- package/src/server-fns/collections/tree.ts +150 -122
- package/src/server-fns/collections/update.ts +110 -86
- package/src/server-fns/collections/upload-singleton.test.node.ts +3 -7
- package/src/server-fns/collections/upload.ts +81 -77
- package/src/server-fns/document-mutation-errors.ts +67 -0
- package/src/server-fns/document-mutation-transport.test.node.ts +211 -0
- package/src/server-fns/editable-revision.test.node.ts +23 -0
- package/src/server-fns/editable-revision.ts +18 -0
- package/src/server-fns/singleton-document-read.ts +7 -3
- package/src/server-fns/singletons/cancel-scheduled-publish.ts +10 -3
- package/src/server-fns/singletons/change-status.ts +10 -3
- package/src/server-fns/singletons/confirm-scheduled-publish.ts +12 -6
- package/src/server-fns/singletons/copy-to-locale.ts +20 -15
- package/src/server-fns/singletons/get.ts +3 -3
- package/src/server-fns/singletons/restore-version.ts +17 -12
- package/src/server-fns/singletons/schedule-publish.ts +18 -7
- package/src/server-fns/singletons/singletons.test.node.ts +63 -25
- package/src/server-fns/singletons/unpublish.ts +10 -3
- package/src/server-fns/singletons/update.ts +21 -16
- package/src/server-fns/structural-receipt.ts +41 -0
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { CollectionHandle, PlaceTreeNodeOptions, RemoveFromTreeOptions } from '@byline/client'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ErrorCodes,
|
|
4
|
+
getDocumentRevisionValidationDetails,
|
|
5
|
+
getDocumentStaleDetails,
|
|
6
|
+
getLockConflictDetails,
|
|
7
|
+
TREE_HOOK_COMMITTED_MARKER,
|
|
8
|
+
TREE_PLACEMENT_STALE_MARKER,
|
|
9
|
+
} from '@byline/core'
|
|
10
|
+
|
|
11
|
+
import { structuralReceipt } from '../structural-receipt.js'
|
|
3
12
|
|
|
4
13
|
function hasErrorCode(error: unknown, code: string): boolean {
|
|
5
14
|
return (
|
|
@@ -27,11 +36,11 @@ export function isCommittedTreeHookFailure(error: unknown): boolean {
|
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
export type AdminTreeMoveOutcome =
|
|
30
|
-
| { status: 'ok' }
|
|
39
|
+
| { status: 'ok'; receipt?: ReturnType<typeof structuralReceipt> }
|
|
31
40
|
| { status: 'suppressed' }
|
|
32
41
|
| { status: 'mutation-failed'; error: unknown; refreshError?: unknown }
|
|
33
42
|
| { status: 'committed-hook-failed'; error: unknown; refreshError?: unknown }
|
|
34
|
-
| { status: 'refresh-failed'; error: unknown }
|
|
43
|
+
| { status: 'refresh-failed'; error: unknown; receipt?: ReturnType<typeof structuralReceipt> }
|
|
35
44
|
|
|
36
45
|
export interface AdminTreeMoveUiEffects {
|
|
37
46
|
rollback: boolean
|
|
@@ -93,9 +102,17 @@ export async function executeAdminTreeMove(
|
|
|
93
102
|
mutate: () => Promise<unknown>,
|
|
94
103
|
refresh: () => Promise<unknown>
|
|
95
104
|
): Promise<AdminTreeMoveOutcome> {
|
|
105
|
+
let receipt: ReturnType<typeof structuralReceipt>
|
|
96
106
|
try {
|
|
97
|
-
await mutate()
|
|
107
|
+
receipt = structuralReceipt(await mutate())
|
|
98
108
|
} catch (error) {
|
|
109
|
+
if (
|
|
110
|
+
getDocumentStaleDetails(error) ||
|
|
111
|
+
getDocumentRevisionValidationDetails(error) ||
|
|
112
|
+
getLockConflictDetails(error) ||
|
|
113
|
+
hasErrorCode(error, ErrorCodes.NOT_FOUND)
|
|
114
|
+
)
|
|
115
|
+
return { status: 'mutation-failed', error }
|
|
99
116
|
const status = isCommittedTreeHookFailure(error)
|
|
100
117
|
? ('committed-hook-failed' as const)
|
|
101
118
|
: ('mutation-failed' as const)
|
|
@@ -109,9 +126,9 @@ export async function executeAdminTreeMove(
|
|
|
109
126
|
|
|
110
127
|
try {
|
|
111
128
|
await refresh()
|
|
112
|
-
return { status: 'ok' }
|
|
129
|
+
return { status: 'ok', ...(receipt ? { receipt } : {}) }
|
|
113
130
|
} catch (error) {
|
|
114
|
-
return { status: 'refresh-failed', error }
|
|
131
|
+
return { status: 'refresh-failed', error, ...(receipt ? { receipt } : {}) }
|
|
115
132
|
}
|
|
116
133
|
}
|
|
117
134
|
|
|
@@ -140,24 +157,24 @@ export function createAdminTreeMoveController(): AdminTreeMoveController {
|
|
|
140
157
|
}
|
|
141
158
|
}
|
|
142
159
|
|
|
143
|
-
/**
|
|
160
|
+
/** Request reconciliation for explicit caller actions; never retry or replace their revision. */
|
|
144
161
|
export function placeAdminTreeNode(
|
|
145
162
|
handle: Pick<CollectionHandle, 'placeTreeNode'>,
|
|
146
163
|
documentId: string,
|
|
147
164
|
options: PlaceTreeNodeOptions
|
|
148
|
-
):
|
|
165
|
+
): ReturnType<CollectionHandle['placeTreeNode']> {
|
|
149
166
|
return handle.placeTreeNode(documentId, {
|
|
150
167
|
...options,
|
|
151
168
|
reconcile: options.reconcile ?? true,
|
|
152
169
|
})
|
|
153
170
|
}
|
|
154
171
|
|
|
155
|
-
/**
|
|
172
|
+
/** Request reconciliation for explicit caller actions; never retry or replace their revision. */
|
|
156
173
|
export function removeAdminTreeNode(
|
|
157
174
|
handle: Pick<CollectionHandle, 'removeFromTree'>,
|
|
158
175
|
documentId: string,
|
|
159
|
-
options: RemoveFromTreeOptions
|
|
160
|
-
):
|
|
176
|
+
options: RemoveFromTreeOptions
|
|
177
|
+
): ReturnType<CollectionHandle['removeFromTree']> {
|
|
161
178
|
return handle.removeFromTree(documentId, {
|
|
162
179
|
...options,
|
|
163
180
|
reconcile: options.reconcile ?? true,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withDocumentMutationErrors } from '../document-mutation-errors.js'
|
|
1
2
|
/**
|
|
2
3
|
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
4
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
@@ -21,7 +22,7 @@ import { createReadContext, ERR_NOT_FOUND, getLogger } from '@byline/core'
|
|
|
21
22
|
|
|
22
23
|
import { ensureCollection } from '../../integrations/api-utils.js'
|
|
23
24
|
import { placeAdminTreeNode, removeAdminTreeNode } from './tree-mutation.js'
|
|
24
|
-
import { getAdminTreeParent
|
|
25
|
+
import { getAdminTreeParent } from './tree-read.js'
|
|
25
26
|
|
|
26
27
|
// ---------------------------------------------------------------------------
|
|
27
28
|
// Place / move a node within the tree (place / reorder / re-parent)
|
|
@@ -30,6 +31,7 @@ import { getAdminTreeParent, getAdminUnplacedTreeDocuments } from './tree-read.j
|
|
|
30
31
|
export const placeTreeNode = createServerFn({ method: 'POST' })
|
|
31
32
|
.validator(
|
|
32
33
|
(input: {
|
|
34
|
+
expectedRevision: number
|
|
33
35
|
collection: string
|
|
34
36
|
documentId: string
|
|
35
37
|
parentDocumentId: string | null
|
|
@@ -38,45 +40,62 @@ export const placeTreeNode = createServerFn({ method: 'POST' })
|
|
|
38
40
|
reconcile?: boolean
|
|
39
41
|
}) => input
|
|
40
42
|
)
|
|
41
|
-
.handler(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
43
|
+
.handler(
|
|
44
|
+
withDocumentMutationErrors(async ({ data }) => {
|
|
45
|
+
const { collection: path, documentId } = data
|
|
46
|
+
const config = await ensureCollection(path)
|
|
47
|
+
if (!config) {
|
|
48
|
+
throw ERR_NOT_FOUND({
|
|
49
|
+
message: 'Collection not found',
|
|
50
|
+
details: { collectionPath: path },
|
|
51
|
+
}).log(getLogger())
|
|
52
|
+
}
|
|
53
|
+
const handle = getAdminBylineClient().collection(path)
|
|
54
|
+
const result = await placeAdminTreeNode(handle, documentId, {
|
|
55
|
+
expectedRevision: data.expectedRevision,
|
|
56
|
+
parentDocumentId: data.parentDocumentId,
|
|
57
|
+
beforeDocumentId: data.beforeDocumentId ?? null,
|
|
58
|
+
afterDocumentId: data.afterDocumentId ?? null,
|
|
59
|
+
reconcile: data.reconcile,
|
|
60
|
+
})
|
|
61
|
+
return { status: 'ok' as const, ...result }
|
|
56
62
|
})
|
|
57
|
-
|
|
58
|
-
})
|
|
63
|
+
)
|
|
59
64
|
|
|
60
65
|
// ---------------------------------------------------------------------------
|
|
61
66
|
// Remove a node from the tree (back to the unplaced state)
|
|
62
67
|
// ---------------------------------------------------------------------------
|
|
63
68
|
|
|
64
69
|
export const removeFromTree = createServerFn({ method: 'POST' })
|
|
65
|
-
.validator(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
.validator(
|
|
71
|
+
(input: {
|
|
72
|
+
expectedRevision: number
|
|
73
|
+
collection: string
|
|
74
|
+
documentId: string
|
|
75
|
+
reconcile?: boolean
|
|
76
|
+
}) => input
|
|
77
|
+
)
|
|
78
|
+
.handler(
|
|
79
|
+
withDocumentMutationErrors(async ({ data }) => {
|
|
80
|
+
const { collection: path, documentId } = data
|
|
81
|
+
const config = await ensureCollection(path)
|
|
82
|
+
if (!config) {
|
|
83
|
+
throw ERR_NOT_FOUND({
|
|
84
|
+
message: 'Collection not found',
|
|
85
|
+
details: { collectionPath: path },
|
|
86
|
+
}).log(getLogger())
|
|
87
|
+
}
|
|
88
|
+
const result = await removeAdminTreeNode(
|
|
89
|
+
getAdminBylineClient().collection(path),
|
|
90
|
+
documentId,
|
|
91
|
+
{
|
|
92
|
+
expectedRevision: data.expectedRevision,
|
|
93
|
+
reconcile: data.reconcile,
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
return { status: 'ok' as const, ...result }
|
|
77
97
|
})
|
|
78
|
-
|
|
79
|
-
})
|
|
98
|
+
)
|
|
80
99
|
|
|
81
100
|
// ---------------------------------------------------------------------------
|
|
82
101
|
// Resolve a document's ancestor chain (root-first), hydrated with titles
|
|
@@ -84,30 +103,32 @@ export const removeFromTree = createServerFn({ method: 'POST' })
|
|
|
84
103
|
|
|
85
104
|
export const getTreeAncestors = createServerFn({ method: 'GET' })
|
|
86
105
|
.validator((input: { collection: string; documentId: string }) => input)
|
|
87
|
-
.handler(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const useAsTitle = config.definition.useAsTitle
|
|
97
|
-
// Admin context: read with `status: 'any'` so draft ancestors still show.
|
|
98
|
-
const ancestors = await getAdminBylineClient()
|
|
99
|
-
.collection(path)
|
|
100
|
-
.getAncestors(documentId, { status: 'any' })
|
|
101
|
-
return ancestors.map((doc) => {
|
|
102
|
-
const fields = doc.fields as Record<string, any> | undefined
|
|
103
|
-
const title = useAsTitle ? fields?.[useAsTitle] : undefined
|
|
104
|
-
return {
|
|
105
|
-
id: doc.id,
|
|
106
|
-
title: typeof title === 'string' && title.length > 0 ? title : doc.path,
|
|
107
|
-
path: doc.path,
|
|
106
|
+
.handler(
|
|
107
|
+
withDocumentMutationErrors(async ({ data }) => {
|
|
108
|
+
const { collection: path, documentId } = data
|
|
109
|
+
const config = await ensureCollection(path)
|
|
110
|
+
if (!config) {
|
|
111
|
+
throw ERR_NOT_FOUND({
|
|
112
|
+
message: 'Collection not found',
|
|
113
|
+
details: { collectionPath: path },
|
|
114
|
+
}).log(getLogger())
|
|
108
115
|
}
|
|
116
|
+
const useAsTitle = config.definition.useAsTitle
|
|
117
|
+
// Admin context: read with `status: 'any'` so draft ancestors still show.
|
|
118
|
+
const ancestors = await getAdminBylineClient()
|
|
119
|
+
.collection(path)
|
|
120
|
+
.getAncestors(documentId, { status: 'any' })
|
|
121
|
+
return ancestors.map((doc) => {
|
|
122
|
+
const fields = doc.fields as Record<string, any> | undefined
|
|
123
|
+
const title = useAsTitle ? fields?.[useAsTitle] : undefined
|
|
124
|
+
return {
|
|
125
|
+
id: doc.id,
|
|
126
|
+
title: typeof title === 'string' && title.length > 0 ? title : doc.path,
|
|
127
|
+
path: doc.path,
|
|
128
|
+
}
|
|
129
|
+
})
|
|
109
130
|
})
|
|
110
|
-
|
|
131
|
+
)
|
|
111
132
|
|
|
112
133
|
// ---------------------------------------------------------------------------
|
|
113
134
|
// Resolve a document's placement state in the tree (unplaced / root / child)
|
|
@@ -115,17 +136,19 @@ export const getTreeAncestors = createServerFn({ method: 'GET' })
|
|
|
115
136
|
|
|
116
137
|
export const getTreeParent = createServerFn({ method: 'GET' })
|
|
117
138
|
.validator((input: { collection: string; documentId: string }) => input)
|
|
118
|
-
.handler(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
139
|
+
.handler(
|
|
140
|
+
withDocumentMutationErrors(async ({ data }) => {
|
|
141
|
+
const { collection: path, documentId } = data
|
|
142
|
+
const config = await ensureCollection(path)
|
|
143
|
+
if (!config) {
|
|
144
|
+
throw ERR_NOT_FOUND({
|
|
145
|
+
message: 'Collection not found',
|
|
146
|
+
details: { collectionPath: path },
|
|
147
|
+
}).log(getLogger())
|
|
148
|
+
}
|
|
149
|
+
return getAdminTreeParent(getAdminBylineClient().collection(path), documentId)
|
|
150
|
+
})
|
|
151
|
+
)
|
|
129
152
|
|
|
130
153
|
// ---------------------------------------------------------------------------
|
|
131
154
|
// Read the whole collection tree as ordered, depth-tagged rows for the built-in
|
|
@@ -135,6 +158,7 @@ export const getTreeParent = createServerFn({ method: 'GET' })
|
|
|
135
158
|
// ---------------------------------------------------------------------------
|
|
136
159
|
|
|
137
160
|
export interface CollectionTreeRow {
|
|
161
|
+
revision: number
|
|
138
162
|
id: string
|
|
139
163
|
/** Parent document id, or `null` for a root or unplaced node. Drives the
|
|
140
164
|
* client-side tree reconstruction for drag-to-reorder / re-parent. */
|
|
@@ -150,70 +174,74 @@ export interface CollectionTreeRow {
|
|
|
150
174
|
|
|
151
175
|
export const getCollectionTree = createServerFn({ method: 'GET' })
|
|
152
176
|
.validator((input: { collection: string; locale?: string }) => input)
|
|
153
|
-
.handler(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
.handler(
|
|
178
|
+
withDocumentMutationErrors(async ({ data }) => {
|
|
179
|
+
const { collection: path } = data
|
|
180
|
+
const config = await ensureCollection(path)
|
|
181
|
+
if (!config) {
|
|
182
|
+
throw ERR_NOT_FOUND({
|
|
183
|
+
message: 'Collection not found',
|
|
184
|
+
details: { collectionPath: path },
|
|
185
|
+
}).log(getLogger())
|
|
186
|
+
}
|
|
187
|
+
const handle = getAdminBylineClient().collection(path)
|
|
188
|
+
// Shared deliberately across the structural and unplaced reads: the
|
|
189
|
+
// beforeRead predicate cache is per-ReadContext, so sharing gives both
|
|
190
|
+
// halves one predicate snapshot — a time-dependent predicate (embargo
|
|
191
|
+
// cutoff) evaluated twice could otherwise disagree between the placed
|
|
192
|
+
// forest and the unplaced list. The request-memoized context factory
|
|
193
|
+
// keeps this sharing safe (one request authority per request).
|
|
194
|
+
const readContext = createReadContext()
|
|
195
|
+
|
|
196
|
+
const { forest, unplaced } = await handle.getTreeForEdit({
|
|
197
|
+
locale: data.locale,
|
|
198
|
+
_readContext: readContext,
|
|
199
|
+
})
|
|
200
|
+
const rows: CollectionTreeRow[] = []
|
|
201
|
+
const placed = new Set<string>()
|
|
202
|
+
const walk = (nodes: typeof forest, depth: number, parentId: string | null): void => {
|
|
203
|
+
for (const node of nodes) {
|
|
204
|
+
const doc = node.document
|
|
205
|
+
rows.push({
|
|
206
|
+
id: doc.id,
|
|
207
|
+
revision: doc.revision,
|
|
208
|
+
parentId,
|
|
209
|
+
depth,
|
|
210
|
+
unplaced: false,
|
|
211
|
+
status: doc.status,
|
|
212
|
+
path: doc.path,
|
|
213
|
+
createdAt: doc.createdAt,
|
|
214
|
+
updatedAt: doc.updatedAt,
|
|
215
|
+
fields: doc.fields as Record<string, any>,
|
|
216
|
+
})
|
|
217
|
+
placed.add(doc.id)
|
|
218
|
+
walk(node.children, depth + 1, doc.id)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
walk(forest, 0, null)
|
|
222
|
+
|
|
223
|
+
// Surface documents not yet in the tree (e.g. freshly created) so they
|
|
224
|
+
// remain reachable. Trees are small by design, so a single wide read is fine.
|
|
225
|
+
for (const doc of unplaced) {
|
|
181
226
|
rows.push({
|
|
182
227
|
id: doc.id,
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
228
|
+
revision: doc.revision,
|
|
229
|
+
parentId: null,
|
|
230
|
+
depth: 0,
|
|
231
|
+
unplaced: true,
|
|
186
232
|
status: doc.status,
|
|
187
233
|
path: doc.path,
|
|
188
234
|
createdAt: doc.createdAt,
|
|
189
235
|
updatedAt: doc.updatedAt,
|
|
190
236
|
fields: doc.fields as Record<string, any>,
|
|
191
237
|
})
|
|
192
|
-
placed.add(doc.id)
|
|
193
|
-
walk(node.children, depth + 1, doc.id)
|
|
194
238
|
}
|
|
195
|
-
}
|
|
196
|
-
walk(forest, 0, null)
|
|
197
|
-
|
|
198
|
-
// Surface documents not yet in the tree (e.g. freshly created) so they
|
|
199
|
-
// remain reachable. Trees are small by design, so a single wide read is fine.
|
|
200
|
-
const unplaced = await getAdminUnplacedTreeDocuments(handle, placed, readContext, data.locale)
|
|
201
|
-
for (const doc of unplaced) {
|
|
202
|
-
rows.push({
|
|
203
|
-
id: doc.id,
|
|
204
|
-
parentId: null,
|
|
205
|
-
depth: 0,
|
|
206
|
-
unplaced: true,
|
|
207
|
-
status: doc.status,
|
|
208
|
-
path: doc.path,
|
|
209
|
-
createdAt: doc.createdAt,
|
|
210
|
-
updatedAt: doc.updatedAt,
|
|
211
|
-
fields: doc.fields as Record<string, any>,
|
|
212
|
-
})
|
|
213
|
-
}
|
|
214
239
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
240
|
+
return {
|
|
241
|
+
rows,
|
|
242
|
+
included: {
|
|
243
|
+
collection: { path: config.collection.path, labels: config.definition.labels },
|
|
244
|
+
},
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
)
|