@byline/host-tanstack-start 4.17.0 → 4.19.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/chrome/router-pager.d.ts +7 -0
- package/dist/admin-shell/chrome/router-pager.js +13 -0
- package/dist/admin-shell/chrome/scroll-to-top.d.ts +20 -0
- package/dist/admin-shell/chrome/scroll-to-top.js +9 -0
- package/dist/admin-shell/collections/create.js +22 -5
- package/dist/admin-shell/collections/edit.js +85 -17
- package/dist/admin-shell/collections/restore-version-modal.js +14 -2
- package/dist/admin-shell/collections/save-navigation.test.d.ts +8 -0
- package/dist/admin-shell/singletons/view.js +10 -4
- package/dist/server-fns/collections/copy-to-locale.d.ts +2 -1
- package/dist/server-fns/collections/copy-to-locale.js +13 -6
- package/dist/server-fns/collections/create.d.ts +1 -1
- package/dist/server-fns/collections/create.js +15 -7
- package/dist/server-fns/collections/delete-locale.d.ts +2 -1
- package/dist/server-fns/collections/delete-locale.js +11 -4
- package/dist/server-fns/collections/duplicate.d.ts +2 -1
- package/dist/server-fns/collections/duplicate.js +10 -3
- package/dist/server-fns/collections/index.d.ts +1 -0
- package/dist/server-fns/collections/index.js +1 -0
- package/dist/server-fns/collections/restore-version.d.ts +1 -1
- package/dist/server-fns/collections/restore-version.js +17 -10
- package/dist/server-fns/collections/save-outcome.d.ts +23 -0
- package/dist/server-fns/collections/save-outcome.js +18 -0
- package/dist/server-fns/collections/update.d.ts +3 -1
- package/dist/server-fns/collections/update.js +17 -7
- package/dist/server-fns/singletons/copy-to-locale.js +14 -5
- package/dist/server-fns/singletons/restore-version.js +10 -1
- package/dist/server-fns/singletons/update.js +12 -5
- package/package.json +10 -10
- package/src/admin-shell/chrome/router-pager.tsx +22 -0
- package/src/admin-shell/chrome/scroll-to-top.test.ts +56 -0
- package/src/admin-shell/chrome/scroll-to-top.ts +28 -0
- package/src/admin-shell/collections/create.tsx +22 -3
- package/src/admin-shell/collections/edit.tsx +63 -10
- package/src/admin-shell/collections/restore-version-modal.tsx +11 -2
- package/src/admin-shell/collections/save-navigation.test.tsx +384 -0
- package/src/admin-shell/singletons/history.test.tsx +43 -0
- package/src/admin-shell/singletons/view.test.tsx +27 -0
- package/src/admin-shell/singletons/view.tsx +35 -16
- package/src/server-fns/collections/copy-to-locale.ts +43 -29
- package/src/server-fns/collections/create.ts +16 -8
- package/src/server-fns/collections/delete-locale.ts +38 -24
- package/src/server-fns/collections/duplicate.ts +38 -24
- package/src/server-fns/collections/index.ts +1 -0
- package/src/server-fns/collections/restore-version.ts +16 -9
- package/src/server-fns/collections/save-outcome.test.node.ts +77 -0
- package/src/server-fns/collections/save-outcome.ts +55 -0
- package/src/server-fns/collections/update.ts +23 -8
- package/src/server-fns/singletons/copy-to-locale.ts +16 -9
- package/src/server-fns/singletons/restore-version.ts +12 -3
- package/src/server-fns/singletons/singletons.test.node.ts +34 -0
- package/src/server-fns/singletons/update.ts +12 -5
|
@@ -10,10 +10,11 @@ import { createServerFn } from '@tanstack/react-start'
|
|
|
10
10
|
|
|
11
11
|
import { getAdminRequestContext } from '@byline/client/server'
|
|
12
12
|
import { ERR_NOT_FOUND, getLogger, getServerConfig } from '@byline/core'
|
|
13
|
-
import type { DocumentLifecycleContext } from '@byline/core/services'
|
|
13
|
+
import type { CreateDocumentResult, DocumentLifecycleContext } from '@byline/core/services'
|
|
14
14
|
import { createDocument } from '@byline/core/services'
|
|
15
15
|
|
|
16
16
|
import { ensureCollection } from '../../integrations/api-utils.js'
|
|
17
|
+
import { toCommittedDocumentHookFailureResponse } from './save-outcome.js'
|
|
17
18
|
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
19
20
|
// Create document
|
|
@@ -59,13 +60,20 @@ export const createCollectionDocument = createServerFn({ method: 'POST' })
|
|
|
59
60
|
requestContext: await getAdminRequestContext(),
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
let result: CreateDocumentResult
|
|
64
|
+
try {
|
|
65
|
+
result = await createDocument(ctx, {
|
|
66
|
+
data: structuredClone(documentData),
|
|
67
|
+
status: documentData.status,
|
|
68
|
+
locale: locale ?? serverConfig.i18n.content.defaultLocale,
|
|
69
|
+
path: explicitPath,
|
|
70
|
+
availableLocales,
|
|
71
|
+
})
|
|
72
|
+
} catch (error) {
|
|
73
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
74
|
+
if (committedFailure != null) return committedFailure
|
|
75
|
+
throw error
|
|
76
|
+
}
|
|
69
77
|
|
|
70
78
|
// The new document's id is returned so the create view can navigate
|
|
71
79
|
// straight to the edit view (create → edit).
|
|
@@ -14,6 +14,10 @@ import type { DeleteLocaleResult, DocumentLifecycleContext } from '@byline/core/
|
|
|
14
14
|
import { deleteLocale } from '@byline/core/services'
|
|
15
15
|
|
|
16
16
|
import { ensureCollection } from '../../integrations/api-utils.js'
|
|
17
|
+
import {
|
|
18
|
+
type CollectionDocumentCommittedHookFailureResponse,
|
|
19
|
+
toCommittedDocumentHookFailureResponse,
|
|
20
|
+
} from './save-outcome.js'
|
|
17
21
|
|
|
18
22
|
// ---------------------------------------------------------------------------
|
|
19
23
|
// Delete one content locale's data from a document
|
|
@@ -31,29 +35,39 @@ import { ensureCollection } from '../../integrations/api-utils.js'
|
|
|
31
35
|
*/
|
|
32
36
|
export const deleteDocumentLocale = createServerFn({ method: 'POST' })
|
|
33
37
|
.validator((input: { collection: string; id: string; locale: string }) => input)
|
|
34
|
-
.handler(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
.handler(
|
|
39
|
+
async ({
|
|
40
|
+
data: input,
|
|
41
|
+
}): Promise<DeleteLocaleResult | CollectionDocumentCommittedHookFailureResponse> => {
|
|
42
|
+
const { collection: path, id, locale } = input
|
|
43
|
+
const logger = getLogger()
|
|
44
|
+
const config = await ensureCollection(path)
|
|
45
|
+
if (!config) {
|
|
46
|
+
throw ERR_NOT_FOUND({
|
|
47
|
+
message: 'Collection not found',
|
|
48
|
+
details: { collectionPath: path },
|
|
49
|
+
}).log(logger)
|
|
50
|
+
}
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
const serverConfig = getServerConfig()
|
|
53
|
+
const ctx: DocumentLifecycleContext = {
|
|
54
|
+
db: serverConfig.db,
|
|
55
|
+
definition: config.definition,
|
|
56
|
+
collectionId: config.collection.id,
|
|
57
|
+
collectionVersion: config.collection.version,
|
|
58
|
+
collectionPath: path,
|
|
59
|
+
logger,
|
|
60
|
+
defaultLocale: serverConfig.i18n.content.defaultLocale,
|
|
61
|
+
slugifier: serverConfig.slugifier,
|
|
62
|
+
requestContext: await getAdminRequestContext(),
|
|
63
|
+
}
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
try {
|
|
66
|
+
return await deleteLocale(ctx, { documentId: id, locale })
|
|
67
|
+
} catch (error) {
|
|
68
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
69
|
+
if (committedFailure != null) return committedFailure
|
|
70
|
+
throw error
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
)
|
|
@@ -14,6 +14,10 @@ import type { DocumentLifecycleContext, DuplicateDocumentResult } from '@byline/
|
|
|
14
14
|
import { duplicateDocument } from '@byline/core/services'
|
|
15
15
|
|
|
16
16
|
import { ensureCollection } from '../../integrations/api-utils.js'
|
|
17
|
+
import {
|
|
18
|
+
type CollectionDocumentCommittedHookFailureResponse,
|
|
19
|
+
toCommittedDocumentHookFailureResponse,
|
|
20
|
+
} from './save-outcome.js'
|
|
17
21
|
|
|
18
22
|
// ---------------------------------------------------------------------------
|
|
19
23
|
// Duplicate document
|
|
@@ -32,29 +36,39 @@ import { ensureCollection } from '../../integrations/api-utils.js'
|
|
|
32
36
|
*/
|
|
33
37
|
export const duplicateCollectionDocument = createServerFn({ method: 'POST' })
|
|
34
38
|
.validator((input: { collection: string; id: string }) => input)
|
|
35
|
-
.handler(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
.handler(
|
|
40
|
+
async ({
|
|
41
|
+
data: input,
|
|
42
|
+
}): Promise<DuplicateDocumentResult | CollectionDocumentCommittedHookFailureResponse> => {
|
|
43
|
+
const { collection: path, id: sourceDocumentId } = input
|
|
44
|
+
const logger = getLogger()
|
|
45
|
+
const config = await ensureCollection(path)
|
|
46
|
+
if (!config) {
|
|
47
|
+
throw ERR_NOT_FOUND({
|
|
48
|
+
message: 'Collection not found',
|
|
49
|
+
details: { collectionPath: path },
|
|
50
|
+
}).log(logger)
|
|
51
|
+
}
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
const serverConfig = getServerConfig()
|
|
54
|
+
const ctx: DocumentLifecycleContext = {
|
|
55
|
+
db: serverConfig.db,
|
|
56
|
+
definition: config.definition,
|
|
57
|
+
collectionId: config.collection.id,
|
|
58
|
+
collectionVersion: config.collection.version,
|
|
59
|
+
collectionPath: path,
|
|
60
|
+
logger,
|
|
61
|
+
defaultLocale: serverConfig.i18n.content.defaultLocale,
|
|
62
|
+
slugifier: serverConfig.slugifier,
|
|
63
|
+
requestContext: await getAdminRequestContext(),
|
|
64
|
+
}
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
try {
|
|
67
|
+
return await duplicateDocument(ctx, { sourceDocumentId })
|
|
68
|
+
} catch (error) {
|
|
69
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
70
|
+
if (committedFailure != null) return committedFailure
|
|
71
|
+
throw error
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
)
|
|
@@ -18,6 +18,7 @@ export * from './list'
|
|
|
18
18
|
export * from './reindex'
|
|
19
19
|
export * from './reorder'
|
|
20
20
|
export * from './restore-version'
|
|
21
|
+
export * from './save-outcome'
|
|
21
22
|
export * from './scheduled-publication'
|
|
22
23
|
export * from './set-list-view-preference'
|
|
23
24
|
export * from './stats'
|
|
@@ -14,6 +14,7 @@ import type { DocumentLifecycleContext } from '@byline/core/services'
|
|
|
14
14
|
import { restoreDocumentVersion as restoreDocumentVersionService } from '@byline/core/services'
|
|
15
15
|
|
|
16
16
|
import { ensureCollection } from '../../integrations/api-utils.js'
|
|
17
|
+
import { toCommittedDocumentHookFailureResponse } from './save-outcome.js'
|
|
17
18
|
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
19
20
|
// Restore a historical document version as the new current version
|
|
@@ -45,15 +46,21 @@ export const restoreDocumentVersion = createServerFn({ method: 'POST' })
|
|
|
45
46
|
requestContext: await getAdminRequestContext(),
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
try {
|
|
50
|
+
const result = await restoreDocumentVersionService(ctx, {
|
|
51
|
+
documentId: id,
|
|
52
|
+
sourceVersionId: versionId,
|
|
53
|
+
})
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
return {
|
|
56
|
+
status: 'ok' as const,
|
|
57
|
+
documentId: result.documentId,
|
|
58
|
+
documentVersionId: result.documentVersionId,
|
|
59
|
+
sourceVersionId: result.sourceVersionId,
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
63
|
+
if (committedFailure != null) return committedFailure
|
|
64
|
+
throw error
|
|
58
65
|
}
|
|
59
66
|
})
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
|
|
9
|
+
import { ERR_DOCUMENT_HOOK_COMMITTED, ErrorCodes } from '@byline/core'
|
|
10
|
+
import { describe, expect, it } from 'vitest'
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
hasCommittedDocumentHookFailure,
|
|
14
|
+
toCommittedDocumentHookFailureResponse,
|
|
15
|
+
} from './save-outcome.js'
|
|
16
|
+
|
|
17
|
+
describe('collection save outcome transport', () => {
|
|
18
|
+
it('returns only allowlisted committed hook metadata', () => {
|
|
19
|
+
const error = ERR_DOCUMENT_HOOK_COMMITTED({
|
|
20
|
+
message: 'internal message',
|
|
21
|
+
cause: new Error('secret hook failure'),
|
|
22
|
+
details: {
|
|
23
|
+
phase: 'afterCreate',
|
|
24
|
+
documentId: 'doc-1',
|
|
25
|
+
documentVersionId: 'version-1',
|
|
26
|
+
sideEffectCode: ErrorCodes.STORAGE,
|
|
27
|
+
privatePath: '/private/cache/key',
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const response = toCommittedDocumentHookFailureResponse(error)
|
|
32
|
+
|
|
33
|
+
expect(response).toEqual({
|
|
34
|
+
status: 'committed-hook-failed',
|
|
35
|
+
documentId: 'doc-1',
|
|
36
|
+
documentVersionId: 'version-1',
|
|
37
|
+
sideEffectFailure: { phase: 'afterCreate', code: 'ERR_STORAGE' },
|
|
38
|
+
})
|
|
39
|
+
expect(JSON.stringify(response)).not.toContain('secret hook failure')
|
|
40
|
+
expect(JSON.stringify(response)).not.toContain('/private/cache/key')
|
|
41
|
+
expect(hasCommittedDocumentHookFailure(response)).toBe(true)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('accepts the singleton afterSave phase exported by core', () => {
|
|
45
|
+
const response = toCommittedDocumentHookFailureResponse(
|
|
46
|
+
ERR_DOCUMENT_HOOK_COMMITTED({
|
|
47
|
+
message: 'singleton committed',
|
|
48
|
+
details: {
|
|
49
|
+
phase: 'afterSave',
|
|
50
|
+
documentId: 'doc-singleton',
|
|
51
|
+
documentVersionId: 'version-singleton',
|
|
52
|
+
sideEffectCode: ErrorCodes.UNHANDLED,
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
expect(response?.sideEffectFailure).toEqual({
|
|
58
|
+
phase: 'afterSave',
|
|
59
|
+
code: ErrorCodes.UNHANDLED,
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('does not classify ordinary or malformed failures as committed', () => {
|
|
64
|
+
expect(toCommittedDocumentHookFailureResponse(new Error('write failed'))).toBeNull()
|
|
65
|
+
expect(
|
|
66
|
+
toCommittedDocumentHookFailureResponse({
|
|
67
|
+
code: ErrorCodes.DOCUMENT_HOOK_COMMITTED,
|
|
68
|
+
message: 'missing ids',
|
|
69
|
+
details: {
|
|
70
|
+
phase: 'afterUpdate',
|
|
71
|
+
sideEffectCode: ErrorCodes.UNHANDLED,
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
).toBeNull()
|
|
75
|
+
expect(hasCommittedDocumentHookFailure({ status: 'ok' })).toBe(false)
|
|
76
|
+
})
|
|
77
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
|
|
9
|
+
import {
|
|
10
|
+
type DocumentHookCommittedPhase,
|
|
11
|
+
type DocumentHookSideEffectCode,
|
|
12
|
+
getDocumentHookCommittedDetails,
|
|
13
|
+
} from '@byline/core/services'
|
|
14
|
+
|
|
15
|
+
export interface CollectionDocumentCommittedHookFailureResponse {
|
|
16
|
+
status: 'committed-hook-failed'
|
|
17
|
+
documentId: string
|
|
18
|
+
documentVersionId: string
|
|
19
|
+
sideEffectFailure: {
|
|
20
|
+
phase: DocumentHookCommittedPhase
|
|
21
|
+
code: DocumentHookSideEffectCode
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Convert only validated core committed-hook errors into safe wire data. Raw
|
|
27
|
+
* hook errors, messages, stack traces, and storage paths remain server-side.
|
|
28
|
+
*/
|
|
29
|
+
export function toCommittedDocumentHookFailureResponse(
|
|
30
|
+
error: unknown
|
|
31
|
+
): CollectionDocumentCommittedHookFailureResponse | null {
|
|
32
|
+
const details = getDocumentHookCommittedDetails(error)
|
|
33
|
+
if (details == null) return null
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
status: 'committed-hook-failed',
|
|
37
|
+
documentId: details.documentId,
|
|
38
|
+
documentVersionId: details.documentVersionId,
|
|
39
|
+
sideEffectFailure: {
|
|
40
|
+
phase: details.phase,
|
|
41
|
+
code: details.sideEffectCode,
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function hasCommittedDocumentHookFailure(
|
|
47
|
+
result: unknown
|
|
48
|
+
): result is CollectionDocumentCommittedHookFailureResponse {
|
|
49
|
+
return (
|
|
50
|
+
typeof result === 'object' &&
|
|
51
|
+
result != null &&
|
|
52
|
+
'status' in result &&
|
|
53
|
+
result.status === 'committed-hook-failed'
|
|
54
|
+
)
|
|
55
|
+
}
|
|
@@ -11,10 +11,14 @@ import { createServerFn } from '@tanstack/react-start'
|
|
|
11
11
|
import { getAdminRequestContext } from '@byline/client/server'
|
|
12
12
|
import { ERR_NOT_FOUND, getLogger, getServerConfig } from '@byline/core'
|
|
13
13
|
import type { DocumentPatch } from '@byline/core/patches'
|
|
14
|
-
import type {
|
|
14
|
+
import type {
|
|
15
|
+
DocumentLifecycleContext,
|
|
16
|
+
UpdateDocumentWithPatchesResult,
|
|
17
|
+
} from '@byline/core/services'
|
|
15
18
|
import { updateDocumentSystemFields, updateDocumentWithPatches } from '@byline/core/services'
|
|
16
19
|
|
|
17
20
|
import { ensureCollection } from '../../integrations/api-utils.js'
|
|
21
|
+
import { toCommittedDocumentHookFailureResponse } from './save-outcome.js'
|
|
18
22
|
|
|
19
23
|
// ---------------------------------------------------------------------------
|
|
20
24
|
// Apply patches (patch-based update — creates a new immutable version)
|
|
@@ -59,14 +63,25 @@ export const updateCollectionDocumentWithPatches = createServerFn({ method: 'POS
|
|
|
59
63
|
requestContext: await getAdminRequestContext(),
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
let result: UpdateDocumentWithPatchesResult
|
|
67
|
+
try {
|
|
68
|
+
result = await updateDocumentWithPatches(ctx, {
|
|
69
|
+
documentId: id,
|
|
70
|
+
patches,
|
|
71
|
+
documentVersionId: versionId,
|
|
72
|
+
locale: locale ?? serverConfig.i18n.content.defaultLocale,
|
|
73
|
+
})
|
|
74
|
+
} catch (error) {
|
|
75
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
76
|
+
if (committedFailure != null) return committedFailure
|
|
77
|
+
throw error
|
|
78
|
+
}
|
|
68
79
|
|
|
69
|
-
return {
|
|
80
|
+
return {
|
|
81
|
+
status: 'ok' as const,
|
|
82
|
+
documentId: result.documentId,
|
|
83
|
+
documentVersionId: result.documentVersionId,
|
|
84
|
+
}
|
|
70
85
|
})
|
|
71
86
|
|
|
72
87
|
// ---------------------------------------------------------------------------
|
|
@@ -10,6 +10,7 @@ import { createServerFn } from '@tanstack/react-start'
|
|
|
10
10
|
|
|
11
11
|
import { getAdminBylineClient } from '@byline/client/server'
|
|
12
12
|
|
|
13
|
+
import { toCommittedDocumentHookFailureResponse } from '../collections/save-outcome.js'
|
|
13
14
|
import { serialise } from '../serialise.js'
|
|
14
15
|
|
|
15
16
|
export const copySingletonToLocale = createServerFn({ method: 'POST' })
|
|
@@ -21,12 +22,18 @@ export const copySingletonToLocale = createServerFn({ method: 'POST' })
|
|
|
21
22
|
overwrite?: boolean
|
|
22
23
|
}) => input
|
|
23
24
|
)
|
|
24
|
-
.handler(async ({ data }) =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
.handler(async ({ data }) => {
|
|
26
|
+
try {
|
|
27
|
+
return serialise(
|
|
28
|
+
await getAdminBylineClient().singleton(data.singleton).copyToLocale({
|
|
29
|
+
sourceLocale: data.sourceLocale,
|
|
30
|
+
targetLocale: data.targetLocale,
|
|
31
|
+
overwrite: data.overwrite,
|
|
32
|
+
})
|
|
33
|
+
)
|
|
34
|
+
} catch (error) {
|
|
35
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
36
|
+
if (committedFailure != null) return committedFailure
|
|
37
|
+
throw error
|
|
38
|
+
}
|
|
39
|
+
})
|
|
@@ -10,10 +10,19 @@ import { createServerFn } from '@tanstack/react-start'
|
|
|
10
10
|
|
|
11
11
|
import { getAdminBylineClient } from '@byline/client/server'
|
|
12
12
|
|
|
13
|
+
import { toCommittedDocumentHookFailureResponse } from '../collections/save-outcome.js'
|
|
13
14
|
import { serialise } from '../serialise.js'
|
|
14
15
|
|
|
15
16
|
export const restoreSingletonVersion = createServerFn({ method: 'POST' })
|
|
16
17
|
.validator((input: { singleton: string; versionId: string }) => input)
|
|
17
|
-
.handler(async ({ data }) =>
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
.handler(async ({ data }) => {
|
|
19
|
+
try {
|
|
20
|
+
return serialise(
|
|
21
|
+
await getAdminBylineClient().singleton(data.singleton).restoreVersion(data.versionId)
|
|
22
|
+
)
|
|
23
|
+
} catch (error) {
|
|
24
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
25
|
+
if (committedFailure != null) return committedFailure
|
|
26
|
+
throw error
|
|
27
|
+
}
|
|
28
|
+
})
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { ERR_DOCUMENT_HOOK_COMMITTED, ErrorCodes } from '@byline/core'
|
|
9
10
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
10
11
|
|
|
11
12
|
const mocks = vi.hoisted(() => ({
|
|
@@ -233,6 +234,39 @@ describe('singleton server functions', () => {
|
|
|
233
234
|
)
|
|
234
235
|
})
|
|
235
236
|
|
|
237
|
+
it.each([
|
|
238
|
+
['update', updateSingleton, { singleton: 'site-settings', data: { title: 'Changed' } }],
|
|
239
|
+
[
|
|
240
|
+
'copyToLocale',
|
|
241
|
+
copySingletonToLocale,
|
|
242
|
+
{ singleton: 'site-settings', sourceLocale: 'en', targetLocale: 'fr' },
|
|
243
|
+
],
|
|
244
|
+
[
|
|
245
|
+
'restoreVersion',
|
|
246
|
+
restoreSingletonVersion,
|
|
247
|
+
{ singleton: 'site-settings', versionId: 'version-1' },
|
|
248
|
+
],
|
|
249
|
+
] as const)('returns a committed hook outcome for %s', async (method, serverFunction, input) => {
|
|
250
|
+
handle[method].mockRejectedValueOnce(
|
|
251
|
+
ERR_DOCUMENT_HOOK_COMMITTED({
|
|
252
|
+
message: 'write committed',
|
|
253
|
+
details: {
|
|
254
|
+
phase: 'afterSave',
|
|
255
|
+
documentId: 'doc-settings',
|
|
256
|
+
documentVersionId: 'version-next',
|
|
257
|
+
sideEffectCode: ErrorCodes.UNHANDLED,
|
|
258
|
+
},
|
|
259
|
+
})
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
await expect(invoke(serverFunction, input)).resolves.toEqual({
|
|
263
|
+
status: 'committed-hook-failed',
|
|
264
|
+
documentId: 'doc-settings',
|
|
265
|
+
documentVersionId: 'version-next',
|
|
266
|
+
sideEffectFailure: { phase: 'afterSave', code: ErrorCodes.UNHANDLED },
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
|
|
236
270
|
it('preserves authentication failures from the handle', async () => {
|
|
237
271
|
const error = { code: 'ERR_UNAUTHENTICATED', message: 'sign in required' }
|
|
238
272
|
handle.get.mockRejectedValueOnce(error)
|
|
@@ -10,6 +10,7 @@ import { createServerFn } from '@tanstack/react-start'
|
|
|
10
10
|
|
|
11
11
|
import { getAdminBylineClient } from '@byline/client/server'
|
|
12
12
|
|
|
13
|
+
import { toCommittedDocumentHookFailureResponse } from '../collections/save-outcome.js'
|
|
13
14
|
import { serialise } from '../serialise.js'
|
|
14
15
|
|
|
15
16
|
export interface UpdateSingletonInput {
|
|
@@ -23,9 +24,15 @@ export const updateSingleton = createServerFn({ method: 'POST' })
|
|
|
23
24
|
.validator((input: UpdateSingletonInput) => input)
|
|
24
25
|
.handler(async ({ data }) => {
|
|
25
26
|
const { singleton, data: fields, locale, expectedVersionId } = data
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
try {
|
|
28
|
+
const result = await getAdminBylineClient().singleton(singleton).update(fields, {
|
|
29
|
+
locale,
|
|
30
|
+
expectedVersionId,
|
|
31
|
+
})
|
|
32
|
+
return serialise(result)
|
|
33
|
+
} catch (error) {
|
|
34
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
35
|
+
if (committedFailure != null) return committedFailure
|
|
36
|
+
throw error
|
|
37
|
+
}
|
|
31
38
|
})
|