@uniweb/core 0.29.2 → 0.29.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/core",
3
- "version": "0.29.2",
3
+ "version": "0.29.3",
4
4
  "description": "Core classes for the Uniweb platform - Uniweb, Website, Page, Block",
5
5
  "type": "module",
6
6
  "exports": {
package/src/index.js CHANGED
@@ -67,6 +67,7 @@ export {
67
67
  splitPathCapture,
68
68
  joinPathCapture,
69
69
  recordHandle,
70
+ recordTitle,
70
71
  routeParamValue,
71
72
  routeParamValues,
72
73
  matchesRouteParam,
@@ -261,6 +261,36 @@ export function recordHandle(record) {
261
261
  return record.slug
262
262
  }
263
263
 
264
+ /**
265
+ * A record's DISPLAY TITLE — what the page about it, and a search result for it, are called.
266
+ *
267
+ * ⭐ ONE RULE, read by every producer of that title (ruled 2026-09-14 [Diego]): the
268
+ * parametric page (`Website._createDynamicPage`, so the SPA and a host's render), the
269
+ * static build's page expansion, and the record search index (`@uniweb/projections`).
270
+ *
271
+ * title → name → the record's handle (`recordHandle`: `$name`, else `slug`)
272
+ *
273
+ * ⛔ Until then a page read `title` alone while the search index read `title || name`,
274
+ * so a record with a `name` and no `title` — a person — titled its search result and
275
+ * left its page carrying the template's own title, which on the site where this was
276
+ * measured was the route token itself: `:slug` (2026-09-14).
277
+ *
278
+ * Only a non-empty string (or a finite number — `title: 2024` in YAML) names a record.
279
+ * A localized map or a rich document is structure this rule cannot render, and
280
+ * `String()` of one is `[object Object]`.
281
+ *
282
+ * @param {Object} record
283
+ * @returns {string|undefined}
284
+ */
285
+ export function recordTitle(record) {
286
+ if (!record || typeof record !== 'object') return undefined
287
+ for (const value of [record.title, record.name, recordHandle(record)]) {
288
+ if (typeof value === 'string' && value.trim() !== '') return value
289
+ if (typeof value === 'number' && Number.isFinite(value)) return String(value)
290
+ }
291
+ return undefined
292
+ }
293
+
264
294
  /**
265
295
  * The record field a route param is matched on — ONE MAP, read by the local match
266
296
  * (`routeParamValue`, below) and by the records service's record question (`match`,
package/src/website.js CHANGED
@@ -11,7 +11,7 @@ import FetcherDispatcher from './fetcher-dispatcher.js'
11
11
  import ObservableState from './observable-state.js'
12
12
  import { normalizeSeo } from './seo.js'
13
13
  import { resolveDefaultLocale, localeLabel } from './locale-config.js'
14
- import { matchDynamicRoute, decodeRouteValue, matchesRouteParam, routeBinding, routeParamName, parentRouteOf } from './route-match.js'
14
+ import { matchDynamicRoute, decodeRouteValue, matchesRouteParam, recordTitle, routeBinding, routeParamName, parentRouteOf } from './route-match.js'
15
15
  import { resolveFetchConfigs, pageRouteQuery, recordPages, routeSelection, siteReaches } from './fetch-config.js'
16
16
  import { declaredKeys } from './data-keys.js'
17
17
  import { buildDetailConfig } from './detail-url.js'
@@ -679,7 +679,11 @@ export default class Website {
679
679
  }
680
680
 
681
681
  if (currentItem) {
682
- if (currentItem.title) pageData.title = currentItem.title
682
+ // ⭐ The record names the page by the one rule its search result is named by
683
+ // too (`recordTitle`: `title`, `name`, then its handle). ⛔ Until 2026-09-14 this
684
+ // read `title` alone, so a person record kept the template's title — `:slug`.
685
+ const title = recordTitle(currentItem)
686
+ if (title) pageData.title = title
683
687
  if (currentItem.description || currentItem.excerpt) {
684
688
  pageData.description = currentItem.description || currentItem.excerpt
685
689
  }