@adukiorg/anza 0.2.0 → 0.2.2

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 (80) hide show
  1. package/CHANGELOG.md +81 -4
  2. package/README.md +97 -133
  3. package/bin/anza/anza +0 -0
  4. package/bin/anza/anza.exe +0 -0
  5. package/bin/anza/find.js +35 -0
  6. package/bin/anza/index.js +34 -0
  7. package/bin/anza/launch.js +19 -0
  8. package/bin/common/index.js +7 -0
  9. package/bin/common/logs.js +62 -0
  10. package/bin/create/copy.js +18 -0
  11. package/bin/create/index.js +45 -0
  12. package/bin/create/run.js +210 -0
  13. package/bin/create/write.js +19 -0
  14. package/importmap.json +4 -0
  15. package/package.json +16 -10
  16. package/src/core/offline/{usage.md → notes/usage.md} +11 -1
  17. package/src/core/router/boot.js +82 -0
  18. package/src/core/router/cascade.js +76 -0
  19. package/src/core/router/container.js +63 -72
  20. package/src/core/router/graph.js +144 -0
  21. package/src/core/router/index.js +12 -2
  22. package/src/core/router/intercept.js +26 -7
  23. package/src/core/router/lca.js +58 -0
  24. package/src/core/router/match.js +49 -36
  25. package/src/core/router/notes/audit-old.md +887 -0
  26. package/src/core/router/notes/audti.md +773 -0
  27. package/src/core/router/notes/tasks.md +473 -0
  28. package/src/core/router/{usage.md → notes/usage.md} +57 -35
  29. package/src/core/router/sync/tab.js +6 -4
  30. package/src/core/router/transitions.js +35 -8
  31. package/src/core/router/trie.js +130 -0
  32. package/src/core/security/{usage.md → notes/usage.md} +1 -2
  33. package/src/core/storage/{usage.md → notes/usage.md} +6 -6
  34. package/src/core/theme/index.js +78 -0
  35. package/src/core/ui/define/index.js +2 -1
  36. package/src/core/ui/define/orchestrator.js +10 -4
  37. package/src/core/ui/defs/dock.js +134 -0
  38. package/src/core/ui/defs/index.js +20 -0
  39. package/src/core/ui/defs/page.js +89 -0
  40. package/src/core/ui/defs/part.js +28 -0
  41. package/src/core/ui/defs/spec.js +96 -0
  42. package/src/core/ui/defs/view.js +23 -0
  43. package/src/core/ui/index.js +16 -3
  44. package/src/core/ui/notes/definations.md +979 -0
  45. package/src/tokens/index.css +1 -0
  46. package/src/tokens/semantic/contrast.css +18 -0
  47. package/src/tokens/semantic/transitions.css +32 -0
  48. package/types/core/platform/index.d.ts +39 -10
  49. package/types/core/router/index.d.ts +9 -0
  50. package/types/core/theme/index.d.ts +18 -0
  51. package/types/core/ui/index.d.ts +11 -0
  52. package/types/index.d.ts +1 -0
  53. package/bin/anza.js +0 -63
  54. package/bin/create.js +0 -150
  55. package/src/core/api/plan.md +0 -209
  56. package/src/core/events/missing.md +0 -103
  57. package/src/core/events/plan.md +0 -177
  58. package/src/core/offline/missing.md +0 -89
  59. package/src/core/offline/plan.md +0 -143
  60. package/src/core/platform/missing.md +0 -119
  61. package/src/core/platform/platform.d.ts +0 -88
  62. package/src/core/router/missing.md +0 -716
  63. package/src/core/router/outlet.js +0 -139
  64. package/src/core/router/plan.md +0 -370
  65. package/src/core/security/missing.md +0 -97
  66. package/src/core/state/missing.md +0 -165
  67. package/src/core/storage/missing.md +0 -165
  68. package/src/core/storage/plan.md +0 -69
  69. package/src/core/ui/implementation.md +0 -170
  70. package/src/core/ui/plan.md +0 -510
  71. package/src/core/ui/ui.types.md +0 -890
  72. /package/src/core/animations/{usage.md → notes/usage.md} +0 -0
  73. /package/src/core/api/{usage.md → notes/usage.md} +0 -0
  74. /package/src/core/events/{usage.md → notes/usage.md} +0 -0
  75. /package/src/core/platform/{usage.md → notes/usage.md} +0 -0
  76. /package/src/core/state/{usage.md → notes/usage.md} +0 -0
  77. /package/src/core/ui/{usage.md → notes/usage.md} +0 -0
  78. /package/src/core/ui/{watch.md → notes/watch.md} +0 -0
  79. /package/src/core/workers/{plan.md → notes/plan.md} +0 -0
  80. /package/src/core/workers/{usage.md → notes/usage.md} +0 -0
