@terminalfour/terminalfour-js 1.0.3 → 1.1.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/dist/cjs/content-cache.d.ts +38 -0
- package/dist/cjs/content-cache.js +42 -0
- package/dist/cjs/element-resolver.d.ts +16 -0
- package/dist/cjs/element-resolver.js +77 -1
- package/dist/cjs/handlebars.js +1 -0
- package/dist/cjs/models/content-item.js +14 -1
- package/dist/cjs/models/media-category-item.js +2 -0
- package/dist/cjs/models/media-item.js +1 -0
- package/dist/cjs/models/section-item.js +1 -0
- package/dist/cjs/resources/content-resource.d.ts +7 -11
- package/dist/cjs/resources/content-resource.js +38 -79
- package/dist/cjs/resources/content-type-resource.d.ts +2 -6
- package/dist/cjs/resources/content-type-resource.js +20 -6
- package/dist/cjs/resources/group-resource.js +1 -0
- package/dist/cjs/resources/list-resource.d.ts +2 -6
- package/dist/cjs/resources/list-resource.js +9 -6
- package/dist/cjs/resources/media-resource.js +9 -2
- package/dist/cjs/resources/media-type-resource.js +1 -0
- package/dist/cjs/resources/navigation-resource.d.ts +17 -0
- package/dist/cjs/resources/navigation-resource.js +14 -2
- package/dist/cjs/resources/page-layout-resource.d.ts +11 -0
- package/dist/cjs/resources/page-layout-resource.js +13 -2
- package/dist/cjs/section-ref.d.ts +3 -1
- package/dist/cjs/section-ref.js +11 -5
- package/dist/cjs/t4-client.d.ts +1 -0
- package/dist/cjs/t4-client.js +6 -1
- package/dist/cjs/utils.d.ts +56 -0
- package/dist/cjs/utils.js +66 -0
- package/dist/esm/content-cache.d.ts +38 -0
- package/dist/esm/content-cache.js +38 -0
- package/dist/esm/element-resolver.d.ts +16 -0
- package/dist/esm/element-resolver.js +77 -1
- package/dist/esm/handlebars.js +2 -1
- package/dist/esm/models/content-item.js +15 -2
- package/dist/esm/models/media-category-item.js +2 -0
- package/dist/esm/models/media-item.js +2 -1
- package/dist/esm/models/section-item.js +2 -1
- package/dist/esm/resources/content-resource.d.ts +7 -11
- package/dist/esm/resources/content-resource.js +39 -80
- package/dist/esm/resources/content-type-resource.d.ts +2 -6
- package/dist/esm/resources/content-type-resource.js +21 -7
- package/dist/esm/resources/group-resource.js +2 -1
- package/dist/esm/resources/list-resource.d.ts +2 -6
- package/dist/esm/resources/list-resource.js +10 -7
- package/dist/esm/resources/media-resource.js +10 -3
- package/dist/esm/resources/media-type-resource.js +2 -1
- package/dist/esm/resources/navigation-resource.d.ts +17 -0
- package/dist/esm/resources/navigation-resource.js +14 -2
- package/dist/esm/resources/page-layout-resource.d.ts +11 -0
- package/dist/esm/resources/page-layout-resource.js +14 -3
- package/dist/esm/section-ref.d.ts +3 -1
- package/dist/esm/section-ref.js +12 -6
- package/dist/esm/t4-client.d.ts +1 -0
- package/dist/esm/t4-client.js +6 -1
- package/dist/esm/utils.d.ts +56 -0
- package/dist/esm/utils.js +59 -0
- package/docs/content-types.md +3 -3
- package/docs/content.md +13 -0
- package/docs/error-handling.md +29 -1
- package/docs/getting-started.md +4 -0
- package/docs/lists.md +2 -2
- package/docs/navigation.md +30 -1
- package/docs/page-layouts.md +26 -0
- package/package.json +1 -1
|
@@ -11,8 +11,8 @@ class List {
|
|
|
11
11
|
this.description = (0, utils_js_2.decodeHtmlEntities)(raw.description ?? '');
|
|
12
12
|
this.isForcedLanguage = raw.isForcedLanguage ?? false;
|
|
13
13
|
this.isDefaultLanguage = raw.isDefaultLanguage ?? false;
|
|
14
|
-
this.primaryGroup =
|
|
15
|
-
this.sharedGroups = (
|
|
14
|
+
this.primaryGroup = (0, utils_js_2.readPrimaryGroup)(raw.primaryGroup);
|
|
15
|
+
this.sharedGroups = (0, utils_js_2.readSharedGroups)(raw.sharedGroups);
|
|
16
16
|
this.items = {};
|
|
17
17
|
for (const item of (raw.items ?? []).sort((a, b) => a.sequence - b.sequence)) {
|
|
18
18
|
const friendlyName = (0, utils_js_2.decodeHtmlEntities)(item.name);
|
|
@@ -56,9 +56,11 @@ class List {
|
|
|
56
56
|
}
|
|
57
57
|
/** Persists current property values to the server via PUT. */
|
|
58
58
|
async save() {
|
|
59
|
+
(0, utils_js_2.assertRequired)(this.name, 'List name');
|
|
59
60
|
if (this.isForcedLanguage && this.isDefaultLanguage) {
|
|
60
61
|
throw new Error('isForcedLanguage and isDefaultLanguage cannot both be true');
|
|
61
62
|
}
|
|
63
|
+
(0, utils_js_2.assertGroupsValid)(this.primaryGroup, this.sharedGroups);
|
|
62
64
|
const items = Object.values(this.items).map((item, i) => ({
|
|
63
65
|
id: String(item._rawId ?? 0),
|
|
64
66
|
name: item.name,
|
|
@@ -73,8 +75,8 @@ class List {
|
|
|
73
75
|
description: this.description,
|
|
74
76
|
isForcedLanguage: this.isForcedLanguage,
|
|
75
77
|
isDefaultLanguage: this.isDefaultLanguage,
|
|
76
|
-
primaryGroup:
|
|
77
|
-
sharedGroups: this.sharedGroups
|
|
78
|
+
primaryGroup: (0, utils_js_2.writePrimaryGroup)(this.primaryGroup),
|
|
79
|
+
sharedGroups: (0, utils_js_2.writeSharedGroups)(this.sharedGroups),
|
|
78
80
|
items,
|
|
79
81
|
};
|
|
80
82
|
await this._httpClient.request({
|
|
@@ -149,6 +151,7 @@ class ListResource {
|
|
|
149
151
|
if (data.isForcedLanguage && data.isDefaultLanguage) {
|
|
150
152
|
throw new Error('isForcedLanguage and isDefaultLanguage cannot both be true');
|
|
151
153
|
}
|
|
154
|
+
(0, utils_js_2.assertGroupsValid)(data.primaryGroup ?? 0, data.sharedGroups);
|
|
152
155
|
const language = (0, utils_js_1.resolveLanguage)(options?.language, this.defaultLanguage);
|
|
153
156
|
const items = (data.items ?? []).map((item, i) => ({
|
|
154
157
|
id: '0',
|
|
@@ -167,8 +170,8 @@ class ListResource {
|
|
|
167
170
|
items,
|
|
168
171
|
isForcedLanguage: data.isForcedLanguage ?? false,
|
|
169
172
|
isDefaultLanguage: data.isDefaultLanguage ?? false,
|
|
170
|
-
sharedGroups: (
|
|
171
|
-
primaryGroup:
|
|
173
|
+
sharedGroups: (0, utils_js_2.writeSharedGroups)(data.sharedGroups),
|
|
174
|
+
primaryGroup: (0, utils_js_2.writePrimaryGroup)(data.primaryGroup ?? 0),
|
|
172
175
|
sortType: 0,
|
|
173
176
|
},
|
|
174
177
|
});
|
|
@@ -53,10 +53,17 @@ class MediaResource {
|
|
|
53
53
|
* Returns the new media item.
|
|
54
54
|
*/
|
|
55
55
|
async create(data) {
|
|
56
|
-
|
|
57
|
-
throw new Error('Media name is required');
|
|
56
|
+
(0, utils_js_1.assertRequired)(data.name, 'Media name');
|
|
58
57
|
if (!data.category)
|
|
59
58
|
throw new Error('Media category is required');
|
|
59
|
+
// A media item is meaningless without its file/binary. Guard the common
|
|
60
|
+
// empty cases: missing, or an empty { file } wrapper.
|
|
61
|
+
const fileValue = data.file && typeof data.file === 'object' && !(data.file instanceof Blob) && 'file' in data.file
|
|
62
|
+
? data.file.file
|
|
63
|
+
: data.file;
|
|
64
|
+
if (fileValue === undefined || fileValue === null || fileValue === '') {
|
|
65
|
+
throw new Error('Media file is required');
|
|
66
|
+
}
|
|
60
67
|
const language = data.language ?? 'smxx';
|
|
61
68
|
const filename = (0, utils_js_1.deriveFilename)(data.file);
|
|
62
69
|
const ext = getExtension(filename);
|
|
@@ -68,6 +68,7 @@ class MediaType {
|
|
|
68
68
|
}
|
|
69
69
|
/** Persists current property values to the server via PUT. */
|
|
70
70
|
async save() {
|
|
71
|
+
(0, utils_js_1.assertRequired)(this.name, 'Media type name');
|
|
71
72
|
// Sync defaultLayout into the layouts array
|
|
72
73
|
if (this.defaultLayout) {
|
|
73
74
|
for (const layout of this.layouts) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HttpClient } from '../http-client.js';
|
|
2
|
+
import { RawPrimaryGroup } from '../utils.js';
|
|
2
3
|
/** SDK-friendly navigation type codes (consistent kebab-case) */
|
|
3
4
|
export type NavigationType = 'a-to-z' | 'breadcrumbs' | 'css-selector' | 'generate-file' | 'keyword-search' | 'language-switcher' | 'link-menu' | 'pagination' | 'previous-next-fulltext' | 'publish-to-one-file' | 'related-content' | 'related-section-branch' | 'return-to-index' | 'section-details' | 'section-iterator' | 'section-meta-info' | 'site-map' | 'top-content' | 'top-stories';
|
|
4
5
|
/** Human-readable type name mapping */
|
|
@@ -23,6 +24,10 @@ interface RawNavigationDetail {
|
|
|
23
24
|
isPreviewModeEnabled: boolean;
|
|
24
25
|
isCachingEnabled: boolean;
|
|
25
26
|
date?: string;
|
|
27
|
+
primaryGroup?: RawPrimaryGroup;
|
|
28
|
+
sharedGroups?: Array<{
|
|
29
|
+
id: number;
|
|
30
|
+
}>;
|
|
26
31
|
properties: Record<string, {
|
|
27
32
|
value?: string;
|
|
28
33
|
attribute: string;
|
|
@@ -42,6 +47,10 @@ export declare class NavigationObject {
|
|
|
42
47
|
enabled: boolean;
|
|
43
48
|
cachingEnabled: boolean;
|
|
44
49
|
previewEnabled: boolean;
|
|
50
|
+
/** Owning group ID. 0 = no primary group (Global). */
|
|
51
|
+
primaryGroup: number;
|
|
52
|
+
/** Group IDs this navigation object is shared with. */
|
|
53
|
+
sharedGroups: number[];
|
|
45
54
|
properties: Record<string, unknown>;
|
|
46
55
|
private readonly _httpClient;
|
|
47
56
|
private _rawData;
|
|
@@ -525,6 +534,10 @@ export interface CreateNavigationData {
|
|
|
525
534
|
description?: string;
|
|
526
535
|
enabled?: boolean;
|
|
527
536
|
previewEnabled?: boolean;
|
|
537
|
+
/** Owning group ID. 0 = no primary group (Global). Defaults to 0. */
|
|
538
|
+
primaryGroup?: number;
|
|
539
|
+
/** Group IDs to share this navigation object with. Defaults to none. */
|
|
540
|
+
sharedGroups?: number[];
|
|
528
541
|
properties?: A2ZProperties | BreadcrumbsProperties | CssSelectorProperties | GenerateFileProperties | LanguageSwitcherProperties | PaginationProperties | PreviousNextProperties | SectionIteratorProperties | RelatedSectionBranchProperties | ReturnToIndexProperties | SectionMetaInfoProperties | TopStoriesProperties | SiteMapProperties | SectionDetailsProperties | RelatedContentProperties | LinkMenuProperties | PublishToOneFileProperties | TopContentProperties | KeywordSearchProperties | Record<string, unknown>;
|
|
529
542
|
}
|
|
530
543
|
/**
|
|
@@ -538,6 +551,10 @@ export interface UpdateNavigationData {
|
|
|
538
551
|
enabled?: boolean;
|
|
539
552
|
previewEnabled?: boolean;
|
|
540
553
|
cachingEnabled?: boolean;
|
|
554
|
+
/** Owning group ID. 0 = no primary group (Global). */
|
|
555
|
+
primaryGroup?: number;
|
|
556
|
+
/** Group IDs to share this navigation object with. */
|
|
557
|
+
sharedGroups?: number[];
|
|
541
558
|
properties?: A2ZProperties | BreadcrumbsProperties | CssSelectorProperties | GenerateFileProperties | LanguageSwitcherProperties | PaginationProperties | PreviousNextProperties | SectionIteratorProperties | RelatedSectionBranchProperties | ReturnToIndexProperties | SectionMetaInfoProperties | TopStoriesProperties | SiteMapProperties | SectionDetailsProperties | RelatedContentProperties | LinkMenuProperties | PublishToOneFileProperties | TopContentProperties | KeywordSearchProperties | Record<string, unknown>;
|
|
542
559
|
}
|
|
543
560
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.NavigationResource = exports.NavigationObject = exports.NAVIGATION_TYPE_NAMES = void 0;
|
|
4
|
+
const utils_js_1 = require("../utils.js");
|
|
4
5
|
/** Maps SDK type codes to API type codes */
|
|
5
6
|
const SDK_TO_API = {
|
|
6
7
|
'a-to-z': 'a2z',
|
|
@@ -988,6 +989,8 @@ class NavigationObject {
|
|
|
988
989
|
this.enabled = raw.isEnabled;
|
|
989
990
|
this.cachingEnabled = raw.isCachingEnabled;
|
|
990
991
|
this.previewEnabled = raw.isPreviewModeEnabled;
|
|
992
|
+
this.primaryGroup = (0, utils_js_1.readPrimaryGroup)(raw.primaryGroup);
|
|
993
|
+
this.sharedGroups = (0, utils_js_1.readSharedGroups)(raw.sharedGroups);
|
|
991
994
|
// Convert properties to camelCase keys with string values
|
|
992
995
|
const originalKeys = [];
|
|
993
996
|
const rawCamelProps = {};
|
|
@@ -1003,6 +1006,8 @@ class NavigationObject {
|
|
|
1003
1006
|
}
|
|
1004
1007
|
/** Persists current property values to the server via PUT. */
|
|
1005
1008
|
async save() {
|
|
1009
|
+
(0, utils_js_1.assertRequired)(this.name, 'Navigation object name');
|
|
1010
|
+
(0, utils_js_1.assertGroupsValid)(this.primaryGroup, this.sharedGroups);
|
|
1006
1011
|
// Apply type-aware write transformation (coerce back to strings, derive hidden fields)
|
|
1007
1012
|
const camelStringProps = transformPropertiesWrite(this.type, this.properties);
|
|
1008
1013
|
// Rebuild properties in API format
|
|
@@ -1022,6 +1027,8 @@ class NavigationObject {
|
|
|
1022
1027
|
isEnabled: this.enabled,
|
|
1023
1028
|
isCachingEnabled: this.cachingEnabled,
|
|
1024
1029
|
isPreviewModeEnabled: this.previewEnabled,
|
|
1030
|
+
primaryGroup: (0, utils_js_1.writePrimaryGroup)(this.primaryGroup),
|
|
1031
|
+
sharedGroups: (0, utils_js_1.writeSharedGroups)(this.sharedGroups),
|
|
1025
1032
|
properties: apiProperties,
|
|
1026
1033
|
};
|
|
1027
1034
|
await this._httpClient.request({
|
|
@@ -1239,6 +1246,7 @@ class NavigationResource {
|
|
|
1239
1246
|
throw new Error('Navigation object type is required');
|
|
1240
1247
|
if (!exports.NAVIGATION_TYPE_NAMES[data.type])
|
|
1241
1248
|
throw new Error(`Unknown navigation type "${data.type}"`);
|
|
1249
|
+
(0, utils_js_1.assertGroupsValid)(data.primaryGroup ?? 0, data.sharedGroups);
|
|
1242
1250
|
const apiType = SDK_TO_API[data.type];
|
|
1243
1251
|
const properties = await this.buildProperties(data.type, (data.properties ?? {}));
|
|
1244
1252
|
const body = {
|
|
@@ -1248,8 +1256,8 @@ class NavigationResource {
|
|
|
1248
1256
|
name: data.name,
|
|
1249
1257
|
description: data.description ?? '',
|
|
1250
1258
|
navigationType: apiType,
|
|
1251
|
-
sharedGroups:
|
|
1252
|
-
primaryGroup:
|
|
1259
|
+
sharedGroups: (0, utils_js_1.writeSharedGroups)(data.sharedGroups),
|
|
1260
|
+
primaryGroup: (0, utils_js_1.writePrimaryGroup)(data.primaryGroup ?? 0),
|
|
1253
1261
|
properties,
|
|
1254
1262
|
};
|
|
1255
1263
|
// CSS Selector quirk: requires "section-name": "on" at the top level
|
|
@@ -1293,6 +1301,10 @@ class NavigationResource {
|
|
|
1293
1301
|
nav.previewEnabled = data.previewEnabled;
|
|
1294
1302
|
if (data.cachingEnabled !== undefined)
|
|
1295
1303
|
nav.cachingEnabled = data.cachingEnabled;
|
|
1304
|
+
if (data.primaryGroup !== undefined)
|
|
1305
|
+
nav.primaryGroup = data.primaryGroup;
|
|
1306
|
+
if (data.sharedGroups !== undefined)
|
|
1307
|
+
nav.sharedGroups = data.sharedGroups;
|
|
1296
1308
|
// Merge properties rather than replace — callers pass only what changes
|
|
1297
1309
|
if (data.properties !== undefined) {
|
|
1298
1310
|
nav.properties = {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HttpClient } from '../http-client.js';
|
|
2
|
+
import { RawPrimaryGroup } from '../utils.js';
|
|
2
3
|
/** Raw page layout detail from GET /pageLayout/{id} */
|
|
3
4
|
interface RawPageLayoutDetail {
|
|
4
5
|
id: number;
|
|
@@ -10,6 +11,10 @@ interface RawPageLayoutDetail {
|
|
|
10
11
|
fileExtension?: string;
|
|
11
12
|
syntaxType?: number;
|
|
12
13
|
layoutProcessor?: number;
|
|
14
|
+
primaryGroup?: RawPrimaryGroup;
|
|
15
|
+
sharedGroups?: Array<{
|
|
16
|
+
id: number;
|
|
17
|
+
}>;
|
|
13
18
|
[key: string]: unknown;
|
|
14
19
|
}
|
|
15
20
|
/** A page layout summary returned from list() */
|
|
@@ -28,6 +33,10 @@ export declare class PageLayout {
|
|
|
28
33
|
fileExtension: string;
|
|
29
34
|
syntax: string;
|
|
30
35
|
processor: string;
|
|
36
|
+
/** Owning group ID. 0 = no primary group (Global). */
|
|
37
|
+
primaryGroup: number;
|
|
38
|
+
/** Group IDs this page layout is shared with. */
|
|
39
|
+
sharedGroups: number[];
|
|
31
40
|
private readonly _httpClient;
|
|
32
41
|
private _rawData;
|
|
33
42
|
private _syntaxMap;
|
|
@@ -63,6 +72,8 @@ export declare class PageLayoutResource {
|
|
|
63
72
|
fileExtension?: string;
|
|
64
73
|
syntax?: string;
|
|
65
74
|
processor?: string;
|
|
75
|
+
primaryGroup?: number;
|
|
76
|
+
sharedGroups?: number[];
|
|
66
77
|
}): Promise<PageLayout>;
|
|
67
78
|
/** Creates a new page layout. */
|
|
68
79
|
create(data: {
|
|
@@ -22,6 +22,8 @@ class PageLayout {
|
|
|
22
22
|
const procName = processorMap.get(raw.layoutProcessor ?? 0) ?? '';
|
|
23
23
|
const procEntry = Object.entries(PAGE_PROCESSOR_MAP).find(([, apiName]) => apiName === procName);
|
|
24
24
|
this.processor = procEntry ? procEntry[0] : procName || `unknown (${raw.layoutProcessor})`;
|
|
25
|
+
this.primaryGroup = (0, utils_js_1.readPrimaryGroup)(raw.primaryGroup);
|
|
26
|
+
this.sharedGroups = (0, utils_js_1.readSharedGroups)(raw.sharedGroups);
|
|
25
27
|
Object.defineProperty(this, '_httpClient', { value: httpClient, enumerable: false });
|
|
26
28
|
Object.defineProperty(this, '_rawData', { value: raw, enumerable: false, writable: true });
|
|
27
29
|
Object.defineProperty(this, '_syntaxMap', { value: syntaxMap, enumerable: false });
|
|
@@ -29,6 +31,8 @@ class PageLayout {
|
|
|
29
31
|
}
|
|
30
32
|
/** Persists current property values to the server via PUT. */
|
|
31
33
|
async save() {
|
|
34
|
+
(0, utils_js_1.assertRequired)(this.name, 'Page layout name');
|
|
35
|
+
(0, utils_js_1.assertGroupsValid)(this.primaryGroup, this.sharedGroups);
|
|
32
36
|
// Resolve syntax name to ID
|
|
33
37
|
let syntaxId = this._rawData.syntaxType;
|
|
34
38
|
if (this._syntaxMap) {
|
|
@@ -53,6 +57,8 @@ class PageLayout {
|
|
|
53
57
|
fileExtension: this.fileExtension,
|
|
54
58
|
syntaxType: String(syntaxId),
|
|
55
59
|
layoutProcessor: String(processorId),
|
|
60
|
+
primaryGroup: (0, utils_js_1.writePrimaryGroup)(this.primaryGroup),
|
|
61
|
+
sharedGroups: (0, utils_js_1.writeSharedGroups)(this.sharedGroups),
|
|
56
62
|
};
|
|
57
63
|
await this._httpClient.request({
|
|
58
64
|
method: 'PUT',
|
|
@@ -136,6 +142,10 @@ class PageLayoutResource {
|
|
|
136
142
|
layout.syntax = data.syntax;
|
|
137
143
|
if (data.processor !== undefined)
|
|
138
144
|
layout.processor = data.processor;
|
|
145
|
+
if (data.primaryGroup !== undefined)
|
|
146
|
+
layout.primaryGroup = data.primaryGroup;
|
|
147
|
+
if (data.sharedGroups !== undefined)
|
|
148
|
+
layout.sharedGroups = data.sharedGroups;
|
|
139
149
|
await layout.save();
|
|
140
150
|
return layout;
|
|
141
151
|
}
|
|
@@ -143,6 +153,7 @@ class PageLayoutResource {
|
|
|
143
153
|
async create(data) {
|
|
144
154
|
if (!data.name?.trim())
|
|
145
155
|
throw new Error('Page layout name is required');
|
|
156
|
+
(0, utils_js_1.assertGroupsValid)(data.primaryGroup ?? 0, data.sharedGroups);
|
|
146
157
|
const [syntaxMap, processorMap] = await Promise.all([
|
|
147
158
|
this.getSyntaxMap(),
|
|
148
159
|
this.getProcessorMap(),
|
|
@@ -196,8 +207,8 @@ class PageLayoutResource {
|
|
|
196
207
|
fileExtension: extensionValue,
|
|
197
208
|
syntaxType: syntaxId,
|
|
198
209
|
layoutProcessor: processorId,
|
|
199
|
-
sharedGroups: (
|
|
200
|
-
primaryGroup:
|
|
210
|
+
sharedGroups: (0, utils_js_1.writeSharedGroups)(data.sharedGroups),
|
|
211
|
+
primaryGroup: (0, utils_js_1.writePrimaryGroup)(data.primaryGroup ?? 0),
|
|
201
212
|
},
|
|
202
213
|
});
|
|
203
214
|
return new PageLayout(raw, this.httpClient, syntaxMap, processorMap);
|
|
@@ -3,6 +3,7 @@ import { LanguageOption, SectionChannel, Owner, AddSectionData } from './types.j
|
|
|
3
3
|
import { ContentResource } from './resources/content-resource.js';
|
|
4
4
|
import { SectionItem } from './models/section-item.js';
|
|
5
5
|
import { MediaCreateFn } from './element-resolver.js';
|
|
6
|
+
import { ContentCache } from './content-cache.js';
|
|
6
7
|
/** A node in the section hierarchy tree */
|
|
7
8
|
export interface SectionTreeNode {
|
|
8
9
|
id: number;
|
|
@@ -23,7 +24,8 @@ export declare class SectionRef {
|
|
|
23
24
|
private readonly sectionId;
|
|
24
25
|
private readonly defaultLanguage;
|
|
25
26
|
private readonly mediaCreateFn;
|
|
26
|
-
|
|
27
|
+
private readonly cache;
|
|
28
|
+
constructor(httpClient: HttpClient, sectionId: number, defaultLanguage: string, mediaCreateFn?: MediaCreateFn | null, cache?: ContentCache);
|
|
27
29
|
/** Returns a mutable section object. Modify properties and call save() to persist. */
|
|
28
30
|
get(options?: LanguageOption): Promise<SectionItem>;
|
|
29
31
|
/** Returns the list of channels associated with this section. */
|
package/dist/cjs/section-ref.js
CHANGED
|
@@ -57,12 +57,13 @@ async function resolveMetaDataTypeId(httpClient, sectionMetaDataType) {
|
|
|
57
57
|
* child section creation, and deletion — all from a single entry point.
|
|
58
58
|
*/
|
|
59
59
|
class SectionRef {
|
|
60
|
-
constructor(httpClient, sectionId, defaultLanguage, mediaCreateFn) {
|
|
60
|
+
constructor(httpClient, sectionId, defaultLanguage, mediaCreateFn, cache) {
|
|
61
61
|
this.httpClient = httpClient;
|
|
62
62
|
this.sectionId = sectionId;
|
|
63
63
|
this.defaultLanguage = defaultLanguage;
|
|
64
64
|
this.mediaCreateFn = mediaCreateFn ?? null;
|
|
65
|
-
this.
|
|
65
|
+
this.cache = cache;
|
|
66
|
+
this.content = new content_resource_js_1.ContentResource(httpClient, sectionId, defaultLanguage, this.mediaCreateFn, this.cache);
|
|
66
67
|
}
|
|
67
68
|
// ── Section metadata ──
|
|
68
69
|
/** Returns a mutable section object. Modify properties and call save() to persist. */
|
|
@@ -77,7 +78,7 @@ class SectionRef {
|
|
|
77
78
|
const meta = raw.metaData;
|
|
78
79
|
if (meta?.enabled && meta.id) {
|
|
79
80
|
try {
|
|
80
|
-
const metaContent = new content_resource_js_1.ContentResource(this.httpClient, this.sectionId, this.defaultLanguage, this.mediaCreateFn);
|
|
81
|
+
const metaContent = new content_resource_js_1.ContentResource(this.httpClient, this.sectionId, this.defaultLanguage, this.mediaCreateFn, this.cache);
|
|
81
82
|
const item = await metaContent.get(meta.id, { language });
|
|
82
83
|
customFields = stripNameField(item.fields ?? null);
|
|
83
84
|
}
|
|
@@ -567,6 +568,7 @@ class SectionRef {
|
|
|
567
568
|
* If `data.customFields` is provided, creates and saves section metadata content.
|
|
568
569
|
*/
|
|
569
570
|
async addSection(data, options) {
|
|
571
|
+
(0, utils_js_1.assertRequired)(data.name, 'Section name');
|
|
570
572
|
const language = (0, utils_js_1.resolveLanguage)(options?.language, this.defaultLanguage);
|
|
571
573
|
// Fetch this section's details to inherit config
|
|
572
574
|
const parentSection = await this.httpClient.request({
|
|
@@ -645,7 +647,7 @@ class SectionRef {
|
|
|
645
647
|
if (metaContentTypeId) {
|
|
646
648
|
// Use a ContentResource on the new section to get full element resolution
|
|
647
649
|
// (list values, SS links, file uploads, etc.)
|
|
648
|
-
const metadataContentResource = new content_resource_js_1.ContentResource(this.httpClient, newSectionId, this.defaultLanguage, this.mediaCreateFn);
|
|
650
|
+
const metadataContentResource = new content_resource_js_1.ContentResource(this.httpClient, newSectionId, this.defaultLanguage, this.mediaCreateFn, this.cache);
|
|
649
651
|
// Create the metadata content item (ContentResource.create handles the full body)
|
|
650
652
|
const metaItem = await metadataContentResource.create({
|
|
651
653
|
type: metaContentTypeId,
|
|
@@ -711,6 +713,10 @@ class SectionRef {
|
|
|
711
713
|
* Returns the updated SectionItem.
|
|
712
714
|
*/
|
|
713
715
|
async update(data, options) {
|
|
716
|
+
// This path PUTs directly (it does not go through SectionItem.save()), so
|
|
717
|
+
// guard the name here. Present-only: omitting name is valid (e.g. delete()
|
|
718
|
+
// calls update({ status: 'inactive' })), but setting it blank is not.
|
|
719
|
+
(0, utils_js_1.assertNotEmptyIfPresent)(data.name, 'Section name');
|
|
714
720
|
const language = (0, utils_js_1.resolveLanguage)(options?.language, this.defaultLanguage);
|
|
715
721
|
const section = await this.httpClient.request({
|
|
716
722
|
method: 'GET',
|
|
@@ -734,7 +740,7 @@ class SectionRef {
|
|
|
734
740
|
if (!metaDataTypeId) {
|
|
735
741
|
throw new Error('Cannot update customFields: no Section Meta Data content type is configured on this T4 instance');
|
|
736
742
|
}
|
|
737
|
-
const contentResource = new content_resource_js_1.ContentResource(this.httpClient, this.sectionId, this.defaultLanguage, this.mediaCreateFn);
|
|
743
|
+
const contentResource = new content_resource_js_1.ContentResource(this.httpClient, this.sectionId, this.defaultLanguage, this.mediaCreateFn, this.cache);
|
|
738
744
|
const metaContentId = meta?.id ?? 0;
|
|
739
745
|
if (metaContentId > 0) {
|
|
740
746
|
await contentResource.update(metaContentId, { fields: data.customFields });
|
package/dist/cjs/t4-client.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export declare class T4Client {
|
|
|
35
35
|
private readonly httpClient;
|
|
36
36
|
private readonly defaultLanguage;
|
|
37
37
|
private readonly mediaCreateFn;
|
|
38
|
+
private readonly contentCache;
|
|
38
39
|
constructor(config: T4ClientConfig);
|
|
39
40
|
/**
|
|
40
41
|
* Returns a section reference scoped to the given section ID.
|
package/dist/cjs/t4-client.js
CHANGED
|
@@ -16,6 +16,7 @@ const page_layout_resource_js_1 = require("./resources/page-layout-resource.js")
|
|
|
16
16
|
const media_type_resource_js_1 = require("./resources/media-type-resource.js");
|
|
17
17
|
const navigation_resource_js_1 = require("./resources/navigation-resource.js");
|
|
18
18
|
const handlebars_js_1 = require("./handlebars.js");
|
|
19
|
+
const content_cache_js_1 = require("./content-cache.js");
|
|
19
20
|
const utils_js_1 = require("./utils.js");
|
|
20
21
|
/**
|
|
21
22
|
* Main entry point for the T4 SDK.
|
|
@@ -57,12 +58,16 @@ class T4Client {
|
|
|
57
58
|
});
|
|
58
59
|
return item.id;
|
|
59
60
|
};
|
|
61
|
+
// Shared content caches (element TypeRegistry, content type templates).
|
|
62
|
+
// Threaded into every SectionRef/ContentResource so hierarchy traversal
|
|
63
|
+
// doesn't re-fetch instance-wide data on each t4.section(id) call.
|
|
64
|
+
this.contentCache = new content_cache_js_1.ContentCache(this.httpClient, this.defaultLanguage, this.mediaCreateFn);
|
|
60
65
|
}
|
|
61
66
|
/**
|
|
62
67
|
* Returns a section reference scoped to the given section ID.
|
|
63
68
|
*/
|
|
64
69
|
section(id) {
|
|
65
|
-
return new section_ref_js_1.SectionRef(this.httpClient, id, this.defaultLanguage, this.mediaCreateFn);
|
|
70
|
+
return new section_ref_js_1.SectionRef(this.httpClient, id, this.defaultLanguage, this.mediaCreateFn, this.contentCache);
|
|
66
71
|
}
|
|
67
72
|
/**
|
|
68
73
|
* Returns a media category reference scoped to the given category ID.
|
package/dist/cjs/utils.d.ts
CHANGED
|
@@ -68,6 +68,62 @@ export declare function flattenGroups(groups: Array<{
|
|
|
68
68
|
name: string;
|
|
69
69
|
groupChildren?: unknown[];
|
|
70
70
|
}>, map?: Map<number, string>): Map<number, string>;
|
|
71
|
+
/**
|
|
72
|
+
* Group/visibility mapping helpers.
|
|
73
|
+
*
|
|
74
|
+
* The T4 API represents an asset's owning group as `primaryGroup` (an object,
|
|
75
|
+
* where the id may be direct or nested under `group`) and its shared groups as
|
|
76
|
+
* `sharedGroups` (an array of `{ id }`). The SDK exposes these as a plain
|
|
77
|
+
* `primaryGroup: number` (0 = none) and `sharedGroups: number[]`. These helpers
|
|
78
|
+
* centralise the read/write mapping so content types, lists, page layouts, and
|
|
79
|
+
* navigation objects all handle groups identically.
|
|
80
|
+
*/
|
|
81
|
+
/** Raw shape of the API `primaryGroup` field. */
|
|
82
|
+
export interface RawPrimaryGroup {
|
|
83
|
+
id: number | null;
|
|
84
|
+
group?: {
|
|
85
|
+
id: number;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** Reads the API `primaryGroup` object into a plain group id (0 = none). */
|
|
89
|
+
export declare function readPrimaryGroup(raw?: RawPrimaryGroup): number;
|
|
90
|
+
/** Reads the API `sharedGroups` array into a plain array of group ids. */
|
|
91
|
+
export declare function readSharedGroups(raw?: Array<{
|
|
92
|
+
id: number;
|
|
93
|
+
}>): number[];
|
|
94
|
+
/** Writes a plain group id back to the API `primaryGroup` shape (0/falsy = none). */
|
|
95
|
+
export declare function writePrimaryGroup(id: number): {
|
|
96
|
+
id: number | null;
|
|
97
|
+
};
|
|
98
|
+
/** Writes a plain array of group ids back to the API `sharedGroups` shape. */
|
|
99
|
+
export declare function writeSharedGroups(ids?: number[]): Array<{
|
|
100
|
+
id: number;
|
|
101
|
+
}>;
|
|
102
|
+
/**
|
|
103
|
+
* Validates that group/visibility values are acceptable to the T4 API before a
|
|
104
|
+
* write. The API returns an opaque 500 when `sharedGroups` contains the same id
|
|
105
|
+
* as `primaryGroup` (a group cannot be both the owner and a shared group), so we
|
|
106
|
+
* catch it here with a clear message. A `primaryGroup` of 0 means "no owning
|
|
107
|
+
* group" and is never a conflict.
|
|
108
|
+
*/
|
|
109
|
+
export declare function assertGroupsValid(primaryGroup: number, sharedGroups?: number[]): void;
|
|
110
|
+
/**
|
|
111
|
+
* Asserts that a required string value is present and not blank.
|
|
112
|
+
*
|
|
113
|
+
* Use on create paths where the field must always be provided. Throws
|
|
114
|
+
* `"<Label> is required"` when the value is missing, empty, or whitespace-only.
|
|
115
|
+
* Prevents opaque API 500s from malformed requests missing a required field.
|
|
116
|
+
*/
|
|
117
|
+
export declare function assertRequired(value: string | null | undefined, label: string): void;
|
|
118
|
+
/**
|
|
119
|
+
* Asserts that a value, *if provided*, is not blank.
|
|
120
|
+
*
|
|
121
|
+
* Use on update paths where a field is optional (omitting it leaves the
|
|
122
|
+
* current value unchanged) but explicitly setting it to an empty or
|
|
123
|
+
* whitespace-only string is invalid. `undefined` passes (means "no change");
|
|
124
|
+
* `''` or whitespace throws `"<Label> cannot be empty"`.
|
|
125
|
+
*/
|
|
126
|
+
export declare function assertNotEmptyIfPresent(value: string | null | undefined, label: string): void;
|
|
71
127
|
/** Accepted file input: a file path, URL, Blob, ReadableStream, or { file, filename } object */
|
|
72
128
|
export type FileInput = string | Blob | NodeJS.ReadableStream | {
|
|
73
129
|
file: string | Blob | NodeJS.ReadableStream;
|
package/dist/cjs/utils.js
CHANGED
|
@@ -42,6 +42,13 @@ exports.parseFileSize = parseFileSize;
|
|
|
42
42
|
exports.parseElementKey = parseElementKey;
|
|
43
43
|
exports.mapStatus = mapStatus;
|
|
44
44
|
exports.flattenGroups = flattenGroups;
|
|
45
|
+
exports.readPrimaryGroup = readPrimaryGroup;
|
|
46
|
+
exports.readSharedGroups = readSharedGroups;
|
|
47
|
+
exports.writePrimaryGroup = writePrimaryGroup;
|
|
48
|
+
exports.writeSharedGroups = writeSharedGroups;
|
|
49
|
+
exports.assertGroupsValid = assertGroupsValid;
|
|
50
|
+
exports.assertRequired = assertRequired;
|
|
51
|
+
exports.assertNotEmptyIfPresent = assertNotEmptyIfPresent;
|
|
45
52
|
exports.resolveFileToBlob = resolveFileToBlob;
|
|
46
53
|
exports.deriveFilename = deriveFilename;
|
|
47
54
|
exports.debugWarn = debugWarn;
|
|
@@ -221,6 +228,65 @@ function flattenGroups(groups, map = new Map()) {
|
|
|
221
228
|
}
|
|
222
229
|
return map;
|
|
223
230
|
}
|
|
231
|
+
/** Reads the API `primaryGroup` object into a plain group id (0 = none). */
|
|
232
|
+
function readPrimaryGroup(raw) {
|
|
233
|
+
return raw?.group?.id ?? raw?.id ?? 0;
|
|
234
|
+
}
|
|
235
|
+
/** Reads the API `sharedGroups` array into a plain array of group ids. */
|
|
236
|
+
function readSharedGroups(raw) {
|
|
237
|
+
return (raw ?? []).map((g) => g.id);
|
|
238
|
+
}
|
|
239
|
+
/** Writes a plain group id back to the API `primaryGroup` shape (0/falsy = none). */
|
|
240
|
+
function writePrimaryGroup(id) {
|
|
241
|
+
return { id: id || null };
|
|
242
|
+
}
|
|
243
|
+
/** Writes a plain array of group ids back to the API `sharedGroups` shape. */
|
|
244
|
+
function writeSharedGroups(ids) {
|
|
245
|
+
return (ids ?? []).map((id) => ({ id }));
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Validates that group/visibility values are acceptable to the T4 API before a
|
|
249
|
+
* write. The API returns an opaque 500 when `sharedGroups` contains the same id
|
|
250
|
+
* as `primaryGroup` (a group cannot be both the owner and a shared group), so we
|
|
251
|
+
* catch it here with a clear message. A `primaryGroup` of 0 means "no owning
|
|
252
|
+
* group" and is never a conflict.
|
|
253
|
+
*/
|
|
254
|
+
function assertGroupsValid(primaryGroup, sharedGroups) {
|
|
255
|
+
if (!primaryGroup)
|
|
256
|
+
return;
|
|
257
|
+
if ((sharedGroups ?? []).includes(primaryGroup)) {
|
|
258
|
+
throw new Error(`sharedGroups cannot contain the primaryGroup id (${primaryGroup}). ` +
|
|
259
|
+
'A group cannot be both the primary (owning) group and a shared group. ' +
|
|
260
|
+
'Remove it from sharedGroups or choose a different primaryGroup.');
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Asserts that a required string value is present and not blank.
|
|
265
|
+
*
|
|
266
|
+
* Use on create paths where the field must always be provided. Throws
|
|
267
|
+
* `"<Label> is required"` when the value is missing, empty, or whitespace-only.
|
|
268
|
+
* Prevents opaque API 500s from malformed requests missing a required field.
|
|
269
|
+
*/
|
|
270
|
+
function assertRequired(value, label) {
|
|
271
|
+
if (!value || !value.trim()) {
|
|
272
|
+
throw new Error(`${label} is required`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Asserts that a value, *if provided*, is not blank.
|
|
277
|
+
*
|
|
278
|
+
* Use on update paths where a field is optional (omitting it leaves the
|
|
279
|
+
* current value unchanged) but explicitly setting it to an empty or
|
|
280
|
+
* whitespace-only string is invalid. `undefined` passes (means "no change");
|
|
281
|
+
* `''` or whitespace throws `"<Label> cannot be empty"`.
|
|
282
|
+
*/
|
|
283
|
+
function assertNotEmptyIfPresent(value, label) {
|
|
284
|
+
if (value === undefined || value === null)
|
|
285
|
+
return;
|
|
286
|
+
if (!value.trim()) {
|
|
287
|
+
throw new Error(`${label} cannot be empty`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
224
290
|
/**
|
|
225
291
|
* Resolves a file input (path, URL, Blob, or ReadableStream) to a Blob.
|
|
226
292
|
*/
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { HttpClient } from './http-client.js';
|
|
2
|
+
import { TypeRegistry } from './type-registry.js';
|
|
3
|
+
import { ElementResolver, MediaCreateFn } from './element-resolver.js';
|
|
4
|
+
import { TtlMap } from './utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Client-level shared caches for content operations.
|
|
7
|
+
*
|
|
8
|
+
* A single instance is created per `T4Client` and threaded into every
|
|
9
|
+
* `ContentResource` (including the short-lived ones created by
|
|
10
|
+
* `SectionRef.get()` / `addSection()`). This ensures that traversing the
|
|
11
|
+
* hierarchy — which creates many `ContentResource` instances via
|
|
12
|
+
* `t4.section(id)` — does not re-fetch instance-wide data on every hop.
|
|
13
|
+
*
|
|
14
|
+
* Two things previously lived on each `ContentResource` and were rebuilt per
|
|
15
|
+
* section, causing repeated API calls (`GET /type/`, `GET /contenttype/{id}`,
|
|
16
|
+
* `GET /content/type/{ct}/{section}`):
|
|
17
|
+
*
|
|
18
|
+
* - The element `TypeRegistry` (`GET /type/`) — instance-wide, so shared here
|
|
19
|
+
* as a single registry and a single `ElementResolver`.
|
|
20
|
+
* - Content type templates — split into:
|
|
21
|
+
* - `contentTypeDefinitions`: the section-independent `GET /contenttype/{id}`
|
|
22
|
+
* response, keyed by content type ID.
|
|
23
|
+
* - `sectionTemplates`: the section-specific `GET /content/type/{ct}/{section}`
|
|
24
|
+
* response (carries channels), keyed by `"{contentTypeId}:{sectionId}"`.
|
|
25
|
+
*
|
|
26
|
+
* All caches respect the global cache epoch, so `T4Client.clearCache()`
|
|
27
|
+
* invalidates them the same way it always has.
|
|
28
|
+
*/
|
|
29
|
+
export declare class ContentCache {
|
|
30
|
+
readonly typeRegistry: TypeRegistry;
|
|
31
|
+
readonly resolver: ElementResolver;
|
|
32
|
+
/** `GET /content/type/{ct}/{section}` responses, keyed by `"{ct}:{section}"`. */
|
|
33
|
+
readonly sectionTemplates: TtlMap<string, unknown>;
|
|
34
|
+
/** `GET /contenttype/{id}` responses, keyed by content type ID. */
|
|
35
|
+
readonly contentTypeDefinitions: TtlMap<number, unknown>;
|
|
36
|
+
constructor(httpClient: HttpClient, defaultLanguage: string, mediaCreateFn?: MediaCreateFn | null);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=content-cache.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { TypeRegistry } from './type-registry.js';
|
|
2
|
+
import { ElementResolver } from './element-resolver.js';
|
|
3
|
+
import { TtlMap } from './utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* Client-level shared caches for content operations.
|
|
6
|
+
*
|
|
7
|
+
* A single instance is created per `T4Client` and threaded into every
|
|
8
|
+
* `ContentResource` (including the short-lived ones created by
|
|
9
|
+
* `SectionRef.get()` / `addSection()`). This ensures that traversing the
|
|
10
|
+
* hierarchy — which creates many `ContentResource` instances via
|
|
11
|
+
* `t4.section(id)` — does not re-fetch instance-wide data on every hop.
|
|
12
|
+
*
|
|
13
|
+
* Two things previously lived on each `ContentResource` and were rebuilt per
|
|
14
|
+
* section, causing repeated API calls (`GET /type/`, `GET /contenttype/{id}`,
|
|
15
|
+
* `GET /content/type/{ct}/{section}`):
|
|
16
|
+
*
|
|
17
|
+
* - The element `TypeRegistry` (`GET /type/`) — instance-wide, so shared here
|
|
18
|
+
* as a single registry and a single `ElementResolver`.
|
|
19
|
+
* - Content type templates — split into:
|
|
20
|
+
* - `contentTypeDefinitions`: the section-independent `GET /contenttype/{id}`
|
|
21
|
+
* response, keyed by content type ID.
|
|
22
|
+
* - `sectionTemplates`: the section-specific `GET /content/type/{ct}/{section}`
|
|
23
|
+
* response (carries channels), keyed by `"{contentTypeId}:{sectionId}"`.
|
|
24
|
+
*
|
|
25
|
+
* All caches respect the global cache epoch, so `T4Client.clearCache()`
|
|
26
|
+
* invalidates them the same way it always has.
|
|
27
|
+
*/
|
|
28
|
+
export class ContentCache {
|
|
29
|
+
constructor(httpClient, defaultLanguage, mediaCreateFn) {
|
|
30
|
+
/** `GET /content/type/{ct}/{section}` responses, keyed by `"{ct}:{section}"`. */
|
|
31
|
+
this.sectionTemplates = new TtlMap();
|
|
32
|
+
/** `GET /contenttype/{id}` responses, keyed by content type ID. */
|
|
33
|
+
this.contentTypeDefinitions = new TtlMap();
|
|
34
|
+
this.typeRegistry = new TypeRegistry(httpClient);
|
|
35
|
+
this.resolver = new ElementResolver(httpClient, defaultLanguage, this.typeRegistry, mediaCreateFn);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=content-cache.js.map
|
|
@@ -83,6 +83,22 @@ export declare class ElementResolver {
|
|
|
83
83
|
* Uses the TypeRegistry to look up type names instead of hardcoded IDs.
|
|
84
84
|
*/
|
|
85
85
|
resolveValue(value: unknown, element: TemplateElement, language: string, allElements?: TemplateElement[], context?: ResolveContext): Promise<unknown>;
|
|
86
|
+
/**
|
|
87
|
+
* Builds the T4 elements map from developer-friendly field names and values.
|
|
88
|
+
* Resolves list values, dates, repeaters, etc. automatically.
|
|
89
|
+
*
|
|
90
|
+
* Shared by both write paths: `ContentResource` (create/update) and
|
|
91
|
+
* `ContentItem.save()`. Keeping a single implementation here prevents the two
|
|
92
|
+
* paths from drifting — the reason repeaters previously only resolved on one
|
|
93
|
+
* of them.
|
|
94
|
+
*/
|
|
95
|
+
buildElements(fields: Record<string, unknown>, elements: TemplateElement[], name: string, language: string, sectionId: number, context?: ResolveContext): Promise<Record<string, unknown>>;
|
|
96
|
+
/**
|
|
97
|
+
* Builds a repeater value array from developer-friendly input.
|
|
98
|
+
* Each repeater item gets its own element key resolution using the
|
|
99
|
+
* repeater's sub-content-type elements from contentTypeElementConfiguration.
|
|
100
|
+
*/
|
|
101
|
+
buildRepeaterValue(items: RepeaterInput[], element: TemplateElement, language: string, sectionId: number): Promise<unknown[]>;
|
|
86
102
|
getList(listId: number, language: string): Promise<ListResponse>;
|
|
87
103
|
private resolveItemName;
|
|
88
104
|
private resolveDate;
|