nodality 1.1.8 → 1.1.9

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.
Files changed (189) hide show
  1. package/bin/mcp-server.mjs +315 -0
  2. package/bin/nodality.js +16 -0
  3. package/dist/animator.cjs.js.LICENSE.txt +1 -1
  4. package/dist/animator.esm.js.LICENSE.txt +1 -1
  5. package/dist/audionew.cjs.js.LICENSE.txt +1 -1
  6. package/dist/audionew.esm.js.LICENSE.txt +1 -1
  7. package/dist/base.cjs.js.LICENSE.txt +1 -1
  8. package/dist/base.esm.js.LICENSE.txt +1 -1
  9. package/dist/beta-desktop-bar.cjs.js.LICENSE.txt +1 -1
  10. package/dist/beta-desktop-bar.esm.js.LICENSE.txt +1 -1
  11. package/dist/beta-mobile-bar.cjs.js.LICENSE.txt +1 -1
  12. package/dist/beta-mobile-bar.esm.js.LICENSE.txt +1 -1
  13. package/dist/bundle.umd.js.LICENSE.txt +1 -1
  14. package/dist/button.cjs.js.LICENSE.txt +1 -1
  15. package/dist/button.esm.js.LICENSE.txt +1 -1
  16. package/dist/card-getter.cjs.js.LICENSE.txt +1 -1
  17. package/dist/card-getter.esm.js.LICENSE.txt +1 -1
  18. package/dist/center.cjs.js.LICENSE.txt +1 -1
  19. package/dist/center.esm.js.LICENSE.txt +1 -1
  20. package/dist/checkbox.cjs.js.LICENSE.txt +1 -1
  21. package/dist/checkbox.esm.js.LICENSE.txt +1 -1
  22. package/dist/code.cjs.js.LICENSE.txt +1 -1
  23. package/dist/code.esm.js.LICENSE.txt +1 -1
  24. package/dist/container.cjs.js.LICENSE.txt +1 -1
  25. package/dist/container.esm.js.LICENSE.txt +1 -1
  26. package/dist/data-list.cjs.js.LICENSE.txt +1 -1
  27. package/dist/data-list.esm.js.LICENSE.txt +1 -1
  28. package/dist/designer.cjs.js.LICENSE.txt +1 -1
  29. package/dist/designer.esm.js.LICENSE.txt +1 -1
  30. package/dist/element-mapper.cjs.js.LICENSE.txt +1 -1
  31. package/dist/element-mapper.esm.js.LICENSE.txt +1 -1
  32. package/dist/finalresult.esm.js.LICENSE.txt +1 -1
  33. package/dist/flex-card.cjs.js.LICENSE.txt +1 -1
  34. package/dist/flex-card.esm.js.LICENSE.txt +1 -1
  35. package/dist/flex-grid.cjs.js.LICENSE.txt +1 -1
  36. package/dist/flex-grid.esm.js.LICENSE.txt +1 -1
  37. package/dist/flex-row.cjs.js.LICENSE.txt +1 -1
  38. package/dist/flex-row.esm.js.LICENSE.txt +1 -1
  39. package/dist/floating-input.cjs.js.LICENSE.txt +1 -1
  40. package/dist/floating-input.esm.js.LICENSE.txt +1 -1
  41. package/dist/free.cjs.js.LICENSE.txt +1 -1
  42. package/dist/free.esm.js.LICENSE.txt +1 -1
  43. package/dist/horizontal-scroller.cjs.js.LICENSE.txt +1 -1
  44. package/dist/horizontal-scroller.esm.js.LICENSE.txt +1 -1
  45. package/dist/image-picker.cjs.js.LICENSE.txt +1 -1
  46. package/dist/image-picker.esm.js.LICENSE.txt +1 -1
  47. package/dist/image.cjs.js.LICENSE.txt +1 -1
  48. package/dist/image.esm.js.LICENSE.txt +1 -1
  49. package/dist/index.cjs.js.LICENSE.txt +1 -1
  50. package/dist/index.d.ts +1 -1
  51. package/dist/index.esm.js.LICENSE.txt +1 -1
  52. package/dist/keyframe-animation.cjs.js.LICENSE.txt +1 -1
  53. package/dist/keyframe-animation.esm.js.LICENSE.txt +1 -1
  54. package/dist/link-getter.cjs.js.LICENSE.txt +1 -1
  55. package/dist/link-getter.esm.js.LICENSE.txt +1 -1
  56. package/dist/link.cjs.js.LICENSE.txt +1 -1
  57. package/dist/link.esm.js.LICENSE.txt +1 -1
  58. package/dist/meta-adder.cjs.js.LICENSE.txt +1 -1
  59. package/dist/meta-adder.esm.js.LICENSE.txt +1 -1
  60. package/dist/modal-2025.cjs.js.LICENSE.txt +1 -1
  61. package/dist/modal-2025.esm.js.LICENSE.txt +1 -1
  62. package/dist/multiswitcher.cjs.js.LICENSE.txt +1 -1
  63. package/dist/multiswitcher.esm.js.LICENSE.txt +1 -1
  64. package/dist/new-nav-bar.cjs.js.LICENSE.txt +1 -1
  65. package/dist/new-nav-bar.esm.js.LICENSE.txt +1 -1
  66. package/dist/picker.cjs.js.LICENSE.txt +1 -1
  67. package/dist/picker.esm.js.LICENSE.txt +1 -1
  68. package/dist/progress.cjs.js.LICENSE.txt +1 -1
  69. package/dist/progress.esm.js.LICENSE.txt +1 -1
  70. package/dist/radio.cjs.js.LICENSE.txt +1 -1
  71. package/dist/radio.esm.js.LICENSE.txt +1 -1
  72. package/dist/range.cjs.js.LICENSE.txt +1 -1
  73. package/dist/range.esm.js.LICENSE.txt +1 -1
  74. package/dist/scroll-video.cjs.js.LICENSE.txt +1 -1
  75. package/dist/scroll-video.esm.js.LICENSE.txt +1 -1
  76. package/dist/side-bar.cjs.js.LICENSE.txt +1 -1
  77. package/dist/side-bar.esm.js.LICENSE.txt +1 -1
  78. package/dist/side-nav-bar.cjs.js.LICENSE.txt +1 -1
  79. package/dist/side-nav-bar.esm.js.LICENSE.txt +1 -1
  80. package/dist/simple-bar.cjs.js.LICENSE.txt +1 -1
  81. package/dist/simple-bar.esm.js.LICENSE.txt +1 -1
  82. package/dist/slider-2025.cjs.js.LICENSE.txt +1 -1
  83. package/dist/slider-2025.esm.js.LICENSE.txt +1 -1
  84. package/dist/spacer.cjs.js.LICENSE.txt +1 -1
  85. package/dist/spacer.esm.js.LICENSE.txt +1 -1
  86. package/dist/stack.cjs.js.LICENSE.txt +1 -1
  87. package/dist/stack.esm.js.LICENSE.txt +1 -1
  88. package/dist/stacker.cjs.js.LICENSE.txt +1 -1
  89. package/dist/stacker.esm.js.LICENSE.txt +1 -1
  90. package/dist/table.cjs.js.LICENSE.txt +1 -1
  91. package/dist/table.esm.js.LICENSE.txt +1 -1
  92. package/dist/text-field.cjs.js.LICENSE.txt +1 -1
  93. package/dist/text-field.esm.js.LICENSE.txt +1 -1
  94. package/dist/text.cjs.js.LICENSE.txt +1 -1
  95. package/dist/text.esm.js.LICENSE.txt +1 -1
  96. package/dist/theme.cjs.js.LICENSE.txt +1 -1
  97. package/dist/theme.esm.js.LICENSE.txt +1 -1
  98. package/dist/transform-anim.cjs.js.LICENSE.txt +1 -1
  99. package/dist/transform-anim.esm.js.LICENSE.txt +1 -1
  100. package/dist/ulist.cjs.js.LICENSE.txt +1 -1
  101. package/dist/ulist.esm.js.LICENSE.txt +1 -1
  102. package/dist/video.cjs.js.LICENSE.txt +1 -1
  103. package/dist/video.esm.js.LICENSE.txt +1 -1
  104. package/dist/wrap.cjs.js.LICENSE.txt +1 -1
  105. package/dist/wrap.esm.js.LICENSE.txt +1 -1
  106. package/dist/zoom-card.cjs.js.LICENSE.txt +1 -1
  107. package/dist/zoom-card.esm.js.LICENSE.txt +1 -1
  108. package/layout/animator.js +1 -1
  109. package/layout/audio.js +1 -1
  110. package/layout/audionew.js +1 -1
  111. package/layout/base.js +1 -1
  112. package/layout/beta-desktop-bar.js +1 -1
  113. package/layout/beta-mobile-bar.js +1 -1
  114. package/layout/button.js +1 -1
  115. package/layout/center.js +1 -1
  116. package/layout/checkbox.js +1 -1
  117. package/layout/circle.js +1 -1
  118. package/layout/code.js +1 -1
  119. package/layout/container.js +1 -1
  120. package/layout/dropdown-2025.js +1 -1
  121. package/layout/flex-card.js +1 -1
  122. package/layout/flex-grid.js +1 -1
  123. package/layout/flex-row.js +1 -1
  124. package/layout/form-components/custom.js +1 -1
  125. package/layout/form-components/data-list.js +1 -1
  126. package/layout/form-components/floating-input.js +1 -1
  127. package/layout/form-components/form-all.js +1 -1
  128. package/layout/form-components/form.js +1 -1
  129. package/layout/form-components/image-picker.js +1 -1
  130. package/layout/form-components/picker.js +1 -1
  131. package/layout/form-components/radio.js +1 -1
  132. package/layout/form-components/radiogroup.js +1 -1
  133. package/layout/form-components/range.js +1 -1
  134. package/layout/free.js +1 -1
  135. package/layout/grid-switcher.js +1 -1
  136. package/layout/grid.js +1 -1
  137. package/layout/horizontal-scroller.js +1 -1
  138. package/layout/image.js +1 -1
  139. package/layout/index.js +1 -1
  140. package/layout/link.js +1 -1
  141. package/layout/list.js +1 -1
  142. package/layout/meta-adder.js +1 -1
  143. package/layout/modal-2025.js +1 -1
  144. package/layout/morph.js +1 -1
  145. package/layout/multiswitcher.js +1 -1
  146. package/layout/nav-bar.js +1 -1
  147. package/layout/nav-factor/custom-div.js +1 -1
  148. package/layout/new-nav-bar.js +1 -1
  149. package/layout/polygon.js +1 -1
  150. package/layout/prerender-site.js +1 -1
  151. package/layout/prerender.js +1 -1
  152. package/layout/progress.js +1 -1
  153. package/layout/row.js +1 -1
  154. package/layout/scroll-video.js +1 -1
  155. package/layout/side-bar.js +1 -1
  156. package/layout/side-nav-bar.js +1 -1
  157. package/layout/simple-bar.js +1 -1
  158. package/layout/slider-2025.js +1 -1
  159. package/layout/spacer.js +1 -1
  160. package/layout/stack.js +1 -1
  161. package/layout/svg.js +1 -1
  162. package/layout/switcher.js +1 -1
  163. package/layout/table.js +1 -1
  164. package/layout/text-field.js +1 -1
  165. package/layout/text.js +1 -1
  166. package/layout/ulist.js +1 -1
  167. package/layout/video.js +1 -1
  168. package/layout/wrap.js +1 -1
  169. package/layout/zoom-card.js +1 -1
  170. package/lib/card-getter.js +1 -1
  171. package/lib/codegen.js +1 -1
  172. package/lib/data.js +1 -1
  173. package/lib/designer.js +1 -1
  174. package/lib/element-mapper.js +1 -1
  175. package/lib/keyframe-animation.js +1 -1
  176. package/lib/link-getter.js +1 -1
  177. package/lib/morph-node.js +1 -1
  178. package/lib/raster-inspect.js +1 -1
  179. package/lib/raster-ops.js +1 -1
  180. package/lib/raster-presets.js +1 -1
  181. package/lib/scroll-video.js +1 -1
  182. package/lib/seo.js +1 -1
  183. package/lib/stacker.js +1 -1
  184. package/lib/suggest.js +1 -1
  185. package/lib/theme.js +1 -1
  186. package/lib/transform-anim.js +1 -1
  187. package/lib/transition.js +1 -1
  188. package/lib/validate-nodes.js +227 -0
  189. package/package.json +2 -1
