lynkow 3.8.74 → 3.8.76

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.d.mts CHANGED
@@ -154,9 +154,21 @@ declare class ContentsService extends BaseService {
154
154
  */
155
155
  getBySlug(slug: string, options?: BaseRequestOptions): Promise<Content>;
156
156
  /**
157
- * Invalidates all cached content responses (lists and single articles).
158
- * Call this after knowing content has been updated to force fresh data
159
- * on the next request.
157
+ * Invalidate every cached response produced by this service (list
158
+ * queries, slug lookups, single-content fetches). Call after an admin
159
+ * mutation or on receipt of a `content.*` webhook so the next public
160
+ * request bypasses the 5-minute SWR cache and hits the origin.
161
+ *
162
+ * @returns void
163
+ * @throws Never throws.
164
+ *
165
+ * @example
166
+ * ```typescript
167
+ * // In a Next.js route handler receiving a Lynkow webhook:
168
+ * if (event.type === 'content.updated') {
169
+ * lynkow.contents.clearCache()
170
+ * }
171
+ * ```
160
172
  */
161
173
  clearCache(): void;
162
174
  }
@@ -251,9 +263,19 @@ declare class CategoriesService extends BaseService {
251
263
  */
252
264
  getBySlug(slug: string, options?: CategoryOptions & BaseRequestOptions): Promise<CategoryDetailResponse>;
253
265
  /**
254
- * Invalidates all cached category responses (lists, tree, and detail views).
255
- * Call this after knowing categories have been updated to force fresh data
256
- * on the next request.
266
+ * Invalidate every cached category response (flat list, hierarchy
267
+ * tree, detail views with paginated contents). Call after an admin
268
+ * mutation or on receipt of a `category.*` webhook so the next public
269
+ * request bypasses the 30-minute SWR cache.
270
+ *
271
+ * @returns void
272
+ * @throws Never throws.
273
+ *
274
+ * @example
275
+ * ```typescript
276
+ * // On category restructuring:
277
+ * lynkow.categories.clearCache()
278
+ * ```
257
279
  */
258
280
  clearCache(): void;
259
281
  }
@@ -287,9 +309,17 @@ declare class TagsService extends BaseService {
287
309
  */
288
310
  list(options?: BaseRequestOptions): Promise<TagsListResponse>;
289
311
  /**
290
- * Invalidates all cached tag responses.
291
- * Call this after knowing tags have been updated to force fresh data
292
- * on the next request.
312
+ * Invalidate every cached tag response (lists, tag-filtered contents).
313
+ * Call after an admin mutation or on receipt of a `tag.*` webhook so
314
+ * the next public request bypasses the SWR cache.
315
+ *
316
+ * @returns void
317
+ * @throws Never throws.
318
+ *
319
+ * @example
320
+ * ```typescript
321
+ * lynkow.tags.clearCache()
322
+ * ```
293
323
  */
294
324
  clearCache(): void;
295
325
  }
@@ -298,7 +328,14 @@ declare class TagsService extends BaseService {
298
328
  * Options for listing pages
299
329
  */
300
330
  interface PagesListOptions extends BaseRequestOptions {
301
- /** Filter pages by tag (e.g., 'legal' for legal documents) */
331
+ /**
332
+ * Restrict results to pages carrying this tag slug (e.g. `'legal'` to
333
+ * list Privacy Policy + Terms of Service). Tags are declared on each
334
+ * page in the admin under SEO / Organization and used mainly for
335
+ * grouping unrelated pages that share a purpose.
336
+ *
337
+ * Omit to return every published page for the site.
338
+ */
302
339
  tag?: string;
303
340
  }
304
341
  /**
@@ -403,9 +440,19 @@ declare class PagesService extends BaseService {
403
440
  */
404
441
  getJsonLd(slug: string, options?: BaseRequestOptions): Promise<Record<string, unknown>>;
405
442
  /**
406
- * Invalidates all cached page responses (lists, slug lookups, path lookups,
407
- * and JSON-LD data). Call this after knowing pages have been updated to
408
- * force fresh data on the next request.
443
+ * Invalidate every cached page response (lists, slug lookups, path
444
+ * lookups, and the JSON-LD endpoint). Call after an admin mutation or
445
+ * on receipt of a `page.*` webhook so the next public request bypasses
446
+ * the SWR cache and re-materializes the resolved data (including
447
+ * `structuredData.graph`).
448
+ *
449
+ * @returns void
450
+ * @throws Never throws.
451
+ *
452
+ * @example
453
+ * ```typescript
454
+ * lynkow.pages.clearCache()
455
+ * ```
409
456
  */
410
457
  clearCache(): void;
411
458
  }
@@ -479,6 +526,9 @@ declare class BlocksService extends BaseService {
479
526
  * @param slug - The block slug
480
527
  * @param options - Request options
481
528
  * @returns The resolved global block
529
+ * @throws {LynkowError} With code `'NOT_FOUND'` when the slug does not
530
+ * match any global block for the site, `'NETWORK_ERROR'` on transport
531
+ * failure. Same surface as {@link getBySlug}.
482
532
  *
483
533
  * @example
484
534
  * ```typescript
@@ -488,9 +538,18 @@ declare class BlocksService extends BaseService {
488
538
  */
489
539
  global(slug: string, options?: BaseRequestOptions): Promise<GlobalBlockResponse>;
490
540
  /**
491
- * Invalidates all cached global block responses (site config and individual blocks).
492
- * Call this after knowing global blocks have been updated to force fresh data
493
- * on the next request.
541
+ * Invalidate every cached global block response (site config payload
542
+ * and per-slug lookups). Call after an admin mutation or on receipt of
543
+ * a `globalBlock.updated` webhook so the next public request hits the
544
+ * origin and re-resolves any referenced variables.
545
+ *
546
+ * @returns void
547
+ * @throws Never throws.
548
+ *
549
+ * @example
550
+ * ```typescript
551
+ * lynkow.globals.clearCache()
552
+ * ```
494
553
  */
495
554
  clearCache(): void;
496
555
  }
@@ -586,8 +645,20 @@ declare class FormsService extends BaseService {
586
645
  */
587
646
  submit(slug: string, data: FormSubmitData, options?: SubmitOptions & BaseRequestOptions): Promise<FormSubmitResponse>;
588
647
  /**
589
- * Invalidates all cached form schema responses. Call this if you know
590
- * a form definition has changed and want to fetch the latest schema.
648
+ * Invalidate every cached form schema response. Call after an admin
649
+ * mutation or on receipt of a `form.*` webhook so the next public
650
+ * request re-fetches the latest field definitions (required fields,
651
+ * validators, spam settings).
652
+ *
653
+ * Does not affect form submissions, which are never cached.
654
+ *
655
+ * @returns void
656
+ * @throws Never throws.
657
+ *
658
+ * @example
659
+ * ```typescript
660
+ * lynkow.forms.clearCache()
661
+ * ```
591
662
  */
592
663
  clearCache(): void;
593
664
  }
@@ -696,6 +767,9 @@ declare class ReviewsService extends BaseService {
696
767
  * // Render email field as required
697
768
  * }
698
769
  * ```
770
+ *
771
+ * @throws {LynkowError} With code `'NETWORK_ERROR'` on transport
772
+ * failure, `'NOT_FOUND'` if the site does not exist.
699
773
  */
700
774
  settings(): Promise<ReviewSettings>;
701
775
  /**
@@ -734,9 +808,19 @@ declare class ReviewsService extends BaseService {
734
808
  */
735
809
  submit(data: ReviewSubmitData, options?: SubmitOptions & BaseRequestOptions): Promise<ReviewSubmitResponse>;
736
810
  /**
737
- * Invalidates all cached review responses (lists, individual reviews, and settings).
738
- * This is called automatically after a successful `submit()`, but can also be
739
- * called manually if needed.
811
+ * Invalidate every cached review response (lists, individual reviews,
812
+ * settings). Automatically invoked after a successful {@link submit};
813
+ * call it manually after an admin moderation action or on receipt of a
814
+ * `review.*` webhook to keep public listings in sync.
815
+ *
816
+ * @returns void
817
+ * @throws Never throws.
818
+ *
819
+ * @example
820
+ * ```typescript
821
+ * // After an admin approved a pending review:
822
+ * lynkow.reviews.clearCache()
823
+ * ```
740
824
  */
741
825
  clearCache(): void;
742
826
  }
