@adia-ai/web-components 0.6.9 → 0.6.11

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog — @adia-ai/web-components
2
2
 
3
+ ## [0.6.11] — 2026-05-20
4
+
5
+ ### Fixed
6
+ - **`<canvas-ui>` — `.d.ts` hand-authored with full runtime API (claims-ui-v2 inbound FB-02 finding 2, P2).** Pre-fix: `canvas.d.ts` was 27-line codegen output declaring only `theme: string` + the `canvas-interaction` event listener — all 8 imperative runtime methods (`process`, `processAll`, `reset`, `pushVersion`, `back`, `forward`, `historyLength`, `historyIndex`) plus `getHTML()` were absent, forcing TS consumers to write `(canvas as any).processAll(...)`. The yaml/a2ui.json sidecar can only express declarative props per ADR-0027; codegen correctly skips imperative methods, but no compensating hand-authored layer existed. **Fix**: replaced `canvas.d.ts` with 63-line hand-authored declaration covering the full public API surface (each method documented with JSDoc matching the JS contract). Added `'canvas'` to the `HAND_AUTHORED_DTS` skip-set in `scripts/build/dts-codegen.mjs` with referencing comment so the next codegen run preserves the hand-authored file. Consumers post-upgrade can drop `(canvas as any).processAll(...)` casts entirely. Closes claims-ui-v2 inbound FEEDBACK-02 finding 2.
7
+
8
+ ## [0.6.10] — 2026-05-21
9
+
10
+ ### Added
11
+ - **`<table-toolbar-ui>` — one-shot dev-mode `console.warn` when unknown opt-out attributes are set (claims-ui FB-04 enhancement).** Triggered by claims-ui FB-04 (RESPONSE-04, v0.6.9 §379): consumers writing `<table-toolbar-ui searchable>` (or `exportable` / `sortable` / `filterable` / `columns-visible`) thinking those attributes toggle visibility saw silent no-ops — the canonical attrs are `no-search` / `no-filter` / `no-sort` / `no-columns`. Pre-v0.6.10 behavior: the unknown attrs simply did nothing. Post-fix: a one-shot `console.warn` per element fires from `connected()`, names the unknown attrs, and points consumers at `npx adia-ai-doc table-toolbar` and the yaml SoT for the canonical reference. Pattern: warn-once-per-element via static `WeakSet` (`UITableToolbar._warnedUnknownAttrs`), matching the `tooltip-ui._warnedMissing` discipline. Scope: case-insensitive attrs starting with `search` / `export` / `sort` / `filter` / `column`, minus the 4 canonical opt-outs. Standard HTML/ARIA attrs (`id`, `class`, `role`, `aria-*`, `data-*`) are never flagged. Pinned by 12 new vitest cases in `table-toolbar.test.js`.
12
+
13
+ ### Internal
14
+ - **`scripts/release/check-browser-safe.mjs` — strict-by-default + `/bin/` allowlist.** Flipped `STRICT = args.has('--strict')` → `STRICT = !args.has('--loose')` to match the convention shared by the other 6 release gates (`check:absolute-imports`, `check:no-self-import-css`, `check:scope-bare-descendants`, `check:lightningcss-build`, `check:rolldown-glob`, `check:with-css-pairing`). Paired with `/\/bin\//` added to `NODE_ONLY_PATTERNS` — silences the only pre-existing warning (`packages/web-components/bin/doc.mjs`, the `adia-ui-doc` CLI shipped v0.6.7). The CLI's `node:fs` / `node:path` / `node:url` imports are by design (#!/usr/bin/env node shebang); the broad `/bin/` pattern covers any future package-bin CLIs. Tracked at journal §378 as deferred-to-v0.6.10. No consumer-facing API change.
15
+
3
16
  ## [0.6.9] — 2026-05-21
4
17
 
5
18
  ### Fixed
@@ -3,11 +3,16 @@
3
3
  *
4
4
  * @see https://ui-kit.exe.xyz/site/components/canvas
5
5
  *
