@otl-core/cms-utils 1.0.0 → 1.0.4
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/index.cjs +1062 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +519 -0
- package/dist/index.d.ts +519 -0
- package/dist/index.js +1007 -0
- package/dist/index.js.map +1 -0
- package/package.json +2 -2
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
import { BlogCategory, ResponsiveValue, BorderConfig, ShadowConfig, ColorReference, LocalizedString, BreakpointWithBase, ResponsiveConfig, OrganizationInfo } from '@otl-core/cms-types';
|
|
2
|
+
import { ClassValue } from 'clsx';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A/B/n Variant Resolver
|
|
6
|
+
*
|
|
7
|
+
* Framework-agnostic logic for resolving multivariate content to a single
|
|
8
|
+
* variant per visitor. Uses a deterministic bucket value (float [0, 1))
|
|
9
|
+
* to ensure sticky variant assignment.
|
|
10
|
+
*
|
|
11
|
+
* This module handles resolving only -- analytics and testing evaluation
|
|
12
|
+
* happen separately downstream.
|
|
13
|
+
*/
|
|
14
|
+
interface VariantConfig {
|
|
15
|
+
id: string;
|
|
16
|
+
weight: number;
|
|
17
|
+
}
|
|
18
|
+
interface MultivariatePageContent {
|
|
19
|
+
page: unknown;
|
|
20
|
+
seo: unknown;
|
|
21
|
+
multivariate: true;
|
|
22
|
+
variants: Array<{
|
|
23
|
+
id: string;
|
|
24
|
+
weight: number;
|
|
25
|
+
sections: unknown[];
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
interface MultivariateFormBlockData {
|
|
29
|
+
definition: unknown;
|
|
30
|
+
multivariate: true;
|
|
31
|
+
variants: Array<{
|
|
32
|
+
id: string;
|
|
33
|
+
weight: number;
|
|
34
|
+
document: unknown;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Given a bucket value [0, 1) and variant weights,
|
|
39
|
+
* deterministically select a variant.
|
|
40
|
+
*
|
|
41
|
+
* The weights don't need to sum to 100 -- they are normalized.
|
|
42
|
+
* A user with a given bucket will always get the same variant
|
|
43
|
+
* for a given weight configuration.
|
|
44
|
+
*/
|
|
45
|
+
declare function selectVariant(bucket: number, variants: VariantConfig[]): string;
|
|
46
|
+
/**
|
|
47
|
+
* Check if page content is multivariate.
|
|
48
|
+
*/
|
|
49
|
+
declare function isMultivariateContent(content: Record<string, unknown>): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve multivariate page content to a single variant's sections.
|
|
52
|
+
* Returns the selected sections array and the variant ID.
|
|
53
|
+
*/
|
|
54
|
+
declare function resolvePageVariant(content: MultivariatePageContent, bucket: number): {
|
|
55
|
+
sections: unknown[];
|
|
56
|
+
variantId: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Resolve multivariate blog post content to a single variant's blocks.
|
|
60
|
+
* Blog post layouts are blog-level (no variants). The post's content blocks
|
|
61
|
+
* are the variant data, returned alongside the shared layout.
|
|
62
|
+
*
|
|
63
|
+
* Mutates the content record in place: replaces "variants" with the selected "blocks".
|
|
64
|
+
*/
|
|
65
|
+
declare function resolveBlogPostVariant(content: Record<string, unknown>, bucket: number): void;
|
|
66
|
+
/**
|
|
67
|
+
* Walk through sections/blocks and resolve any multivariate form block data.
|
|
68
|
+
* This should be called on the server before passing data to client components,
|
|
69
|
+
* so client-side FormBlock never has to deal with variant resolution.
|
|
70
|
+
*/
|
|
71
|
+
declare function resolveFormVariantsInSections(sections: unknown[], bucket: number): unknown[];
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Category utility functions
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Tree node with children
|
|
79
|
+
*/
|
|
80
|
+
interface CategoryTreeNode extends BlogCategory {
|
|
81
|
+
children: CategoryTreeNode[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Builds a hierarchical tree from flat category array
|
|
85
|
+
* @param categories Flat array of categories
|
|
86
|
+
* @returns Array of root-level categories with nested children
|
|
87
|
+
*/
|
|
88
|
+
declare function buildCategoryTree(categories: BlogCategory[]): CategoryTreeNode[];
|
|
89
|
+
/**
|
|
90
|
+
* Flattens a category tree back to a flat array
|
|
91
|
+
* @param tree Hierarchical category tree
|
|
92
|
+
* @returns Flat array of categories
|
|
93
|
+
*/
|
|
94
|
+
declare function flattenCategoryTree(tree: CategoryTreeNode[]): BlogCategory[];
|
|
95
|
+
/**
|
|
96
|
+
* Gets the full path of a category (all ancestors)
|
|
97
|
+
* @param categoryId Category ID to get path for
|
|
98
|
+
* @param categories Flat array of all categories
|
|
99
|
+
* @param locale Locale to use for names (optional)
|
|
100
|
+
* @returns Array of categories from root to target
|
|
101
|
+
*/
|
|
102
|
+
declare function getCategoryPath(categoryId: string, categories: BlogCategory[], locale?: string): BlogCategory[];
|
|
103
|
+
/**
|
|
104
|
+
* Gets the display name for a category in a specific locale
|
|
105
|
+
* @param category Category object
|
|
106
|
+
* @param locale Locale code (e.g., 'en', 'de-DE')
|
|
107
|
+
* @param fallbackLocale Fallback locale if primary not found
|
|
108
|
+
* @returns Display name string
|
|
109
|
+
*/
|
|
110
|
+
declare function getCategoryName(category: BlogCategory, locale: string, fallbackLocale?: string): string;
|
|
111
|
+
/**
|
|
112
|
+
* Gets the slug for a category in a specific locale
|
|
113
|
+
* @param category Category object
|
|
114
|
+
* @param locale Locale code
|
|
115
|
+
* @param fallbackLocale Fallback locale if primary not found
|
|
116
|
+
* @returns Slug string
|
|
117
|
+
*/
|
|
118
|
+
declare function getCategorySlug(category: BlogCategory, locale: string, fallbackLocale?: string): string;
|
|
119
|
+
/**
|
|
120
|
+
* Checks if a category is an ancestor of another category
|
|
121
|
+
* @param ancestorId Potential ancestor category ID
|
|
122
|
+
* @param descendantId Descendant category ID
|
|
123
|
+
* @param categories Flat array of all categories
|
|
124
|
+
* @returns True if ancestorId is an ancestor of descendantId
|
|
125
|
+
*/
|
|
126
|
+
declare function isAncestor(ancestorId: string, descendantId: string, categories: BlogCategory[]): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Gets all descendants of a category (recursive)
|
|
129
|
+
* @param categoryId Parent category ID
|
|
130
|
+
* @param categories Flat array of all categories
|
|
131
|
+
* @returns Array of all descendant categories
|
|
132
|
+
*/
|
|
133
|
+
declare function getDescendants(categoryId: string, categories: BlogCategory[]): BlogCategory[];
|
|
134
|
+
/**
|
|
135
|
+
* Finds a category by slug in a specific locale
|
|
136
|
+
* @param slug Slug to search for
|
|
137
|
+
* @param locale Locale code
|
|
138
|
+
* @param categories Flat array of all categories
|
|
139
|
+
* @returns Category if found, undefined otherwise
|
|
140
|
+
*/
|
|
141
|
+
declare function findCategoryBySlug(slug: string, locale: string, categories: BlogCategory[]): BlogCategory | undefined;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Utility for merging Tailwind CSS class names
|
|
145
|
+
* Combines clsx for conditional classes with tailwind-merge to handle conflicts
|
|
146
|
+
*/
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Merge class names intelligently
|
|
150
|
+
* - Uses clsx for conditional classes
|
|
151
|
+
* - Uses tailwind-merge to deduplicate and resolve conflicts
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* cn("px-2 py-1", condition && "bg-blue-500")
|
|
155
|
+
* cn({ "font-bold": isActive }, "text-lg")
|
|
156
|
+
*/
|
|
157
|
+
declare function cn(...inputs: ClassValue[]): string;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* CSS Resolution Utilities
|
|
161
|
+
* Converts CMS type system values (ColorReference, BorderConfig, ResponsiveValue)
|
|
162
|
+
* into CSS strings for rendering.
|
|
163
|
+
*/
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Minify CSS by removing comments, extra whitespace, and unnecessary characters
|
|
167
|
+
*/
|
|
168
|
+
declare function minifyCSS(css: string): string;
|
|
169
|
+
/**
|
|
170
|
+
* Resolve a ColorReference to a CSS color string
|
|
171
|
+
*/
|
|
172
|
+
declare function resolveColorToCSS(colorRef: ColorReference | undefined, target?: "background" | "foreground"): string | undefined;
|
|
173
|
+
/**
|
|
174
|
+
* Resolve multiple ColorReferences to CSS color strings
|
|
175
|
+
*/
|
|
176
|
+
declare function resolveColorsToCSS<T extends Record<string, ColorReference | undefined>>(colorRefs: T): Partial<Record<keyof T, string>>;
|
|
177
|
+
/**
|
|
178
|
+
* Resolve a BorderConfig to CSS border properties
|
|
179
|
+
*/
|
|
180
|
+
declare function resolveBorderToCSS(borderConfig: BorderConfig | undefined): Record<string, string> | undefined;
|
|
181
|
+
interface ResponsiveSpacingConfig {
|
|
182
|
+
border?: ResponsiveValue<BorderConfig>;
|
|
183
|
+
margin?: ResponsiveValue<string>;
|
|
184
|
+
padding?: ResponsiveValue<string>;
|
|
185
|
+
gap?: ResponsiveValue<string>;
|
|
186
|
+
shadow?: ResponsiveValue<ShadowConfig>;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Generate responsive CSS for spacing (margin, padding, gap, border) with media queries
|
|
190
|
+
*/
|
|
191
|
+
declare function generateResponsiveSpacingCSS(className: string, config: ResponsiveSpacingConfig): string | null;
|
|
192
|
+
/**
|
|
193
|
+
* Check if a ResponsiveSpacingConfig has any margin values set
|
|
194
|
+
*/
|
|
195
|
+
declare function hasAnyMargin(config: ResponsiveSpacingConfig): boolean;
|
|
196
|
+
/**
|
|
197
|
+
* Map animation timing name to CSS timing function
|
|
198
|
+
*/
|
|
199
|
+
declare function getAnimationTimingFunction(timing?: string): string;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get localized string with proper fallback logic
|
|
203
|
+
* 1. Try user's preferred locale (from cookie or browser)
|
|
204
|
+
* 2. Try default locale (from deployment config)
|
|
205
|
+
* 3. Try 'en' as ultimate fallback
|
|
206
|
+
* 4. Return first available value as last resort
|
|
207
|
+
*/
|
|
208
|
+
declare function getLocalizedString(value: string | LocalizedString | null | undefined, options?: {
|
|
209
|
+
preferredLocale?: string | null;
|
|
210
|
+
defaultLocale?: string;
|
|
211
|
+
supportedLocales?: string[];
|
|
212
|
+
}): string;
|
|
213
|
+
/**
|
|
214
|
+
* Get all available locales from a localized string
|
|
215
|
+
*/
|
|
216
|
+
declare function getAvailableLocales(value: string | LocalizedString): string[];
|
|
217
|
+
/**
|
|
218
|
+
* Check if a value is a localized string object
|
|
219
|
+
*/
|
|
220
|
+
declare function isLocalizedString(value: unknown): value is LocalizedString;
|
|
221
|
+
/**
|
|
222
|
+
* Detect locale from various sources (URL, browser, config)
|
|
223
|
+
* Priority: URL param > path segment > browser language > default
|
|
224
|
+
* Note: This function requires browser environment
|
|
225
|
+
*/
|
|
226
|
+
declare function detectLocale(defaultLocale?: string, supportedLocales?: string[]): string;
|
|
227
|
+
/**
|
|
228
|
+
* Format locale for HTML lang attribute (e.g., 'en-US')
|
|
229
|
+
*/
|
|
230
|
+
declare function formatLocaleForHtml(locale: string): string;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Utility functions for working with responsive values
|
|
234
|
+
*/
|
|
235
|
+
|
|
236
|
+
/** A responsive value where every breakpoint -- including base -- is optional. */
|
|
237
|
+
type NormalizedResponsiveValue<T> = {
|
|
238
|
+
base?: T;
|
|
239
|
+
sm?: T;
|
|
240
|
+
md?: T;
|
|
241
|
+
lg?: T;
|
|
242
|
+
xl?: T;
|
|
243
|
+
"2xl"?: T;
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
246
|
+
* Normalize an optional `ResponsiveValue<T>` into a flat object where every
|
|
247
|
+
* breakpoint key is optional. Useful for CSS generation loops.
|
|
248
|
+
*
|
|
249
|
+
* - `undefined` -> `{}`
|
|
250
|
+
* - `"8px"` -> `{ base: "8px" }`
|
|
251
|
+
* - `{ base: "8px", md: "16px" }` -> same
|
|
252
|
+
*/
|
|
253
|
+
declare function normalizeResponsiveValue<T>(value: ResponsiveValue<T> | undefined): NormalizedResponsiveValue<T>;
|
|
254
|
+
/**
|
|
255
|
+
* Type guard to check if a value is a ResponsiveConfig
|
|
256
|
+
* Requires the object to have a "base" property
|
|
257
|
+
*/
|
|
258
|
+
declare function isResponsiveConfig<T>(value: ResponsiveValue<T>): value is ResponsiveConfig<T>;
|
|
259
|
+
/**
|
|
260
|
+
* Get value for a specific breakpoint with fallback to base
|
|
261
|
+
*/
|
|
262
|
+
declare function getBreakpointValue<T>(value: ResponsiveValue<T>, breakpoint: BreakpointWithBase): T;
|
|
263
|
+
/**
|
|
264
|
+
* Get all defined breakpoints in a responsive value
|
|
265
|
+
*/
|
|
266
|
+
declare function getDefinedBreakpoints<T>(value: ResponsiveValue<T>): BreakpointWithBase[];
|
|
267
|
+
/**
|
|
268
|
+
* Convert a single value or responsive value to a ResponsiveConfig
|
|
269
|
+
*/
|
|
270
|
+
declare function toResponsiveConfig<T>(fields: {
|
|
271
|
+
base: T;
|
|
272
|
+
sm?: T;
|
|
273
|
+
md?: T;
|
|
274
|
+
lg?: T;
|
|
275
|
+
xl?: T;
|
|
276
|
+
"2xl"?: T;
|
|
277
|
+
}): ResponsiveConfig<T>;
|
|
278
|
+
/**
|
|
279
|
+
* Convert a ResponsiveConfig to flat breakpoint fields
|
|
280
|
+
*/
|
|
281
|
+
declare function fromResponsiveConfig<T>(value: ResponsiveValue<T>): {
|
|
282
|
+
base: T;
|
|
283
|
+
sm?: T;
|
|
284
|
+
md?: T;
|
|
285
|
+
lg?: T;
|
|
286
|
+
xl?: T;
|
|
287
|
+
"2xl"?: T;
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Schedule Resolver
|
|
292
|
+
* Framework-agnostic content scheduling utilities
|
|
293
|
+
*/
|
|
294
|
+
/**
|
|
295
|
+
* Check if content is currently visible based on schedule metadata.
|
|
296
|
+
* Returns true if the content should be shown to visitors.
|
|
297
|
+
*
|
|
298
|
+
* @param publishAt - ISO 8601 timestamp when content should become visible
|
|
299
|
+
* @param expiresAt - ISO 8601 timestamp when content should stop being visible
|
|
300
|
+
* @param now - Optional current time (defaults to new Date())
|
|
301
|
+
* @returns true if content is visible, false otherwise
|
|
302
|
+
*/
|
|
303
|
+
declare function isContentVisible(publishAt: string | null | undefined, expiresAt: string | null | undefined, now?: Date): boolean;
|
|
304
|
+
/**
|
|
305
|
+
* Filter a list of items based on schedule metadata.
|
|
306
|
+
* Each item must have optional publish_at and expires_at fields.
|
|
307
|
+
*
|
|
308
|
+
* @param items - Array of items with schedule metadata
|
|
309
|
+
* @param now - Optional current time (defaults to new Date())
|
|
310
|
+
* @returns Filtered array containing only currently visible items
|
|
311
|
+
*/
|
|
312
|
+
declare function filterScheduledContent<T extends {
|
|
313
|
+
publish_at?: string;
|
|
314
|
+
expires_at?: string;
|
|
315
|
+
}>(items: T[], now?: Date): T[];
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Style Utilities
|
|
319
|
+
* Helpers to convert CMS config values to CSS strings and Tailwind classes.
|
|
320
|
+
* For CSS resolution utilities (resolveColorToCSS, resolveBorderToCSS, etc.)
|
|
321
|
+
* see css.utils.ts.
|
|
322
|
+
*/
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Convert border config to CSS string
|
|
326
|
+
*/
|
|
327
|
+
declare function borderToStyle(border: ResponsiveValue<BorderConfig> | undefined): string;
|
|
328
|
+
/**
|
|
329
|
+
* Format shadow config to CSS box-shadow value
|
|
330
|
+
*/
|
|
331
|
+
declare function formatShadow(shadow: ShadowConfig | undefined): string;
|
|
332
|
+
/**
|
|
333
|
+
* Convert padding config to Tailwind class
|
|
334
|
+
*/
|
|
335
|
+
declare function paddingToClass(padding?: string): string;
|
|
336
|
+
/**
|
|
337
|
+
* Convert padding top config to Tailwind class
|
|
338
|
+
*/
|
|
339
|
+
declare function paddingTopToClass(paddingTop?: string): string;
|
|
340
|
+
/**
|
|
341
|
+
* Convert padding bottom config to Tailwind class
|
|
342
|
+
*/
|
|
343
|
+
declare function paddingBottomToClass(paddingBottom?: string): string;
|
|
344
|
+
/**
|
|
345
|
+
* Convert spacing config to Tailwind class
|
|
346
|
+
*/
|
|
347
|
+
declare function spacingToClass(spacing?: string): string;
|
|
348
|
+
/**
|
|
349
|
+
* Convert max-width config to Tailwind class
|
|
350
|
+
*/
|
|
351
|
+
declare function widthToClass(width?: string): string;
|
|
352
|
+
/**
|
|
353
|
+
* Convert color config to inline style object
|
|
354
|
+
*/
|
|
355
|
+
declare function colorToStyle(color?: string): {
|
|
356
|
+
backgroundColor?: string;
|
|
357
|
+
};
|
|
358
|
+
/**
|
|
359
|
+
* Convert gap config to Tailwind class
|
|
360
|
+
*/
|
|
361
|
+
declare function gapToClass(gap?: number): string;
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Password Protection Resolution Utilities
|
|
365
|
+
*
|
|
366
|
+
* Framework-agnostic utilities for handling password-protected content.
|
|
367
|
+
* These utilities determine if content is password-protected and filter
|
|
368
|
+
* out protected content from lists (e.g., blog post listings).
|
|
369
|
+
*
|
|
370
|
+
* According to the architecture:
|
|
371
|
+
* - Backend returns ALL content with metadata
|
|
372
|
+
* - Engine/frontend decides visibility at render time
|
|
373
|
+
* - Password protection is checked on the client side
|
|
374
|
+
*/
|
|
375
|
+
/**
|
|
376
|
+
* Check if content is password-protected
|
|
377
|
+
* @param password_protected - Whether the content requires a password
|
|
378
|
+
* @returns True if content is password-protected, false otherwise
|
|
379
|
+
*/
|
|
380
|
+
declare function isPasswordProtected(password_protected?: boolean): boolean;
|
|
381
|
+
/**
|
|
382
|
+
* Filter password-protected content from a list
|
|
383
|
+
* Useful for listings like blog posts where we don't want to show protected items
|
|
384
|
+
* @param items - Array of items with password_protected metadata
|
|
385
|
+
* @returns Filtered array with only non-protected items
|
|
386
|
+
*/
|
|
387
|
+
declare function filterPasswordProtectedContent<T extends {
|
|
388
|
+
password_protected?: boolean;
|
|
389
|
+
}>(items: T[]): T[];
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* JSON-LD structured data generation utilities.
|
|
393
|
+
*
|
|
394
|
+
* Produces a Schema.org @graph for pages, blog posts, and the overall site,
|
|
395
|
+
* using data already available in the CMS (website config, page/post metadata,
|
|
396
|
+
* organization config).
|
|
397
|
+
*/
|
|
398
|
+
|
|
399
|
+
interface JsonLdInput {
|
|
400
|
+
/** Canonical site origin, e.g. "https://example.com" */
|
|
401
|
+
siteUrl: string;
|
|
402
|
+
/** Current page path, e.g. "/about" */
|
|
403
|
+
path: string;
|
|
404
|
+
/** Current locale code */
|
|
405
|
+
locale: string;
|
|
406
|
+
/** Human-readable site name */
|
|
407
|
+
siteName: string;
|
|
408
|
+
/** Optional site description */
|
|
409
|
+
siteDescription?: string;
|
|
410
|
+
/** Organization config from WebsiteConfig */
|
|
411
|
+
organization?: OrganizationInfo;
|
|
412
|
+
/** Page-specific data (mutually exclusive with blogPost) */
|
|
413
|
+
page?: {
|
|
414
|
+
title: string;
|
|
415
|
+
description?: string;
|
|
416
|
+
schemaType?: string;
|
|
417
|
+
datePublished?: string;
|
|
418
|
+
dateModified?: string;
|
|
419
|
+
ogImage?: string;
|
|
420
|
+
};
|
|
421
|
+
/** Blog post data (mutually exclusive with page) */
|
|
422
|
+
blogPost?: {
|
|
423
|
+
title: string;
|
|
424
|
+
excerpt?: string;
|
|
425
|
+
datePublished: string;
|
|
426
|
+
dateModified?: string;
|
|
427
|
+
authorName?: string;
|
|
428
|
+
authorUrl?: string;
|
|
429
|
+
categories?: string[];
|
|
430
|
+
featuredImage?: string;
|
|
431
|
+
};
|
|
432
|
+
/** Explicit breadcrumb items; auto-built from path if omitted */
|
|
433
|
+
breadcrumbs?: {
|
|
434
|
+
name: string;
|
|
435
|
+
path: string;
|
|
436
|
+
}[];
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Derive breadcrumb items from a URL path.
|
|
440
|
+
*
|
|
441
|
+
* "/" -> [{ name: "Home", path: "/" }]
|
|
442
|
+
* "/about" -> [{ name: "Home", path: "/" }, { name: "About", path: "/about" }]
|
|
443
|
+
* "/blog/my-post" -> [Home, Blog, My Post]
|
|
444
|
+
*/
|
|
445
|
+
declare function buildBreadcrumbs(path: string, pageTitle: string): {
|
|
446
|
+
name: string;
|
|
447
|
+
path: string;
|
|
448
|
+
}[];
|
|
449
|
+
/**
|
|
450
|
+
* Generate a JSON-LD `@graph` object for the given page/post.
|
|
451
|
+
*
|
|
452
|
+
* When an override is provided externally (structured_data_override), callers
|
|
453
|
+
* should use the override directly instead of calling this function.
|
|
454
|
+
*/
|
|
455
|
+
declare function generateJsonLd(input: JsonLdInput): Record<string, unknown>;
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* SEO utility functions for OTL CMS.
|
|
459
|
+
*
|
|
460
|
+
* These helpers extract common metadata from deployment configs and are used
|
|
461
|
+
* by both the engine's `generateMetadata` and component-level code.
|
|
462
|
+
*
|
|
463
|
+
* They are environment-agnostic: callers pass an explicit `siteUrlOverride`
|
|
464
|
+
* (e.g. from `process.env.NEXT_PUBLIC_SITE_URL`) instead of reading env vars
|
|
465
|
+
* directly, keeping the package framework-independent.
|
|
466
|
+
*/
|
|
467
|
+
/**
|
|
468
|
+
* Derive the public site origin (no trailing slash).
|
|
469
|
+
*
|
|
470
|
+
* Priority:
|
|
471
|
+
* 1. `siteUrlOverride` (e.g. env var)
|
|
472
|
+
* 2. First custom domain from deployment config
|
|
473
|
+
* 3. Subdomain-based OTL Studio URL
|
|
474
|
+
*/
|
|
475
|
+
declare function deriveSiteUrl(configs: Record<string, unknown>, siteUrlOverride?: string): string;
|
|
476
|
+
/** Extract the localized site name from configs. */
|
|
477
|
+
declare function deriveSiteName(configs: Record<string, unknown>, locale: string): string;
|
|
478
|
+
/** Extract the localized site description from configs. */
|
|
479
|
+
declare function deriveSiteDescription(configs: Record<string, unknown>, locale: string): string | undefined;
|
|
480
|
+
/**
|
|
481
|
+
* Detect a locale prefix from the first URL segment.
|
|
482
|
+
*
|
|
483
|
+
* Returns `[locale, pathWithoutPrefix]`. If the first segment is not a
|
|
484
|
+
* recognised locale, the deployment's default locale and the original path
|
|
485
|
+
* are returned unchanged.
|
|
486
|
+
*/
|
|
487
|
+
declare function detectLocaleFromSegments(segments: string[], fullPath: string, supportedLocales: string[], defaultLocale: string): [string, string];
|
|
488
|
+
/**
|
|
489
|
+
* Convert a short locale code to the `og:locale` format.
|
|
490
|
+
*
|
|
491
|
+
* "en" -> "en_US", "de" -> "de_DE", etc.
|
|
492
|
+
* If the code is already in `xx_XX` form it is returned as-is.
|
|
493
|
+
*/
|
|
494
|
+
declare function localeToOgFormat(locale: string): string;
|
|
495
|
+
/**
|
|
496
|
+
* Build hreflang alternate URLs for multi-locale support.
|
|
497
|
+
*
|
|
498
|
+
* Uses the locale-prefix pattern: the default locale maps to the bare path
|
|
499
|
+
* while other locales are prefixed (`/de/path`). An `x-default` entry
|
|
500
|
+
* points to the un-prefixed URL.
|
|
501
|
+
*
|
|
502
|
+
* Returns `undefined` when there is only a single locale (hreflang would be
|
|
503
|
+
* pointless).
|
|
504
|
+
*/
|
|
505
|
+
declare function buildHreflangAlternates(siteUrl: string, path: string, supportedLocales: string[], defaultLocale: string): Record<string, string> | undefined;
|
|
506
|
+
interface PaginationInfo {
|
|
507
|
+
page: number;
|
|
508
|
+
totalPages: number;
|
|
509
|
+
hasNext: boolean;
|
|
510
|
+
hasPrev: boolean;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Walk through enriched layout sections/blocks and return the first
|
|
514
|
+
* pagination object found (from a `blog-post-list` or `blog-pagination`
|
|
515
|
+
* block's `data.pagination`).
|
|
516
|
+
*/
|
|
517
|
+
declare function extractPaginationFromLayout(layout: unknown[] | undefined): PaginationInfo | null;
|
|
518
|
+
|
|
519
|
+
export { type CategoryTreeNode, type JsonLdInput, type MultivariateFormBlockData, type MultivariatePageContent, type NormalizedResponsiveValue, type PaginationInfo, type ResponsiveSpacingConfig, type VariantConfig, borderToStyle, buildBreadcrumbs, buildCategoryTree, buildHreflangAlternates, cn, colorToStyle, deriveSiteDescription, deriveSiteName, deriveSiteUrl, detectLocale, detectLocaleFromSegments, extractPaginationFromLayout, filterPasswordProtectedContent, filterScheduledContent, findCategoryBySlug, flattenCategoryTree, formatLocaleForHtml, formatShadow, fromResponsiveConfig, gapToClass, generateJsonLd, generateResponsiveSpacingCSS, getAnimationTimingFunction, getAvailableLocales, getBreakpointValue, getCategoryName, getCategoryPath, getCategorySlug, getDefinedBreakpoints, getDescendants, getLocalizedString, hasAnyMargin, isAncestor, isContentVisible, isLocalizedString, isMultivariateContent, isPasswordProtected, isResponsiveConfig, localeToOgFormat, minifyCSS, normalizeResponsiveValue, paddingBottomToClass, paddingToClass, paddingTopToClass, resolveBlogPostVariant, resolveBorderToCSS, resolveColorToCSS, resolveColorsToCSS, resolveFormVariantsInSections, resolvePageVariant, selectVariant, spacingToClass, toResponsiveConfig, widthToClass };
|