@@ -782,9 +866,20 @@ declare class SiteService extends BaseService {
782
866
  */
783
867
  getConfig(): Promise<SiteConfig>;
784
868
  /**
785
- * Invalidates the cached site configuration. Call this if you know the
786
- * site settings have changed (e.g. after a locale is added) and want to
787
- * force a fresh fetch on the next `getConfig()` call.
869
+ * Invalidate the cached site configuration. Call after a settings
870
+ * change on the admin (branding, enabled locales, default author,
871
+ * search config) or on receipt of a `site.updated` webhook so the next
872
+ * `getConfig()` fetches fresh values.
873
+ *
874
+ * @returns void
875
+ * @throws Never throws.
876
+ *
877
+ * @example
878
+ * ```typescript
879
+ * // After enabling a new locale in the admin:
880
+ * lynkow.site.clearCache()
881
+ * const fresh = await lynkow.site.getConfig()
882
+ * ```
788
883
  */
789
884
  clearCache(): void;
790
885
  }
@@ -854,9 +949,20 @@ declare class LegalService extends BaseService {
854
949
  */
855
950
  getBySlug(slug: string, options?: BaseRequestOptions): Promise<LegalDocument>;
856
951
  /**
857
- * Invalidates all cached legal document responses.
952
+ * Invalidate every cached legal document response. Kept for
953
+ * backwards compatibility with sites that still reference the legacy
954
+ * `/legal/*` surface.
955
+ *
956
+ * @deprecated This service is deprecated. Use `lynkow.pages` instead,
957
+ * which covers legal pages along with every other page type and is
958
+ * actively maintained.
959
+ * @returns void
960
+ * @throws Never throws.
858
961
  *
859
- * @deprecated This service is deprecated. Use `lynkow.pages` instead.
962
+ * @example
963
+ * ```typescript
964
+ * lynkow.legal.clearCache() // Deprecated; prefer lynkow.pages.clearCache()
965
+ * ```
860
966
  */
861
967
  clearCache(): void;
862
968
  }
@@ -932,9 +1038,19 @@ declare class CookiesService extends BaseService {
932
1038
  */
933
1039
  logConsent(preferences: CookiePreferences, options?: BaseRequestOptions): Promise<ConsentLogResponse>;
934
1040
  /**
935
- * Invalidates the cached cookie consent configuration. Call this if you
936
- * know the consent settings have changed and want to force a fresh fetch
937
- * on the next `getConfig()` call.
1041
+ * Invalidate the cached cookie consent configuration (categories,
1042
+ * scripts, banner appearance). Call after a settings change on the
1043
+ * admin or on receipt of a `cookies.updated` webhook so the next
1044
+ * `getConfig()` returns the updated categories and the consent banner
1045
+ * reflects the new policy.
1046
+ *
1047
+ * @returns void
1048
+ * @throws Never throws.
1049
+ *
1050
+ * @example
1051
+ * ```typescript
1052
+ * lynkow.cookies.clearCache()
1053
+ * ```
938
1054
  */
939
1055
  clearCache(): void;
940
1056
  }
@@ -969,6 +1085,8 @@ declare class SeoService extends BaseService {
969
1085
  *
970
1086
  * @param options - Request options (custom fetch options)
971
1087
  * @returns The sitemap XML content as a raw string
1088
+ * @throws {LynkowError} With code `'NETWORK_ERROR'` on transport
1089
+ * failure, `'NOT_FOUND'` if the site has sitemap generation disabled.
972
1090
  *
973
1091
  * @example
974
1092
  * ```typescript
@@ -1011,6 +1129,8 @@ declare class SeoService extends BaseService {
1011
1129
  *
1012
1130
  * @param options - Request options (custom fetch options)
1013
1131
  * @returns The robots.txt content as a raw string
1132
+ * @throws {LynkowError} With code `'NETWORK_ERROR'` on transport
1133
+ * failure, `'NOT_FOUND'` if the site has robots.txt disabled.
1014
1134
  *
1015
1135
  * @example
1016
1136
  * ```typescript
@@ -1035,6 +1155,9 @@ declare class SeoService extends BaseService {
1035
1155
  * When set, the API returns `/{locale}/llms.txt`. When omitted, returns
1036
1156
  * the default locale version.
1037
1157
  * @returns The llms.txt Markdown content as a raw string
1158
+ * @throws {LynkowError} With code `'NETWORK_ERROR'` on transport
1159
+ * failure, `'NOT_FOUND'` if `llmsTxtEnabled` is `false` on the site's
1160
+ * SEO settings or the locale has no llms.txt.
1038
1161
  *
1039
1162
  * @example
1040
1163
  * ```typescript
@@ -1061,6 +1184,9 @@ declare class SeoService extends BaseService {
1061
1184
  * - `locale` — fetch the full content for a specific locale (e.g. `'en'`).
1062
1185
  * When set, only content in that locale is included.
1063
1186
  * @returns The full Markdown content of all published articles and pages as a raw string
1187
+ * @throws {LynkowError} With code `'NETWORK_ERROR'` on transport
1188
+ * failure, `'NOT_FOUND'` if `llmsTxtEnabled` is `false` on the site's
1189
+ * SEO settings.
1064
1190
  *
1065
1191
  * @example
1066
1192
  * ```typescript
@@ -1071,6 +1197,9 @@ declare class SeoService extends BaseService {
1071
1197
  * headers: { 'Content-Type': 'text/plain; charset=utf-8' }
1072
1198
  * })
1073
1199
  * }
