@uniweb/core 0.36.0 → 0.37.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/core",
3
- "version": "0.36.0",
3
+ "version": "0.37.1",
4
4
  "description": "Core classes for the Uniweb platform - Uniweb, Website, Page, Block",
5
5
  "type": "module",
6
6
  "exports": {
@@ -49,8 +49,8 @@
49
49
  "vitest": "^4.1.7"
50
50
  },
51
51
  "dependencies": {
52
- "@uniweb/semantic-parser": "^1.5.1",
53
- "@uniweb/theming": "^0.2.0"
52
+ "@uniweb/theming": "^0.2.0",
53
+ "@uniweb/semantic-parser": "^1.5.1"
54
54
  },
55
55
  "scripts": {
56
56
  "test": "vitest run"
package/src/index.js CHANGED
@@ -93,7 +93,10 @@ export {
93
93
  landingRoute,
94
94
  RESOLUTION_STATUS,
95
95
  } from './resolve-route.js'
96
- export { isRichSchema } from './schemas.js'
96
+ // ⛔ No `isRichSchema` (removed 2026-10-05). It told a form-shaped `data:` value from a field
97
+ // map for the runtime's field defaults and the build's runtime schema; both went when the
98
+ // runtime stopped filling field defaults. A registered schema says it outright — `kind: 'form'`
99
+ // in format 3 (`@uniweb/schemas/foundation`) — and the editor, its last reader, reads that.
97
100
  // ⛔ `Tracker` is NOT on the package entry. It is a FEATURE, not part of the
98
101
  // object graph this package exists to define, and putting it here made every
99
102
  // consumer of core carry 1,576 gzip of it -- press, unipress, `@uniweb/api`
@@ -297,6 +297,27 @@ export function recordTitle(record) {
297
297
  return undefined
298
298
  }
299
299
 
300
+ /**
301
+ * A record's SHARED IMAGE — what a link to the page about it shows (its `og:image`).
302
+ *
303
+ * ONE RULE, read where `recordTitle` is: the parametric page (`Website._createDynamicPage`)
304
+ * and the static build's page expansion. It is the record's `image` — the card's picture,
305
+ * which a page names itself from as it does from the card's title — and it takes the
306
+ * place of the page's own `seo.image`, which then serves a record that has none. A site's
307
+ * `seo.image` comes last (`Page.getHeadMeta`).
308
+ *
309
+ * Only a non-empty string is an image: a localized map or an object is structure this
310
+ * rule cannot write into a meta tag.
311
+ *
312
+ * @param {Object} record
313
+ * @returns {string|undefined}
314
+ */
315
+ export function recordImage(record) {
316
+ if (!record || typeof record !== 'object') return undefined
317
+ const image = record.image
318
+ return typeof image === 'string' && image.trim() !== '' ? image : undefined
319
+ }
320
+
300
321
  /**
301
322
  * The record field a route param is matched on — ONE MAP, read by the local match
302
323
  * (`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, recordTitle, routeBinding, routeParamName, parentRouteOf } from './route-match.js'
14
+ import { matchDynamicRoute, recordTitle, recordImage, routeBinding, routeParamName, parentRouteOf } from './route-match.js'
15
15
  import { resolveFetchConfigs, pageRouteQuery, recordPages } from './fetch-config.js'
16
16
  import { declaredKeys } from './data-keys.js'
17
17
  import { pageRecordConfig, planFor, runPlanSync } from './page-data.js'
@@ -636,6 +636,11 @@ export default class Website {
636
636
  if (currentItem.description || currentItem.excerpt) {
637
637
  pageData.description = currentItem.description || currentItem.excerpt
638
638
  }
639
+ // ⭐ And by its picture: a link to the page shows the record's image (`recordImage`).
640
+ // ⛔ Until 2026-10-05 it showed the page's or the site's `seo.image`, the same for
641
+ // every record.
642
+ const image = recordImage(currentItem)
643
+ if (image) pageData.seo = { ...(pageData.seo || {}), image }
639
644
  } else if (answered) {
640
645
  // The cache answered and the record is not there: the set is loaded and this ID is
641
646
  // not in it, or the record question answered `[]` — definitive not found.
@@ -755,7 +760,7 @@ export default class Website {
755
760
  /**
756
761
  * Get layout metadata from foundation config
757
762
  * @param {string} layoutName - Layout name
758
- * @returns {Object|null} Layout meta { areas, transitions, defaults }
763
+ * @returns {Object|null} Layout meta { transitions, layers, scroll, defaults } — a page's areas are its site's `layout/` folder (`getLayoutAreas`)
759
764
  */
760
765
  getLayoutMeta(layoutName) {
761
766
  return findLayoutEntry(globalThis.uniweb?.foundationConfig?.layoutMeta, layoutName) || null
package/src/schemas.js DELETED
@@ -1,45 +0,0 @@
1
- /**
2
- * The rich-form-schema predicate. One function, and deliberately only one.
3
- *
4
- * Rich schemas live under `data.schemas` in a component's meta.js. They drive
5
- * two author input paths that both land at `content.data[schema-id]`: tagged
6
- * markdown blocks (``` ```yaml:<id> ``` ```) and the editor's FormBlock widget.
7
- *
8
- * `isRichSchema` is a **dispatch** predicate — "is this already the rich shape?"
9
- * — and it is read at render by `runtime/src/prepare-props.js` (`applySchemas`)
10
- * and at build by `build/src/runtime-schema.js`. Both are on the hot path of
11
- * every site, which is what earns it a place in this package.
12
- *
13
- * ⛔ **Do not add editor-side schema code here.** `normalizeSchema` — "can this
14
- * be edited, and as what?" — sat beside this function until 2026-09-01 purely
15
- * because the two were adjacent; it never called `isRichSchema` and had no
16
- * consumer outside the editor. `@uniweb/core` loads on every site in every lane
17
- * and is not tree-shaken on the hosted one, so an editor-only function on its
18
- * entry is paid for by every visitor of every site. It now lives in the
19
- * zero-dependency leaf that owns what a data-schema *means*:
20
- * **`@uniweb/schemas/editor-form`**.
21
- *
22
- * Conditional field visibility (a rich-schema feature) is likewise editor-only
23
- * and is not implemented here — the editor owns its own evaluator.
24
- */
25
-
26
- /**
27
- * Does a `data.schemas` entry look like a rich form schema?
28
- *
29
- * Rich schemas have an ordered `fields` array (composite arrays and
30
- * nested objects), distinct from simple keyed-object schemas used for
31
- * tagged blocks. Markers (any of):
32
- * - `fields` is an array
33
- * - `isComposite: true`
34
- * - `childSchema` present
35
- *
36
- * @param {*} schema - Schema value to inspect.
37
- * @returns {boolean}
38
- */
39
- export function isRichSchema(schema) {
40
- if (!schema || typeof schema !== 'object') return false
41
- if (Array.isArray(schema.fields)) return true
42
- if (schema.isComposite === true) return true
43
- if (schema.childSchema && typeof schema.childSchema === 'object') return true
44
- return false
45
- }