@uniweb/core 0.15.0 → 0.16.0

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.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Core classes for the Uniweb platform - Uniweb, Website, Page, Block",
5
5
  "type": "module",
6
6
  "exports": {
@@ -42,8 +42,8 @@
42
42
  "vitest": "^4.1.7"
43
43
  },
44
44
  "dependencies": {
45
- "@uniweb/semantic-parser": "^1.4.0",
46
- "@uniweb/theming": "^0.1.15"
45
+ "@uniweb/theming": "^0.1.15",
46
+ "@uniweb/semantic-parser": "^1.4.0"
47
47
  },
48
48
  "scripts": {
49
49
  "test": "vitest run"
package/src/index.js CHANGED
@@ -49,11 +49,6 @@ export {
49
49
  } from './data-paths.js'
50
50
  export { evaluate as evaluateWhere, match as matchWhere } from './where.js'
51
51
  export { isRichSchema } from './schemas.js'
52
- // ⛔ `normalizeSchema` MOVED to `@uniweb/schemas/editor-form` (2026-09-01) —
53
- // it is editor-only and never called `isRichSchema`. Still exported here for
54
- // ONE more release so `frontend` (a `workspace:*` pin, so a removal is live
55
- // at commit time) can migrate its single import. Delete both lines then.
56
- export { normalizeSchema } from './schemas.js'
57
52
  // ⛔ `Tracker` is NOT on the package entry. It is a FEATURE, not part of the
58
53
  // object graph this package exists to define, and putting it here made every
59
54
  // consumer of core carry 1,576 gzip of it -- press, unipress, `@uniweb/api`
package/src/schemas.js CHANGED
@@ -1,18 +1,26 @@
1
1
  /**
2
- * Shared helpers for rich form schemas.
3
- *
4
- * Rich schemas live under `data.schemas` in a component's meta.js. They
5
- * drive two author input paths that both land at `content.data[schema-id]`:
6
- *
7
- * 1. Tagged markdown blocks (``` ```yaml:<id> ``` ```)
8
- * 2. The FormBlock editor widget
9
- *
10
- * Detection is shared across the build pipeline (emit path), the runtime
11
- * (dispatch in applySchemas), and the editor (filter for FormBlock menu)
12
- * so all three agree on what counts as a rich schema.
13
- *
14
- * Conditional field visibility (a rich-schema feature) is an editor-only
15
- * concern and is not implemented here the editor owns its own evaluator.
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.
16
24
  */
17
25
 
18
26
  /**
@@ -35,91 +43,3 @@ export function isRichSchema(schema) {
35
43
  if (schema.childSchema && typeof schema.childSchema === 'object') return true
36
44
  return false
37
45
  }
38
-
39
- /**
40
- * ⛔ **DEPRECATED — MOVED to `@uniweb/schemas/editor-form` (2026-09-01).**
41
- * This copy stays only so `frontend`, which pins core as `workspace:*` and
42
- * would break at commit time, can migrate its one import. Delete it once that
43
- * lands; nothing else in any repo reads it.
44
- *
45
- * ⭐ **It never belonged here.** It does not call `isRichSchema` and never did
46
- * — it re-inlines the same three checks — so nothing but adjacency held it in
47
- * this file. The two answer different questions for different audiences:
48
- * `isRichSchema` is a render-time dispatch predicate (`prepare-props.js`,
49
- * `runtime-schema.js`) and stays; this is read only by an editor, while core
50
- * ships to every site in every lane and is not tree-shaken on the hosted one.
51
- *
52
- * Normalize any authored `data:` schema shape to the rich form the editor
53
- * renders, or null when it is not a single form at all.
54
- *
55
- * WHY THIS EXISTS. `isRichSchema` answers "is this already the rich shape?",
56
- * which is the right question for dispatch and the wrong one for "can this be
57
- * edited". There are THREE authored shapes and it accepts exactly one:
58
- *
59
- * { fields: [ {id, …} ] } meta.js inline rich-form → true
60
- * { fields: { name: spec } } a RESOLVED NAMED REF → FALSE
61
- * { name: spec } meta.js inline field map → false
62
- *
63
- * The middle row is the important one and the reason this helper exists at all
64
- * (it once read "the reason it is in `core` rather than in the editor" — that
65
- * was the wrong conclusion from a right observation: the gap is real, closing
66
- * it in one shared place is right, and `@uniweb/schemas` is that place).
67
- * A named ref (`'@/article'`, `'@std/person'`) is the
68
- * FIRST authoring form the docs show, and `validateAndNormalizeSchema` in the
69
- * build resolves it to `{ fields: <MAP> }` — a map, not an array. So filtering
70
- * with `isRichSchema` discards not merely "simple" schemas but the primary
71
- * documented one, and any consumer that wants to render it has to re-derive the
72
- * conversion. Three consumers re-deriving it is exactly the divergence the
73
- * shared predicate was introduced to prevent.
74
- *
75
- * A field map is an unordered `fields[]`, so the conversion is mechanical.
76
- * Ordering comes from `Object.entries`, which is insertion order for string keys
77
- * — i.e. the order the author wrote, which is the order a form should show.
78
- *
79
- * `sections` returns null on purpose: a sectioned data-schema describes a Model
80
- * with several sections, which is not one form. Flattening it would invent a
81
- * layout the author never expressed.
82
- *
83
- * @param {*} schema - any authored or resolved `data:` schema value
84
- * @returns {{ fields: Array<object> } | null} the rich shape, or null
85
- */
86
- export function normalizeSchema(schema) {
87
- if (!schema || typeof schema !== 'object' || Array.isArray(schema))
88
- return null
89
-
90
- // Already rich — hand back untouched. Composite/childSchema variants are rich
91
- // by `isRichSchema`'s definition and are not ours to reshape.
92
- if (Array.isArray(schema.fields)) return schema
93
- if (schema.isComposite === true || schema.childSchema) return schema
94
-
95
- // A sectioned Model is not a single form.
96
- if (schema.sections !== undefined) return null
97
-
98
- const mapToFields = (map) =>
99
- Object.entries(map).map(([id, spec]) =>
100
- typeof spec === 'string' ? { id, type: spec } : { id, ...spec }
101
- )
102
-
103
- // A resolved named ref: `fields` present, as a map.
104
- if (schema.fields && typeof schema.fields === 'object') {
105
- const { fields, ...rest } = schema
106
- return { ...rest, fields: mapToFields(fields) }
107
- }
108
-
109
- // An inline field map: no `fields` key, so every value must be an OBJECT
110
- // carrying `type`.
111
- //
112
- // The bare-type string shorthand (`{ cpu: 'string' }`) is deliberately NOT
113
- // accepted here, even though schema FILES support it. Without a `fields` key
114
- // there is nothing to distinguish it from ordinary data: `{ name: 'Acme' }` and
115
- // `{ cpu: 'string' }` are the same shape, and an earlier cut of this function
116
- // turned `{ name, description }` into a two-field form. Erring toward null
117
- // costs an author the object spelling; erring the other way invents a form out
118
- // of a config block.
119
- const entries = Object.entries(schema)
120
- if (!entries.length) return null
121
- const isFieldSpec = ([, v]) =>
122
- v && typeof v === 'object' && !Array.isArray(v) && v.type !== undefined
123
- if (!entries.every(isFieldSpec)) return null
124
- return { fields: mapToFields(schema) }
125
- }