1200
+ *
1201
+ * // Fetch the French version
1202
+ * const md = await lynkow.seo.llmsFullTxt({ locale: 'fr' })
1074
1203
  * ```
1075
1204
  */
1076
1205
  llmsFullTxt(options?: BaseRequestOptions): Promise<string>;
@@ -1231,35 +1360,68 @@ declare class PathsService extends BaseService {
1231
1360
  * Only published content appears in search results.
1232
1361
  */
1233
1362
  interface SearchHit {
1234
- /** Content UUID */
1363
+ /** Content UUID. Matches `id` on `Content` / `ContentSummary`. */
1235
1364
  id: string;
1236
- /** Article title */
1365
+ /**
1366
+ * Content title in the searched locale. Never empty (the indexer skips
1367
+ * untitled drafts). May contain `<em>` markers when highlighting is
1368
+ * enabled; see `_formatted.title` for the highlighted variant.
1369
+ */
1237
1370
  title: string;
1238
- /** URL slug */
1371
+ /** URL-safe slug. Unique within the site + locale combination. */
1239
1372
  slug: string;
1240
- /** Short summary / excerpt */
1373
+ /**
1374
+ * Short text summary shown in the search dropdown. Taken from the
1375
+ * content's `excerpt` field, or generated from the first paragraph
1376
+ * when `excerpt` is empty. Always non-null in search results.
1377
+ */
1241
1378
  excerpt: string;
1242
- /** SEO meta title */
1379
+ /**
1380
+ * SEO meta title. Falls back to {@link title} when the content has no
1381
+ * override. Max ~255 characters, generally kept under 60 for search
1382
+ * result listings.
1383
+ */
1243
1384
  metaTitle: string;
1244
- /** SEO meta description */
1385
+ /**
1386
+ * SEO meta description. Falls back to {@link excerpt} when the content
1387
+ * has no override. Max ~500 characters, typically under 160 for
1388
+ * search snippets.
1389
+ */
1245
1390
  metaDescription: string;
1246
1391
  /** Content locale code (e.g. `'fr'`, `'en'`) */
1247
1392
  locale: string;
1248
1393
  /** Full URL path including locale and category prefix (e.g. `'/fr/guides/forms'`) */
1249
1394
  path: string;
1250
- /** Content type (e.g. `'post'`) */
1395
+ /**
1396
+ * Content type slug. Currently always `'post'`; the value is exposed
1397
+ * to support future content types (product, event, etc.) without a
1398
+ * breaking schema change.
1399
+ */
1251
1400
  type: string;
1252
- /** Categories assigned to this content */
1401
+ /**
1402
+ * Categories assigned to this content as minimal `{ name, slug }`
1403
+ * projections. Empty array when the content has no category. Use the
1404
+ * `slug` to build category URLs and pass to
1405
+ * {@link CategoriesService.getBySlug} for full detail.
1406
+ */
1253
1407
  categories: Array<{
1254
1408
  name: string;
1255
1409
  slug: string;
1256
1410
  }>;
1257
- /** Tags assigned to this content */
1411
+ /**
1412
+ * Tags assigned to this content as minimal `{ name, slug }`
1413
+ * projections. Empty array when the content has no tag. Use the
1414
+ * `slug` for tag-filtered listings.
1415
+ */
1258
1416
  tags: Array<{
1259
1417
  name: string;
1260
1418
  slug: string;
1261
1419
  }>;
1262
- /** Full name of the content author */
1420
+ /**
1421
+ * Full name of the content author (or the site's default author
1422
+ * when the content has no explicit author). Always non-empty because
1423
+ * the indexer requires a tokenizable value.
1424
+ */
1263
1425
  authorName: string;
1264
1426
  /** Featured image URL, or `null` if none */
1265
1427
  featuredImage: string | null;
@@ -1285,19 +1447,42 @@ interface SearchHit {
1285
1447
  * Contains an array of matching articles and pagination metadata.
1286
1448
  */
1287
1449
  interface SearchResponse {
1288
- /** Array of matching articles, ordered by relevance */
1450
+ /**
1451
+ * Matching articles for the current page of results, already ordered
1452
+ * by relevance. Empty array when the query matches nothing.
1453
+ */
1289
1454
  data: SearchHit[];
1290
- /** Pagination and query metadata */
1455
+ /**
1456
+ * Pagination and query metadata for rendering controls like "N results
1457
+ * in 42 ms" or a paginator. Uses snake-free naming (`page`,
1458
+ * `totalPages`, `perPage`) that differs from the standard
1459
+ * {@link PaginationMeta} because the search engine response predates
1460
+ * the common pagination type.
1461
+ */
1291
1462
  meta: {
1292
- /** Total number of matching results across all pages */
1463
+ /**
1464
+ * Total number of matching results across every page. Useful for
1465
+ * "Showing 1-10 of 142" UI. Capped at the search engine's
1466
+ * configured upper bound (50_000 by default).
1467
+ */
1293
1468
  total: number;
1294
1469
  /** Current page number (1-based) */
1295
1470
  page: number;
1296
- /** Total number of pages */
1471
+ /**
1472
+ * Total number of pages for the current query + `perPage`. `1` when
1473
+ * results fit on one page, `0` when `total === 0`.
1474
+ */
1297
1475
  totalPages: number;
1298
- /** Number of results per page */
1476
+ /**
1477
+ * Number of results per page as actually applied by the search
1478
+ * engine (the request's `limit` clamped to 1-100).
1479
+ */
1299
1480
  perPage: number;
1300
- /** The search query as received */
1481
+ /**
1482
+ * The search query echoed back from the server, useful for
1483
+ * debouncing UIs that drop late responses when the input has
1484
+ * changed. Whitespace is preserved as-sent.
1485
+ */
1301
1486
  query: string;
1302
1487
  /** Search engine processing time in milliseconds */
1303
1488
  processingTimeMs: number;
@@ -1329,11 +1514,19 @@ interface SearchOptions extends BaseRequestOptions {
1329
1514
  * round-tripping through your server.
1330
1515
  */
1331
1516
  interface SearchConfig {
1332
- /** Public search host URL (e.g. `'https://search.lynkow.com'`) */
1517
+ /**
1518
+ * Public search engine host URL (e.g. `'https://search.lynkow.com'`).
1519
+ * Use as the `host` when instantiating a Meilisearch-compatible
1520
+ * browser client. Does not include a trailing slash.
1521
+ */
1333
1522
  host: string;
1334
1523
  /** Short-lived tenant token (JWT, 1-hour expiry) scoped to your site's index */
1335
1524
  apiKey: string;
1336
- /** Your site's search index name */
1525
+ /**
1526
+ * Meilisearch index name for this site, of the form
1527
+ * `site-<siteId>_<locale>` or `site-<siteId>` for single-locale sites.
1528
+ * Pass verbatim as the `index` parameter in browser search queries.
1529
+ */
1337
1530
  indexName: string;
1338
1531
  }
1339
1532
  /**
@@ -1872,6 +2065,102 @@ interface ContentSchema {
1872
2065
  fields: SchemaField[];
1873
2066
  }
1874
2067
 
2068
+ /**
2069
+ * JSON-LD cascade types mirroring the Lynkow API schema.
2070
+ *
2071
+ * The Lynkow cascade declares schema.org nodes at four levels:
2072
+ *
2073
+ * 1. Site (SEO settings): shared across every page of the site.
2074
+ * 2. Category: inherited by all contents assigned to the category.
2075
+ * 3. Page (site block): inherited along the page surface.
2076
+ * 4. Content: article-specific overrides.
2077
+ *
2078
+ * Each level stores its own `jsonLdGraph` plus an optional `jsonLdExclusions`
2079
+ * that suppresses a parent node by `@id`. The server resolves the effective
2080
+ * `@graph` at render time and exposes it on the public API response as
2081
+ * `structuredData.graph: object[]`. This SDK only reads that pre-resolved
2082
+ * array; no client-side cascade logic is needed.
2083
+ *
2084
+ * Reserved ids beginning with `auto:` identify the implicit nodes Lynkow
2085
+ * injects automatically (Article, FAQPage, BreadcrumbList, Organization,
2086
+ * WebPage). They may appear in `jsonLdExclusions` to opt out of the
2087
+ * corresponding auto-node, but MUST NOT be used as user-provided ids.
2088
+ */
2089
+ /**
2090
+ * Source of a JSON-LD node.
2091
+ *
2092
+ * - `preset`: the node was created from a Lynkow typed preset (Organization,
2093
+ * LocalBusiness, Product, etc.). The server fills unspecified fields from
2094
+ * the page context and mapped preset schema.
2095
+ * - `custom`: the node is a raw JSON-LD object supplied verbatim by the
2096
+ * caller. The server only performs basic sanity checks (non-empty `@type`,
2097
+ * prototype-pollution guard).
2098
+ */
2099
+ type JsonLdNodeSource = 'preset' | 'custom';
2100
+ /**
2101
+ * A single JSON-LD node as stored at one cascade level.
2102
+ *
2103
+ * This is the shape the admin / MCP writes into the cascade columns. The
2104
+ * public API materializes each entry into a fully resolved schema.org object
2105
+ * (adding `@context`, `@id`, and preset-driven fields) before returning it
2106
+ * under `structuredData.graph`.
2107
+ */
2108
+ interface JsonLdNode {
2109
+ /**
2110
+ * Stable `@id` for this node. Server-generated (pattern `own-<8hex>`) when
2111
+ * the caller omits it on create. Safe to treat as an opaque string; the
2112
+ * public API rewrites it to an absolute URL (`<pageUrl>#<id>`) in the
2113
+ * resolved graph. Never starts with the reserved `auto:` prefix, which is
2114
+ * reserved for nodes Lynkow injects automatically.
2115
+ */
2116
+ id: string;
2117
+ /**
2118
+ * schema.org `@type` identifier (e.g. `"Organization"`, `"LocalBusiness"`,
2119
+ * `"Product"`). Any schema.org type is accepted; 27 first-class presets
2120
+ * get typed field mapping server-side, unknown types pass through via
2121
+ * `source: 'custom'`.
2122
+ */
2123
+ type: string;
2124
+ /**
2125
+ * Node payload. The accepted shape depends on {@link source}:
2126
+ *
2127
+ * - `preset`: only the fields documented for the matching preset
2128
+ * (e.g. `name`, `address`, `geo` for `LocalBusiness`). Unknown keys are
2129
+ * ignored. Missing context fields (url, inLanguage, etc.) are filled
2130
+ * from the page context at render time.
2131
+ * - `custom`: the full schema.org object, minus `@context` and `@id`
2132
+ * which the server injects. Passed through verbatim after basic sanity
2133
+ * checks (non-empty `@type`, no prototype-pollution keys).
2134
+ */
2135
+ data: Record<string, unknown>;
2136
+ /** Whether the node was created from a Lynkow typed preset or from raw JSON-LD. */
2137
+ source: JsonLdNodeSource;
2138
+ }
2139
+ /**
2140
+ * Cascade configuration stored at any level (site, category, page, content).
2141
+ *
2142
+ * This is the wire shape persisted in the Lynkow database. The public API
2143
+ * exposes the already-resolved graph on `structuredData.graph`; SDK consumers
2144
+ * normally read that resolved array rather than reconstructing one from the
2145
+ * raw configs at each level.
2146
+ */
2147
+ interface JsonLdGraphConfig {
2148
+ /**
2149
+ * Nodes declared at this cascade level. Order is preserved in the emitted
2150
+ * `@graph` but is not semantically significant for schema.org consumers.
2151
+ * Empty array when no nodes are declared.
2152
+ */
2153
+ graph: JsonLdNode[];
2154
+ /**
2155
+ * Parent-level `@id` values this level opts out of. Accepts both
2156
+ * user-provided ids (e.g. `"site-org"`) and the reserved
2157
+ * `auto:article` / `auto:faqpage` / `auto:breadcrumb` /
2158
+ * `auto:organization` / `auto:webpage` identifiers to suppress
2159
+ * Lynkow's implicit nodes. Empty array when no parent node is excluded.
2160
+ */
2161
+ exclusions: string[];
2162
+ }
2163
+
1875
2164
  /**
1876
2165
  * A content category as returned in content relations and list select projections.
1877
2166
  *
@@ -1997,6 +2286,19 @@ interface CategoryDetail extends CategoryWithCount {
1997
2286
  * Categories support unlimited nesting depth.
1998
2287
  */
