@crombolo/core 0.0.0-stage → 0.9.1
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/LICENSE +21 -0
- package/NOTICE +69 -0
- package/package.json +28 -4
- package/src/api-paths.ts +22 -0
- package/src/content-types.ts +257 -0
- package/src/documents.ts +124 -0
- package/src/form-submissions.ts +307 -0
- package/src/forms.ts +331 -0
- package/src/index.ts +15 -0
- package/src/notifications.ts +28 -0
- package/src/object-types.ts +122 -0
- package/src/paging.ts +27 -0
- package/src/permissions.ts +190 -0
- package/src/problem-details.ts +57 -0
- package/src/property-values.ts +129 -0
- package/src/return-urls.ts +42 -0
- package/src/sections.ts +89 -0
- package/src/uploads.ts +41 -0
- package/src/users.ts +51 -0
- package/README.md +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kineco
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
crombolo
|
|
2
|
+
Copyright (c) 2026 Kineco
|
|
3
|
+
|
|
4
|
+
This product includes third-party software. crombolo is an independent project.
|
|
5
|
+
It is not affiliated with, endorsed by, or sponsored by Umbraco A/S, which owns
|
|
6
|
+
the Umbraco trademark.
|
|
7
|
+
|
|
8
|
+
There are three places third-party material is redistributed, rather than merely
|
|
9
|
+
depended on:
|
|
10
|
+
|
|
11
|
+
@crombolo/backoffice-dist the built Umbraco backoffice and the browser
|
|
12
|
+
packages bundled into it, produced by
|
|
13
|
+
`bun run vendor:backoffice`
|
|
14
|
+
@crombolo/backoffice-dist three CSS files copied unmodified from the Umbraco
|
|
15
|
+
client source tree
|
|
16
|
+
@crombolo/contracts the Umbraco Management API OpenAPI document,
|
|
17
|
+
copied unmodified
|
|
18
|
+
|
|
19
|
+
No Umbraco trademark is redistributed. The Umbraco wordmark, the "U" roundel
|
|
20
|
+
favicon and the installer illustration are deliberately not carried in this
|
|
21
|
+
repository: MIT grants copyright permission and says nothing about trademarks, so
|
|
22
|
+
crombolo's own marks are served at those paths instead. See `BRANDED_ASSETS` in
|
|
23
|
+
`@crombolo/backoffice-host`.
|
|
24
|
+
|
|
25
|
+
The Umbraco backoffice
|
|
26
|
+
----------------------
|
|
27
|
+
@umbraco-cms/backoffice 18.2.0 — MIT — https://github.com/umbraco/Umbraco-CMS
|
|
28
|
+
@umbraco-ui/uui 2.0.2 — MIT — https://github.com/umbraco/Umbraco.UI
|
|
29
|
+
|
|
30
|
+
Static files from the Umbraco client source tree
|
|
31
|
+
------------------------------------------------
|
|
32
|
+
css/umb-css.css, css/rte-content.css, css/umbraco-blockgridlayout.css
|
|
33
|
+
— copied unmodified from Umbraco-CMS release-18.2.0 — MIT
|
|
34
|
+
— https://github.com/umbraco/Umbraco-CMS
|
|
35
|
+
Shipped in @crombolo/backoffice-dist under `upstream-static/`.
|
|
36
|
+
|
|
37
|
+
`rte-content.css` itself carries Tiptap's default editor styles, copied from
|
|
38
|
+
tiptap v2.11.7 (MIT); the file records this in its own header.
|
|
39
|
+
|
|
40
|
+
The Umbraco Management API contract
|
|
41
|
+
-----------------------------------
|
|
42
|
+
OpenApi.json — copied unmodified from Umbraco-CMS release-18.2.0
|
|
43
|
+
(src/Umbraco.Cms.Api.Management/OpenApi.json) — MIT
|
|
44
|
+
— https://github.com/umbraco/Umbraco-CMS
|
|
45
|
+
Shipped in @crombolo/contracts, which also ships the TypeScript types generated
|
|
46
|
+
from it.
|
|
47
|
+
|
|
48
|
+
Browser runtime dependencies bundled into the backoffice build
|
|
49
|
+
--------------------------------------------------------------
|
|
50
|
+
@heximal/expressions 0.1.5 — BSD-3-Clause
|
|
51
|
+
@microsoft/signalr 10.0.11 — MIT
|
|
52
|
+
@tiptap/* 3.31.3 — MIT
|
|
53
|
+
diff 9.0.0 — BSD-3-Clause
|
|
54
|
+
dompurify 3.4.16 — Apache-2.0, elected from "MPL-2.0 OR Apache-2.0"
|
|
55
|
+
element-internals-polyfill 3.0.2 — MIT
|
|
56
|
+
lit 3.3.3 — BSD-3-Clause
|
|
57
|
+
luxon 3.7.2 — MIT
|
|
58
|
+
marked 18.0.14 — MIT
|
|
59
|
+
monaco-editor 0.57.0 — MIT
|
|
60
|
+
rxjs 7.8.2 — Apache-2.0
|
|
61
|
+
uuid 14.0.2 — MIT
|
|
62
|
+
|
|
63
|
+
dompurify is offered under either licence; Apache-2.0 is elected here, so no
|
|
64
|
+
MPL-2.0 obligation attaches to the copy bundled into the backoffice build. Of the
|
|
65
|
+
Apache-2.0 packages above, none ships a NOTICE file of its own, so Apache-2.0
|
|
66
|
+
section 4(d) adds nothing to this file.
|
|
67
|
+
|
|
68
|
+
Each package's own licence text ships inside its npm distribution and applies
|
|
69
|
+
unchanged to the copies bundled here.
|
package/package.json
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crombolo/core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "Domain model, entity keys and shared contracts for crombolo. No I/O.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Crombolo",
|
|
7
|
+
"homepage": "https://github.com/kineco-au/crombolo-cms#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/kineco-au/crombolo-cms.git",
|
|
11
|
+
"directory": "packages/core"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/kineco-au/crombolo-cms/issues"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"bun": ">=1.3.0"
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": "./src/index.ts"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"src",
|
|
25
|
+
"NOTICE"
|
|
26
|
+
],
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/api-paths.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The paths the vendored client hard-codes.
|
|
3
|
+
*
|
|
4
|
+
* Umbraco's `UmbracoPath` moves the editor, not its API: the Management API is
|
|
5
|
+
* declared by the contract and the client's generated SDK calls it at
|
|
6
|
+
* `/umbraco/management/api/v1/...` however the backoffice is mounted. The two
|
|
7
|
+
* SignalR hubs are built the same way in the client's own source. So these stay
|
|
8
|
+
* put while `backOfficePath` moves the shell, the login page, the static assets
|
|
9
|
+
* and the SPA's own routes.
|
|
10
|
+
*/
|
|
11
|
+
export const MANAGEMENT_API_PREFIX = '/umbraco/management/api/'
|
|
12
|
+
|
|
13
|
+
export const MANAGEMENT_API_PATH = `${MANAGEMENT_API_PREFIX}v1`
|
|
14
|
+
|
|
15
|
+
/** `server-event.context.js`: `${serverURL}/umbraco/serverEventHub`. */
|
|
16
|
+
export const SERVER_EVENT_HUB_PATH = '/umbraco/serverEventHub'
|
|
17
|
+
|
|
18
|
+
/** `preview.context.js`: `${serverUrl}/umbraco/PreviewHub`. */
|
|
19
|
+
export const PREVIEW_HUB_PATH = '/umbraco/PreviewHub'
|
|
20
|
+
|
|
21
|
+
/** Where the editor is mounted unless a site overrides `backOfficePath`. */
|
|
22
|
+
export const DEFAULT_BACKOFFICE_PATH = '/crombolo'
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The content-type aggregate: a content type plus its property groups, property
|
|
3
|
+
* types, compositions, allowed children and templates.
|
|
4
|
+
*
|
|
5
|
+
* Kept as one aggregate because the editor saves it as one document, and because
|
|
6
|
+
* the property set is only meaningful together with the compositions it inherits.
|
|
7
|
+
*/
|
|
8
|
+
export interface PropertyTypeModel {
|
|
9
|
+
key: string
|
|
10
|
+
alias: string
|
|
11
|
+
name: string
|
|
12
|
+
description: string | null
|
|
13
|
+
dataTypeKey: string
|
|
14
|
+
/** The group this property renders in, or null for the generic tab. */
|
|
15
|
+
containerKey: string | null
|
|
16
|
+
sortOrder: number
|
|
17
|
+
variesByCulture: boolean
|
|
18
|
+
variesBySegment: boolean
|
|
19
|
+
mandatory: boolean
|
|
20
|
+
mandatoryMessage: string | null
|
|
21
|
+
regEx: string | null
|
|
22
|
+
regExMessage: string | null
|
|
23
|
+
labelOnTop: boolean
|
|
24
|
+
/** The file's `since`, when written. */
|
|
25
|
+
sinceVersion?: string | null
|
|
26
|
+
/** Set when the element is not yet live on the reading node: the state it goes live in. */
|
|
27
|
+
pending?: { version: string; revision: string } | null
|
|
28
|
+
/** Member types only: shown to the member on their profile. */
|
|
29
|
+
memberCanView?: boolean
|
|
30
|
+
/** Member types only: editable by the member on their profile. */
|
|
31
|
+
memberCanEdit?: boolean
|
|
32
|
+
/** Member types only: hidden from users without sensitive-data access. */
|
|
33
|
+
isSensitive?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface PropertyGroupModel {
|
|
37
|
+
key: string
|
|
38
|
+
name: string | null
|
|
39
|
+
alias: string | null
|
|
40
|
+
/** 'Tab' or 'Group'; the wire format is a string. */
|
|
41
|
+
type: string
|
|
42
|
+
sortOrder: number
|
|
43
|
+
parentKey: string | null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type CompositionType = 'Composition' | 'Inheritance'
|
|
47
|
+
|
|
48
|
+
export interface CompositionModel {
|
|
49
|
+
contentTypeKey: string
|
|
50
|
+
compositionType: CompositionType
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface AllowedChildModel {
|
|
54
|
+
contentTypeKey: string
|
|
55
|
+
sortOrder: number
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface CleanupModel {
|
|
59
|
+
preventCleanup: boolean
|
|
60
|
+
keepAllVersionsNewerThanDays: number | null
|
|
61
|
+
keepLatestVersionPerDayForDays: number | null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ContentTypeAggregate {
|
|
65
|
+
key: string
|
|
66
|
+
alias: string
|
|
67
|
+
name: string
|
|
68
|
+
description: string | null
|
|
69
|
+
icon: string
|
|
70
|
+
allowedAsRoot: boolean
|
|
71
|
+
variesByCulture: boolean
|
|
72
|
+
variesBySegment: boolean
|
|
73
|
+
isElement: boolean
|
|
74
|
+
allowedInLibrary: boolean
|
|
75
|
+
collectionKey: string | null
|
|
76
|
+
cleanup: CleanupModel
|
|
77
|
+
properties: PropertyTypeModel[]
|
|
78
|
+
containers: PropertyGroupModel[]
|
|
79
|
+
compositions: CompositionModel[]
|
|
80
|
+
allowedContentTypes: AllowedChildModel[]
|
|
81
|
+
allowedComponentKeys: string[]
|
|
82
|
+
defaultComponentKey: string | null
|
|
83
|
+
/** Folder placement in the tree, which is not the composition graph. */
|
|
84
|
+
parentKey: string | null
|
|
85
|
+
sinceVersion?: string | null
|
|
86
|
+
pending?: { version: string; revision: string } | null
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Umbraco's system media types (`IsSystemMediaType`): always present, not
|
|
91
|
+
* deletable, alias fixed. Keys from its installer.
|
|
92
|
+
*/
|
|
93
|
+
export const SYSTEM_MEDIA_TYPE_KEYS: Readonly<Record<'Folder' | 'Image' | 'File', string>> = {
|
|
94
|
+
Folder: 'f38bd2d7-65d0-48e6-95dc-87ce06ec2d3d',
|
|
95
|
+
Image: 'cc07b313-0843-4aa8-bbda-871c8da728c8',
|
|
96
|
+
File: '4c52d8ab-54e6-40cd-999c-7a5f24903e4d',
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function isSystemMediaType(key: string): boolean {
|
|
100
|
+
return Object.values(SYSTEM_MEDIA_TYPE_KEYS).includes(key.toLowerCase())
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Umbraco's one built-in member type, "Member", by the key its installer uses:
|
|
105
|
+
* always present, so a fresh install can create a member without a schema file.
|
|
106
|
+
*/
|
|
107
|
+
export const SYSTEM_MEMBER_TYPE_KEY = 'd59be02f-1df9-4228-aa1e-01917d806cda'
|
|
108
|
+
|
|
109
|
+
export function isSystemMemberType(key: string): boolean {
|
|
110
|
+
return key.toLowerCase() === SYSTEM_MEMBER_TYPE_KEY
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Content types this CMS ships: always present, not deletable, alias fixed. A
|
|
115
|
+
* site may add fields to one, but not remove or change the ones it ships with.
|
|
116
|
+
*/
|
|
117
|
+
export const SYSTEM_CONTENT_TYPE_KEYS: Readonly<Record<'SeoFields', string>> = {
|
|
118
|
+
SeoFields: '5e0f1c2a-7b3d-4c8e-9a61-0d2b4f6e8a13',
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function isSystemContentType(kind: 'document' | 'media' | 'member', key: string): boolean {
|
|
122
|
+
if (kind === 'media') return isSystemMediaType(key)
|
|
123
|
+
if (kind === 'member') return isSystemMemberType(key)
|
|
124
|
+
return Object.values(SYSTEM_CONTENT_TYPE_KEYS).includes(key.toLowerCase())
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** A field a system content type ships with, which a site may not remove or change. */
|
|
128
|
+
export interface SystemPropertyModel {
|
|
129
|
+
key: string
|
|
130
|
+
alias: string
|
|
131
|
+
name: string
|
|
132
|
+
description: string
|
|
133
|
+
dataTypeKey: string
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The "Search & sharing" composition's own fields. Invariant as shipped: the data
|
|
138
|
+
* layer does not mask a property's variance by the composing type's, so a site
|
|
139
|
+
* that wants per-language values turns variance on in its own file.
|
|
140
|
+
*/
|
|
141
|
+
export const SEO_FIELDS_PROPERTIES: readonly SystemPropertyModel[] = [
|
|
142
|
+
{
|
|
143
|
+
key: '8d1f3a52-6c0e-4b7a-9e24-1a5c7d9f0b31',
|
|
144
|
+
alias: 'seoTitle',
|
|
145
|
+
name: 'Search title',
|
|
146
|
+
description: 'Shown in search results and browser tabs. Leave empty to use the page name.',
|
|
147
|
+
dataTypeKey: '0cc0eba1-9960-42c9-bf9b-60e150b429ae',
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
key: '2b7e9c41-0d5f-4a63-8b18-6e4c2a0f7d52',
|
|
151
|
+
alias: 'seoDescription',
|
|
152
|
+
name: 'Search description',
|
|
153
|
+
description: 'A sentence or two shown under the title in search results and when shared.',
|
|
154
|
+
dataTypeKey: 'c6bac0dd-4ab9-45b1-8e30-e4b619ee5da3',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
key: 'f4a60d83-9e2b-4c15-a7d0-3b8e1f5c9a64',
|
|
158
|
+
alias: 'seoImage',
|
|
159
|
+
name: 'Sharing image',
|
|
160
|
+
description: 'Shown when the page is shared. Leave empty to use the site image.',
|
|
161
|
+
dataTypeKey: 'ad9f0cf2-bda2-45d5-9ea1-a63cfc873fd3',
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
key: '6c3b8e14-2f7a-4d90-b5e1-9a0d4c7f2e75',
|
|
165
|
+
alias: 'seoCanonical',
|
|
166
|
+
name: 'Preferred address',
|
|
167
|
+
description:
|
|
168
|
+
'Only when the same content lives at another address search engines should use instead.',
|
|
169
|
+
dataTypeKey: '0cc0eba1-9960-42c9-bf9b-60e150b429ae',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
key: '0e9d5a27-4b1c-4f86-8c32-7d6f0a3e1b86',
|
|
173
|
+
alias: 'seoNoindex',
|
|
174
|
+
name: 'Hide from search engines',
|
|
175
|
+
description: 'Asks search engines to leave this page out of their results.',
|
|
176
|
+
dataTypeKey: '92897bc6-a5f3-4ffe-ae27-f2e7e33dda49',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
key: 'a7f2c6e9-8d4b-4e31-b0a5-2c9e6f1d3a97',
|
|
180
|
+
alias: 'seoSitemapHide',
|
|
181
|
+
name: 'Hide from sitemap',
|
|
182
|
+
description: 'Leaves this page out of the list of pages offered to search engines.',
|
|
183
|
+
dataTypeKey: '92897bc6-a5f3-4ffe-ae27-f2e7e33dda49',
|
|
184
|
+
},
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
/** The fields a system content type ships with, by the type's key. */
|
|
188
|
+
export function systemContentTypeProperties(key: string): readonly SystemPropertyModel[] {
|
|
189
|
+
return key.toLowerCase() === SYSTEM_CONTENT_TYPE_KEYS.SeoFields ? SEO_FIELDS_PROPERTIES : []
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Why a change to a system content type's fields is refused, or undefined when
|
|
194
|
+
* it keeps every shipped field's key, alias and data type. Fields may be added,
|
|
195
|
+
* relabelled, moved, and switched to vary by culture.
|
|
196
|
+
*/
|
|
197
|
+
export function systemFieldViolation(
|
|
198
|
+
key: string,
|
|
199
|
+
properties: ReadonlyArray<{ key: string; alias: string; dataTypeKey: string }>,
|
|
200
|
+
): string | undefined {
|
|
201
|
+
for (const shipped of systemContentTypeProperties(key)) {
|
|
202
|
+
const found = properties.find((p) => p.key.toLowerCase() === shipped.key)
|
|
203
|
+
if (
|
|
204
|
+
!found ||
|
|
205
|
+
found.alias !== shipped.alias ||
|
|
206
|
+
found.dataTypeKey.toLowerCase() !== shipped.dataTypeKey
|
|
207
|
+
)
|
|
208
|
+
return `The '${shipped.name}' field is provided by Crombolo and cannot be removed or changed. You can add your own fields beside it.`
|
|
209
|
+
}
|
|
210
|
+
return undefined
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** How the editor labels a pending element. */
|
|
214
|
+
export function pendingLabel(until: { version: string }): string {
|
|
215
|
+
return `goes live in ${until.version}`
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface DataTypeModel {
|
|
219
|
+
key: string
|
|
220
|
+
/** Schema files reference a data type by this; Umbraco itself has only key and name. */
|
|
221
|
+
alias: string | null
|
|
222
|
+
name: string
|
|
223
|
+
editorAlias: string
|
|
224
|
+
editorUiAlias: string | null
|
|
225
|
+
/** One of ValueStorageType. */
|
|
226
|
+
dbType: string
|
|
227
|
+
values: Array<{ alias: string; value: unknown }>
|
|
228
|
+
parentKey: string | null
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface ComponentModel {
|
|
232
|
+
key: string
|
|
233
|
+
name: string
|
|
234
|
+
alias: string
|
|
235
|
+
content: string | null
|
|
236
|
+
/** Derived from `export const layout = '…'` in the view; the tree hangs children under it. */
|
|
237
|
+
masterKey?: string | null
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** One entry in a backoffice tree. */
|
|
241
|
+
export interface TreeItem {
|
|
242
|
+
key: string
|
|
243
|
+
name: string
|
|
244
|
+
hasChildren: boolean
|
|
245
|
+
parentKey: string | null
|
|
246
|
+
icon: string | null
|
|
247
|
+
isFolder: boolean
|
|
248
|
+
isElement?: boolean
|
|
249
|
+
editorUiAlias?: string | null
|
|
250
|
+
/** The content type of a blueprint in the blueprint tree. */
|
|
251
|
+
contentTypeKey?: string | null
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface Page<T> {
|
|
255
|
+
total: number
|
|
256
|
+
items: T[]
|
|
257
|
+
}
|
package/src/documents.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The document aggregate.
|
|
3
|
+
*
|
|
4
|
+
* Values are a flat list keyed by (alias, culture, segment), matching the wire
|
|
5
|
+
* format, because that is also how they are stored: one `property_data` row per
|
|
6
|
+
* combination.
|
|
7
|
+
*/
|
|
8
|
+
export interface DocumentValue {
|
|
9
|
+
alias: string
|
|
10
|
+
culture: string | null
|
|
11
|
+
segment: string | null
|
|
12
|
+
value: unknown
|
|
13
|
+
/** Present on responses so the editor knows which editor rendered the value. */
|
|
14
|
+
editorAlias?: string
|
|
15
|
+
/** The data type's configuration, on reads; converters that depend on it use it. */
|
|
16
|
+
config?: Record<string, unknown>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Per-culture state. `NotCreated` means the culture has no name yet;
|
|
21
|
+
* `PublishedPendingChanges` means the draft differs from what is published.
|
|
22
|
+
*/
|
|
23
|
+
export type VariantState =
|
|
24
|
+
| 'NotCreated'
|
|
25
|
+
| 'Draft'
|
|
26
|
+
| 'Published'
|
|
27
|
+
| 'PublishedPendingChanges'
|
|
28
|
+
| 'Trashed'
|
|
29
|
+
|
|
30
|
+
export interface DocumentVariant {
|
|
31
|
+
culture: string | null
|
|
32
|
+
segment: string | null
|
|
33
|
+
name: string
|
|
34
|
+
state: VariantState
|
|
35
|
+
createDate: Date
|
|
36
|
+
updateDate: Date
|
|
37
|
+
publishDate: Date | null
|
|
38
|
+
/** When a scheduled publish of this variant will run. */
|
|
39
|
+
scheduledPublishDate?: Date | null
|
|
40
|
+
scheduledUnpublishDate?: Date | null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface DocumentAggregate {
|
|
44
|
+
key: string
|
|
45
|
+
contentTypeKey: string
|
|
46
|
+
contentTypeAlias: string
|
|
47
|
+
contentTypeIcon: string
|
|
48
|
+
/** The list view the type shows its children in, if any. */
|
|
49
|
+
contentTypeCollectionKey?: string | null
|
|
50
|
+
componentKey: string | null
|
|
51
|
+
parentKey: string | null
|
|
52
|
+
sortOrder: number
|
|
53
|
+
isTrashed: boolean
|
|
54
|
+
/** True when any culture is published. */
|
|
55
|
+
published: boolean
|
|
56
|
+
/** True when the draft differs from the published version. */
|
|
57
|
+
edited: boolean
|
|
58
|
+
values: DocumentValue[]
|
|
59
|
+
variants: DocumentVariant[]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** One property that failed validation, in the terms the editor highlights. */
|
|
63
|
+
export interface DocumentValidationError {
|
|
64
|
+
alias: string
|
|
65
|
+
culture: string | null
|
|
66
|
+
segment: string | null
|
|
67
|
+
messages: string[]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** What the content tree shows for a document, beyond the generic tree item. */
|
|
71
|
+
export interface DocumentTreeItem {
|
|
72
|
+
key: string
|
|
73
|
+
name: string
|
|
74
|
+
hasChildren: boolean
|
|
75
|
+
parentKey: string | null
|
|
76
|
+
contentTypeKey: string
|
|
77
|
+
contentTypeCollectionKey?: string | null
|
|
78
|
+
icon: string
|
|
79
|
+
isTrashed: boolean
|
|
80
|
+
createDate: Date
|
|
81
|
+
ancestorKeys: string[]
|
|
82
|
+
variants: Array<{ culture: string | null; name: string; state: VariantState }>
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* A row in the Library tree, which mixes folders and elements: a folder carries
|
|
87
|
+
* the name and nothing else, so `documentType` is reported as null and its single
|
|
88
|
+
* variant as `NotCreated` — the contract's own way of saying there is no content
|
|
89
|
+
* here.
|
|
90
|
+
*/
|
|
91
|
+
export interface ElementTreeItem extends DocumentTreeItem {
|
|
92
|
+
isFolder: boolean
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface DocumentVersionSummary {
|
|
96
|
+
id: string
|
|
97
|
+
documentKey: string
|
|
98
|
+
versionDate: Date
|
|
99
|
+
/** The draft that is currently being edited. */
|
|
100
|
+
isCurrentDraft: boolean
|
|
101
|
+
isCurrentPublished: boolean
|
|
102
|
+
preventCleanup: boolean
|
|
103
|
+
userKey: string | null
|
|
104
|
+
culture: string | null
|
|
105
|
+
/** What made this version: a save, publish, rollback or schema migration. */
|
|
106
|
+
kind?: 'save' | 'publish' | 'rollback' | 'migrate'
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** A value keyed for lookup, so variance handling stays explicit. */
|
|
110
|
+
export function valueKey(alias: string, culture: string | null, segment: string | null): string {
|
|
111
|
+
return `${alias}|${culture ?? ''}|${segment ?? ''}`
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The culture a value belongs to, honouring the effective variance: a value sent
|
|
116
|
+
* with a culture for an invariant property is stored invariant, which is what
|
|
117
|
+
* keeps a type change from orphaning data.
|
|
118
|
+
*/
|
|
119
|
+
export function normaliseValueCulture(
|
|
120
|
+
culture: string | null,
|
|
121
|
+
propertyVariesByCulture: boolean,
|
|
122
|
+
): string | null {
|
|
123
|
+
return propertyVariesByCulture ? culture : null
|
|
124
|
+
}
|