@@ -1,510 +0,0 @@
1
- # Native UI Runtime Implementation Plan
2
-
3
- This plan turns the existing `tags`, `refs`, `on`, and proposed `watch` APIs into a robust implementation track for `src/core/ui`. It covers the browser-side primitive JavaScript runtime, the Rust template scanner, lifecycle integration, cache invalidation, safety rules, and documentation work.
4
-
5
- ## 1. Scope
6
-
7
- Implement a small native-first component runtime around `ui.element` and `ui.container`:
8
-
9
- - `refs`: O(1) named element anchors from `ref="name"`.
10
- - `tags`: cached Shadow DOM selector access.
11
- - `on`: lifecycle-safe delegated event binding.
12
- - `watch`: lifecycle-safe scoped `MutationObserver` binding.
13
- - `anza` HTML scan: Rust-generated `.tags.json` descriptors for runtime prewarming and optional type generation.
14
- - `usage.md`: complete public API examples and usage rules.
15
-
16
- This is not a virtual DOM. Rendering remains direct DOM mutation inside a component-owned shadow root, scheduled through the existing scheduler when work is not part of initial mount.
17
-
18
- ## 2. Files Read
19
-
20
- Runtime:
21
-
22
- - `src/core/ui/base.js`
23
- - `src/core/ui/index.js`
24
- - `src/core/ui/observe.js`
25
- - `src/core/ui/schedule.js`
26
- - `src/core/ui/template.js`
27
- - `src/core/ui/transitions.js`
28
- - `src/core/ui/define/element.js`
29
- - `src/core/ui/define/container.js`
30
- - `src/core/ui/define/proxy.js`
31
- - `src/core/ui/define/utils.js`
32
- - `src/core/ui/define/state.js`
33
- - `src/core/ui/define/orchestrator.js`
34
-
35
- Tooling:
36
-
37
- - `tools/src/main.rs`
38
- - `tools/src/extract/runner.rs`
39
- - `tools/src/extract/html.rs`
40
- - `tools/src/watcher/runner.rs`
41
- - `tools/src/server/runner.rs`
42
- - `tools/src/types/mod.rs`
43
-
44
- Architecture notes consulted:
45
-
46
- - `docs/notes/04 - Web Components.md`
47
- - `docs/notes/06 - Rendering.md`
48
- - `docs/notes/07 - Reactivity.md`
49
- - `docs/notes/10 - Event Architecture.md`
50
- - `docs/notes/12 - Performance.md`
51
- - `docs/notes/14 - Memory Management.md`
52
- - `docs/notes/17 - Browser API.md`
53
- - `docs/notes/18 - Limitations, Browser Gaps, and Polyfill Strategy.md`
54
-
55
- ## 3. Current State
56
-
57
- The codebase already has the core skeleton:
58
-
59
- | Area | Current state | Required change |
60
- | --- | --- | --- |
61
- | Lifecycle | `BaseElement` creates `this.ctrl` on connect and aborts on disconnect. | Keep. Ensure all injected helpers derive cleanup from this signal. |
62
- | Template/style loading | `preloadResources` fetches HTML, CSS, and optional `.tags.json`. | Keep. Add stricter descriptor validation and inline-template fallback scanning where possible. |
63
- | `refs` | Built in `element.js` from descriptor refs. Frozen after mount. | Support descriptor-free fallback scan and document stable-template semantics. |
64
- | `tags` | `TagsCache` has `one`, `all`, `each`, `prewarmId`, `clear`. | Split single/all caches to avoid selector shape collisions. Add safer invalidation hooks. |
65
- | `on` | Proxy creates a new listener on the shadow root for every call. | Change to one root listener per event type, with registration records per selector. Add `.once` and options support. |
66
- | `watch` | Spec exists in `watch.md`; no runtime helper is injected. | Implement `createMutationWatcher` and inject it into mount/update. |
67
- | Rust scan | `parse_and_emit` emits refs, ids, classes, tags, compound selectors. | Return results/errors, scan more selector hints, add duplicate-ref warnings, optional type output. |
68
- | Watcher | HTML changes trigger `.tags.json` regeneration. | Keep. Add removal cleanup and better debounce result handling. |
69
- | Usage docs | `usage.md` documents element/container/tags/on/scheduler. | Add watch, observe, transition, template, signal/options, direct listener escape hatches. |
70
-
71
- ## 4. Target Injection Shape
72
-
73
- Every `mount` and `update` receives the same base context. Update additionally receives the changed property data.
74
-
75
- ```javascript
76
- mount({ el, ctrl, tags, on, refs, watch, internals }) {}
77
-
78
- update({
79
- el,
80
- ctrl,
81
- tags,
82
- on,
83
- refs,
84
- watch,
85
- internals,
86
- name,
87
- val,
88
- prev
89
- }) {}
90
-
91
- unmount({ el, tags, refs, internals }) {}
92
- ```
93
-
94
- Implementation rule: build this context once per connected lifecycle and reuse the same helper instances for mount and every scheduled update during that connection.
95
-
96
- ## 5. Primitive JavaScript Runtime Plan & Optimizations
97
-
98
- ### 5.1 Context Builder & Runtime Core Optimizations
99
-
100
- Create a small internal helper in `src/core/ui/define/proxy.js` or a new `context.js`:
101
-
102
- ```javascript
103
- export function createComponentContext({ el, shadowRoot, ctrl, descriptor, internals }) {
104
- const tags = new TagsCache(shadowRoot);
105
- const refs = createRefs(shadowRoot, descriptor);
106
- const on = createEventDelegator(shadowRoot, ctrl.signal);
107
- const watch = createMutationWatcher(shadowRoot, ctrl.signal);
108
-
109
- prewarmTags(tags, shadowRoot, descriptor);
110
- installInvalidationHooks(shadowRoot, tags, refs, watch);
111
-
112
- return Object.freeze({ el, ctrl, tags, on, refs, watch, internals });
113
- }
114
- ```
115
-
116
- #### Core Runtime Performance Optimizations
117
-
118
- 1. **Single-Allocation Symbol Backing Store**: Component attributes and values are cached in local `Symbol` properties bound to the element prototype at instantiation. This guarantees $O(1)$ property access and prevents repetitive layout invalidations from reading native attributes.
119
- 2. **Visual vs. Non-Visual State Microtask Batching**: ARIA attributes and custom non-visual properties are scheduled using `queueMicrotask` to instantly execute without animation frame delay. Visual mutations continue using `requestAnimationFrame` for stutter-free frame-synced drawing.
120
- 3. **Constructable Stylesheet HMR Memory Leak Protection**: Standard styles hot-swapping is handled via a global cache map `hmrStyleListeners` on the runtime, instead of attaching style listeners on individual custom elements. This stops active stylesheets from accumulating memory leak records across hot reloads.
121
- 4. **Synchronous Connected Lifecycle Connection Check**: If standard custom element templates or stylesheets are already in memory, the connection lifecycle executes connection hooks synchronously to avoid microtask paint delays and enable instantaneous first paint.
122
- 5. **Scroll-Blocking Passive Delegator**: The `on` event delegator registers shadow-root events with `passive: true` by default. It upgrades listeners to `passive: false` only if the registered event specifically overrides browser defaults via `preventDefault()`, maximizing touch and scroll responsiveness.
123
- 6. **Target-Specific MutationObservers**: Low-level observers bind directly to target elements with `{ subtree: false }` unless tree traversal watches are specifically requested.
124
- 7. **Cache Invalidation MutationObserver**: A dedicated `childList` MutationObserver is placed on the shadowRoot to safely flush tags selector maps and refs lists whenever DOM elements are mutated, replacing slow prototype monkey-patching of `innerHTML` or `replaceChildren`.
125
- 8. **Single-Pass Ref fallback**: Descriptor fallback extraction utilizes a single `querySelectorAll('[ref]')` query to compile anchors in one pass, avoiding multiple traversal runs.
126
-
127
- ### 5.2 Context Helper Mapping
128
-
129
- Target behavior:
130
-
131
- ```javascript
132
- refs.submit
133
- refs.email
134
- refs.status
135
- ```
136
-
137
- Rules:
138
-
139
- - `refs` is a plain frozen object.
140
- - Descriptor refs are preferred.
141
- - If no descriptor exists, fallback to `shadowRoot.querySelectorAll('[ref]')`.
142
- - Missing refs return `undefined`.
143
- - Duplicate refs are a development warning; the first element wins.
144
- - Runtime does not remove the `ref` attribute. It remains inspectable in DevTools.
145
-
146
- Implementation:
147
-
148
- ```javascript
149
- function createRefs(root, descriptor) {
150
- const names = descriptor?.refs;
151
- const out = Object.create(null);
152
-
153
- if (Array.isArray(names) && names.length) {
154
- for (const name of names) {
155
- const found = root.querySelectorAll(`[ref="${CSS.escape(name)}"]`);
156
- if (found[0]) out[name] = found[0];
157
- if (found.length > 1) warnDuplicateRef(name, found.length);
158
- }
159
- } else {
160
- for (const node of root.querySelectorAll('[ref]')) {
161
- const name = node.getAttribute('ref');
162
- if (name && !(name in out)) out[name] = node;
163
- else if (name) warnDuplicateRef(name);
164
- }
165
- }
166
-
167
- return Object.freeze(out);
168
- }
169
- ```
170
-
171
- ### 5.3 `tags`
172
-
173
- Target methods:
174
-
175
- ```javascript
176
- tags.one(selector) // Element | null
177
- tags.all(selector) // Element[]
178
- tags.each(selector, fn) // void
179
- tags.has(selector) // boolean, planned convenience
180
- tags.clear() // internal/public escape hatch
181
- ```
182
-
183
- Required fix: the current `TagsCache` stores `one()` and `all()` results in the same `Map`. Calling `tags.one('button')` and then `tags.all('button')` can return the wrong shape. Use separate maps or namespace keys:
184
-
185
- ```javascript
186
- class TagsCache {
187
- #one = new Map();
188
- #all = new Map();
189
-
190
- one(selector) {
191
- if (!this.#one.has(selector)) {
192
- this.#one.set(selector, this.root.querySelector(selector));
193
- }
194
- return this.#one.get(selector);
195
- }
196
-
197
- all(selector) {
198
- if (!this.#all.has(selector)) {
199
- this.#all.set(selector, Array.from(this.root.querySelectorAll(selector)));
200
- }
201
- return this.#all.get(selector);
202
- }
203
- }
204
- ```
205
-
206
- Invalidation:
207
-
208
- - Clear caches after `shadowRoot.replaceChildren(...)`.
209
- - Clear caches after writes to `shadowRoot.innerHTML`.
210
- - Clear caches after appending a full template fragment during first mount is not needed if caches are created after append.
211
- - Clear caches when `watch` observes structural changes caused by component code only if the component opts in, because clearing on every mutation defeats caching for dynamic lists.
212
- - Document that `tags` is best for stable anchors. Dynamic collections should call `tags.all()` after rendering or use delegated `on`.
213
-
214
- Prewarming:
215
-
216
- - `descriptor.ids`: set `#id` entries using `shadowRoot.getElementById(id)` where available.
217
- - `descriptor.refs`: optionally set `[ref="name"]` entries after refs are built.
218
- - Do not prewarm every class/tag by default. Large templates would do unnecessary work.
219
-
220
- ### 5.4 `on`
221
-
222
- Target usage:
223
-
224
- ```javascript
225
- on.click('button', handler)
226
- on.click('button', handler, ctrl.signal)
227
- on.click('button', handler, { signal: ctrl.signal, passive: true })
228
- on.click.once('button', handler)
229
- on['nav:change']('[data-tab]', handler)
230
- ```
231
-
232
- Handler signature:
233
-
234
- ```javascript
235
- (event, matchedElement) => void
236
- ```
237
-
238
- Implementation model:
239
-
240
- - One native `addEventListener` per event type on the shadow root.
241
- - Each event type has a registry of bindings.
242
- - Each binding stores `{ selector, handler, signal, once, options }`.
243
- - The shared root listener dispatches to matching bindings using `event.target.closest(selector)`.
244
- - The match must satisfy `shadowRoot.contains(match)`.
245
- - If a binding signal aborts, remove only that binding.
246
- - If all bindings for an event type are removed, the root listener can remain until component abort, or be removed for tidiness.
247
-
248
- Options:
249
-
250
- ```javascript
251
- on.click(selector, handler)
252
- on.click(selector, handler, signal)
253
- on.click(selector, handler, {
254
- signal,
255
- once,
256
- capture,
257
- passive
258
- })
259
- ```
260
-
261
- Non-delegated escape hatch:
262
-
263
- Use raw platform APIs for events that cannot be delegated cleanly, such as `scroll`, `resize`, `load`, and some focus flows:
264
-
265
- ```javascript
266
- refs.scroller.addEventListener('scroll', handleScroll, { signal: ctrl.signal });
267
- ```
268
-
269
- ### 5.5 `watch`
270
-
271
- Implement `createMutationWatcher(shadowRoot, defaultSignal)` as documented in `watch.md`.
272
-
273
- Public methods:
274
-
275
- ```javascript
276
- watch.attr(target, attr, handler, signalOrOptions?)
277
- watch.kids(target, handler, signalOrOptions?)
278
- watch.kids(target, { deep: true }, handler, signalOrOptions?)
279
- watch.text(target, handler, signalOrOptions?)
280
- watch.tree(target, handler, signalOrOptions?)
281
-
282
- watch.attr.once(...)
283
- watch.kids.once(...)
284
- watch.text.once(...)
285
- watch.tree.once(...)
286
- ```
287
-
288
- Core data structure:
289
-
290
- ```javascript
291
- {
292
- id,
293
- kind: 'attr' | 'kids' | 'text' | 'tree',
294
- target, // Element
295
- selector, // string | null
296
- attrs, // Set<string> | '*'
297
- deep, // boolean
298
- handler,
299
- signal,
300
- once
301
- }
302
- ```
303
-
304
- Observer model:
305
-
306
- - One `MutationObserver` per component instance.
307
- - Observe the shadow root with the union of options required by all active registrations.
308
- - Recompute observer options when registrations are added or removed.
309
- - Disconnect on lifecycle abort.
310
- - Guard callback with `if (defaultSignal.aborted) return;` because queued mutation callbacks can still run after `disconnect()`.
311
-
312
- Matching:
313
-
314
- - Selector targets are resolved to element lists at registration time.
315
- - Direct element targets must be inside the shadow root.
316
- - `watch.attr` matches `attributes` records whose target is the watched element or a matching descendant when appropriate.
317
- - `watch.kids` matches `childList` records on the target, or descendants when `{ deep: true }`.
318
- - `watch.text` matches `characterData` records whose parent is the target or inside the target.
319
- - `watch.tree` receives the raw batch after filtering to the target subtree.
320
-
321
- ### 5.6 Error Behavior
322
-
323
- Development:
324
-
325
- - Invalid selector: warn and skip registration.
326
- - Direct target outside shadow root: throw `WatchError` / `EventBindingError`.
327
- - Missing target selector: warn and return a no-op disposer.
328
- - Duplicate refs: warn.
329
-
330
- Production:
331
-
332
- - Invalid bindings return a no-op disposer where possible.
333
- - Do not throw for missing optional DOM. Components may render conditionally.
334
-
335
- ### 5.7 Disposer Return Values
336
-
337
- All `on.*` and `watch.*` calls should return a disposer:
338
-
339
- ```javascript
340
- const off = on.click('button', handler);
341
- off();
342
-
343
- const stop = watch.attr(refs.submit, 'disabled', handler);
344
- stop();
345
- ```
346
-
347
- This does not replace signal cleanup. It gives component code an explicit way to stop a temporary binding early.
348
-
349
- ## 6. Rust Tooling Plan
350
-
351
- The current Rust scanner lives in `tools/src/extract/html.rs` and is invoked from `extract::run` and the dev watcher.
352
-
353
- ### 6.1 Make HTML Parsing Testable
354
-
355
- Refactor:
356
-
357
- ```rust
358
- pub fn parse_html(content: &str) -> TagsDescriptor
359
- pub fn parse_file(path: &Path) -> anyhow::Result<TagsDescriptor>
360
- pub fn emit_descriptor(html_path: &Path, descriptor: &TagsDescriptor) -> anyhow::Result<PathBuf>
361
- pub fn parse_and_emit(html_path: &Path) -> anyhow::Result<PathBuf>
362
- ```
363
-
364
- Why: the current `parse_and_emit` logs and silently returns. Returning `Result` lets build/watch modes surface real compiler feedback.
365
-
366
- ### 6.2 Descriptor Shape
367
-
368
- Keep the current fields and add optional metadata:
369
-
370
- ```json
371
- {
372
- "version": 1,
373
- "refs": ["email", "submit"],
374
- "ids": ["email"],
375
- "classes": ["btn", "primary"],
376
- "tags": ["form", "input", "button"],
377
- "compound": ["button.btn", "input.field"],
378
- "attrs": ["aria-expanded", "data-state"],
379
- "refTypes": {
380
- "email": "HTMLInputElement",
381
- "submit": "HTMLButtonElement"
382
- }
383
- }
384
- ```
385
-
386
- Required behavior:
387
-
388
- - Deterministic sorted arrays.
389
- - HTML5-compliant parsing through `scraper` initially.
390
- - Duplicate ref diagnostics with file path and ref name.
391
- - Invalid ref names diagnostics if the name is not a valid JS property identifier; still emit the descriptor so bracket access can be used in the future.
392
-
393
- ### 6.3 CLI and Watch Methods
394
-
395
- Current binary:
396
-
397
- ```bash
398
- anza --src src --dist dist --build
399
- anza --src src --port 3000
400
- ```
401
-
402
- Target methods:
403
-
404
- ```bash
405
- anza scan --src src
406
- anza scan --src src --watch
407
- anza build --src src --dist dist
408
- anza dev --src src --port 3000
409
- ```
410
-
411
- If command subcommands are too large for the first pass, keep the current flags but document them clearly in `usage.md`:
412
-
413
- ```bash
414
- anza --src src --build
415
- anza --src src --port 3000
416
- ```
417
-
418
- Watcher requirements:
419
-
420
- - On `.html` change: regenerate adjacent `.tags.json`.
421
- - On `.js` change: regenerate element typings.
422
- - On `.css` change: send HMR CSS payload.
423
- - On deleted `.html`: remove stale `.tags.json` if it was generated.
424
- - Debounce per path, not only per batch, to avoid losing rapid save events.
425
-
426
- ### 6.4 Optional Type Output
427
-
428
- Generate `index.tags.d.ts` next to `index.tags.json`:
429
-
430
- ```typescript
431
- export interface TemplateRefs {
432
- email: HTMLInputElement;
433
- submit: HTMLButtonElement;
434
- }
435
- ```
436
-
437
- This is optional for the runtime but valuable for editor completion.
438
-
439
- ## 7. Integration Steps
440
-
441
- 1. Fix `TagsCache` shape caching.
442
- 2. Extract context creation from `element.js`.
443
- 3. Add descriptor-free `refs` fallback and duplicate diagnostics.
444
- 4. Replace `createEventDelegator` internals with a registry per event type.
445
- 5. Implement `createMutationWatcher`.
446
- 6. Inject `watch` into `mount` and `update`.
447
- 7. Add invalidation hooks for `replaceChildren` and `innerHTML`.
448
- 8. Refactor Rust `parse_and_emit` into testable methods.
449
- 9. Add scanner tests for refs, ids, classes, tags, compound, duplicate refs, and malformed HTML.
450
- 10. Add browser tests for `tags`, `refs`, `on`, and `watch`.
451
- 11. Update `usage.md` with all supported public usage.
452
-
453
- ## 8. Test Plan
454
-
455
- JavaScript runtime tests:
456
-
457
- - `tags.one()` returns an element and caches it.
458
- - `tags.all()` returns an array and does not collide with `one()`.
459
- - `tags.each()` iterates with stable indexes.
460
- - Cache clears after `replaceChildren`.
461
- - `refs` builds from descriptor.
462
- - `refs` falls back to scanning `[ref]`.
463
- - Duplicate refs warn in development.
464
- - `on.click` delegates from child content to the matching ancestor.
465
- - Multiple `on.click` bindings share one root listener.
466
- - `on.click.once` removes itself after first fire.
467
- - Custom signal abort removes only that binding.
468
- - Lifecycle abort removes all bindings.
469
- - `watch.attr` receives `(attr, next, prev, el)`.
470
- - `watch.kids` receives arrays for added/removed nodes.
471
- - `watch.kids({ deep: true })` sees descendant child changes.
472
- - `watch.text` receives text changes.
473
- - `watch.tree` receives raw records.
474
- - `watch.*.once` removes only its registration.
475
- - `watch` ignores queued mutations after lifecycle abort.
476
-
477
- Rust tests:
478
-
479
- - Parses refs, ids, class names, tag names, and compound selectors.
480
- - Emits sorted deterministic JSON.
481
- - Handles malformed fragments without panicking.
482
- - Warns on duplicate refs.
483
- - Rebuilds descriptors on HTML watcher events.
484
- - Build mode copies source then emits descriptors.
485
-
486
- ## 9. Usage Documentation Requirements
487
-
488
- `usage.md` must cover:
489
-
490
- - `ui.element`
491
- - `ui.container`
492
- - `props` and `update`
493
- - `refs`
494
- - `tags.one`, `tags.all`, `tags.each`, `tags.clear`
495
- - `on.event`, custom event names, `.once`, signals/options
496
- - `watch.attr`, `watch.kids`, `watch.text`, `watch.tree`, `.once`, direct refs, custom signals/options
497
- - `ui.schedule`, `ui.scheduleFrame`, `ui.yield`
498
- - `ui.observe.resize`, `intersection`, `mutation`, `performance`
499
- - `ui.transition`
500
- - `ui.template`
501
- - `anza` scan/build/dev behavior
502
- - Escape hatches and when to use raw platform APIs
503
-
504
- ## 10. Non-Goals
505
-
506
- - No runtime WASM DOM access. Rust stays in build/dev tooling.
507
- - No virtual DOM.
508
- - No framework-style global reactive auto-tracking in the UI runtime.
509
- - No closed shadow roots.
510
- - No cross-component DOM watching. Components communicate outward with composed `CustomEvent`s and downward through properties or methods.