1999
2288
  parentId: number | null;
2289
+ /**
2290
+ * Category-level JSON-LD cascade nodes. Inherited by every content
2291
+ * attached to the category. Merged server-side into the content's
2292
+ * final `structuredData.graph`; SDK consumers normally read the
2293
+ * resolved graph on the content and don't need to re-merge.
2294
+ */
2295
+ jsonLdGraph?: JsonLdNode[];
2296
+ /**
2297
+ * Reserved `@id` values this category opts out of. Typically used to
2298
+ * suppress site-level nodes that should not apply to contents in this
2299
+ * category (e.g. exclude `'auto:organization'` for a sub-brand).
2300
+ */
2301
+ jsonLdExclusions?: string[];
2000
2302
  }
2001
2303
  /**
2002
2304
  * Recursive category tree node, returned by the tree endpoint (`GET /public/categories/tree`).
@@ -2194,6 +2496,18 @@ interface StructuredData {
2194
2496
  */
2195
2497
  jsonLd: JsonLdArticle;
2196
2498
  };
2499
+ /**
2500
+ * Resolved JSON-LD `@graph` for this content, produced by the Lynkow
2501
+ * cascade (site + category + content + auto-nodes). Each entry is a
2502
+ * fully-formed schema.org object ready to be serialized inside a
2503
+ * `<script type="application/ld+json">` tag. Use
2504
+ * {@link renderJsonLdGraph} to emit the whole array as a single tag.
2505
+ *
2506
+ * `undefined` or empty on responses from older API versions that do not
2507
+ * populate the cascade. Prefer the individual `faq.jsonLd` / `article.jsonLd`
2508
+ * fields as a fallback.
2509
+ */
2510
+ graph?: object[];
2197
2511
  /**
2198
2512
  * Alternate language versions of this content.
2199
2513
  * Empty array if the site has only one locale or no translations exist.
@@ -2504,6 +2818,21 @@ interface Content extends ContentSummary {
2504
2818
  * ```
2505
2819
  */
2506
2820
  customData?: Record<string, any> | null;
2821
+ /**
2822
+ * Content-level cascade nodes declared by the admin or via the MCP
2823
+ * `create_content` / `update_content` tools. Merged with site + category
2824
+ * nodes server-side; consumers should read {@link StructuredData.graph}
2825
+ * instead of re-merging client-side.
2826
+ *
2827
+ * See `/features/json-ld-cascade` in the Lynkow docs for the full model.
2828
+ */
2829
+ jsonLdGraph?: JsonLdNode[];
2830
+ /**
2831
+ * Reserved `@id` values this content opts out of. Typically used to
2832
+ * suppress auto-generated nodes (e.g. `'auto:article'`) when a custom
2833
+ * replacement has been supplied in `jsonLdGraph`.
2834
+ */
2835
+ jsonLdExclusions?: string[];
2507
2836
  }
2508
2837
 
2509
2838
  /**
@@ -2747,6 +3076,27 @@ interface Page extends PageSummary {
2747
3076
  * Useful for debugging during development; avoid displaying these to end users.
2748
3077
  */
2749
3078
  _warnings?: string[];
3079
+ /**
3080
+ * Resolved JSON-LD cascade for the page. Server-merged and ready to
3081
+ * render; no client-side cascade logic required.
3082
+ *
3083
+ * `undefined` on responses from older API versions that do not populate
3084
+ * the cascade; in that case, fall back to the dedicated
3085
+ * `/public/:siteId/pages/:slug/json-ld` endpoint which always returns
3086
+ * the array directly under `data`.
3087
+ */
3088
+ structuredData?: {
3089
+ /**
3090
+ * The resolved `@graph` array: site-level nodes + this page's own
3091
+ * nodes + auto-nodes (WebPage, BreadcrumbList, Organization when
3092
+ * configured), minus any excluded ids. Each entry is a fully-formed
3093
+ * schema.org object with `@context`, `@id`, and `@type`. Use
3094
+ * {@link renderJsonLdGraph} to serialize the whole array as a single
3095
+ * `<script type="application/ld+json">` tag. Empty array when nothing
3096
+ * is declared and all auto-nodes are excluded.
3097
+ */
3098
+ graph: object[];
3099
+ };
2750
3100
  }
2751
3101
 
2752
3102
  /**
@@ -4046,7 +4396,18 @@ interface CategoryDetailResponse {
4046
4396
  * Supports the same pagination options as `contents.list()`.
4047
4397
  */
4048
4398
  contents: {
4399
+ /**
4400
+ * Current page of matching content summaries, already filtered to
4401
+ * this category + locale. Empty array when the category has no
4402
+ * published content on this page.
4403
+ */
4049
4404
  data: ContentSummary[];
4405
+ /**
4406
+ * Pagination cursors for the paginated list: `total`, `perPage`,
4407
+ * `currentPage`, `lastPage`, `hasMorePages`. Use these to drive
4408
+ * "Load more" or page-number UIs; shape matches the generic
4409
+ * {@link PaginationMeta}.
4410
+ */
4050
4411
  meta: PaginationMeta;
4051
4412
  };
4052
4413
  /**
@@ -4287,7 +4648,16 @@ interface CategoryResolveResponse {
4287
4648
  * Pagination can be controlled via query parameters on the resolve request.
4288
4649
  */
4289
4650
  contents: {
4651
+ /**
4652
+ * Current page of matching content summaries for the resolved
4653
+ * category. Order follows the site's configured content sort
4654
+ * (typically publication date desc).
4655
+ */
4290
4656
  data: ContentSummary[];
4657
+ /**
4658
+ * Pagination cursors. Shape matches {@link PaginationMeta}; use
4659
+ * `hasMorePages` to decide whether to show a "Load more" button.
4660
+ */
4291
4661
  meta: PaginationMeta;
4292
4662
  };
4293
4663
  }
@@ -4503,23 +4873,48 @@ declare function isCategoryResolve(response: ResolveResponse): response is Categ
4503
4873
  */
4504
4874
 
