bitwrench 2.0.24 → 2.0.25
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/README.md +8 -6
- package/dist/bitwrench-bccl.cjs.js +1 -1
- package/dist/bitwrench-bccl.cjs.min.js +1 -1
- package/dist/bitwrench-bccl.cjs.min.js.gz +0 -0
- package/dist/bitwrench-bccl.esm.js +1 -1
- package/dist/bitwrench-bccl.esm.min.js +1 -1
- package/dist/bitwrench-bccl.esm.min.js.gz +0 -0
- package/dist/bitwrench-bccl.umd.js +1 -1
- package/dist/bitwrench-bccl.umd.min.js +1 -1
- package/dist/bitwrench-bccl.umd.min.js.gz +0 -0
- package/dist/bitwrench-code-edit.cjs.js +1 -1
- package/dist/bitwrench-code-edit.cjs.min.js +1 -1
- package/dist/bitwrench-code-edit.es5.js +1 -1
- package/dist/bitwrench-code-edit.es5.min.js +1 -1
- package/dist/bitwrench-code-edit.esm.js +1 -1
- package/dist/bitwrench-code-edit.esm.min.js +1 -1
- package/dist/bitwrench-code-edit.umd.js +1 -1
- package/dist/bitwrench-code-edit.umd.min.js +1 -1
- package/dist/bitwrench-code-edit.umd.min.js.gz +0 -0
- package/dist/bitwrench-debug.js +1 -1
- package/dist/bitwrench-debug.min.js +1 -1
- package/dist/bitwrench-lean.cjs.js +41 -22
- package/dist/bitwrench-lean.cjs.min.js +4 -4
- package/dist/bitwrench-lean.cjs.min.js.gz +0 -0
- package/dist/bitwrench-lean.es5.js +43 -24
- package/dist/bitwrench-lean.es5.min.js +4 -4
- package/dist/bitwrench-lean.es5.min.js.gz +0 -0
- package/dist/bitwrench-lean.esm.js +41 -22
- package/dist/bitwrench-lean.esm.min.js +4 -4
- package/dist/bitwrench-lean.esm.min.js.gz +0 -0
- package/dist/bitwrench-lean.umd.js +41 -22
- package/dist/bitwrench-lean.umd.min.js +4 -4
- package/dist/bitwrench-lean.umd.min.js.gz +0 -0
- package/dist/bitwrench-util-css.cjs.js +1 -1
- package/dist/bitwrench-util-css.cjs.min.js +1 -1
- package/dist/bitwrench-util-css.es5.js +1 -1
- package/dist/bitwrench-util-css.es5.min.js +1 -1
- package/dist/bitwrench-util-css.esm.js +1 -1
- package/dist/bitwrench-util-css.esm.min.js +1 -1
- package/dist/bitwrench-util-css.umd.js +1 -1
- package/dist/bitwrench-util-css.umd.min.js +1 -1
- package/dist/bitwrench-util-css.umd.min.js.gz +0 -0
- package/dist/bitwrench.cjs.js +41 -22
- package/dist/bitwrench.cjs.min.js +6 -6
- package/dist/bitwrench.cjs.min.js.gz +0 -0
- package/dist/bitwrench.css +6 -6
- package/dist/bitwrench.d.ts +659 -0
- package/dist/bitwrench.es5.js +43 -24
- package/dist/bitwrench.es5.min.js +4 -4
- package/dist/bitwrench.es5.min.js.gz +0 -0
- package/dist/bitwrench.esm.js +41 -22
- package/dist/bitwrench.esm.min.js +4 -4
- package/dist/bitwrench.esm.min.js.gz +0 -0
- package/dist/bitwrench.min.css +1 -1
- package/dist/bitwrench.umd.js +41 -22
- package/dist/bitwrench.umd.min.js +6 -6
- package/dist/bitwrench.umd.min.js.gz +0 -0
- package/dist/builds.json +87 -87
- package/dist/bwserve.cjs.js +2 -2
- package/dist/bwserve.esm.js +2 -2
- package/dist/sri.json +46 -46
- package/docs/README.md +5 -3
- package/docs/bitwrench-mcp.md +1 -1
- package/docs/bitwrench-taco-schema-discussion.md +694 -0
- package/docs/bitwrench_api.md +4 -4
- package/docs/bitwrench_typescript_usage.md +441 -0
- package/docs/component-cheatsheet.md +1 -1
- package/docs/framework-translation-table.md +1 -1
- package/docs/llm-bitwrench-guide.md +28 -1
- package/docs/routing.md +1 -1
- package/docs/thinking-in-bitwrench.md +3 -3
- package/docs/tutorial-bwserve.md +1 -1
- package/docs/tutorial-website.md +1 -1
- package/package.json +7 -3
- package/readme.html +14 -8
- package/src/bitwrench-styles.js +17 -17
- package/src/bitwrench.d.ts +659 -0
- package/src/bitwrench.js +21 -2
- package/src/cli/serve.js +1 -0
- package/src/mcp/live.js +3 -1
- package/src/mcp/server.js +7 -7
- package/src/version.js +3 -3
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
# UI as Schema: Validating TACO Objects
|
|
2
|
+
|
|
3
|
+
**Status**: Discussion document -- not on the implementation roadmap
|
|
4
|
+
**Date**: 2026-03-28
|
|
5
|
+
|
|
6
|
+
This document explores what becomes possible when your UI layer is plain
|
|
7
|
+
data. It is not a specification or a product plan. It lives here to invite
|
|
8
|
+
discussion from developers who see the potential.
|
|
9
|
+
|
|
10
|
+
If you have thoughts, open an issue or reach out.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## The Observation
|
|
15
|
+
|
|
16
|
+
A bitwrench UI is a tree of `{t, a, c, o}` objects. That tree is JSON.
|
|
17
|
+
Not "JSON-like" or "serializable to JSON" -- it IS JSON (modulo function
|
|
18
|
+
references in `a.onclick` and `o.handle`, which are the only non-serializable
|
|
19
|
+
parts and can be handled with a registry pattern).
|
|
20
|
+
|
|
21
|
+
This means something that no compiled UI framework can claim: **the UI
|
|
22
|
+
definition is data that can be validated, transformed, and enforced using
|
|
23
|
+
the same tools we already use for APIs, configs, and data interchange.**
|
|
24
|
+
|
|
25
|
+
A React component tree? Opaque compiled code. A Vue template? A string
|
|
26
|
+
that gets parsed into a virtual DOM you never see. A Svelte component?
|
|
27
|
+
Gone after compilation. You cannot point a schema validator at any of
|
|
28
|
+
these and ask "is this UI well-formed?" -- the UI description doesn't
|
|
29
|
+
exist as inspectable data at runtime.
|
|
30
|
+
|
|
31
|
+
TACO objects do.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Historical Precedent: We've Done This Before
|
|
36
|
+
|
|
37
|
+
If you're old enough to remember XML-based UI frameworks, this idea
|
|
38
|
+
will feel familiar:
|
|
39
|
+
|
|
40
|
+
**XAML (WPF/.NET)** -- Microsoft's UI markup was XML. You could validate
|
|
41
|
+
it with XSD schemas, transform it with XSLT, generate it from code, and
|
|
42
|
+
enforce organizational standards with schema-based tooling. The Visual
|
|
43
|
+
Studio designer was possible because the UI was self-describing data.
|
|
44
|
+
|
|
45
|
+
**Android XML Layouts** -- `res/layout/*.xml` files are validated against
|
|
46
|
+
the Android SDK's schema. The lint tool (`android lint`) catches invalid
|
|
47
|
+
attributes, accessibility violations, and deprecated APIs -- all because
|
|
48
|
+
the UI is structured data, not code.
|
|
49
|
+
|
|
50
|
+
**XUL (Mozilla)** -- Firefox's entire UI was defined in XUL/XML. Extensions
|
|
51
|
+
could inspect and modify the UI tree as data. Schema validation told you
|
|
52
|
+
if your XUL was well-formed before the browser tried to render it.
|
|
53
|
+
|
|
54
|
+
**Qt QML** -- A JSON-like declarative UI language with a type system and
|
|
55
|
+
static analysis tools. Qt Creator's design mode works because QML is
|
|
56
|
+
inspectable structured data.
|
|
57
|
+
|
|
58
|
+
These frameworks understood something the modern JS ecosystem forgot:
|
|
59
|
+
**when UI is data, tooling comes for free.** The shift to JSX and
|
|
60
|
+
compilation-first frameworks traded that capability for developer
|
|
61
|
+
ergonomics. TACO gets it back without the XML tax.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## What JSON Schema / Pydantic Developers Will Recognize
|
|
66
|
+
|
|
67
|
+
If you use JSON Schema for API validation or Pydantic for Python data
|
|
68
|
+
models, the TACO schema concept maps directly:
|
|
69
|
+
|
|
70
|
+
**JSON Schema parallel**: A TACO schema defines the valid shape of a UI
|
|
71
|
+
subtree the same way a JSON Schema defines the valid shape of an API
|
|
72
|
+
response. The difference is that the "response" is a UI component, and
|
|
73
|
+
the "properties" are HTML tags, attributes, and lifecycle hooks.
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
// This is conceptually identical to a JSON Schema definition:
|
|
77
|
+
var cardSchema = {
|
|
78
|
+
t: { type: 'string', const: 'div' },
|
|
79
|
+
a: {
|
|
80
|
+
properties: {
|
|
81
|
+
class: { type: 'string', pattern: /bw_card/ }
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
c: {
|
|
85
|
+
type: 'array',
|
|
86
|
+
items: { oneOf: [titleSchema, bodySchema, footerSchema] }
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Pydantic parallel**: Pydantic validates Python objects against a model
|
|
92
|
+
definition and gives you clear error messages with field paths. A TACO
|
|
93
|
+
validator would do the same thing:
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
// Pydantic-style: define the model, validate input, get path-specific errors
|
|
97
|
+
var result = validate(taco, cardSchema);
|
|
98
|
+
// => { valid: false, errors: [{ path: '.a.class', message: 'missing bw_card' }] }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**The key insight**: bitwrench could offer this natively because TACO
|
|
102
|
+
objects already have a consistent structure (every node has `t`, `a`,
|
|
103
|
+
`c`, `o`). JSON Schema and Pydantic work on arbitrary shapes. A TACO
|
|
104
|
+
schema knows the domain -- it knows that `t` is a tag name, `a` has
|
|
105
|
+
HTML attributes, `c` is content, and `o` has lifecycle hooks. Domain
|
|
106
|
+
knowledge makes the validator smaller, faster, and more helpful than
|
|
107
|
+
a generic tool.
|
|
108
|
+
|
|
109
|
+
And crucially, this tooling would ship as a separate add-on -- not part
|
|
110
|
+
of bitwrench core. The core library stays zero-dep and lean. The schema
|
|
111
|
+
tool is for teams that want governance, not a tax on everyone.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Scenarios Where This Matters
|
|
116
|
+
|
|
117
|
+
### Scenario 1: The Wrong Property Name
|
|
118
|
+
|
|
119
|
+
You write `bw.makeAccordion({ items: [{ label: 'FAQ', body: 'Answer' }] })`.
|
|
120
|
+
Nothing renders. No error. You stare at it for 10 minutes before realizing
|
|
121
|
+
the properties are `title` and `content`, not `label` and `body`.
|
|
122
|
+
|
|
123
|
+
With schema validation:
|
|
124
|
+
```
|
|
125
|
+
[bw-schema] makeAccordion: items[0] has unknown property "label".
|
|
126
|
+
Did you mean "title"?
|
|
127
|
+
[bw-schema] makeAccordion: items[0] has unknown property "body".
|
|
128
|
+
Did you mean "content"?
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
This is the simplest, most immediately useful scenario. Every bitwrench
|
|
132
|
+
user has hit some version of this.
|
|
133
|
+
|
|
134
|
+
### Scenario 2: Server-Driven UI (bwserve)
|
|
135
|
+
|
|
136
|
+
A Python backend sends TACO over SSE to the browser. The browser renders
|
|
137
|
+
it with `bw.DOM()`. What happens when the server sends:
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{ "t": "script", "c": "alert('pwned')" }
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Or a malformed payload from a bug? Today: it renders. With validation:
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
bwserve.onMessage = function(msg) {
|
|
147
|
+
var result = validate(msg.taco, {
|
|
148
|
+
denyTags: ['script', 'iframe', 'object'],
|
|
149
|
+
denyStringHandlers: true,
|
|
150
|
+
denyRawHtml: true
|
|
151
|
+
});
|
|
152
|
+
if (!result.valid) return; // reject
|
|
153
|
+
bw.DOM(msg.target, msg.taco);
|
|
154
|
+
};
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This is a real security boundary. When UI is data arriving over the wire,
|
|
158
|
+
validation isn't optional -- it's the same as validating API input.
|
|
159
|
+
|
|
160
|
+
### Scenario 3: Embedded Devices (ESP32, RPi)
|
|
161
|
+
|
|
162
|
+
A microcontroller sends JSON TACO updates to a browser dashboard. The
|
|
163
|
+
firmware is written in C. Bugs in the JSON serialization are catchable.
|
|
164
|
+
Schema validation on the browser side catches malformed payloads before
|
|
165
|
+
they break the UI -- and reports exactly what's wrong, which helps debug
|
|
166
|
+
the firmware.
|
|
167
|
+
|
|
168
|
+
### Scenario 4: LLM-Generated UI
|
|
169
|
+
|
|
170
|
+
You ask an LLM to generate a TACO dashboard. It produces:
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"t": "div",
|
|
175
|
+
"attributes": { "className": "dashboard" },
|
|
176
|
+
"children": [...]
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Wrong keys everywhere -- `attributes` instead of `a`, `className` instead
|
|
181
|
+
of `class`, `children` instead of `c`. The LLM hallucinated a React-shaped
|
|
182
|
+
object. Schema validation catches this immediately and the error messages
|
|
183
|
+
are specific enough to include in a retry prompt.
|
|
184
|
+
|
|
185
|
+
### Scenario 5: Enterprise UI Governance
|
|
186
|
+
|
|
187
|
+
A large team building internal tools with bitwrench. The tech lead wants:
|
|
188
|
+
- No inline styles (use design tokens)
|
|
189
|
+
- No string event handlers (security policy)
|
|
190
|
+
- All images must have alt text (accessibility)
|
|
191
|
+
- Only approved BCCL components in production code
|
|
192
|
+
|
|
193
|
+
Today this requires code review discipline. With a schema policy engine,
|
|
194
|
+
it's automated -- run it in CI, same as a linter.
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
var policy = {
|
|
198
|
+
rules: [
|
|
199
|
+
{ rule: 'no-inline-styles', severity: 'warn' },
|
|
200
|
+
{ rule: 'no-string-handlers', severity: 'error' },
|
|
201
|
+
{ rule: 'require-alt-text', severity: 'error' },
|
|
202
|
+
{ rule: 'allowed-components', value: ['card', 'button', 'table', 'nav', 'form'] }
|
|
203
|
+
]
|
|
204
|
+
};
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Scenario 6: Cross-Language Portability
|
|
208
|
+
|
|
209
|
+
Because TACO schema definitions would be plain JSON (no JS-specific
|
|
210
|
+
constructs), the same schema works in:
|
|
211
|
+
- **JavaScript/Node** -- validate in browser or server
|
|
212
|
+
- **Python** -- validate bwserve payloads before sending
|
|
213
|
+
- **Go/Rust** -- validate in microservices that generate UI
|
|
214
|
+
- **C** -- embedded devices that serialize TACO
|
|
215
|
+
|
|
216
|
+
This is the JSON Schema advantage: the schema itself is data, portable
|
|
217
|
+
across any language that can parse JSON.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## How It Might Work
|
|
222
|
+
|
|
223
|
+
### The Simplest Useful Thing
|
|
224
|
+
|
|
225
|
+
A single function that validates the `{t, a, c, o}` shape:
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
function validateTaco(obj) {
|
|
229
|
+
var errors = [];
|
|
230
|
+
if (!obj || typeof obj !== 'object') {
|
|
231
|
+
return { valid: false, errors: [{ message: 'not an object' }] };
|
|
232
|
+
}
|
|
233
|
+
if (typeof obj.t !== 'string') {
|
|
234
|
+
errors.push({ path: '.t', message: 'tag must be a string' });
|
|
235
|
+
}
|
|
236
|
+
if (obj.a !== undefined && typeof obj.a !== 'object') {
|
|
237
|
+
errors.push({ path: '.a', message: 'attributes must be an object' });
|
|
238
|
+
}
|
|
239
|
+
if (obj.o !== undefined) {
|
|
240
|
+
var knownKeys = ['state', 'mounted', 'unmount', 'render', 'handle', 'slots', 'type'];
|
|
241
|
+
Object.keys(obj.o).forEach(function(k) {
|
|
242
|
+
if (knownKeys.indexOf(k) === -1) {
|
|
243
|
+
errors.push({ path: '.o.' + k, message: 'unknown option "' + k + '"' });
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return { valid: errors.length === 0, errors: errors };
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
That's maybe 30 lines and it already catches the most common mistakes.
|
|
252
|
+
No build step, no dependencies, works in any JS environment.
|
|
253
|
+
|
|
254
|
+
### Recursive Tree Validation
|
|
255
|
+
|
|
256
|
+
Walk the content tree and validate every node:
|
|
257
|
+
|
|
258
|
+
```javascript
|
|
259
|
+
function validateTree(taco, path) {
|
|
260
|
+
path = path || '';
|
|
261
|
+
var result = validateTaco(taco);
|
|
262
|
+
// Recurse into content
|
|
263
|
+
if (Array.isArray(taco.c)) {
|
|
264
|
+
taco.c.forEach(function(child, i) {
|
|
265
|
+
if (child && typeof child === 'object' && child.t) {
|
|
266
|
+
var childResult = validateTree(child, path + '.c[' + i + ']');
|
|
267
|
+
result.errors = result.errors.concat(childResult.errors);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
} else if (taco.c && typeof taco.c === 'object' && taco.c.t) {
|
|
271
|
+
var childResult = validateTree(taco.c, path + '.c');
|
|
272
|
+
result.errors = result.errors.concat(childResult.errors);
|
|
273
|
+
}
|
|
274
|
+
result.valid = result.errors.length === 0;
|
|
275
|
+
return result;
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### BCCL Config Validation with Fuzzy Matching
|
|
280
|
+
|
|
281
|
+
```javascript
|
|
282
|
+
var knownConfigs = {
|
|
283
|
+
makeCard: ['title', 'content', 'footer', 'image', 'variant', 'shadow',
|
|
284
|
+
'clickable', 'style', 'class'],
|
|
285
|
+
makeAccordion: ['items', 'multiple', 'variant', 'style', 'class'],
|
|
286
|
+
makeNav: ['items', 'variant', 'vertical', 'style', 'class']
|
|
287
|
+
// ... all 45+ components
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
function validateConfig(factoryName, config) {
|
|
291
|
+
var known = knownConfigs[factoryName];
|
|
292
|
+
if (!known) return { valid: true, errors: [] };
|
|
293
|
+
var errors = [];
|
|
294
|
+
Object.keys(config).forEach(function(key) {
|
|
295
|
+
if (known.indexOf(key) === -1) {
|
|
296
|
+
var suggestion = findClosest(key, known); // Levenshtein
|
|
297
|
+
errors.push({
|
|
298
|
+
path: '.' + key,
|
|
299
|
+
message: factoryName + ': unknown property "' + key + '"'
|
|
300
|
+
+ (suggestion ? '. Did you mean "' + suggestion + '"?' : '')
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
return { valid: errors.length === 0, errors: errors };
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Policy Rules as Functions
|
|
309
|
+
|
|
310
|
+
```javascript
|
|
311
|
+
var builtinRules = {
|
|
312
|
+
'no-inline-styles': function(taco, path) {
|
|
313
|
+
if (taco.a && taco.a.style && typeof taco.a.style === 'object') {
|
|
314
|
+
return { path: path + '.a.style', message: 'inline styles not allowed by policy' };
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
'no-string-handlers': function(taco, path) {
|
|
318
|
+
if (!taco.a) return;
|
|
319
|
+
var errors = [];
|
|
320
|
+
Object.keys(taco.a).forEach(function(k) {
|
|
321
|
+
if (k.indexOf('on') === 0 && typeof taco.a[k] === 'string') {
|
|
322
|
+
errors.push({
|
|
323
|
+
path: path + '.a.' + k,
|
|
324
|
+
message: k + ' must be a function, not a string'
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
return errors;
|
|
329
|
+
},
|
|
330
|
+
'require-alt-text': function(taco, path) {
|
|
331
|
+
if (taco.t === 'img' && (!taco.a || !taco.a.alt)) {
|
|
332
|
+
return { path: path + '.a.alt', message: '<img> requires alt attribute' };
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
None of this is rocket science. The power comes from the fact that TACO
|
|
339
|
+
gives you a uniform data structure to walk. Every node has the same shape.
|
|
340
|
+
The validator doesn't need to understand React hooks, Vue reactivity, or
|
|
341
|
+
Svelte compilation -- it just walks a tree of `{t, a, c, o}` objects.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Proposed v0 Specification Skeleton (Additive)
|
|
346
|
+
|
|
347
|
+
This section is intentionally concrete so implementers can converge on one
|
|
348
|
+
validator behavior. It does not replace the discussion above. It turns the
|
|
349
|
+
discussion into a proposed minimum contract that can be implemented in a
|
|
350
|
+
standalone package (`bw-schema`) without changing bitwrench core.
|
|
351
|
+
|
|
352
|
+
### 1) Scope
|
|
353
|
+
|
|
354
|
+
v0 covers:
|
|
355
|
+
|
|
356
|
+
- Structural validation of TACO trees (`{t, a, c, o}`)
|
|
357
|
+
- Optional policy validation (security, accessibility, governance)
|
|
358
|
+
- Validation for both in-process objects and wire payloads (bwserve/LLM)
|
|
359
|
+
- Machine-readable errors suitable for IDEs, CI, and LLM retry loops
|
|
360
|
+
|
|
361
|
+
v0 does not cover:
|
|
362
|
+
|
|
363
|
+
- Full semantic DOM validation against every HTML spec edge case
|
|
364
|
+
- TypeScript replacement
|
|
365
|
+
- Compiler transforms or code generation
|
|
366
|
+
|
|
367
|
+
### 2) Canonical Node Shape (Normative)
|
|
368
|
+
|
|
369
|
+
A node is valid TACO if:
|
|
370
|
+
|
|
371
|
+
- `t` exists and is a non-empty string (tag name)
|
|
372
|
+
- `a` is optional; when present, it is an object
|
|
373
|
+
- `c` is optional; when present, it is one of:
|
|
374
|
+
- string
|
|
375
|
+
- number
|
|
376
|
+
- `null`/`undefined`/`false` (treated as skipped content)
|
|
377
|
+
- `BwRaw` sentinel (`{ __bw_raw: true, v: string }`)
|
|
378
|
+
- nested TACO node
|
|
379
|
+
- array of any allowed content values above
|
|
380
|
+
- `o` is optional; when present, it is an object with known keys only:
|
|
381
|
+
- `state`, `mounted`, `unmount`, `render`, `handle`, `slots`, `type`
|
|
382
|
+
|
|
383
|
+
Unknown top-level keys are validator findings (severity depends on mode).
|
|
384
|
+
|
|
385
|
+
### 3) Validation Modes (Normative)
|
|
386
|
+
|
|
387
|
+
`validateTree(input, options)` supports:
|
|
388
|
+
|
|
389
|
+
- `mode: 'permissive'` (default for developer ergonomics)
|
|
390
|
+
- checks required shape (`t`, object-ness)
|
|
391
|
+
- unknown keys are `warn`
|
|
392
|
+
- `mode: 'strict'`
|
|
393
|
+
- unknown keys are `error`
|
|
394
|
+
- invalid content unions are `error`
|
|
395
|
+
- malformed `o` options are `error`
|
|
396
|
+
- `mode: 'wire'` (for bwserve/LLM/embedded payload boundaries)
|
|
397
|
+
- strict shape checks
|
|
398
|
+
- policy defaults enabled:
|
|
399
|
+
- deny dangerous tags (`script`, `iframe`, `object`, `embed`)
|
|
400
|
+
- deny string event handlers
|
|
401
|
+
- configurable `bw.raw` allowance (default deny)
|
|
402
|
+
|
|
403
|
+
### 4) Error Model (Normative)
|
|
404
|
+
|
|
405
|
+
Each finding follows a stable shape:
|
|
406
|
+
|
|
407
|
+
```javascript
|
|
408
|
+
{
|
|
409
|
+
code: 'BW_SCHEMA_UNKNOWN_KEY',
|
|
410
|
+
severity: 'error', // 'error' | 'warn' | 'info'
|
|
411
|
+
path: '.c[2].a.className', // JSONPath-like pointer
|
|
412
|
+
message: 'Unknown key "className"; did you mean "class"?',
|
|
413
|
+
hint: 'Use TACO key "a.class" for CSS classes.',
|
|
414
|
+
suggestion: 'class',
|
|
415
|
+
meta: { received: 'className', allowed: ['id', 'class', 'style', '...'] }
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Result envelope:
|
|
420
|
+
|
|
421
|
+
```javascript
|
|
422
|
+
{
|
|
423
|
+
valid: false, // true if no "error" findings
|
|
424
|
+
summary: { errors: 2, warns: 1, infos: 0 },
|
|
425
|
+
findings: [/* ordered by path, then severity */],
|
|
426
|
+
normalized: null // optional output if auto-fix is enabled
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Codes should be stable across versions for tooling interoperability.
|
|
431
|
+
|
|
432
|
+
### 5) Suggested v0 Error Code Set
|
|
433
|
+
|
|
434
|
+
- `BW_SCHEMA_NOT_OBJECT`
|
|
435
|
+
- `BW_SCHEMA_MISSING_TAG`
|
|
436
|
+
- `BW_SCHEMA_INVALID_TAG_TYPE`
|
|
437
|
+
- `BW_SCHEMA_UNKNOWN_TOP_LEVEL_KEY`
|
|
438
|
+
- `BW_SCHEMA_INVALID_ATTRS_TYPE`
|
|
439
|
+
- `BW_SCHEMA_INVALID_OPTIONS_TYPE`
|
|
440
|
+
- `BW_SCHEMA_UNKNOWN_OPTION_KEY`
|
|
441
|
+
- `BW_SCHEMA_INVALID_CONTENT_TYPE`
|
|
442
|
+
- `BW_SCHEMA_UNKNOWN_ATTR_KEY` (optional, configurable)
|
|
443
|
+
- `BW_POLICY_DENY_TAG`
|
|
444
|
+
- `BW_POLICY_DENY_STRING_HANDLER`
|
|
445
|
+
- `BW_POLICY_DENY_RAW_HTML`
|
|
446
|
+
- `BW_POLICY_REQUIRE_ALT_TEXT`
|
|
447
|
+
|
|
448
|
+
### 6) Policy Engine Contract (Normative)
|
|
449
|
+
|
|
450
|
+
Policy rules are pure functions:
|
|
451
|
+
|
|
452
|
+
```javascript
|
|
453
|
+
function rule(node, ctx) {
|
|
454
|
+
// ctx: { path, mode, parent, options, helpers }
|
|
455
|
+
// return: null | finding | finding[]
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Evaluation order:
|
|
460
|
+
|
|
461
|
+
1. Structural validation
|
|
462
|
+
2. Built-in policies (if enabled)
|
|
463
|
+
3. User policies
|
|
464
|
+
|
|
465
|
+
Policy merge behavior:
|
|
466
|
+
|
|
467
|
+
- Global defaults
|
|
468
|
+
- Mode defaults
|
|
469
|
+
- User overrides (last write wins)
|
|
470
|
+
|
|
471
|
+
### 7) Integration Points (Normative Recommendations)
|
|
472
|
+
|
|
473
|
+
#### A) bwserve ingress (high priority)
|
|
474
|
+
|
|
475
|
+
Validate message payloads before `bw.DOM()`/`bw.apply()`:
|
|
476
|
+
|
|
477
|
+
```javascript
|
|
478
|
+
var result = validateTree(msg.node, { mode: 'wire', policy: wirePolicy });
|
|
479
|
+
if (!result.valid) {
|
|
480
|
+
// reject payload, log findings, optionally patch an error placeholder
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
#### B) LLM output boundary
|
|
486
|
+
|
|
487
|
+
Validate parsed model output before render:
|
|
488
|
+
|
|
489
|
+
```javascript
|
|
490
|
+
var r = validateTree(parsed, { mode: 'wire', autoSuggest: true });
|
|
491
|
+
if (!r.valid) {
|
|
492
|
+
// feed r.findings back into retry prompt
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
#### C) CLI/CI
|
|
497
|
+
|
|
498
|
+
`bwcli validate` should:
|
|
499
|
+
|
|
500
|
+
- return non-zero on `error` findings
|
|
501
|
+
- optionally fail on `warn` with `--strict-warn`
|
|
502
|
+
- emit JSON (`--json`) and human text by default
|
|
503
|
+
|
|
504
|
+
### 8) Function and Serialization Profiles
|
|
505
|
+
|
|
506
|
+
Because function references are the main non-JSON part of TACO, v0 should
|
|
507
|
+
define explicit profiles:
|
|
508
|
+
|
|
509
|
+
- `profile: 'runtime'`
|
|
510
|
+
- allows function values in `a.on*`, `o.mounted`, `o.render`, etc.
|
|
511
|
+
- `profile: 'wire-safe'`
|
|
512
|
+
- disallows function values
|
|
513
|
+
- allows only data payload representation
|
|
514
|
+
- `profile: 'wire-registry'`
|
|
515
|
+
- allows function references only as registry tokens/IDs
|
|
516
|
+
|
|
517
|
+
This avoids ambiguity between "authoring objects" and "transport objects."
|
|
518
|
+
|
|
519
|
+
### 9) Normalization / Auto-Fix (Optional v0.1)
|
|
520
|
+
|
|
521
|
+
Auto-fix is opt-in and conservative:
|
|
522
|
+
|
|
523
|
+
- key aliases only (safe rewrites), e.g.:
|
|
524
|
+
- `attributes` -> `a`
|
|
525
|
+
- `children` -> `c`
|
|
526
|
+
- `className` -> `class`
|
|
527
|
+
- never auto-enable dangerous features (`bw.raw`, string handlers)
|
|
528
|
+
- every rewrite emits an `info` finding with before/after path
|
|
529
|
+
|
|
530
|
+
Recommended API:
|
|
531
|
+
|
|
532
|
+
```javascript
|
|
533
|
+
validateTree(input, { normalize: true, aliasMap: defaultAliasMap });
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### 10) Performance Expectations
|
|
537
|
+
|
|
538
|
+
Target complexity:
|
|
539
|
+
|
|
540
|
+
- Structural walk: O(n) nodes
|
|
541
|
+
- Policy checks: O(n * p) where `p` is active rule count
|
|
542
|
+
|
|
543
|
+
Operational guidance:
|
|
544
|
+
|
|
545
|
+
- Validate at boundaries (wire/LLM ingress), not every frame
|
|
546
|
+
- Cache by stable hash for repeated payloads
|
|
547
|
+
- Offer depth/node-count limits to protect against pathological payloads
|
|
548
|
+
|
|
549
|
+
### 11) Versioning and Compatibility
|
|
550
|
+
|
|
551
|
+
Schema contract should include:
|
|
552
|
+
|
|
553
|
+
```javascript
|
|
554
|
+
{
|
|
555
|
+
schemaVersion: '1.0.0',
|
|
556
|
+
tacoVersion: '2.x',
|
|
557
|
+
profile: 'runtime'
|
|
558
|
+
}
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Compatibility policy:
|
|
562
|
+
|
|
563
|
+
- Minor version: additive checks/codes only
|
|
564
|
+
- Major version: breaking code/path semantics
|
|
565
|
+
- Error codes remain stable within major versions
|
|
566
|
+
|
|
567
|
+
### 12) JSON Schema Interop Track
|
|
568
|
+
|
|
569
|
+
Two-layer model is recommended:
|
|
570
|
+
|
|
571
|
+
- Native validator contract (optimized for TACO semantics)
|
|
572
|
+
- Optional JSON Schema export/import for tooling interoperability
|
|
573
|
+
|
|
574
|
+
Practical approach:
|
|
575
|
+
|
|
576
|
+
1. Maintain canonical TACO spec in native format
|
|
577
|
+
2. Generate JSON Schema artifacts from canonical spec
|
|
578
|
+
3. Allow editor/tooling consumers to use `$schema` references
|
|
579
|
+
|
|
580
|
+
### 13) Suggested MVP Sequence
|
|
581
|
+
|
|
582
|
+
Phase 1 (smallest useful):
|
|
583
|
+
|
|
584
|
+
- `validateTaco` + `validateTree`
|
|
585
|
+
- strict/permissive modes
|
|
586
|
+
- stable findings envelope + core error codes
|
|
587
|
+
|
|
588
|
+
Phase 2 (boundary safety):
|
|
589
|
+
|
|
590
|
+
- `wire` mode
|
|
591
|
+
- deny-tag/string-handler/raw-html policies
|
|
592
|
+
- bwserve integration examples
|
|
593
|
+
|
|
594
|
+
Phase 3 (tooling):
|
|
595
|
+
|
|
596
|
+
- `bwcli validate`
|
|
597
|
+
- JSON output for CI
|
|
598
|
+
- LLM retry prompt formatter
|
|
599
|
+
|
|
600
|
+
Phase 4 (governance):
|
|
601
|
+
|
|
602
|
+
- policy packs
|
|
603
|
+
- optional normalization aliases
|
|
604
|
+
- JSON Schema export
|
|
605
|
+
|
|
606
|
+
### 14) Open Design Defaults (Proposed)
|
|
607
|
+
|
|
608
|
+
If no options are provided:
|
|
609
|
+
|
|
610
|
+
- mode: `permissive`
|
|
611
|
+
- profile: `runtime`
|
|
612
|
+
- findings include suggestions when confidence is high
|
|
613
|
+
- validator never mutates input unless `normalize: true`
|
|
614
|
+
|
|
615
|
+
For boundary validation (recommended default):
|
|
616
|
+
|
|
617
|
+
- mode: `wire`
|
|
618
|
+
- profile: `wire-safe`
|
|
619
|
+
- deny dangerous tags + string handlers + raw HTML unless explicitly allowed
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
## What This Is NOT
|
|
624
|
+
|
|
625
|
+
- **Not part of bitwrench core.** The core library stays zero-dep and
|
|
626
|
+
lean. Schema tooling is a separate add-on for teams that want it.
|
|
627
|
+
- **Not a build step.** Validation runs at dev time or at data boundaries
|
|
628
|
+
(bwserve, LLM output). It does not require a compiler or bundler.
|
|
629
|
+
- **Not a type system.** TypeScript already provides static types for
|
|
630
|
+
bitwrench (see `dist/bitwrench.d.ts`). Schema validation is runtime
|
|
631
|
+
checking for data that arrives dynamically -- server payloads, LLM
|
|
632
|
+
output, config-driven UI.
|
|
633
|
+
- **Not on the roadmap.** This document describes what's possible. If
|
|
634
|
+
demand materializes, the architecture is ready. The TACO format
|
|
635
|
+
doesn't need to change to support any of this.
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
## Why This Matters Strategically
|
|
640
|
+
|
|
641
|
+
The JS framework landscape is dominated by compiled, opaque approaches.
|
|
642
|
+
Every major framework (React, Vue, Svelte, Solid) compiles away the UI
|
|
643
|
+
description before runtime. This makes certain categories of tooling
|
|
644
|
+
impossible:
|
|
645
|
+
|
|
646
|
+
| Capability | Compiled frameworks | TACO |
|
|
647
|
+
|------------|-------------------|------|
|
|
648
|
+
| Schema-validate a component tree | Impossible | Walk the JSON |
|
|
649
|
+
| Enforce UI policies in CI | Requires AST parsing | Validate the data |
|
|
650
|
+
| Validate server-sent UI before render | No standard format | `validate(payload)` |
|
|
651
|
+
| Cross-language UI generation | Each needs its own SDK | Emit JSON |
|
|
652
|
+
| LLM UI output validation | Framework-specific | `validate(parse(response))` |
|
|
653
|
+
| Accessibility audit at data level | Requires rendered DOM | Walk the tree |
|
|
654
|
+
| Generate docs from component specs | Custom per framework | Read the schema |
|
|
655
|
+
| Diff two UI versions | Render and compare | `JSON.diff(v1, v2)` |
|
|
656
|
+
|
|
657
|
+
This isn't an argument that TACO is better than React for building UIs.
|
|
658
|
+
It's an argument that data-as-UI opens a category of tooling that
|
|
659
|
+
code-as-UI structurally cannot access. For enterprise environments where
|
|
660
|
+
governance, security, and cross-platform generation matter, that's a
|
|
661
|
+
meaningful differentiator.
|
|
662
|
+
|
|
663
|
+
The XML UI world understood this. XAML, Android XML, and XUL all had
|
|
664
|
+
schema validation, design-time tooling, and cross-language generation
|
|
665
|
+
because the UI was structured data. The modern web lost that when it
|
|
666
|
+
moved to JSX. TACO brings it back in a lighter, JSON-native form.
|
|
667
|
+
|
|
668
|
+
---
|
|
669
|
+
|
|
670
|
+
## Discussion Questions
|
|
671
|
+
|
|
672
|
+
We'd welcome input on any of these:
|
|
673
|
+
|
|
674
|
+
1. Which scenarios resonate most with your use case?
|
|
675
|
+
|
|
676
|
+
2. Is JSON Schema interop important (standard `$schema` references,
|
|
677
|
+
editor autocomplete via schema files), or is a JS-native format
|
|
678
|
+
sufficient?
|
|
679
|
+
|
|
680
|
+
3. For the bwserve/embedded security boundary -- what validation rules
|
|
681
|
+
would you need for server-sent TACO in your environment?
|
|
682
|
+
|
|
683
|
+
4. Would a CLI tool (`bwcli validate myapp.js`) be useful for CI
|
|
684
|
+
integration, or is runtime-only validation enough?
|
|
685
|
+
|
|
686
|
+
5. How strict should a default schema be? Should `validate()` with
|
|
687
|
+
no options be permissive (just check `t` exists) or strict (validate
|
|
688
|
+
everything we know about)?
|
|
689
|
+
|
|
690
|
+
6. For cross-language use: which server-side languages would benefit
|
|
691
|
+
most from a portable TACO schema? Python? Go? Rust?
|
|
692
|
+
|
|
693
|
+
Open an issue at [github.com/deftio/bitwrench](https://github.com/deftio/bitwrench/issues)
|
|
694
|
+
to share your thoughts.
|