@adia-ai/adia-ui-forge 0.8.39 → 0.8.41
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/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +21 -0
- package/package.json +1 -1
- package/scripts/release-pretag-docs-gate +1 -1
- package/skills/a2ui-maintenance/SKILL.md +26 -6
- package/skills/a2ui-maintenance/references/anti-patterns.md +1 -1
- package/skills/a2ui-maintenance/references/chunk-authoring.md +2 -2
- package/skills/a2ui-maintenance/references/eval-diagnostics.md +1 -1
- package/skills/a2ui-maintenance/references/mcp-pipeline-ops.md +2 -2
- package/skills/a2ui-maintenance/references/pipeline-overview.md +2 -2
- package/skills/demo-audit/scripts/analyze.mjs +1 -1
- package/skills/package-release/SKILL.md +1 -1
- package/skills/package-release/references/cut-procedure.md +14 -4
- package/skills/package-release/references/gates-catalog.md +7 -1
- package/skills/package-release/scripts/gate-roster.mjs +28 -1
- package/skills/package-release/scripts/release-pack.mjs +43 -4
- package/skills/primitive-authoring/SKILL.md +1 -0
- package/skills/primitive-authoring/references/INDEX.md +2 -1
- package/skills/primitive-authoring/references/anti-patterns.md +27 -0
- package/skills/primitive-authoring/references/api-contract.md +39 -0
- package/skills/primitive-authoring/references/composite-demo-protocol.md +1 -1
- package/skills/primitive-authoring/references/css-patterns.md +7 -1
- package/skills/primitive-authoring/references/shell-patterns.md +1 -1
- package/skills/primitive-authoring/references/svg-authoring.md +277 -0
- package/skills/primitive-authoring/references/token-contract.md +1 -1
- package/skills/primitive-authoring/references/yaml-contract.md +165 -5
- package/skills/ssr-compatibility/SKILL.md +10 -5
- package/skills/ssr-compatibility/references/consumer-workarounds.md +14 -6
- package/skills/ssr-compatibility/references/failure-shapes.md +30 -7
- package/skills/ssr-compatibility/references/guard-patterns.md +28 -0
- package/skills/ssr-compatibility/references/status-ledger.md +13 -0
- package/skills/ssr-compatibility/references/test-without-linkedom.md +29 -14
|
@@ -19,14 +19,37 @@ or a write to `document.adoptedStyleSheets`.
|
|
|
19
19
|
not a partial/quirky implementation, an absence. Any unconditional call throws
|
|
20
20
|
`TypeError` or `ReferenceError` (undefined global) immediately.
|
|
21
21
|
|
|
22
|
-
**Status: FIXED
|
|
23
|
-
(`packages/web-components/core/element.js`)
|
|
24
|
-
|
|
22
|
+
**Status: FIXED, twice — and now GATED.** First wave gh#285 (PR #292, merged
|
|
23
|
+
2026-07-17): `UIElement`'s constructor (`packages/web-components/core/element.js`)
|
|
24
|
+
plus a sweep of component/trait/module files — the per-file tally is
|
|
25
25
|
[`status-ledger.md`](status-ledger.md)'s #285 row (the ledger, not this line, is
|
|
26
|
-
the count of record).
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
the count of record). Second wave gh#1430 + gh#1436 (2026-08-17, found by adiav2's
|
|
27
|
+
first SSR admission trial at 0.8.40): the sweep had missed
|
|
28
|
+
- **environment-detection instead of feature-detection at MODULE scope** —
|
|
29
|
+
`core/responsive.js` guarded `window.matchMedia(...)` behind `typeof window !==
|
|
30
|
+
'undefined'`; linkedom HAS a `window`, just no `matchMedia` and no numeric
|
|
31
|
+
`innerWidth`, so six components (`block`/`col`/`grid`/`row`/`text`/`demo-toggle`)
|
|
32
|
+
could not be IMPORTED server-side at all;
|
|
33
|
+
- **bare `instanceof Node|Element|HTMLElement`** (15 sites — `core/template.js`
|
|
34
|
+
`applyValue`, table/list-window renderer results, event-target checks) — a
|
|
35
|
+
`ReferenceError`, not `false`, because custom-elements-ssr installs ONLY
|
|
36
|
+
`HTMLElement` as a global (never `Node`, `Element`, `Text`, `DocumentFragment`);
|
|
37
|
+
now `core/dom.js` `isNode()`/`isElement()` (structural `nodeType` tests);
|
|
38
|
+
- **`requestAnimationFrame` / `MutationObserver` / `getBoundingClientRect` at
|
|
39
|
+
connect** in feed-item/toast, noodles, preview, toolbar (rAF), nav-group,
|
|
40
|
+
stepper (MutationObserver — the #292 sweep guarded four Observer sites, not
|
|
41
|
+
these two) and nav-ui (`getBoundingClientRect` is absent, not zero, on linkedom).
|
|
42
|
+
|
|
43
|
+
**Why happy-dom could not see any of it:** it implements every one of those APIs.
|
|
44
|
+
The gate that closes the class is `scripts/dev/ssr-linkedom-smoke.mjs`, run by
|
|
45
|
+
`packages/web-components/test/ssr-linkedom-smoke.test.js` — a real `linkedom`
|
|
46
|
+
devDependency installing EXACTLY the six globals `custom-elements-ssr/server-shim.js`
|
|
47
|
+
installs, then importing every `components/*/*.js` entry and constructing +
|
|
48
|
+
connecting every registered tag (124/124 import, every tag renders). A new
|
|
49
|
+
shape-1 instance fails that test, not a consumer's build. The fix pattern
|
|
50
|
+
(feature-detect + fallback matched to how the reference is used downstream) is
|
|
51
|
+
[`guard-patterns.md`](guard-patterns.md) — apply it to any NEW call site; don't
|
|
52
|
+
re-derive the shape from scratch, and don't guard on `typeof window`.
|
|
30
53
|
|
|
31
54
|
## 2 · `connectedCallback` destructively re-stamps existing DOM → content loss
|
|
32
55
|
|
|
@@ -51,6 +51,34 @@ const observer = typeof MutationObserver !== 'undefined'
|
|
|
51
51
|
// every later use: observer?.observe(...) / bootstrap() no-ops via the same check
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
**(d) Feature, not environment — `typeof window` is NOT a guard (gh#1430).**
|
|
55
|
+
linkedom defines `window` (an object) but neither `matchMedia` nor a numeric
|
|
56
|
+
`innerWidth`, so `typeof window !== 'undefined'` passes and the call still throws —
|
|
57
|
+
at MODULE scope that takes every importer off the server. Test the member:
|
|
58
|
+
```js
|
|
59
|
+
// packages/web-components/core/responsive.js
|
|
60
|
+
const w = typeof window !== 'undefined' ? window.innerWidth : undefined;
|
|
61
|
+
if (typeof w !== 'number') return 'lg'; // fallback
|
|
62
|
+
if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') { … }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**(e) `instanceof <DOM constructor>` — use `core/dom.js` (gh#1436).** Only
|
|
66
|
+
`HTMLElement` is a global under custom-elements-ssr; `v instanceof Node` /
|
|
67
|
+
`Element` / `Text` / `DocumentFragment` is a `ReferenceError`, not `false`.
|
|
68
|
+
`isNode(v)` / `isElement(v)` test `nodeType` structurally (and also accept nodes
|
|
69
|
+
from another realm, which the constructor identity check never did):
|
|
70
|
+
```js
|
|
71
|
+
import { isNode, isElement } from '../../core/dom.js';
|
|
72
|
+
if (isNode(result)) cell.replaceChildren(result); // renderer output
|
|
73
|
+
const row = isElement(e.target) ? e.target.closest('…') : null; // event target
|
|
74
|
+
```
|
|
75
|
+
`isElement` is deliberately the SUPERSET of a former `instanceof HTMLElement` (it
|
|
76
|
+
admits SVG elements too) — every swept site only needed "an element", none needed
|
|
77
|
+
"an HTML element specifically". Absent-method form of the same idea:
|
|
78
|
+
`typeof this.getBoundingClientRect === 'function' && …` (nav-ui) — linkedom has no
|
|
79
|
+
rect API at all, so an unguarded call is a TypeError, not a zero rect (that zero-rect
|
|
80
|
+
case is §3).
|
|
81
|
+
|
|
54
82
|
**The `this.internals` special case — a shim, not `undefined`.** `UIElement`'s
|
|
55
83
|
constructor (`packages/web-components/core/element.js`) needed a DIFFERENT answer
|
|
56
84
|
than "guard and leave unset," because ~12 files across the framework call
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# SSR support — status ledger
|
|
2
2
|
|
|
3
|
+
**Filing a new downstream SSR-seam report?** Use the `ssr` label plus
|
|
4
|
+
[`.github/ISSUE_TEMPLATE/ssr-seam-report.md`](../../../../../../.github/ISSUE_TEMPLATE/ssr-seam-report.md)
|
|
5
|
+
(gh#1384) — it captures the resolved `@adia-ai/web-components` version, the
|
|
6
|
+
downstream kit version, the component + fixture, an SSR-vs-CSR diff or
|
|
7
|
+
minimal repro, and a cosmetic/behavioral/blocked-upstream classification, so
|
|
8
|
+
a report lands triageable instead of free-form (the archaeology the #284/#288
|
|
9
|
+
rows below needed). `docs/ops/spec/spec-ssr-kit.md` is referenced by some
|
|
10
|
+
downstream reports but does not exist in this repo — it lives in adiav2's own
|
|
11
|
+
repo (`@adiav2/ssr-kit`'s spec); do not look for it here.
|
|
12
|
+
|
|
3
13
|
**This file drifts fast — re-verify against the live issue tracker
|
|
4
14
|
(`gh issue view <n>`) before trusting it for anything beyond a quick orientation.**
|
|
5
15
|
Every row was true as of 2026-07-17; a closed issue below may have re-opened, a PR
|
|
@@ -9,6 +19,9 @@ the territory — the territory is `gh issue list` / `gh issue view`.
|
|
|
9
19
|
| Issue | Failure shape | Status | Shipped in | Notes |
|
|
10
20
|
|---|---|---|---|---|
|
|
11
21
|
| [#285](https://github.com/adiahealth/gen-ui-kit/issues/285) | §1 — browser-only API called unconditionally | **CLOSED** | PR #292 (merged 2026-07-17) | `UIElement` constructor + `adoptStyles()` + 22 component/trait/module files guarded; 10 more already correct from an earlier pass |
|
|
22
|
+
| [#1430](https://github.com/adiahealth/gen-ui-kit/issues/1430) | §1 — browser-only API called unconditionally (module scope: `window.matchMedia` behind a `typeof window` guard) | **FIXED 2026-08-17** | `core/responsive.js` feature-detects `matchMedia` + `innerWidth`; `swiper.class.js` `play()` guarded; `scripts/dev/ssr-linkedom-smoke.mjs` + `test/ssr-linkedom-smoke.test.js` gate the whole catalog under linkedom | 6 components (`block`/`col`/`grid`/`row`/`text`/`demo-toggle`) were un-importable server-side; 118→124 of 124. Side findings on the issue: gh#285's "shim no longer necessary" over-reached (two of the consumer's shim sections patch `custom-elements-ssr` itself, not this library — see `consumer-workarounds.md`); `audit-template-child-conflict.mjs` isn't shipped in the package. |
|
|
23
|
+
| [#1436](https://github.com/adiahealth/gen-ui-kit/issues/1436) | §1 — bare `instanceof Node/Element/HTMLElement` (ReferenceError under linkedom, only `HTMLElement` is global) | **FIXED 2026-08-17** | `core/dom.js` `isNode()`/`isElement()`; 15 sites swept; plus rAF/MutationObserver/getBoundingClientRect guards in feed, noodles, preview, toolbar, nav-group, stepper, nav found by the same gate's render sweep | Same PR as #1430 — one root-cause class. |
|
|
24
|
+
| [#1435](https://github.com/adiahealth/gen-ui-kit/issues/1435) | reported as §2 — check/radio/switch "lose" a `<span slot="label">` child | **NOT an SSR seam** (2026-08-17) | yaml/a2ui `slots` descriptions clarified for the three (separate follow-up PR — a docs-contract change, not a shape-1 guard) | The fixture is not a supported form in CSR either: `label` on all three is ATTRIBUTE-driven (`label="…"` → `aria-label` + `::after content: attr(label)` / template-owned span); a late CSR upgrade replaces the foreign child the same way. The yaml `slots:` entries name template-OWNED anatomy parts (`box`/`dot`/`track`/`thumb`/`label`), which a downstream reimplementation of the audit read as consumer insertion points — the audit's own definition (`slots.default` + non-null template) is what "consumer children" means here. |
|
|
12
25
|
| [#286](https://github.com/adiahealth/gen-ui-kit/issues/286) | §3 — connect-time measurement treated as confirmed | **CLOSED** (for `admin-sidebar` specifically) | PR #290 (merged 2026-07-17) | The GENERAL pattern (any other component reading a rect/size synchronously at connect) is NOT swept — only this one instance is fixed |
|
|
13
26
|
| [#284](https://github.com/adiahealth/gen-ui-kit/issues/284) | §2 — destructive `stamp()` on connect | **CLOSED 2026-07-18 — narrowed AND separately fixed** | `scripts/dev/audit-template-child-conflict.mjs` (PR #295) + `packages/web-components/core/element.js` connectedCallback resync (PR #309) | Two distinct findings. (1) The ORIGINAL diagnosis (destructive `stamp()`) was narrowed 2026-07-17: every component the issue names has `static template = () => null`, so `stamp()` never runs for them — zero shipped components exposed; a static audit gate catches a future regression instead of a lifecycle rewrite. (2) A SEPARATE, real, live bug was found in the same investigation area and fixed 2026-07-18: happy-dom/linkedom don't replay `attributeChangedCallback` for attributes already present at custom-element upgrade (spec §4.13.5 step 6) — any `reflect: true` property seeded only from pre-parsed/SSR HTML stayed at its class default after upgrade (e.g. `<nav-item-ui text="Profile">` rendered with an empty label). `connectedCallback` now re-syncs every declared property from its live attribute before `connected()` runs. See `failure-shapes.md` §2 for the full narrative. |
|
|
14
27
|
| [#288](https://github.com/adiahealth/gen-ui-kit/issues/288) | §4 — property-only components can't seed from SSR HTML | **CLOSED 2026-07-18** | `table.class.js` `data="[…]"` attribute hydration | Never actually blocked on #284 (table-ui/chart-ui/select-ui all use `static template = () => null`, so the stamp() question never applied). Scope was narrower than filed: `select-ui` already parsed declarative `<option>` children, `chart-ui` already hydrated `.data` from a `data="[…]"` attribute — only `table-ui`'s `.data` had no declarative form. Fixed in the same attribute-hydration shape as chart-ui, not the JSON-script-child form originally proposed. |
|
|
@@ -1,10 +1,24 @@
|
|
|
1
|
-
# Testing an SSR gap
|
|
1
|
+
# Testing an SSR gap — the linkedom gate first, then the deletion pattern
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
**UPDATE 2026-08-17 (gh#1430/#1436): this repo now HAS a `linkedom` root
|
|
4
|
+
devDependency and a shipped shim gate.** `scripts/dev/ssr-linkedom-smoke.mjs`
|
|
5
|
+
installs exactly the six globals `custom-elements-ssr/server-shim.js` installs
|
|
6
|
+
(document, window, customElements, HTMLElement, Event, CustomEvent — and nothing
|
|
7
|
+
else: no Node/Element/Text/DocumentFragment, no matchMedia, no
|
|
8
|
+
requestAnimationFrame, no getBoundingClientRect), imports every
|
|
9
|
+
`components/*/*.js` entry, and constructs + connects every registered tag the way
|
|
10
|
+
`CustomElementRender` does; `packages/web-components/test/ssr-linkedom-smoke.test.js`
|
|
11
|
+
runs it as a Node child process and fails on any import or render throw. **For a
|
|
12
|
+
shape-1 question ("does this crash under SSR"), run that first** — `node
|
|
13
|
+
scripts/dev/ssr-linkedom-smoke.mjs` — it is the consumer's environment, not an
|
|
14
|
+
approximation. Add a row to its `FIXTURES` table when a guard lands on a branch only
|
|
15
|
+
some attribute value reaches (`swiper-ui[autoplay]` is the model). The rest of this
|
|
16
|
+
file is the UNIT-level pattern for a focused regression test in the happy-dom
|
|
17
|
+
suite; it stays valid, but it is no longer the only proof available.
|
|
18
|
+
|
|
19
|
+
The main test suite still runs on `happy-dom` (`vitest.config.js`), which
|
|
20
|
+
implements most of the APIs linkedom lacks — so a naive test using the default
|
|
21
|
+
environment will not reproduce an SSR-shaped bug. Two consequences:
|
|
8
22
|
|
|
9
23
|
1. **You cannot trust "the tests pass" as proof an SSR fix works** unless the test
|
|
10
24
|
itself removes the API under test. A guard around `attachInternals` that's never
|
|
@@ -118,12 +132,13 @@ actually present. Both gh#285 and gh#286's fixes were verified this second way b
|
|
|
118
132
|
shipping (a real `ElementInternals` instance still used and functional; a real
|
|
119
133
|
`ResizeObserver` tick still corrects state) — do both, never one instead of the other.
|
|
120
134
|
|
|
121
|
-
##
|
|
135
|
+
## The deeper simulation — DONE 2026-08-17
|
|
122
136
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
137
|
+
The real-`linkedom` simulation this section used to propose is now the shipped
|
|
138
|
+
gate described at the top of this file (`scripts/dev/ssr-linkedom-smoke.mjs`,
|
|
139
|
+
`linkedom` pinned as a root devDependency, used only there — the main suite stays
|
|
140
|
+
on happy-dom). It sweeps import + connect for the whole catalog; a shape-2/4
|
|
141
|
+
question (content preserved across a late upgrade, declarative data seeding) is
|
|
142
|
+
still answered by a targeted test — extend the script's `FIXTURES` table with the
|
|
143
|
+
attrs/children in question, or write the assertion in
|
|
144
|
+
`test/ssr-upgrade-adoption-contract.test.js`'s late-upgrade shape.
|