4505
4875
  /**
4506
- * Data for tracking pageviews
4876
+ * Payload accepted by {@link AnalyticsService.trackPageview}. Every field
4877
+ * is optional; unset values fall back to the browser context at call time.
4507
4878
  */
4508
4879
  interface PageviewData {
4509
- /** Page path (default: window.location.pathname) */
4880
+ /**
4881
+ * URL path to record. Should start with `/`. Query string and hash are
4882
+ * preserved as-is; strip them when they do not identify a distinct
4883
+ * pageview (e.g. tracker-specific UTM parameters). Defaults to
4884
+ * `window.location.pathname` when omitted.
4885
+ */
4510
4886
  path?: string;
4511
- /** Page title (default: document.title) */
4887
+ /**
4888
+ * Human-readable page title as it appears in the browser tab, used for
4889
+ * the analytics dashboard listing. Defaults to `document.title` when
4890
+ * omitted. Max ~255 characters before server-side truncation.
4891
+ */
4512
4892
  title?: string;
4513
- /** Referrer URL */
4893
+ /**
4894
+ * Referring URL for this pageview, typically `document.referrer`.
4895
+ * Pass `''` to record "direct" traffic when you want to override the
4896
+ * browser's value. Omit to let the tracker fill it from
4897
+ * `document.referrer`.
4898
+ */
4514
4899
  referrer?: string;
4515
4900
  }
4516
4901
  /**
4517
- * Data for tracking custom events
4902
+ * Payload accepted by {@link AnalyticsService.trackEvent}. `type` is the
4903
+ * only required field; every other key becomes an ad-hoc property on the
4904
+ * event recorded by the tracker.
4518
4905
  */
4519
4906
  interface EventData {
4520
- /** Event type */
4907
+ /**
4908
+ * Short slug identifying the event (e.g. `'cta_click'`,
4909
+ * `'signup_complete'`). Prefer snake_case so events group cleanly in
4910
+ * the analytics dashboard. Required and must be non-empty.
4911
+ */
4521
4912
  type: string;
4522
- /** Additional event data */
4913
+ /**
4914
+ * Arbitrary extra properties to attach to the event. Values must be
4915
+ * JSON-serializable. Keys prefixed with `_` are reserved for the
4916
+ * tracker and may be overwritten server-side.
4917
+ */
4523
4918
  [key: string]: unknown;
4524
4919
  }
4525
4920
  /**
@@ -4658,6 +5053,9 @@ declare class AnalyticsService {
4658
5053
  * }
4659
5054
  * })
4660
5055
  * ```
5056
+ *
5057
+ * @returns void
5058
+ * @throws Never throws.
4661
5059
  */
4662
5060
  enable(): void;
4663
5061
  /**
@@ -4665,6 +5063,9 @@ declare class AnalyticsService {
4665
5063
  * `trackPageview()` calls become no-ops. The tracker script remains loaded;
4666
5064
  * call `destroy()` to fully remove it.
4667
5065
  *
5066
+ * @returns void
5067
+ * @throws Never throws.
5068
+ *
4668
5069
  * @example
4669
5070
  * ```typescript
4670
5071
  * // Disable tracking when the user revokes analytics consent
@@ -4673,25 +5074,56 @@ declare class AnalyticsService {
4673
5074
  */
4674
5075
  disable(): void;
4675
5076
  /**
4676
- * Returns whether analytics tracking is currently enabled.
5077
+ * Returns whether analytics tracking is currently enabled (i.e. whether
5078
+ * `trackEvent` and `trackPageview` will actually emit). Does not
5079
+ * indicate whether the underlying tracker script has loaded; use
5080
+ * {@link isInitialized} for that.
5081
+ *
5082
+ * @returns `true` if tracking is enabled (default), `false` after
5083
+ * {@link disable} has been called.
5084
+ * @throws Never throws.
4677
5085
  *
4678
- * @returns `true` if tracking is enabled (default), `false` if `disable()` was called
5086
+ * @example
5087
+ * ```typescript
5088
+ * if (lynkow.analytics.isEnabled()) {
5089
+ * // Safe to track
5090
+ * }
5091
+ * ```
4679
5092
  */
4680
5093
  isEnabled(): boolean;
4681
5094
  /**
4682
5095
  * Returns whether the tracker.js script has been loaded and the
4683
- * `window.LynkowAnalytics` global is available.
5096
+ * `window.LynkowAnalytics` global is available. Always `false` on the
5097
+ * server.
4684
5098
  *
4685
- * @returns `true` if the tracker is fully initialized and ready to track events
5099
+ * @returns `true` if the tracker is fully loaded and ready to accept
5100
+ * events, `false` otherwise.
5101
+ * @throws Never throws.
5102
+ *
5103
+ * @example
5104
+ * ```typescript
5105
+ * if (lynkow.analytics.isInitialized()) {
5106
+ * lynkow.analytics.trackEvent({ type: 'cta_click' })
5107
+ * }
5108
+ * ```
4686
5109
  */
4687
5110
  isInitialized(): boolean;
4688
5111
  /**
4689
- * Returns the underlying `window.LynkowAnalytics` global object for advanced
4690
- * usage when you need direct access to the tracker API beyond what this
4691
- * service wraps.
5112
+ * Return the underlying `window.LynkowAnalytics` global for advanced
5113
+ * use cases that need direct access to the tracker's lower-level API
5114
+ * (e.g. calling `init` again with different options, or invoking
5115
+ * undocumented tracker internals).
5116
+ *
5117
+ * @returns The `LynkowAnalyticsGlobal` with `init()` and `track()`
5118
+ * methods, or `undefined` if running on the server or the tracker
5119
+ * script has not loaded yet.
5120
+ * @throws Never throws.
4692
5121
  *
4693
- * @returns The `LynkowAnalyticsGlobal` object with `init()` and `track()` methods,
4694
- * or `undefined` if running on the server or the tracker is not loaded
5122
+ * @example
5123
+ * ```typescript
5124
+ * const tracker = lynkow.analytics.getTracker()
5125
+ * tracker?.track({ custom: 'payload' })
5126
+ * ```
4695
5127
  */
4696
5128
  getTracker(): LynkowAnalyticsGlobal | undefined;
4697
5129
  /**
@@ -4753,9 +5185,25 @@ type EventName = keyof LynkowEvents;
4753
5185
  */
4754
5186
  type EventListener<T> = (data: T) => void;
4755
5187
  /**
4756
- * Create a new event emitter instance.
4757
- * Returns an object with `on`, `off`, `emit`, `once`, and `removeAllListeners` methods.
4758
- * Each SDK client creates its own emitter; events do not leak between client instances.
5188
+ * Create a new event emitter instance backing the `lynkow.on/off/once`
5189
+ * API of the client. Each SDK client creates its own emitter, so events
5190
+ * do not leak between client instances (e.g. multi-tenant server rendering).
5191
+ *
5192
+ * @returns An {@link EventEmitter} object exposing `on`, `off`, `emit`,
5193
+ * `once`, and `removeAllListeners`. `on()` and `once()` return an
5194
+ * unsubscribe closure for convenience.
5195
+ *
5196
+ * @example
5197
+ * ```typescript
5198
+ * import { createEventEmitter } from 'lynkow'
5199
+ *
5200
+ * const events = createEventEmitter()
5201
+ * const unsubscribe = events.on('locale-changed', (locale) => {
5202
+ * refetchContent(locale)
5203
+ * })
5204
+ * events.emit('locale-changed', 'fr')
5205
+ * unsubscribe()
5206
+ * ```
4759
5207
  */
4760
5208
  declare function createEventEmitter(): {
4761
5209
  on: <K extends EventName>(event: K, listener: EventListener<LynkowEvents[K]>) => () => void;
@@ -4776,16 +5224,38 @@ type EventEmitter = ReturnType<typeof createEventEmitter>;
4776
5224
  */
4777
5225
 
4778
5226
  /**
4779
- * Consent categories
5227
+ * User consent state for each cookie category Lynkow manages. Used as
5228
+ * input to {@link ConsentService.setCategories} and emitted as the payload
5229
+ * of the `'consent-changed'` client event.
5230
+ *
5231
+ * A category set to `false` means the corresponding scripts (from the
5232
+ * site's cookie configuration) MUST NOT be loaded; a category set to
5233
+ * `true` authorises them.
4780
5234
  */
4781
5235
  interface ConsentCategories {
4782
- /** Always true, not modifiable */
5236
+ /**
5237
+ * Strictly necessary cookies (session, CSRF). Always `true`: users
5238
+ * cannot opt out because the site would be non-functional without them.
5239
+ * Included in the type so consent payloads remain uniform.
5240
+ */
4783
5241
  necessary: boolean;
4784
- /** Analytics cookies */
5242
+ /**
5243
+ * Consent to first-party and third-party analytics (e.g. the Lynkow
5244
+ * tracker, Google Analytics). When `false`, the analytics service
5245
+ * automatically becomes a no-op.
5246
+ */
4785
5247
  analytics: boolean;
4786
- /** Marketing cookies */
5248
+ /**
5249
+ * Consent to marketing and advertising cookies (ad networks,
5250
+ * retargeting). Required before loading scripts classified as
5251
+ * `marketing` in the site's cookie config.
5252
+ */
4787
5253
  marketing: boolean;
4788
- /** Preference cookies */
5254
+ /**
5255
+ * Consent to personalization and preference cookies (theme, language,
5256
+ * non-essential UI state). Required before loading scripts classified
5257
+ * as `preferences` in the site's cookie config.
5258
+ */
4789
5259
  preferences: boolean;
4790
5260
  }
4791
5261
  /**
@@ -4834,8 +5304,19 @@ declare class ConsentService {
4834
5304
  * Works on both server and browser.
4835
5305
  *
4836
5306
  * @returns A `CookieConfig` object with banner settings, categories, texts,
4837
- * theming options, and third-party script definitions
4838
- * @throws {Error} If the API request fails (non-2xx response)
5307
+ * theming options, and third-party script definitions.
5308
+ * @throws {Error} If the API request fails (non-2xx response). The SDK
5309
+ * does not wrap this in a {@link LynkowError} for this endpoint,
5310
+ * since consent config is fetched via a raw `fetch` call rather than
5311
+ * the shared request pipeline.
5312
+ *
5313
+ * @example
5314
+ * ```typescript
5315
+ * const config = await lynkow.consent.getConfig()
5316
+ * if (config.enabled) {
5317
+ * console.log('Categories:', config.categories.map((c) => c.id))
5318
+ * }
5319
+ * ```
4839
5320
  */
4840
5321
  getConfig(): Promise<CookieConfig>;
4841
5322
  /**
@@ -4848,7 +5329,19 @@ declare class ConsentService {
4848
5329
  * (e.g. `{ necessary: true, analytics: true, marketing: false }`)
4849
5330
  * @param action - Optional explicit action type. If omitted, the action is inferred
4850
5331
  * from the preferences (all true = `'accept_all'`, all false = `'reject_all'`,
4851
- * mixed = `'customize'`)
5332
+ * mixed = `'customize'`). `'withdraw'` must be passed explicitly.
5333
+ * @returns A promise that resolves once the POST has completed or
5334
+ * been suppressed on error. Never rejects.
5335
+ * @throws Never throws. Network or server errors are caught and silently
5336
+ * discarded so the consent UI flow is never interrupted.
5337
+ *
5338
+ * @example
5339
+ * ```typescript
5340
+ * await lynkow.consent.logConsent(
5341
+ * { necessary: true, analytics: true, marketing: false },
5342
+ * 'customize'
5343
+ * )
5344
+ * ```
4852
5345
  */
4853
5346
  logConsent(preferences: CookiePreferences, action?: 'accept_all' | 'reject_all' | 'customize' | 'withdraw'): Promise<void>;
4854
5347
  private getOrCreateVisitorId;
@@ -4864,6 +5357,9 @@ declare class ConsentService {
4864
5357
  * When theme is `'auto'`, a MutationObserver watches for site theme changes
4865
5358
  * and updates the banner colors in real-time.
4866
5359
  *
5360
+ * @returns void
5361
+ * @throws Never throws. Config-fetch rejections are caught internally.
5362
+ *
4867
5363
  * @example
4868
5364
  * ```typescript
4869
5365
  * // Show the consent banner on page load (skips if already consented)
@@ -4873,15 +5369,37 @@ declare class ConsentService {
4873
5369
  */
4874
5370
  show(): void;
4875
5371
  /**
4876
- * Hides and removes the consent banner from the DOM. Does not affect stored
4877
- * consent preferences. No-op on server or if the banner is not shown.
5372
+ * Hide and remove the consent banner from the DOM. Does not affect
5373
+ * stored consent preferences (the user will not be re-prompted on the
5374
+ * next page load if they already chose). No-op on server or if the
5375
+ * banner is not currently shown.
5376
+ *
5377
+ * @returns void
5378
+ * @throws Never throws.
5379
+ *
5380
+ * @example
5381
+ * ```typescript
5382
+ * // Close the banner after the user clicked "Accept all" via custom UI:
5383
+ * lynkow.consent.hide()
5384
+ * ```
4878
5385
  */
4879
5386
  hide(): void;
4880
5387
  /**
4881
- * Opens the preferences modal, allowing the user to toggle individual
4882
- * consent categories (analytics, marketing, preferences). The necessary
4883
- * category is always checked and disabled. Pre-populates checkboxes with
4884
- * the user's current consent state. No-op on server.
5388
+ * Open the preferences modal so the user can toggle individual consent
5389
+ * categories (analytics, marketing, preferences). The `necessary`
5390
+ * category is always checked and disabled. Pre-populates checkboxes
5391
+ * with the user's current consent state. No-op on server or if the
5392
+ * modal is already mounted.
5393
+ *
5394
+ * @returns void
5395
+ * @throws Never throws.
5396
+ *
5397
+ * @example
5398
+ * ```tsx
5399
+ * <button onClick={() => lynkow.consent.showPreferences()}>
5400
+ * Cookie settings
5401
+ * </button>
5402
+ * ```
4885
5403
  */
4886
5404
  showPreferences(): void;
4887
5405
  /**
@@ -4974,6 +5492,9 @@ declare class ConsentService {
4974
5492
  * banner. Useful for providing a "manage cookies" link that lets users
4975
5493
  * change their preferences. No-op on server.
4976
5494
  *
5495
+ * @returns void
5496
+ * @throws Never throws. localStorage errors are silently ignored.
5497
+ *
4977
5498
  * @example
4978
5499
  * ```typescript
4979
5500
  * // Add a "Manage cookies" link in the footer to let users re-choose
@@ -5002,10 +5523,22 @@ declare class ConsentService {
5002
5523
  private attachBannerEvents;
5003
5524
  private attachPreferencesEvents;
5004
5525
  /**
5005
- * Cleans up all consent UI resources: removes the banner and preferences
5006
- * modal from the DOM, removes injected third-party scripts, and stops
5007
- * the theme observer. Call this when unmounting the SDK (e.g. in a
5008
- * React useEffect cleanup). No-op on server.
5526
+ * Clean up every DOM resource this service owns: removes the banner
5527
+ * and preferences modal, removes injected third-party scripts, and
5528
+ * stops the theme observer. Call when unmounting the SDK (e.g. inside
5529
+ * a React `useEffect` cleanup) so the page can be navigated without
5530
+ * leaking listeners. No-op on server.
5531
+ *
5532
+ * @returns void
5533
+ * @throws Never throws.
5534
+ *
5535
+ * @example
5536
+ * ```tsx
5537
+ * useEffect(() => {
5538
+ * lynkow.consent.show()
5539
+ * return () => lynkow.consent.destroy()
5540
+ * }, [])
5541
+ * ```
5009
5542
  */
5010
5543
  destroy(): void;
5011
5544
  }
@@ -5066,21 +5599,50 @@ declare class BrandingService extends BaseService {
5066
5599
  */
5067
5600
  inject(): Promise<void>;
5068
5601
  /**
5069
- * Removes the branding badge and its associated styles from the DOM,
5070
- * and stops the theme observer. No-op on server or if the badge is
5071
- * not currently injected.
5602
+ * Remove the branding badge and its associated `<style>` block from
5603
+ * the DOM and stop the theme observer. No-op on server or if the
5604
+ * badge is not currently injected.
5605
+ *
5606
+ * @returns void
5607
+ * @throws Never throws.
5608
+ *
5609
+ * @example
5610
+ * ```typescript
5611
+ * lynkow.branding.remove()
5612
+ * ```
5072
5613
  */
5073
5614
  remove(): void;
5074
5615
  /**
5075
- * Checks whether the branding badge is currently present in the DOM.
5616
+ * Check whether the branding badge is currently mounted in the DOM.
5617
+ * Useful for conditional logic (e.g. show a custom "Powered by" only
5618
+ * when the badge is not already rendered).
5619
+ *
5620
+ * @returns `true` if the badge container element exists in the
5621
+ * document, `false` otherwise. Always returns `false` on the server.
5622
+ * @throws Never throws.
5076
5623
  *
5077
- * @returns `true` if the badge container element exists in the document,
5078
- * `false` otherwise. Always returns `false` on the server.
5624
+ * @example
5625
+ * ```typescript
5626
+ * if (!lynkow.branding.isVisible()) {
5627
+ * // Render our own attribution
5628
+ * }
5629
+ * ```
5079
5630
  */
5080
5631
  isVisible(): boolean;
5081
5632
  /**
5082
- * Alias for `remove()`. Cleans up the badge, styles, and theme observer.
5083
- * Use this in cleanup callbacks (e.g. React useEffect cleanup).
5633
+ * Alias for {@link remove}. Cleans up the badge, styles, and theme
5634
+ * observer. Matches the `destroy()` naming used on other lifecycle
5635
+ * services so cleanup code can stay uniform.
5636
+ *
5637
+ * @returns void
5638
+ * @throws Never throws.
5639
+ *
5640
+ * @example
5641
+ * ```tsx
5642
+ * useEffect(() => {
5643
+ * return () => lynkow.branding.destroy()
5644
+ * }, [])
5645
+ * ```
5084
5646
  */
5085
5647
  destroy(): void;
5086
5648
  }
@@ -5164,17 +5726,40 @@ declare class EnhancementsService {
5164
5726
  */
5165
5727
  init(): void;
5166
5728
  /**
5167
- * Checks whether the enhancements service has been initialized.
5729
+ * Check whether the enhancements service has been initialized for the
5730
+ * current page. Returns `false` on the server and after a call to
5731
+ * {@link destroy} (until `init()` is invoked again).
5732
+ *
5733
+ * @returns `true` if `init()` has been called successfully, `false`
5734
+ * otherwise.
5735
+ * @throws Never throws.
5168
5736
  *
5169
- * @returns `true` if `init()` has been called successfully, `false` otherwise
5737
+ * @example
5738
+ * ```typescript
5739
+ * if (!lynkow.enhancements.isInitialized()) {
5740
+ * lynkow.enhancements.init()
5741
+ * }
5742
+ * ```
5170
5743
  */
5171
5744
  isInitialized(): boolean;
5172
5745
  /**
5173
- * Cleans up all enhancement resources: disconnects the MutationObserver,
5174
- * removes the widget resize listener, cancels any pending animation frame,
5175
- * removes injected styles and cloned scripts from `<head>`, and resets
5176
- * the initialized state. After calling `destroy()`, you can re-initialize
5177
- * by calling `init()` again. No-op on server.
5746
+ * Clean up every resource the enhancements service owns: disconnect the
5747
+ * MutationObserver, remove the widget resize listener, cancel any
5748
+ * pending animation frame, remove injected styles and cloned scripts
5749
+ * from `<head>`, and reset the initialized state. Safe to call
5750
+ * repeatedly; subsequent calls are no-ops. After `destroy()` you can
5751
+ * re-attach by calling `init()` again. No-op on server.
5752
+ *
5753
+ * @returns void
5754
+ * @throws Never throws.
5755
+ *
5756
+ * @example
5757
+ * ```tsx
5758
+ * useEffect(() => {
5759
+ * lynkow.enhancements.init()
5760
+ * return () => lynkow.enhancements.destroy()
5761
+ * }, [])
5762
+ * ```
5178
5763
  */
5179
5764
  destroy(): void;
5180
5765
  }
@@ -5183,32 +5768,74 @@ declare class EnhancementsService {
5183
5768
  * Options for building srcset URLs
5184
5769
  */
5185
5770
  interface SrcsetOptions {
5186
- /** Image widths to include in srcset (default: [400, 800, 1200, 1920]) */
5771
+ /**
5772
+ * Pixel widths to generate in the srcset, in ascending order. Each
5773
+ * width becomes a `Nw` entry in the returned string. Defaults to
5774
+ * `[400, 800, 1200, 1920]` to cover phone, tablet, desktop, large
5775
+ * desktop. Supply a custom list when you know your layout's
5776
+ * breakpoints to avoid bandwidth waste.
5777
+ */
5187
5778
  widths?: number[];
5188
- /** Resize fit mode (default: 'scale-down') */
5779
+ /**
5780
+ * Cloudflare resize fit mode. `'scale-down'` (default) preserves
5781
+ * aspect ratio and never upscales; `'cover'` fills the box and crops;
5782
+ * `'contain'` fits inside the box with letterboxing; `'crop'` hard
5783
+ * crops to the exact dimensions. Must pair with `gravity` for
5784
+ * `'cover'` / `'crop'` when the subject is not centered.
5785
+ */
5189
5786
  fit?: 'cover' | 'contain' | 'scale-down' | 'crop';
5190
- /** Image quality 1-100 (default: 80) */
5787
+ /**
5788
+ * JPEG / WebP quality on a 1-100 scale. Higher values produce larger
5789
+ * files. Default `80` strikes a good balance for photography; drop to
5790
+ * 60-70 for hero images on slow connections.
5791
+ */
5191
5792
  quality?: number;
5192
- /** Focal point for crop (e.g., '0.5x0.3') */
5793
+ /**
5794
+ * Focal point for `fit: 'cover' | 'crop'` as an `XxY` pair of
5795
+ * fractions (e.g. `'0.5x0.3'` keeps the horizontal center but biases
5796
+ * towards the upper third). Omit to center the crop.
5797
+ */
5193
5798
  gravity?: string;
5194
5799
  }
5195
5800
  /**
5196
- * Options for building a single transformed URL
5801
+ * Options for building a single transformed URL. Matches the Cloudflare
5802
+ * Image Transformations query parameters; omit any value to let the CDN
5803
+ * choose a sensible default.
5197
5804
  */
5198
5805
  interface TransformOptions {
5199
- /** Target width in pixels */
5806
+ /**
5807
+ * Target width in pixels. When set alone, height is derived from the
5808
+ * image's aspect ratio (unless `fit` requires both).
5809
+ */
5200
5810
  w?: number;
5201
- /** Target height in pixels */
5811
+ /**
5812
+ * Target height in pixels. When set alone, width is derived from the
5813
+ * image's aspect ratio (unless `fit` requires both).
5814
+ */
5202
5815
  h?: number;
5203
- /** Resize fit mode */
5816
+ /**
5817
+ * Resize fit mode. See {@link SrcsetOptions.fit} for semantics; the
5818
+ * default here is `'scale-down'` to avoid accidental upscaling.
5819
+ */
5204
5820
  fit?: 'cover' | 'contain' | 'scale-down' | 'crop';
5205
5821
  /** Image quality 1-100 (default: 80) */
5206
5822
  quality?: number;
5207
- /** Output format (default: 'auto') */
5823
+ /**
5824
+ * Output image format. `'auto'` (default) lets the CDN negotiate
5825
+ * based on `Accept` (WebP on modern browsers, AVIF on the newest).
5826
+ * Force a specific format only when the consumer is known.
5827
+ */
5208
5828
  format?: 'auto' | 'webp' | 'avif' | 'jpeg';
5209
- /** Focal point for crop */
5829
+ /**
5830
+ * Focal point for `fit: 'cover' | 'crop'` as an `XxY` pair of
5831
+ * fractions (see {@link SrcsetOptions.gravity}). Omit to center.
5832
+ */
5210
5833
  gravity?: string;
5211
- /** Device Pixel Ratio 1-4 */
5834
+ /**
5835
+ * Device Pixel Ratio multiplier on a `1`-`4` scale. Multiplies the
5836
+ * rendered width/height so a 400px box renders sharp at 2x on
5837
+ * Retina. Prefer using a `srcset` over a hard-coded DPR.
5838
+ */
5212
5839
  dpr?: number;
5213
5840
  }
5214
5841
  /**
@@ -5711,4 +6338,56 @@ declare function detectSiteTheme(): 'dark' | 'light';
5711
6338
  */
5712
6339
  declare function onSiteThemeChange(callback: (theme: 'dark' | 'light') => void): () => void;
5713
6340
 
5714
- export { type Alternate, AnalyticsService, type ApiErrorDetail, type Author, type BaseRequestOptions, BlocksService, BrandingService, type CategoriesListResponse, CategoriesService, type Category, type CategoryDetail, type CategoryDetailResponse, type CategoryOptions, type CategoryResolveResponse, type CategoryTreeNode, type CategoryTreeResponse, type CategoryWithCount, type Client, type ClientConfig, type ConsentCategories, type ConsentLogResponse, ConsentService, type Content, type ContentBody, type ContentResolveResponse, type ContentSchema, type ContentSummary, type ContentsFilters, type ContentsListResponse, ContentsService, type CookieCategory, type CookieConfig, type CookiePreferences, type CookieTexts, CookiesService, EnhancementsService, type ErrorCode, type EventData, type EventName, type Form, type FormField, type FormFieldOption, type FormFieldType, type FormFieldValidation, type FormSettings, type FormSubmitData, type FormSubmitResponse, FormsService, type GlobalBlock, type GlobalBlockResponse, type ImageVariants, type LegalDocument, LegalService, type LynkowClient, type LynkowConfig, LynkowError, type LynkowEvents, MediaHelperService, type Page, type PageSeo, type PageSummary, type PagesListResponse, PagesService, type PageviewData, type PaginatedResponse, type PaginationMeta, type PaginationOptions, type Path, type PathsListResponse, PathsService, type Redirect, type ResolveResponse, type Review, type ReviewResponse, type ReviewSettings, type ReviewSubmitData, type ReviewSubmitResponse, type ReviewsFilters, type ReviewsListResponse, ReviewsService, type SchemaField, type SchemaFieldOption, type SchemaFieldType, type SchemaFieldValidation, type SearchConfig, type SearchHit, type SearchOptions, type SearchResponse, SearchService, SeoService, type SiteConfig, type SiteConfigResponse, SiteService, type SortOptions, type SrcsetOptions, type SubmitOptions, type Tag, type TagsListResponse, TagsService, type TipTapMark, type TipTapNode, type TransformOptions, browserOnly, browserOnlyAsync, createClient, createLynkowClient, detectSiteTheme, isBrowser, isCategoryResolve, isContentResolve, isLynkowError, isServer, onSiteThemeChange };
6341
+ /**
6342
+ * Render a resolved JSON-LD `@graph` as a single `<script type="application/ld+json">`
6343
+ * tag ready to inject into a page `<head>`.
6344
+ *
6345
+ * The input is the array returned by the Lynkow public API at
6346
+ * `content.structuredData.graph` (for articles) or `page.structuredData.graph`
6347
+ * (for site blocks of type page). Each entry is already a fully-formed
6348
+ * schema.org object with `@context`, `@id`, and `@type`. This helper strips
6349
+ * the per-node `@context` and wraps the array in a top-level `@context` +
6350
+ * `@graph` to keep the emitted script as compact as possible.
6351
+ *
6352
+ * No HTML escaping is performed on the JSON body itself, but `</script>`
6353
+ * sequences that would otherwise break the enclosing tag are guarded against
6354
+ * via a Unicode-safe replacement.
6355
+ *
6356
+ * @param nodes - Array of JSON-LD node objects. `null`, `undefined`, or an
6357
+ * empty array returns an empty string so you can safely
6358
+ * spread the result into server-rendered HTML without
6359
+ * conditional branches.
6360
+ * @returns Serialized `<script>` tag string. Empty string when there are
6361
+ * no nodes to render.
6362
+ * @throws Never throws. The function is designed for server components
6363
+ * and is safe to call with `undefined` or malformed payloads
6364
+ * (values that cannot be `JSON.stringify`-ed, e.g. circular
6365
+ * references, will throw from the underlying `JSON.stringify`
6366
+ * call - caller's responsibility, not a library behaviour).
6367
+ *
6368
+ * @example
6369
+ * ```tsx
6370
+ * // Next.js App Router (server component)
6371
+ * import { createClient, renderJsonLdGraph } from 'lynkow'
6372
+ *
6373
+ * const client = createClient({ siteId: process.env.LYNKOW_SITE_ID! })
6374
+ *
6375
+ * export default async function ArticlePage({ params }: { params: { slug: string } }) {
6376
+ * const article = await client.contents.getBySlug(params.slug)
6377
+ * return (
6378
+ * <>
6379
+ * <div
6380
+ * dangerouslySetInnerHTML={{
6381
+ * __html: renderJsonLdGraph(article.structuredData?.graph),
6382
+ * }}
6383
+ * />
6384
+ * <h1>{article.title}</h1>
6385
+ * <article dangerouslySetInnerHTML={{ __html: article.body }} />
6386
+ * </>
6387
+ * )
6388
+ * }
6389
+ * ```
6390
+ */
6391
+ declare function renderJsonLdGraph(nodes: object[] | null | undefined): string;
6392
+
6393
+ export { type Alternate, AnalyticsService, type ApiErrorDetail, type Author, type BaseRequestOptions, BlocksService, BrandingService, type CategoriesListResponse, CategoriesService, type Category, type CategoryDetail, type CategoryDetailResponse, type CategoryOptions, type CategoryResolveResponse, type CategoryTreeNode, type CategoryTreeResponse, type CategoryWithCount, type Client, type ClientConfig, type ConsentCategories, type ConsentLogResponse, ConsentService, type Content, type ContentBody, type ContentResolveResponse, type ContentSchema, type ContentSummary, type ContentsFilters, type ContentsListResponse, ContentsService, type CookieCategory, type CookieConfig, type CookiePreferences, type CookieTexts, CookiesService, EnhancementsService, type ErrorCode, type EventData, type EventName, type Form, type FormField, type FormFieldOption, type FormFieldType, type FormFieldValidation, type FormSettings, type FormSubmitData, type FormSubmitResponse, FormsService, type GlobalBlock, type GlobalBlockResponse, type ImageVariants, type JsonLdGraphConfig, type JsonLdNode, type JsonLdNodeSource, type LegalDocument, LegalService, type LynkowClient, type LynkowConfig, LynkowError, type LynkowEvents, MediaHelperService, type Page, type PageSeo, type PageSummary, type PagesListResponse, PagesService, type PageviewData, type PaginatedResponse, type PaginationMeta, type PaginationOptions, type Path, type PathsListResponse, PathsService, type Redirect, type ResolveResponse, type Review, type ReviewResponse, type ReviewSettings, type ReviewSubmitData, type ReviewSubmitResponse, type ReviewsFilters, type ReviewsListResponse, ReviewsService, type SchemaField, type SchemaFieldOption, type SchemaFieldType, type SchemaFieldValidation, type SearchConfig, type SearchHit, type SearchOptions, type SearchResponse, SearchService, SeoService, type SiteConfig, type SiteConfigResponse, SiteService, type SortOptions, type SrcsetOptions, type SubmitOptions, type Tag, type TagsListResponse, TagsService, type TipTapMark, type TipTapNode, type TransformOptions, browserOnly, browserOnlyAsync, createClient, createLynkowClient, detectSiteTheme, isBrowser, isCategoryResolve, isContentResolve, isLynkowError, isServer, onSiteThemeChange, renderJsonLdGraph };