Skip to content

feat(i18n,a11y,dnd,media): graduate four modules toward Stable (#141–#144) - #157

Merged
JosunLP merged 4 commits into
devfrom
feat/i18n-a11y-dnd-media-stable
Jun 29, 2026
Merged

feat(i18n,a11y,dnd,media): graduate four modules toward Stable (#141–#144)#157
JosunLP merged 4 commits into
devfrom
feat/i18n-a11y-dnd-media-stable

Conversation

@JosunLP

@JosunLP JosunLP commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

Collected PR for the next four open stabilization tickets, implemented on one branch.

Closes #141
Closes #142
Closes #143
Closes #144

#141 — i18n toward Stable: ICU MessageFormat + message extraction

  • ICU MessageFormat routed through a locale-aware formatter backed by Intl.PluralRules: plural, selectordinal, select, nested arguments, offset:, exact =N selectors, the # token, and apostrophe escaping. ICU is detected automatically; plain {name} interpolation and the legacy singular | plural pipe form keep the untouched fast path.
  • New authoring helpers defineMessages() (identity + extraction anchor) and formatMessage() (standalone single-message formatter).
  • Optional, dependency-free message-extraction tooling at the new @bquery/bquery/i18n/extract entry + bquery-i18n CLI: scans defineMessages catalogs and t()/tc() calls, merges into nested JSON catalogs without overwriting translations (--prune opt-in). Zero-build runtime path preserved.
  • i18n guide gains a Stability section: frozen surface, ICU coverage table (with documented non-coverage — inline number/date skeletons, rich-text), extraction, and lazy-loading. Tests cover ICU + extraction + CLI.

#142 — a11y toward Stable: documented audit scope

  • Every AuditFinding now carries a wcag criterion; the full rule catalog is exported as auditRules (rule → WCAG → severity → what it cannot detect).
  • a11y guide gains the audit-scope table with known limitations (contrast, focus order, reading order, motion, meaningfulness, cross-root refs) and a frozen surface reference. Tests cover audit scope, focus trapping, live regions, inert/scrollLock, and preference signals.

#143 — dnd toward Stable: keyboard accessibility statement

  • Frozen surface + accessibility statement: the keyboard model (pick up / move / drop / cancel, aria-grabbed) and confirmation that drag announcements route through the shared a11y live-region announcer (no second channel).
  • Tests cover keyboard pick/move/drop/cancel, aria-grabbed transitions, and the grid/delay/viewport option surface.

#144 — media toward Stable: SSR-safe defaults + cleanup (bake-and-verify)

  • Per-composable SSR fallback + cleanup contract documented; frozen surface. No new features.
  • Tests cover SSR-safe defaults, idempotent destroy(), listener detachment (no leaks), and AbortSignal teardown.

Shared / build

  • Externalize node:* in the Vite lib build so the CLI entries emit real import('node:fs/promises') instead of Vite's browser-external stub — this also fixes the previously-shipped bquery-view-compile CLI in dist.
  • README stability matrix, introduction.md stability paragraphs, CHANGELOG, src/full.ts exports, and package.json (./i18n/extract export + bquery-i18n bin) updated.

Verification

  • bun test2929 pass / 0 fail (47 new tests across 5 files).
  • tsc --noEmit clean; check:doc-exports 100% for i18n/a11y/dnd/media; check:full-bundle and check:ai-guidance pass.
  • bquery-i18n extract "src/**/*.ts" --out en.json smoke-tested end-to-end against the built dist.

🤖 Generated with Claude Code

…142, #143, #144)

Collected PR for the next four open stabilization tickets, implemented on one branch.

Closes #141
Closes #142
Closes #143
Closes #144

## #141 — i18n toward Stable: ICU MessageFormat + message extraction

- ICU MessageFormat support routed through a locale-aware formatter backed by
  `Intl.PluralRules`: `plural`, `selectordinal`, `select`, nested arguments,
  `offset:`, exact `=N` selectors, the `#` token, and apostrophe escaping.
  Plain `{name}` interpolation and the legacy `singular | plural` pipe form are
  unchanged (legacy fast path is untouched; ICU is detected automatically).
- New authoring helpers `defineMessages()` (identity + extraction anchor) and
  `formatMessage()` (standalone single-message formatter).
- Optional, dependency-free message-extraction tooling at the new
  `@bquery/bquery/i18n/extract` entry + `bquery-i18n` CLI: scans `defineMessages`
  catalogs and `t()`/`tc()` calls, merges into JSON catalogs without overwriting
  translations (`--prune` opt-in). Zero-build runtime path preserved.
- Frozen-surface, ICU coverage table (with documented non-coverage), extraction,
  and lazy-loading docs in the i18n guide. Tests for ICU + extraction + CLI.

## #142 — a11y toward Stable: documented audit scope

- Every `AuditFinding` now carries a `wcag` criterion; the full rule catalog is
  exported as `auditRules` (rule → WCAG → severity → what it cannot detect).
- Audit-scope table with known limitations (contrast, focus order, etc.) in the
  a11y guide; frozen surface reference. Tests for audit scope, focus trapping,
  live regions, inert/scrollLock, and preference signals.

## #143 — dnd toward Stable: keyboard accessibility statement

- Frozen surface + accessibility statement documenting the keyboard model
  (pick up / move / drop / cancel, `aria-grabbed`) and confirming drag
  announcements route through the shared a11y live-region announcer.
- Tests for keyboard pick/move/drop/cancel, aria-grabbed transitions, and the
  grid/delay/viewport option surface.

## #144 — media toward Stable: SSR-safe defaults + cleanup (bake-and-verify)

- Per-composable SSR fallback + cleanup contract documented; frozen surface.
- Tests for SSR-safe defaults, idempotent destroy(), listener detachment (no
  leaks), and AbortSignal teardown. No new features.

## Shared / build

- Externalize `node:*` in the Vite lib build so the CLI entries emit real
  `import('node:fs/promises')` instead of Vite's browser-external stub — this
  also fixes the previously-shipped `bquery-view-compile` CLI in dist.
- README stability matrix, introduction.md stability paragraphs, CHANGELOG,
  `src/full.ts` exports, and package.json (`./i18n/extract` export + bin) updated.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b9a7ec3a-7ae3-4290-8f59-cef0a883b0fc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/i18n-a11y-dnd-media-stable

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added docs Changes to the documentation tests Chenges to the tests build Changes to the build and meta files i18n Changes to the i18n module a11y Changes to the a11y module dnd Changes to the dnd module media Changes to the media module labels Jun 28, 2026
JosunLP added a commit that referenced this pull request Jun 29, 2026
…ork (#159)

## Problem

Vite's library build bundled `node:*` imports into its
**browser-external stub** (an empty default export). That left `await
import('node:fs/promises')` in the optional CLI entry resolving to
`undefined`, so the published **`bquery-view-compile`** CLI crashed at
runtime with `e is not a function` when run against the `dist` build.

## Fix

Mark `node:*` as external in `rollupOptions` so those imports are
emitted verbatim and resolved by Node at runtime:

```ts
rollupOptions: {
  external: (id) => id.startsWith('node:'),
  output: { ... },
}
```

Browser entries never import `node:*`, so this is a no-op for them.

## Verification

- Rebuilt `dist`; the `vite-browser-external` stub is **gone** from
`dist/view-compiler.es.mjs` (0 references, was 1).
- `bquery-view-compile --out-dir … file.html` runs end-to-end against
the built bundle (`1/1 compiled`).

## Note

This is the standalone hotfix for `dev`. The same one-line change is
also present in the stable-graduation PR #157 (which introduces the
`bquery-i18n` CLI that surfaced the bug); since the change is
byte-identical, the branches reconcile cleanly when both land — or the
line can be dropped from #157 once this merges.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <[email protected]>
JosunLP and others added 3 commits June 29, 2026 15:13
- a11y-stable: coalesce optional activeElement id to '' so toContain
  receives a string instead of `string | undefined` (TS2769).
- dnd-stable: drop the unsupported `keyboard` option from the
  useSortable() call. sortable() is pointer-only; keyboard a11y lives on
  draggable, which the first test already covers (TS2353).

The PR's local `tsc --noEmit` used the default tsconfig (excludes tests/);
CI runs tsconfig.component-test.json, which includes them and caught this.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
… Stable (#145#148) (#158)

Collected PR for the next four open stabilization tickets, implemented
on one branch.

> **Stacked on #157.** Base is set to the #157 branch so this diff shows
only the plugin/devtools/testing/storybook changes. It targets **dev**
after #157 lands (GitHub auto-retargets to `dev` when #157 merges).
Merge #157 first.

Closes #145
Closes #146
Closes #147
Closes #148

## #145 — plugin toward Stable: frozen extension contract

- New additive **`definePlugin()`** authoring helper (identity +
install-options inference) — a single, stable entry point for
third-party authors.
- Hook-bus / DI / install-lifecycle / directive-registration surface
frozen; **plugin-author guide** published (lifecycle, hook timing, DI
resolution, directive namespacing).
- **Install/uninstall symmetry** proven with tests: nothing a plugin
registers (directives, filters, actions, DI bindings) leaks after
`uninstall()`; re-install is clean.

## #146 — devtools toward Stable: bridge protocol + browser extension

- New stable, **versioned bridge protocol**: `connectDevtoolsBridge()`
(over `window.postMessage`), the transport-agnostic
`createBridgeServer()`, `serializeComponentTree()`, and
`BRIDGE_PROTOCOL_VERSION` / `BRIDGE_SOURCE` / `BRIDGE_CAPABILITIES`.
Built-in methods `ping` / `getSnapshot` / `getTimeline` /
`getComponentTree`; timeline events stream as `event` messages.
- Reference **Manifest V3 extension** in
[`extension/`](../tree/feat/plugin-devtools-testing-storybook-stable/extension)
(component tree, signal/store inspection, live timeline) that connects
over the protocol.
- `installBrowserBridge()` advertises the protocol version
(back-compatible).

## #147 — testing toward Stable: runner-agnostic + shadow-DOM parity

- Surface frozen; **runner integration beyond `bun:test`** documented
(Vitest / Jest) with `cleanup()` / `autoCleanup()` guidance.
- Shadow-DOM-aware `screen` / `within` queries, `userEvent` /
`fireEvent`, and a mock are **tested across light + shadow DOM**.
`expectAccessible` documented as sharing the a11y audit's rule
definitions.

## #148 — storybook toward Stable: pinned `unsafeHtml` contract

- Helper surface frozen; the **`unsafeHtml` security contract** is
pinned and documented — `storyHtml`/`storySvg` sanitize every
interpolation via the security module, and only brand-checked,
author-controlled fragments are inserted verbatim. Sanitization, the
bypass, brand-spoofing rejection, and each helper's output are covered
by tests.

## Shared / build

- README stability matrix, `introduction.md` stability paragraphs,
CHANGELOG, and `src/full.ts` exports (`definePlugin` + the devtools
bridge surface).

## Verification

- `bun test` — **2959 pass / 0 fail** (30 new tests across 4 files).
- `tsc --noEmit` clean; `check:doc-exports` 100% for
plugin/devtools/testing/storybook; `check:full-bundle` passes; eslint
clean; `build:lib` emits the devtools bundle.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>
@github-actions github-actions Bot added plugin Changes to the plugin module devtools Changes to the devtools module testing Changes to the testing module storybook Changes to the storybook module labels Jun 29, 2026
@JosunLP
JosunLP merged commit d3de9a1 into dev Jun 29, 2026
9 checks passed
@JosunLP
JosunLP deleted the feat/i18n-a11y-dnd-media-stable branch June 29, 2026 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y Changes to the a11y module build Changes to the build and meta files devtools Changes to the devtools module dnd Changes to the dnd module docs Changes to the documentation i18n Changes to the i18n module media Changes to the media module plugin Changes to the plugin module storybook Changes to the storybook module testing Changes to the testing module tests Chenges to the tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant