Tags: tonioloewald/tosijs
Tags
v1.7.9: take() works in list templates — descriptor is data, memo is … …per-row The take descriptor moves out of bindTake's closure onto the binding entry (DataBinding.take): the closure froze the template's ^. paths forever (the transform ran on xin['^.…'] = undefined instead of the row's value) and shared ONE lastInputs memo across every cloned row, so the first row's update suppressed its siblings' (one row transformed-wrong, the next never updated). Now row instantiation rewrites take.paths exactly like the entry's own path — both dispatchers (touchElement, updateRelativeBindings) route through a shared applyDataBinding in metadata.ts — and the memo rides the per-element (per-row-cloned) take object itself (no extra WeakMap; cloneWithBindings already deep-clones entries). Also fixes one descriptor reused across two elements starving the second. Regression suite: src/take-list-binding.test.ts (relative-path rows, sibling independence, late-added rows, descriptor reuse, absolute paths in rows). Gate: 673/673, tsc clean, lint clean, build 0, Playwright 4/4. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EVU6HWRPnVwqc5XgTSnSv
v1.7.8: detached part with no replacement returns the cached node ins… …tead of throwing (tosijs#21) Root cause of #21 ("this.value stale after interaction on FF/WebKit"): 1.7.7's parts self-healing re-validated cached parts with isConnected and re-resolved stale ones — but when a part had been REMOVED with no replacement (segmented's optional `custom` input: a structural rebuild does options.textContent = '' and only conditionally re-appends it), re-resolution found nothing and THREW where 1.7.5 leniently returned the detached node. The throw fired inside change handlers that destructure this.parts unconditionally, killing the handler before it committed this.value — stale value, correct DOM. The suspected computed-getter change was exonerated (scalar values never touch it); bisect pinned component.ts. Fix: when re-resolution fails but a previously-resolved node exists, return it (1.7.5 leniency); self-healing still wins when a replacement exists; the throw is reserved for refs that never resolved. Verified against tosijs-ui's real tosi-segmented Playwright lane: Firefox 2/2-failing -> green, WebKit 4/4, Chromium green; tosijs-ui tree restored untouched. New coverage this class of bug was missing: - unit tests: detach-then-access, and the full click -> change -> value-commit round-trip (reproduces in happy-dom — the bug was deterministic, not truly browser-specific) - tests/value-commit.pw.ts: the same round-trip in a real browser per engine Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EVU6HWRPnVwqc5XgTSnSv
v1.7.7: parts resolves your OWN part via pre-hydration ownership capt… …ure (supersedes broken 1.7.6, tosijs#20) parts.foo resolves through a single _partsCache that is SEEDED at hydration with this component's own [part] elements — captured from the content tree BEFORE it is inserted and before any nested sub-component hydrates or slots. At that moment the tree is exactly what the component built, so every [part] is unambiguously its own, regardless of nesting depth, <tosi-slot> projection, or whether a sub-component is light- or shadow-DOM. Misses fall back to querySelector (lazily-built parts, static cloned content, css-selector refs) and are cached back into the same map. Reads re-validate isConnected, so a part a render() replaced re-resolves (self-heals), and a missing part is never cached, so a later access resolves once present. This is ownership, not structure — it defeats the case that broke every structural attempt: an own part placed inside a light-DOM slotting sub-component (captured before it's slotted). 1.7.6's custom-element-boundary attempt broke parts for any component laying parts out inside a sub-component (e.g. tosi-tabs); 1.7.6 is deprecated on npm. Also deprecates data-ref (a React-era "refs" fossil): still resolves but warns once, removed from the docs, slated for removal in 1.8.0 — use part="…". All other 1.7.6 content retained (computed-property fix, #19, Tosi* blueprint types, docs). Nine new regression tests in component.test.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EVU6HWRPnVwqc5XgTSnSv
v1.7.6: parts scoping + computed-property crash + XinProps type fixes… …; blueprint Tosi* types; docs overhaul Fixes: - Light-DOM `this.parts.foo` no longer reaches into nested components — part resolution stops at nested custom-element boundaries (shadow DOM was already scoped). Fixes the wrong-part grab when a component contains another instance (tosijs#20, filed from tosijs-ui's self-hosting doc demo). - Registering an object with a getter no longer throws "assign to readonly property" — the set-trap unwrap loop only rewrites writable data properties and never invokes getters during registration; computed properties are legal state. - xinValue/xinPath restored to XinProps (parity with BoxedScalarAPI) — fixes the silent typecheck-only break for object/array proxies (tosijs#19). Added: - TosiBlueprint/TosiFactory/TosiPackagedComponent/TosiComponentSpec canonical type names; Xin* kept as @deprecated aliases (type-only, no runtime change). Changed: - Accessor docs lead with .value/.tosi.value; tosiPath()/tosiValue() as the programmatic alternative. - (dev) editableSources:true; build host bumped to tosijs-ui 1.7.2. Docs: README overhaul (ecosystem table, sharpened case, xin-proxy purge), new tosijs-history page, new Angular page + expanded React page, Building-Apps boxed-proxy gotcha. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EVU6HWRPnVwqc5XgTSnSv
v1.7.5: on<Event> collision warning suggests handle<Event> vs add<Eve… …nt>Listener by intent The member-collision warning (and the Component docs) previously offered only handle<Event> as the rename. It now distinguishes the two real intents: - handle<Event> for a handler function the component invokes (handleClick) - add<Event>Listener for a method that registers listeners for a synthetic event the component dispatches (addClickListener) Warning text + docs only; no behavior change. Test asserts both conventions appear in the warning. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EVU6HWRPnVwqc5XgTSnSv
v1.7.4: faster dispatch via getElementsByClassName; rename -xin-data … …-> -tosi-data; export the marker constants Dispatch enumeration (the global "any state changed" observer, the MutationObserver re-discovery, and the list-binding relative refresh) now uses getElementsByClassName — which gathers from the browser's class-name bucket index — instead of querySelectorAll's whole-tree walk. Measured 1.6-2.6x faster in Blink on the global scan, gap widening with DOM size. Result set identical; still snapshotted to a static array so toDOM mutations mid-dispatch can't perturb a live collection. Renamed the data-binding marker class -xin-data -> -tosi-data (the last xin-era name in the runtime DOM), and — the point of this addition — EXPORTED BOUND_CLASS and BOUND_SELECTOR from the package root. They were internal, so anyone referencing the marker had to hardcode the literal; that hardcoding is the only reason the rename is breaking. Integrations import the constant now and follow future renames automatically. The marker stays a class (not a data-attribute) because getElementsByClassName is class-only and that's the fast path; it can't be a WeakMap because dispatch enumerates by path. Potentially breaking (unlikely): anyone selecting .-xin-data (undocumented internal) uses .-tosi-data or, better, BOUND_CLASS now. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EVU6HWRPnVwqc5XgTSnSv
v1.7.3: retire the -xin-event marker class from on() on() no longer stamps a `-xin-event` class onto elements. Event delegation used that class to climb the ancestor chain via closest(); it now walks the ancestor chain (across open-shadow boundaries) consulting the elementToHandlers WeakMap directly, which is already the authoritative record of which elements have handlers. Behavior is identical — including delegation across open shadow roots — but on()-bound elements are no longer mutated: nothing lands in className, and clones carry no stray marker (so they're skipped naturally, not via a defensive class check). The -xin-data marker on data-bound elements is retained: a MutationObserver re-discovers those via querySelectorAll, which a WeakMap can't provide. That asymmetry is why events need no class and data bindings do. Potentially breaking (unlikely): anyone styling/selecting `.-xin-event` (an undocumented internal) loses it — surfaced in the changelog. New regression test: on() does not mutate className, delegation still resolves. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EVU6HWRPnVwqc5XgTSnSv
v1.7.2: fix custom-property line-height losing its px suffix (1.7.0 r… …egression) `_lineHeight: 25` emitted `--line-height: 25` instead of `--line-height: 25px`. 1.7.0's `_opacity: 0.5px` fix started stripping the `_` before the unitless-list test, and `line-height` was in that list, so custom-property line-heights matched and lost their px. Subtle and lethal — the vars system uses lineHeight as a length in calc(), so downstream computed sizes silently broke. line-height is now dual-mode: real declaration keeps the unitless multiplier (lineHeight: 1.5 -> 1.5); custom property takes px (_lineHeight: 25 -> 25px); opt out with a string (_lineHeight: '1.5'). The always-unitless set (opacity, z-index, font-weight, ...) still suppresses px for both real and custom props, so the 1.7.0 _opacity fix is preserved. Fail-first regression test in css.test.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EVU6HWRPnVwqc5XgTSnSv
v1.7.1: ship CHANGELOG.md + llms.txt to npm; internal cleanup Packaging fix (the headline): CHANGELOG.md and llms.txt were built and committed but omitted from the `files` allowlist, so they never reached npm installers. Both now publish. No API or behavior changes. Internal cleanup: - guard composedPath() behind event.composed in on()'s origin resolution - remove dead DATEISH constant from dom.ts - extract shared __tjs bootstrap into configure-tjs.ts (debug/safe) - extract duplicated blueprint-batch loader into settleBlueprints() - correct a list-binding.ts comment (SVG/MathML namespaced, not table mode) Docs: Building-Apps "boxed proxies are minted fresh per access" gotcha; date-family control round-trip test coverage in dom.test.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014EVU6HWRPnVwqc5XgTSnSv
v1.7.0: the correctness release Consolidates 1.7.0-beta.1 + beta.2 into the stable 1.7.0. Outcome of the 2026-07-17 whole-codebase review (~45 verified defects, all passing the prior happy-path suite) plus the medium-backlog triage and the dynamic- theming docs. No API removed/renamed; the behavior changes (shadow-DOM on() via composedPath, segment-exact path matching, typed getValue, Component change bubbling, reactive class replace, deepClone Date/Map/Set) are documented in CHANGELOG + Migration.md. Ships a first CI (unit + multi-engine Playwright doc-test lane), pre- release-reviewed (GO), built against tosijs-ui 1.7.0-rc.1 (tsc failure now fatal) and tjs-lang 0.10.1. Suite 646 green; browser lane green on Chromium + Firefox. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PreviousNext