6
- * Type declarations generated by scripts/build/dts-codegen.mjs from
7
- * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
- * run `npm run build:components`, then `npm run codegen:dts` to
9
- * regenerate; or hand-author this file fully if rich event types are
10
- * needed beyond what the yaml `events:` block can express.
6
+ * Hand-authored .d.ts (not codegen output). The yaml/a2ui.json sidecars
7
+ * describe declarative component props; canvas-ui's primary public API is
8
+ * a set of imperative runtime methods (`process`, `processAll`, `reset`,
9
+ * `pushVersion`, `back`, `forward`) which cannot be expressed in the yaml
10
+ * shape. Per ADR-0027, dts-codegen skips imperative methods this file
11
+ * carries them by hand so TypeScript consumers don't need `(canvas as any)`
12
+ * casts. Keep this file in sync with `canvas.js` whenever the public API
13
+ * surface changes.
14
+ *
15
+ * Closes FB-02 finding 2 (claims-ui-v2 2026-05-20 cold-start).
11
16
  */
12
17
 
13
18
  import { UIElement } from '../../core/element.js';
@@ -18,6 +23,36 @@ export class UICanvas extends UIElement {
18
23
  /** Component property: theme. */
19
24
  theme: string;
20
25
 
26
+ /** Process a single A2UI message and apply it to the current surface. */
27
+ process(message: unknown): void;
28
+
29
+ /** Process an array of A2UI messages in order. */
30
+ processAll(messages: unknown[]): void;
31
+
32
+ /** Clear the canvas surface — discards all components. */
33
+ reset(): void;
34
+
35
+ /** Return the formatted innerHTML of the internal `<a2ui-root>`. */
36
+ getHTML(): string;
37
+
38
+ /**
39
+ * Save the given messages to the history stack (enables back/forward
40
+ * navigation). Truncates forward history if not at the newest version.
41
+ */
42
+ pushVersion(messages: unknown[]): void;
43
+
44
+ /** Navigate to the previous history version. Returns false if at oldest. */
45
+ back(): boolean;
46
+
47
+ /** Navigate to the next history version. Returns false if at newest. */
48
+ forward(): boolean;
49
+
50
+ /** Number of saved versions in the history stack. */
51
+ readonly historyLength: number;
52
+
53
+ /** Current position in the history stack (0-based). */
54
+ readonly historyIndex: number;
55
+
21
56
  addEventListener<K extends keyof HTMLElementEventMap>(
22
57
  type: K,
23
58
  listener: (this: UICanvas, ev: HTMLElementEventMap[K]) => unknown,
@@ -138,10 +138,21 @@ export class UITableToolbar extends UIElement {
138
138
  #docListenerRaf = null;
139
139
  #sortIndicatorRafs = new Set();
140
140
 
141
+ // §381 (v0.6.10, slice F): one-shot dev-mode warning when consumers
142
+ // set common-but-unknown opt-out attributes (e.g. `searchable`,
143
+ // `exportable`, `sortable`) thinking they control table-toolbar
144
+ // visibility. Canonical attrs are `no-search` / `no-filter` /
145
+ // `no-sort` / `no-columns`. WeakSet pinned static so each element
146
+ // warns at most once even across reconnects.
147
+ static _warnedUnknownAttrs = new WeakSet();
148
+ static _CANONICAL_OPT_OUT_ATTRS = new Set(['no-search', 'no-filter', 'no-sort', 'no-columns']);
149
+ static _OPT_OUT_PREFIX_RE = /^(search|export|sort|filter|column)/i;
150
+
141
151
  // ── Lifecycle ────────────────────────────────────────────────────────────
142
152
 
143
153
  connected() {
144
154
  this.setAttribute('role', 'toolbar');
155
+ this.#warnUnknownOptOutAttrs();
145
156
  this.#stamp();
146
157
  this.#resolveTarget();
147
158
  this.#syncFromTarget();
@@ -165,6 +176,44 @@ export class UITableToolbar extends UIElement {
165
176
  this.#updateTitle();
166
177
  }
167
178
 
179
+ // ── Unknown opt-out attribute warning (§381, v0.6.10 slice F) ────────────
180
+
181
+ /**
182
+ * Warn once (per element) when the consumer sets attributes that look
183
+ * like opt-outs but aren't canonical. Triggered by claims-ui FB-04:
184
+ * consumers set `searchable` / `exportable` / `sortable` thinking they
185
+ * toggle visibility; the canonical attrs are `no-search`/`no-filter`/
186
+ * `no-sort`/`no-columns`. Silent no-op was the previous behavior —
187
+ * confusing UX. Pattern: warn-once-per-element via static WeakSet.
188
+ *
189
+ * Scope: attributes whose names start with `search`, `export`, `sort`,
190
+ * `filter`, `column` (case-insensitive). Canonical attrs are
191
+ * subtracted before the warn fires. Standard HTML/ARIA attrs (`id`,
192
+ * `class`, `role`, `aria-*`, `data-*`, etc.) are never flagged.
193
+ *
194
+ * Pinned by table-toolbar.test.js.
195
+ */
196
+ #warnUnknownOptOutAttrs() {
197
+ if (UITableToolbar._warnedUnknownAttrs.has(this)) return;
198
+ const unknowns = [];
199
+ for (const attr of this.attributes) {
200
+ const name = attr.name.toLowerCase();
201
+ if (UITableToolbar._CANONICAL_OPT_OUT_ATTRS.has(name)) continue;
202
+ if (!UITableToolbar._OPT_OUT_PREFIX_RE.test(name)) continue;
203
+ unknowns.push(name);
204
+ }
205
+ if (unknowns.length === 0) return;
206
+ UITableToolbar._warnedUnknownAttrs.add(this);
207
+ const list = unknowns.map((n) => `"${n}"`).join(', ');
208
+ // eslint-disable-next-line no-console
209
+ console.warn(
210
+ `[table-toolbar-ui] Unknown opt-out attribute(s) ${list}. ` +
211
+ `Canonical opt-out attrs are: no-search, no-filter, no-sort, no-columns. ` +
212
+ `See packages/web-components/components/table-toolbar/table-toolbar.yaml ` +
213
+ `or run \`npx adia-ui-doc table-toolbar\` for the full attribute reference.`,
214
+ );
215
+ }
216
+
168
217
  // ── Target resolution ────────────────────────────────────────────────────
169
218
 
170
219
  #resolveTarget() {
@@ -0,0 +1,152 @@
1
+ /**
2
+ * table-toolbar-ui tests
3
+ *
4
+ * Pinning the §381 (v0.6.10 slice F) one-shot dev-mode warning for
5
+ * unknown opt-out attributes. Triggered by claims-ui FB-04: consumers
6
+ * set `searchable` / `exportable` / `sortable` thinking they toggle
7
+ * visibility; canonical attrs are `no-search`/`no-filter`/`no-sort`/
8
+ * `no-columns`. Previous behavior: silent no-op.
9
+ *
10
+ * Contract:
11
+ * 1. Unknown attrs matching /^(search|export|sort|filter|column)/i
12
+ * → console.warn fires once per element with the attr names listed
13
+ * 2. Canonical attrs (no-search/no-filter/no-sort/no-columns) → no warn
14
+ * 3. Unrelated attrs (id, class, role, aria-*, data-*) → no warn
15
+ * 4. Multiple unknown attrs in one declaration → single warn (one warn
16
+ * per element, all unknown attrs in the message)
17
+ * 5. Repeated reconnects of the same element → still one warn total
18
+ */
19
+
20
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
21
+ import '../../core/element.js';
22
+ import './table-toolbar.js';
23
+
24
+ const tick = () => new Promise((r) => queueMicrotask(r));
25
+
26
+ function mount(html) {
27
+ const wrap = document.createElement('div');
28
+ wrap.innerHTML = html;
29
+ document.body.appendChild(wrap);
30
+ return wrap.firstElementChild;
31
+ }
32
+
33
+ describe('table-toolbar-ui — unknown opt-out attribute warning (§381 / FB-04)', () => {
34
+ beforeEach(() => { document.body.innerHTML = ''; });
35
+
36
+ it('warns when an unknown `searchable` attribute is set', async () => {
37
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
38
+ mount('<table-toolbar-ui searchable></table-toolbar-ui>');
39
+ await tick();
40
+ expect(warn).toHaveBeenCalledTimes(1);
41
+ const msg = warn.mock.calls[0][0];
42
+ expect(msg).toContain('[table-toolbar-ui]');
43
+ expect(msg).toContain('"searchable"');
44
+ expect(msg).toContain('no-search');
45
+ warn.mockRestore();
46
+ });
47
+
48
+ it('warns when an unknown `exportable` attribute is set', async () => {
49
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
50
+ mount('<table-toolbar-ui exportable></table-toolbar-ui>');
51
+ await tick();
52
+ expect(warn).toHaveBeenCalledTimes(1);
53
+ const msg = warn.mock.calls[0][0];
54
+ expect(msg).toContain('"exportable"');
55
+ warn.mockRestore();
56
+ });
57
+
58
+ it('warns when an unknown `sortable` attribute is set', async () => {
59
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
60
+ mount('<table-toolbar-ui sortable></table-toolbar-ui>');
61
+ await tick();
62
+ expect(warn).toHaveBeenCalledTimes(1);
63
+ expect(warn.mock.calls[0][0]).toContain('"sortable"');
64
+ warn.mockRestore();
65
+ });
66
+
67
+ it('warns when an unknown `filterable` attribute is set', async () => {
68
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
69
+ mount('<table-toolbar-ui filterable></table-toolbar-ui>');
70
+ await tick();
71
+ expect(warn).toHaveBeenCalledTimes(1);
72
+ expect(warn.mock.calls[0][0]).toContain('"filterable"');
73
+ warn.mockRestore();
74
+ });
75
+
76
+ it('warns when an unknown `columns-visible` attribute is set', async () => {
77
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
78
+ mount('<table-toolbar-ui columns-visible></table-toolbar-ui>');
79
+ await tick();
80
+ expect(warn).toHaveBeenCalledTimes(1);
81
+ expect(warn.mock.calls[0][0]).toContain('"columns-visible"');
82
+ warn.mockRestore();
83
+ });
84
+
85
+ it('does NOT warn on canonical no-search', async () => {
86
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
87
+ mount('<table-toolbar-ui no-search></table-toolbar-ui>');
88
+ await tick();
89
+ expect(warn).not.toHaveBeenCalled();
90
+ warn.mockRestore();
91
+ });
92
+
93
+ it('does NOT warn on canonical no-filter / no-sort / no-columns', async () => {
94
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
95
+ mount('<table-toolbar-ui no-filter no-sort no-columns></table-toolbar-ui>');
96
+ await tick();
97
+ expect(warn).not.toHaveBeenCalled();
98
+ warn.mockRestore();
99
+ });
100
+
101
+ it('does NOT warn on unrelated attributes (id, class, role, aria-*, data-*)', async () => {
102
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
103
+ mount('<table-toolbar-ui id="x" class="y" role="toolbar" aria-label="z" data-test="ok"></table-toolbar-ui>');
104
+ await tick();
105
+ expect(warn).not.toHaveBeenCalled();
106
+ warn.mockRestore();
107
+ });
108
+
109
+ it('does NOT warn on canonical attrs even when placeholder is also set', async () => {
110
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
111
+ mount('<table-toolbar-ui no-search no-sort placeholder="Find..."></table-toolbar-ui>');
112
+ await tick();
113
+ expect(warn).not.toHaveBeenCalled();
114
+ warn.mockRestore();
115
+ });
116
+
117
+ it('emits a single warn that names all unknown opt-out attrs', async () => {
118
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
119
+ mount('<table-toolbar-ui searchable exportable sortable></table-toolbar-ui>');
120
+ await tick();
121
+ expect(warn).toHaveBeenCalledTimes(1);
122
+ const msg = warn.mock.calls[0][0];
123
+ expect(msg).toContain('"searchable"');
124
+ expect(msg).toContain('"exportable"');
125
+ expect(msg).toContain('"sortable"');
126
+ warn.mockRestore();
127
+ });
128
+
129
+ it('warns at most once per element across reconnects', async () => {
130
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
131
+ const el = mount('<table-toolbar-ui searchable></table-toolbar-ui>');
132
+ await tick();
133
+ expect(warn).toHaveBeenCalledTimes(1);
134
+ // Disconnect + reconnect.
135
+ el.remove();
136
+ document.body.appendChild(el);
137
+ await tick();
138
+ expect(warn).toHaveBeenCalledTimes(1); // unchanged
139
+ warn.mockRestore();
140
+ });
141
+
142
+ it('mixes canonical + unknown — names only the unknown in the warning', async () => {
143
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
144
+ mount('<table-toolbar-ui no-search searchable></table-toolbar-ui>');
145
+ await tick();
146
+ expect(warn).toHaveBeenCalledTimes(1);
147
+ const msg = warn.mock.calls[0][0];
148
+ expect(msg).toContain('"searchable"');
149
+ expect(msg).not.toContain('"no-search"');
150
+ warn.mockRestore();
151
+ });
152
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/web-components",
3
- "version": "0.6.9",
3
+ "version": "0.6.11",
4
4
  "description": "AdiaUI web components \u2014 vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui-runtime.",
5
5
  "type": "module",
6
6
  "types": "./index.d.ts",