@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
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import { dirname, join } from 'node:path'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ERR_CONFLICT,
|
|
6
|
+
ERR_DOCUMENT_HOOK_COMMITTED,
|
|
7
|
+
ERR_DOCUMENT_STALE,
|
|
8
|
+
ERR_LOCK_CONFLICT,
|
|
9
|
+
ERR_NOT_FOUND,
|
|
10
|
+
ERR_TREE_HOOK_COMMITTED,
|
|
11
|
+
ERR_VALIDATION,
|
|
12
|
+
getDocumentRevisionValidationDetails,
|
|
13
|
+
getDocumentStaleDetails,
|
|
14
|
+
getLockConflictDetails,
|
|
15
|
+
} from '@byline/core'
|
|
16
|
+
import { getDocumentHookCommittedDetails } from '@byline/core/services'
|
|
17
|
+
import { beforeAll, describe, expect, it, vi } from 'vitest'
|
|
18
|
+
|
|
19
|
+
import { documentMutationError, withDocumentMutationErrors } from './document-mutation-errors.js'
|
|
20
|
+
|
|
21
|
+
// Exercise the installed Start HTTP handler and its real Seroval protocol. Only
|
|
22
|
+
// manifest lookup and request-local response state are supplied by this harness;
|
|
23
|
+
// the production application's compiled actions are covered by browser tests.
|
|
24
|
+
const require = createRequire(import.meta.url)
|
|
25
|
+
const startRequire = createRequire(require.resolve('@tanstack/react-start/package.json'))
|
|
26
|
+
const serverPackage = startRequire.resolve('@tanstack/start-server-core/package.json')
|
|
27
|
+
const esm = join(dirname(serverPackage), 'src')
|
|
28
|
+
const serverRequire = createRequire(serverPackage)
|
|
29
|
+
let handler: (input: { request: Request; context: object; serverFnId: string }) => Promise<Response>
|
|
30
|
+
let seroval: {
|
|
31
|
+
toJSONAsync(value: unknown): Promise<unknown>
|
|
32
|
+
fromCrossJSON(value: unknown, options: object): unknown
|
|
33
|
+
}
|
|
34
|
+
let runWithStartContext: (context: object, fn: () => Promise<Response>) => Promise<Response>
|
|
35
|
+
let action: (input: { data: unknown }) => Promise<unknown>
|
|
36
|
+
|
|
37
|
+
beforeAll(async () => {
|
|
38
|
+
vi.doMock(join(esm, 'getServerFnById.ts'), () => ({ getServerFnById: async () => action }))
|
|
39
|
+
vi.doMock(join(esm, 'request-response.ts'), () => ({
|
|
40
|
+
getResponse: () => ({ status: 200, headers: new Headers() }),
|
|
41
|
+
}))
|
|
42
|
+
handler = (await import(join(esm, 'server-functions-handler.ts'))).handleServerAction
|
|
43
|
+
seroval = await import(serverRequire.resolve('seroval'))
|
|
44
|
+
runWithStartContext = (
|
|
45
|
+
await import(
|
|
46
|
+
join(
|
|
47
|
+
dirname(serverRequire.resolve('@tanstack/start-storage-context/package.json')),
|
|
48
|
+
'dist/esm/index.js'
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
).runWithStartContext
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
async function roundTrip(data: unknown, operation: (data: unknown) => Promise<unknown>) {
|
|
55
|
+
const guarded = withDocumentMutationErrors(operation)
|
|
56
|
+
action = async ({ data }) => {
|
|
57
|
+
try {
|
|
58
|
+
return { result: await guarded(data) }
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return { error }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const request = new Request('http://localhost/_serverFn/document-mutation', {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: { 'content-type': 'application/json', 'x-tsr-serverFn': 'true' },
|
|
66
|
+
body: JSON.stringify(await seroval.toJSONAsync({ data })),
|
|
67
|
+
})
|
|
68
|
+
const response = await runWithStartContext(
|
|
69
|
+
{
|
|
70
|
+
request,
|
|
71
|
+
startOptions: {},
|
|
72
|
+
contextAfterGlobalMiddlewares: {},
|
|
73
|
+
executedRequestMiddlewares: new Set(),
|
|
74
|
+
handlerType: 'serverFn',
|
|
75
|
+
getRouter: () => {
|
|
76
|
+
throw new Error('Router not used')
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
() => handler({ request, context: {}, serverFnId: 'document-mutation' })
|
|
80
|
+
)
|
|
81
|
+
const wire = await response.text()
|
|
82
|
+
const decoded = seroval.fromCrossJSON(JSON.parse(wire), {})
|
|
83
|
+
if (typeof decoded !== 'object' || decoded === null) throw new Error('Expected wire envelope')
|
|
84
|
+
return {
|
|
85
|
+
value: { error: Reflect.get(decoded, 'error'), result: Reflect.get(decoded, 'result') },
|
|
86
|
+
wire,
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const staleCases = [
|
|
91
|
+
{ reason: 'revision_mismatch', documentId: 'doc', expectedRevision: 1, currentRevision: 2 },
|
|
92
|
+
{
|
|
93
|
+
reason: 'version_parent_mismatch',
|
|
94
|
+
documentId: 'doc',
|
|
95
|
+
previousVersionId: 'old',
|
|
96
|
+
currentVersionId: 'new',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
reason: 'singleton_slot_changed',
|
|
100
|
+
singletonPath: 'settings',
|
|
101
|
+
expectedState: 'empty',
|
|
102
|
+
currentState: 'document',
|
|
103
|
+
},
|
|
104
|
+
] as const
|
|
105
|
+
|
|
106
|
+
describe('document failures over Start HTTP serialization', () => {
|
|
107
|
+
for (const details of staleCases) {
|
|
108
|
+
it(`retains ${details.reason} and excludes diagnostics`, async () => {
|
|
109
|
+
const { value, wire } = await roundTrip({}, async () => {
|
|
110
|
+
throw ERR_DOCUMENT_STALE({
|
|
111
|
+
message: 'private driver detail',
|
|
112
|
+
details,
|
|
113
|
+
cause: new Error('SELECT secret'),
|
|
114
|
+
logExtra: { private: true },
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
expect(getDocumentStaleDetails(value.error)).toEqual(details)
|
|
118
|
+
expect(wire).not.toMatch(/private driver|SELECT secret|logExtra/)
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
for (const reason of ['missing_document_revision', 'invalid_document_revision'] as const) {
|
|
122
|
+
it(`retains reload-required validation: ${reason}`, async () => {
|
|
123
|
+
const { value } = await roundTrip({}, async () => {
|
|
124
|
+
throw ERR_VALIDATION({ message: 'internal', details: { reason } })
|
|
125
|
+
})
|
|
126
|
+
expect(getDocumentRevisionValidationDetails(value.error)).toEqual({ reason })
|
|
127
|
+
expect(getDocumentStaleDetails(value.error)).toBeNull()
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
it('retains a confirmed rollback without relabelling it stale', async () => {
|
|
131
|
+
const details = { reason: 'lock_conflict', rolledBack: true, retryable: true }
|
|
132
|
+
const { value, wire } = await roundTrip({}, async () => {
|
|
133
|
+
throw ERR_LOCK_CONFLICT({
|
|
134
|
+
message: 'driver message',
|
|
135
|
+
details,
|
|
136
|
+
cause: new Error('SQL private'),
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
expect(getLockConflictDetails(value.error)).toEqual(details)
|
|
140
|
+
expect(getDocumentStaleDetails(value.error)).toBeNull()
|
|
141
|
+
expect(wire).not.toMatch(/driver message|SQL private/)
|
|
142
|
+
})
|
|
143
|
+
it('keeps committed hook warnings separate and preserves their committed revision', async () => {
|
|
144
|
+
const details = {
|
|
145
|
+
phase: 'afterUpdate',
|
|
146
|
+
documentId: 'doc',
|
|
147
|
+
documentVersionId: 'version',
|
|
148
|
+
revision: 3,
|
|
149
|
+
sideEffectCode: 'ERR_UNHANDLED',
|
|
150
|
+
}
|
|
151
|
+
const { value, wire } = await roundTrip({}, async () => {
|
|
152
|
+
throw ERR_DOCUMENT_HOOK_COMMITTED({
|
|
153
|
+
message: 'private hook failure',
|
|
154
|
+
details,
|
|
155
|
+
cause: new Error('secret hook'),
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
expect(getDocumentHookCommittedDetails(value.error)).toEqual(details)
|
|
159
|
+
expect(getDocumentStaleDetails(value.error)).toBeNull()
|
|
160
|
+
expect(getLockConflictDetails(value.error)).toBeNull()
|
|
161
|
+
expect(wire).not.toMatch(/private hook failure|secret hook/)
|
|
162
|
+
})
|
|
163
|
+
it('round-trips safe integer observations and receipts as numbers', async () => {
|
|
164
|
+
const { value } = await roundTrip(
|
|
165
|
+
{ expectedRevision: Number.MAX_SAFE_INTEGER - 1 },
|
|
166
|
+
async (data) => {
|
|
167
|
+
expect(data).toEqual({ expectedRevision: Number.MAX_SAFE_INTEGER - 1 })
|
|
168
|
+
return { documentId: 'doc', revision: Number.MAX_SAFE_INTEGER }
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
expect(value.result).toEqual({ documentId: 'doc', revision: Number.MAX_SAFE_INTEGER })
|
|
172
|
+
})
|
|
173
|
+
it('does not classify unrelated or malformed conflicts by message', () => {
|
|
174
|
+
const conflict = ERR_CONFLICT({ message: 'This document has changed.' })
|
|
175
|
+
expect(documentMutationError(conflict)).toBe(conflict)
|
|
176
|
+
const malformed = { code: 'ERR_DOCUMENT_STALE', details: { reason: 'revision_mismatch' } }
|
|
177
|
+
expect(documentMutationError(malformed)).toBe(malformed)
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('transports the authorized structural receipt on committed tree hook failure without diagnostics', async () => {
|
|
182
|
+
const receipt = {
|
|
183
|
+
documentId: 'doc',
|
|
184
|
+
revision: 3,
|
|
185
|
+
affectedDocuments: [{ documentId: 'allowed-sibling', revision: 4 }],
|
|
186
|
+
scheduledPublicationsNeedReconfirmation: true,
|
|
187
|
+
}
|
|
188
|
+
const { value, wire } = await roundTrip({}, async () => {
|
|
189
|
+
throw ERR_TREE_HOOK_COMMITTED({
|
|
190
|
+
message: 'private hook diagnostics',
|
|
191
|
+
cause: new Error('SQL secret'),
|
|
192
|
+
details: { ...receipt, change: 'reorder', affectedDocumentIds: ['private'], internal: true },
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
expect(value.error).toEqual({
|
|
196
|
+
code: 'ERR_TREE_HOOK_COMMITTED',
|
|
197
|
+
message: 'The structure was saved, but a follow-up action failed.',
|
|
198
|
+
details: receipt,
|
|
199
|
+
})
|
|
200
|
+
expect(wire).not.toMatch(/private|SQL secret|internal/)
|
|
201
|
+
})
|
|
202
|
+
it('transports unavailable-document errors without provider details', async () => {
|
|
203
|
+
const { value, wire } = await roundTrip({}, async () => {
|
|
204
|
+
throw ERR_NOT_FOUND({ message: 'private unavailable reason', details: { internal: true } })
|
|
205
|
+
})
|
|
206
|
+
expect(value.error).toEqual({
|
|
207
|
+
code: 'ERR_NOT_FOUND',
|
|
208
|
+
message: 'This document is no longer available.',
|
|
209
|
+
})
|
|
210
|
+
expect(wire).not.toMatch(/private|internal/)
|
|
211
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { withEditableRevision } from './editable-revision.js'
|
|
4
|
+
|
|
5
|
+
describe('editable response revision binding', () => {
|
|
6
|
+
const observed = { id: 'document', versionId: 'current', revision: 7 }
|
|
7
|
+
it('restores only the source revision without changing populated targets', () => {
|
|
8
|
+
const related = { id: 'related', versionId: 'published', fields: {} }
|
|
9
|
+
expect(
|
|
10
|
+
withEditableRevision({ id: 'document', versionId: 'current', fields: { related } }, observed)
|
|
11
|
+
).toEqual({ id: 'document', versionId: 'current', revision: 7, fields: { related } })
|
|
12
|
+
expect(related).not.toHaveProperty('revision')
|
|
13
|
+
})
|
|
14
|
+
it.each([
|
|
15
|
+
{ id: 'document', versionId: 'historical' },
|
|
16
|
+
{ id: 'another-document', versionId: 'current' },
|
|
17
|
+
])('rejects a transferred observation: %o', (document) => {
|
|
18
|
+
expect(() => withEditableRevision(document, observed)).toThrow('source observation')
|
|
19
|
+
})
|
|
20
|
+
it('rejects a row removed from the editable result', () => {
|
|
21
|
+
expect(() => withEditableRevision(observed, undefined)).toThrow('source observation')
|
|
22
|
+
})
|
|
23
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ERR_DATABASE, isDocumentRevision } from '@byline/core'
|
|
2
|
+
|
|
3
|
+
/** Restore metadata stripped by schema parsing only onto the exact source version.
|
|
4
|
+
* Never attach by array position: filtering/reordering must not transfer tokens. */
|
|
5
|
+
export function withEditableRevision<T extends object>(
|
|
6
|
+
document: T,
|
|
7
|
+
observation: { id: string; versionId: string; revision: number } | undefined
|
|
8
|
+
): T & { revision: number } {
|
|
9
|
+
if (
|
|
10
|
+
!observation ||
|
|
11
|
+
!isDocumentRevision(observation.revision) ||
|
|
12
|
+
Reflect.get(document, 'id') !== observation.id ||
|
|
13
|
+
Reflect.get(document, 'versionId') !== observation.versionId
|
|
14
|
+
) {
|
|
15
|
+
throw ERR_DATABASE({ message: 'Editable response no longer matches its source observation' })
|
|
16
|
+
}
|
|
17
|
+
return { ...document, revision: observation.revision }
|
|
18
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { getAdminBylineClient } from '@byline/client/server'
|
|
10
|
-
import { getCollectionDefinition } from '@byline/core'
|
|
10
|
+
import { ERR_NOT_FOUND, getCollectionDefinition } from '@byline/core'
|
|
11
11
|
|
|
12
12
|
import { resolveAdminDocumentRead } from './admin-document-presentation.js'
|
|
13
13
|
import { serialise } from './serialise.js'
|
|
@@ -27,6 +27,10 @@ export interface SingletonDocumentReadInput {
|
|
|
27
27
|
*/
|
|
28
28
|
export async function readSingletonDocument(data: SingletonDocumentReadInput) {
|
|
29
29
|
const handle = getAdminBylineClient().singleton(data.singleton)
|
|
30
|
-
const {
|
|
31
|
-
|
|
30
|
+
const {
|
|
31
|
+
options: { status: _status, ...options },
|
|
32
|
+
} = resolveAdminDocumentRead(getCollectionDefinition(data.singleton), data)
|
|
33
|
+
const snapshot = await handle.getForEdit(options)
|
|
34
|
+
if (snapshot == null) throw ERR_NOT_FOUND({ message: 'Singleton document is unavailable' })
|
|
35
|
+
return snapshot.state === 'empty' ? null : serialise(snapshot.document)
|
|
32
36
|
}
|
|
@@ -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
|
|
@@ -13,7 +14,13 @@ import { getAdminBylineClient } from '@byline/client/server'
|
|
|
13
14
|
import { serialise } from '../serialise.js'
|
|
14
15
|
|
|
15
16
|
export const cancelSingletonScheduledPublish = createServerFn({ method: 'POST' })
|
|
16
|
-
.validator((input: { singleton: string }) => input)
|
|
17
|
-
.handler(
|
|
18
|
-
|
|
17
|
+
.validator((input: { expectedRevision: number; singleton: string }) => input)
|
|
18
|
+
.handler(
|
|
19
|
+
withDocumentMutationErrors(async ({ data }) =>
|
|
20
|
+
serialise(
|
|
21
|
+
await getAdminBylineClient()
|
|
22
|
+
.singleton(data.singleton)
|
|
23
|
+
.cancelScheduledPublish({ expectedRevision: data.expectedRevision })
|
|
24
|
+
)
|
|
25
|
+
)
|
|
19
26
|
)
|
|
@@ -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
|
|
@@ -13,7 +14,13 @@ import { getAdminBylineClient } from '@byline/client/server'
|
|
|
13
14
|
import { serialise } from '../serialise.js'
|
|
14
15
|
|
|
15
16
|
export const changeSingletonStatus = createServerFn({ method: 'POST' })
|
|
16
|
-
.validator((input: { singleton: string; status: string }) => input)
|
|
17
|
-
.handler(
|
|
18
|
-
|
|
17
|
+
.validator((input: { expectedRevision: number; singleton: string; status: string }) => input)
|
|
18
|
+
.handler(
|
|
19
|
+
withDocumentMutationErrors(async ({ data }) =>
|
|
20
|
+
serialise(
|
|
21
|
+
await getAdminBylineClient()
|
|
22
|
+
.singleton(data.singleton)
|
|
23
|
+
.changeStatus(data.status, { expectedRevision: data.expectedRevision })
|
|
24
|
+
)
|
|
25
|
+
)
|
|
19
26
|
)
|
|
@@ -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
|
|
@@ -13,11 +14,16 @@ import { getAdminBylineClient } from '@byline/client/server'
|
|
|
13
14
|
import { serialise } from '../serialise.js'
|
|
14
15
|
|
|
15
16
|
export const confirmSingletonScheduledPublish = createServerFn({ method: 'POST' })
|
|
16
|
-
.validator(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
.validator(
|
|
18
|
+
(input: { expectedRevision: number; singleton: string; expectedVersionId: string }) => input
|
|
19
|
+
)
|
|
20
|
+
.handler(
|
|
21
|
+
withDocumentMutationErrors(async ({ data }) =>
|
|
22
|
+
serialise(
|
|
23
|
+
await getAdminBylineClient().singleton(data.singleton).confirmScheduledPublish({
|
|
24
|
+
expectedRevision: data.expectedRevision,
|
|
25
|
+
expectedVersionId: data.expectedVersionId,
|
|
26
|
+
})
|
|
27
|
+
)
|
|
22
28
|
)
|
|
23
29
|
)
|
|
@@ -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
|
|
@@ -16,24 +17,28 @@ import { serialise } from '../serialise.js'
|
|
|
16
17
|
export const copySingletonToLocale = createServerFn({ method: 'POST' })
|
|
17
18
|
.validator(
|
|
18
19
|
(input: {
|
|
20
|
+
expectedRevision: number
|
|
19
21
|
singleton: string
|
|
20
22
|
sourceLocale: string
|
|
21
23
|
targetLocale: string
|
|
22
24
|
overwrite?: boolean
|
|
23
25
|
}) => input
|
|
24
26
|
)
|
|
25
|
-
.handler(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
.handler(
|
|
28
|
+
withDocumentMutationErrors(async ({ data }) => {
|
|
29
|
+
try {
|
|
30
|
+
return serialise(
|
|
31
|
+
await getAdminBylineClient().singleton(data.singleton).copyToLocale({
|
|
32
|
+
expectedRevision: data.expectedRevision,
|
|
33
|
+
sourceLocale: data.sourceLocale,
|
|
34
|
+
targetLocale: data.targetLocale,
|
|
35
|
+
overwrite: data.overwrite,
|
|
36
|
+
})
|
|
37
|
+
)
|
|
38
|
+
} catch (error) {
|
|
39
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
40
|
+
if (committedFailure != null) return committedFailure
|
|
41
|
+
throw error
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
)
|
|
@@ -23,9 +23,9 @@ export interface GetSingletonInput {
|
|
|
23
23
|
* - this shared host layer owns lenient reads, raw missing-locale values,
|
|
24
24
|
* relation-summary population, transport serialization, and preservation of
|
|
25
25
|
* restore warnings and locale metadata;
|
|
26
|
-
* - the route loader composes published-version
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* - the route loader composes the informational published-version badge, feature
|
|
27
|
+
* enablement, capability, and schedule aliases. Schedule state itself comes
|
|
28
|
+
* from the same editable snapshot as the source document.
|
|
29
29
|
*/
|
|
30
30
|
export const getSingleton = createServerFn({ method: 'GET' })
|
|
31
31
|
.validator((input: GetSingletonInput) => input)
|
|
@@ -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
|
|
@@ -14,15 +15,19 @@ import { toCommittedDocumentHookFailureResponse } from '../collections/save-outc
|
|
|
14
15
|
import { serialise } from '../serialise.js'
|
|
15
16
|
|
|
16
17
|
export const restoreSingletonVersion = createServerFn({ method: 'POST' })
|
|
17
|
-
.validator((input: { singleton: string; versionId: string }) => input)
|
|
18
|
-
.handler(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
.validator((input: { expectedRevision: number; singleton: string; versionId: string }) => input)
|
|
19
|
+
.handler(
|
|
20
|
+
withDocumentMutationErrors(async ({ data }) => {
|
|
21
|
+
try {
|
|
22
|
+
return serialise(
|
|
23
|
+
await getAdminBylineClient()
|
|
24
|
+
.singleton(data.singleton)
|
|
25
|
+
.restoreVersion(data.versionId, { expectedRevision: data.expectedRevision })
|
|
26
|
+
)
|
|
27
|
+
} catch (error) {
|
|
28
|
+
const committedFailure = toCommittedDocumentHookFailureResponse(error)
|
|
29
|
+
if (committedFailure != null) return committedFailure
|
|
30
|
+
throw error
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
)
|
|
@@ -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
|
|
@@ -13,12 +14,22 @@ import { getAdminBylineClient } from '@byline/client/server'
|
|
|
13
14
|
import { serialise } from '../serialise.js'
|
|
14
15
|
|
|
15
16
|
export const scheduleSingletonPublish = createServerFn({ method: 'POST' })
|
|
16
|
-
.validator(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
.validator(
|
|
18
|
+
(input: {
|
|
19
|
+
expectedRevision: number
|
|
20
|
+
singleton: string
|
|
21
|
+
publishAt: string
|
|
22
|
+
expectedVersionId: string
|
|
23
|
+
}) => input
|
|
24
|
+
)
|
|
25
|
+
.handler(
|
|
26
|
+
withDocumentMutationErrors(async ({ data }) =>
|
|
27
|
+
serialise(
|
|
28
|
+
await getAdminBylineClient().singleton(data.singleton).schedulePublish({
|
|
29
|
+
publishAt: data.publishAt,
|
|
30
|
+
expectedRevision: data.expectedRevision,
|
|
31
|
+
expectedVersionId: data.expectedVersionId,
|
|
32
|
+
})
|
|
33
|
+
)
|
|
23
34
|
)
|
|
24
35
|
)
|