@@ -0,0 +1,227 @@
1
+ /*!
2
+ * nodality v1.1.9
3
+ * (c) 2026 Filip Vabrousek
4
+ * License: MIT
5
+ */
6
+
7
+ /*!
8
+ * validate-nodes.js — check an `N` array before it is applied.
9
+ *
10
+ * The library already validates an op DEFINITION when it is registered
11
+ * (`validateRasterOp`), which catches a bad op author. Nothing checked a
12
+ * node INSTANCE — the thing a page, or an agent, actually writes. A
13
+ * misspelled op name, a parameter that belongs to a different op, a
14
+ * driver that does not exist: all of these were silent, and a silent
15
+ * no-op is the worst possible failure for a generator that cannot see
16
+ * the screen.
17
+ *
18
+ * The report shape is deliberately identical to the one `layout/morph.js`
19
+ * `validate()` returns — `{ ok, errors: [{ code, path, got, suggestions,
20
+ * valid }] }` — so that a consumer, human or agent, learns one shape and
21
+ * reads every diagnostic this library produces with it.
22
+ *
23
+ * Suggestions are the point. An agent that writes "dithr" should be told
24
+ * "dither" and repair itself in one turn, rather than receiving nothing
25
+ * and concluding the effect simply does not work.
26
+ */
27
+
28
+ import {
29
+ REGISTRY, RASTER_OP_NAMES, DRIVER_NAMES, RASTER_UNITS,
30
+ FRAMEWORK_DOC, EASING_NAMES,
31
+ } from "./raster-ops.js";
32
+ import { didYouMean, suggest } from "./suggest.js";
33
+
34
+ /** Node families, told apart exactly the way the runtime tells them apart. */
35
+ const familyOf = (node) => {
36
+ if (!node || typeof node !== "object") return "invalid";
37
+ const op = node.op;
38
+ if (typeof op === "string") return op === "morph" ? "morph" : "raster";
39
+ if (op && typeof op === "object" && typeof op.name === "string") return "design";
40
+ return "invalid";
41
+ };
42
+
43
+ /**
44
+ * Check an array of nodes against the registry.
45
+ *
46
+ * @param {Array} nodes the `N` array
47
+ * @param {Array} [elements] the `E` array; when given, `target` and a
48
+ * morph's `from`/`to` are checked to name ids that actually exist,
49
+ * which is the other half of the "silent no-op" problem: a correctly
50
+ * spelled op aimed at an element that is not there does nothing at all.
51
+ */
52
+ export function validateNodes(nodes, elements) {
53
+ const errors = [];
54
+ const push = (code, path, got, suggestions, valid) => {
55
+ errors.push({
56
+ code, path, got,
57
+ suggestions: suggestions || [],
58
+ valid: valid || [],
59
+ });
60
+ };
61
+
62
+ if (!Array.isArray(nodes)) {
63
+ push("BAD_NODES", "nodes", nodes, [], ["an array of node objects"]);
64
+ return { ok: false, errors };
65
+ }
66
+
67
+ // Ids declared by E, including children, since `target` may name any
68
+ // of them. Absent E, id checking is skipped rather than guessed at.
69
+ const ids = [];
70
+ const walk = (list) => {
71
+ if (!Array.isArray(list)) return;
72
+ for (const el of list) {
73
+ if (!el || typeof el !== "object") continue;
74
+ if (typeof el.id === "string") ids.push(el.id);
75
+ walk(el.children);
76
+ }
77
+ };
78
+ if (elements !== undefined) walk(elements);
79
+ const idsKnown = elements !== undefined;
80
+
81
+ const checkTargets = (value, path) => {
82
+ if (value === undefined) return;
83
+ const list = Array.isArray(value) ? value : [value];
84
+ list.forEach((t, i) => {
85
+ const at = Array.isArray(value) ? `${path}[${i}]` : path;
86
+ if (typeof t !== "string") {
87
+ push("BAD_TARGET", at, t, [], ["an element id"]);
88
+ return;
89
+ }
90
+ // Ids are written bare in E and may be written with a leading
91
+ // "#" in a node; accept both rather than inventing a rule.
92
+ const bare = t.startsWith("#") ? t.slice(1) : t;
93
+ if (idsKnown && !ids.includes(bare)) {
94
+ push("UNKNOWN_TARGET", at, t, suggest(bare, ids), ids);
95
+ }
96
+ });
97
+ };
98
+
99
+ nodes.forEach((node, i) => {
100
+ const path = `nodes[${i}]`;
101
+ const family = familyOf(node);
102
+
103
+ if (family === "invalid") {
104
+ push("BAD_NODE", path, node, [],
105
+ ["{ op: \"<raster op>\" }", "{ op: { name: \"<design op>\" } }",
106
+ "{ op: \"morph\", from, to }"]);
107
+ return;
108
+ }
109
+
110
+ checkTargets(node.target, `${path}.target`);
111
+
112
+ if (family === "design") return; // design nodes carry their own vocabulary
113
+
114
+ if (family === "morph") {
115
+ // The morph node's own contract. `from` and `to` are what make
116
+ // it a morph; without them the node is inert.
117
+ if (typeof node.from !== "string") {
118
+ push("MISSING_FIELD", `${path}.from`, node.from, [],
119
+ ["the id of the element that morphs"]);
120
+ } else {
121
+ checkTargets(node.from, `${path}.from`);
122
+ }
123
+ if (node.to === undefined) {
124
+ push("MISSING_FIELD", `${path}.to`, undefined, [],
125
+ ["[\"id\", ...]", "{ \"Link label\": \"id\" }"]);
126
+ } else if (Array.isArray(node.to)) {
127
+ checkTargets(node.to, `${path}.to`);
128
+ } else if (node.to && typeof node.to === "object") {
129
+ for (const label in node.to) {
130
+ checkTargets(node.to[label], `${path}.to.${label}`);
131
+ }
132
+ } else {
133
+ push("BAD_FIELD", `${path}.to`, node.to, [],
134
+ ["an array of ids, or a map of link label to id"]);
135
+ }
136
+ if (node.duration !== undefined &&
137
+ (typeof node.duration !== "number" || !(node.duration > 0))) {
138
+ push("BAD_VALUE", `${path}.duration`, node.duration, [],
139
+ ["a positive number of milliseconds"]);
140
+ }
141
+ if (node.effect !== undefined &&
142
+ typeof node.effect !== "string" && !Array.isArray(node.effect)) {
143
+ push("BAD_FIELD", `${path}.effect`, node.effect, [],
144
+ ["a preset name, or an inline array of raster nodes"]);
145
+ }
146
+ return;
147
+ }
148
+
149
+ // ── raster nodes ────────────────────────────────────────────
150
+ const def = REGISTRY[node.op];
151
+ if (!def) {
152
+ push("UNKNOWN_OP", `${path}.op`, node.op,
153
+ suggest(node.op, RASTER_OP_NAMES), RASTER_OP_NAMES);
154
+ return; // parameters cannot be judged without a definition
155
+ }
156
+
157
+ const params = (def.doc && def.doc.params) || {};
158
+ const known = Object.keys(params);
159
+ const shared = Object.keys(FRAMEWORK_DOC);
160
+
161
+ for (const key in node) {
162
+ if (key === "op") continue;
163
+ if (shared.includes(key) || known.includes(key)) continue;
164
+ // A parameter that belongs to no op is a typo; one that belongs
165
+ // to a DIFFERENT op is the more interesting mistake, and the
166
+ // suggestion list covers both because it is drawn from this
167
+ // op's vocabulary plus the shared one.
168
+ push("UNKNOWN_PARAM", `${path}.${key}`, key,
169
+ suggest(key, known.concat(shared)), known.concat(shared));
170
+ }
171
+
172
+ if (node.by !== undefined && !DRIVER_NAMES.includes(node.by)) {
173
+ push("UNKNOWN_DRIVER", `${path}.by`, node.by,
174
+ suggest(node.by, DRIVER_NAMES), DRIVER_NAMES);
175
+ }
176
+ if (node.ease !== undefined && typeof node.ease === "string" &&
177
+ !EASING_NAMES.includes(node.ease)) {
178
+ push("UNKNOWN_EASING", `${path}.ease`, node.ease,
179
+ suggest(node.ease, EASING_NAMES), EASING_NAMES);
180
+ }
181
+ if (node.side !== undefined && !["old", "new"].includes(node.side)) {
182
+ push("BAD_VALUE", `${path}.side`, node.side,
183
+ suggest(String(node.side), ["old", "new"]), ["old", "new"]);
184
+ }
185
+ });
186
+
187
+ return { ok: errors.length === 0, errors };
188
+ }
189
+
190
+ /**
191
+ * The vocabulary, as data — what an agent needs before writing anything.
192
+ * Assembled from the registry rather than maintained separately, so it
193
+ * cannot drift from what the pipeline actually accepts.
194
+ */
195
+ export function describeOps() {
196
+ const ops = RASTER_OP_NAMES.map((name) => {
197
+ const def = REGISTRY[name] || {};
198
+ const doc = def.doc || {};
199
+ const params = doc.params || {};
200
+ return {
201
+ op: name,
202
+ stage: Array.isArray(def.stage) ? def.stage : [def.stage].filter(Boolean),
203
+ summary: doc.summary || "",
204
+ params: Object.keys(params).map((p) => ({
205
+ name: p,
206
+ default: params[p].default,
207
+ unit: params[p].unit,
208
+ structural: !!params[p].structural,
209
+ summary: params[p].summary || "",
210
+ })),
211
+ };
212
+ });
213
+ return {
214
+ ops,
215
+ shared: Object.keys(FRAMEWORK_DOC).map((k) => ({
216
+ name: k,
217
+ default: FRAMEWORK_DOC[k].default,
218
+ unit: FRAMEWORK_DOC[k].unit,
219
+ summary: FRAMEWORK_DOC[k].summary || "",
220
+ })),
221
+ drivers: DRIVER_NAMES,
222
+ easings: EASING_NAMES,
223
+ units: RASTER_UNITS,
224
+ };
225
+ }
226
+
227
+ export { didYouMean };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodality",
3
- "version": "1.1.8",
3
+ "version": "1.1.9",
4
4
  "description": "A lightweight library for declarative UI elements.",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",
@@ -28,6 +28,7 @@
28
28
  "./morph": "./layout/morph.js",
29
29
  "./presets": "./lib/raster-presets.js",
30
30
  "./raster": "./lib/raster-ops.js",
31
+ "./validate": "./lib/validate-nodes.js",
31
32
  "./inspect": "./lib/raster-inspect.js",
32
33
  "./examples/*": "./examples/*.js",
33
34
  "./transition": "./lib/transition.js",