basalt-ui 2.0.0 → 2.0.1

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,3 +1,10 @@
1
+ ## [2.0.1](https://github.com/jkrumm/basalt-ui/compare/v2.0.0...v2.0.1) (2026-10-14)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * close argo friction rows 1-3 ([41627c3](https://github.com/jkrumm/basalt-ui/commit/41627c328ad278fc303ca9317cada2d2ab5f99a4))
7
+
1
8
  # [2.0.0](https://github.com/jkrumm/basalt-ui/compare/v1.41.0...v2.0.0) (2026-10-14)
2
9
 
3
10
 
package/MIGRATING.md CHANGED
@@ -66,18 +66,18 @@ There is one row per SURFACE, not per export, and a surface earns a row once any
66
66
  shipped lefthook preset both changed in 1.30.0 with no row that owned them — which is how a
67
67
  consumer read this index, found nothing, and diffed a KPI row that had silently gone 4-up → 2-up.
68
68
 
69
- | Surface | 1.27.0 | 1.28.0 | 1.29.0 | 1.30.0 | 1.30.1 | 1.30.2 | 1.31.0 | 1.32.0 | 1.32.1 | 1.33.0 | 1.34.0 | 1.34.1 | 1.35.0 | 1.36.0 | 1.36.1 | 1.37.0 | 1.37.1 | 1.38.0 | 1.38.1 | 1.39.0 | 1.40.0 | 1.40.1 | 1.40.2 | 1.40.3 | 1.41.0 | 2.0.0 | Unreleased |
70
- | ----------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------- | ------ | ------ | -------------------------------------------------------------------------------------------- | ------ | ------ | ------ | ------ | ------ | ----------------------------------------------- | ------ | ------ | ------ | ------ | ------------------------------------------------------------ | ---------- | --- | --- |
71
- | **tokens** (`./tokens`) | — | `--vx-divider` → `rgba()` on both schemes (§ Shell) | `--vx-space-touch-target` added (§ Additions) | eight spacing numbers (§ Chrome) | — | — | `--vx-hit` replaces the touch vars; `SIZE_CLASSES` (§ 1.31.0) | — | `SIZE_CLASSES`/`CONTAINER_CLASSES` actually exported (§ Fixes) | `CONTAINER_GRID_BREAKPOINTS`; `raw-breakpoint` trusts it (§ 1.33.0) | `CONTAINER_KEYS` (§ 1.34.0) | — | — | — | — | — | — | — | | `appShellInset` 20 → 16 (§ 1.38.1) | — | — | — | — | — | — | — | — |
72
- | **charts** (`./charts`) | — | phone tier, `curve`, formatters, `state`, log axis (§ preamble) | `ChartState.empty: string`, `AxisBottomNumeric` (§ Additions) | responsive `height`, `legend.maxRows` (§ Charts) | `height` reaches the kinds; `maxRows` beats the `fill` fit (§ Charts) | — | container-class tier + `height` keys (§ 1.31.0) | — | `ResponsiveChartHeight` `sm` → key (§ Container recipes) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
73
- | **shell** (`.`) | — | scrollport, region seams, **the brand left the sidebar** (§§ scrollport, brand, Shell) | `PageTitle`, `BasaltDevDock`, `useBreakpoint` (§ Additions) | More popover, aside pill, the responsive gutter, `useBasaltSpacing` (§ Chrome) | — | — | `theme.breakpoints` derived, `useSizeClass`, `data-basalt-host`, `useBreakpoint` on notice (§ 1.31.0) | `useBreakpoint` removal → 1.33.0; `useSizeClass` is shell-only (§ 1.32.0) | shell-less `shell/` gutter, `AppShell` breakpoints, `useSizeClass` for overlays (§§ Shell-less, `useSizeClass`) | — | `useBreakpoint` removed (§ 1.34.0) | — | — | `useCoarsePointer()` (§ `useCoarsePointer()`) | — | — | — | — | | content gutter 16, block 8 from `sm` (§ 1.38.1) | — | — | — | — | — | — | — | — |
74
- | **controls** (`./controls`) | `NumberFilter`, `field.number` (§ Controls) | sheet renders panel rows; `PanelRow`, `SliderControl` (§ `basalt-ui/controls`) | — | — | — | — | — | — | — | `NumberFilter clearable`; `createSearchStore` `resets` (§ State and controls) | — | — | — | `FormSection`/`FormRow`/`FormGroup`/`FormActions` move here (§ The form layout moves) | — | — | — | — | — | — | — | — | — | — | — | — | — |
75
- | **forms** (`./forms`) | — | `field` → `inputProps`, **`inputProps` drops `key`**, the layout tier (§ `inputProps`) | `field` DELETED — and the `--fix` recipe no longer works (§ Consolidation) | — | — | — | — | — | `FormGroup` row no longer stretches (§ Fixes) | — | — | — | — | the layout + `useForm`/`schemaResolver` deprecated, removed 1.37.0 (§ The form layout moves) | — | — | — | — | — | — | — | — | — | — | — | — | — |
76
- | **data** (`./data/table`) | — | root is a `<div>`, row selection, `getItemKey` required (§ `basalt-ui/data`) | `./data` + `./query` + `./connectivity` dropped (§ Consolidation) | — | — | — | — | — | `renderCard` documented (§ `renderCard`) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
77
- | **dashboard** (`.`) | `StatCard`: `unit`, `breakdown`, delta format (§ `StatCard`) | `StatGroup`, `WidgetGrid`, query-aware `StatCard` — additive | — | **the column law keys on the CONTAINER; `cols={4}` is 2-up at `sm`** (§ Dashboard) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
78
- | **toolchain** (`./configs/*`) | — | — | the shipped lefthook preset's `check-theme` BROKE — the CLI resolver stopped ascending (§ CLI) | the preset's default bin, and the `root:` recipe (§ Toolchain) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
79
- | **CLI** (`basalt-ui`) | — | — | **resolver stops relocating; `doctor` loses 6 of 10 checks** (§ CLI) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | the managed block moves `CLAUDE.md` → `AGENTS.md` (§ 1.41.0) | — | — |
80
- | **guards** | five rules → `error` (§ Guards) | six new ids at `warn` (§ preamble) | `query-dual-import` retired in 1.29.1 (§ Guards) | four rules → `error` (§ Guards) | — | — | — | `raw-breakpoint`: the container-grid answer, `error` at 1.33.0 (§ 1.32.0) | `FormRow`/`FormGroup` homes in both lanes; `basalt-main` dropped (§ Fixes) | C1 + breakpoint pairs → `error`; `unknown-vx-token` `warn` (§ Guards) | — | — | — | `unknown-vx-token` → `error`; the token factories throw (§ Guards) | — | — | — | — | — | — | — | — | — | — | — | — | — |
69
+ | Surface | 1.27.0 | 1.28.0 | 1.29.0 | 1.30.0 | 1.30.1 | 1.30.2 | 1.31.0 | 1.32.0 | 1.32.1 | 1.33.0 | 1.34.0 | 1.34.1 | 1.35.0 | 1.36.0 | 1.36.1 | 1.37.0 | 1.37.1 | 1.38.0 | 1.38.1 | 1.39.0 | 1.40.0 | 1.40.1 | 1.40.2 | 1.40.3 | 1.41.0 | 2.0.0 | 2.0.1 | Unreleased |
70
+ | ----------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------- | ------ | ------ | -------------------------------------------------------------------------------------------- | ------ | ------ | ------ | ------ | ------ | ----------------------------------------------- | ------ | ------ | ------ | ------ | ------------------------------------------------------------ | ---------- | --- | --- | --- |
71
+ | **tokens** (`./tokens`) | — | `--vx-divider` → `rgba()` on both schemes (§ Shell) | `--vx-space-touch-target` added (§ Additions) | eight spacing numbers (§ Chrome) | — | — | `--vx-hit` replaces the touch vars; `SIZE_CLASSES` (§ 1.31.0) | — | `SIZE_CLASSES`/`CONTAINER_CLASSES` actually exported (§ Fixes) | `CONTAINER_GRID_BREAKPOINTS`; `raw-breakpoint` trusts it (§ 1.33.0) | `CONTAINER_KEYS` (§ 1.34.0) | — | — | — | — | — | — | — | | `appShellInset` 20 → 16 (§ 1.38.1) | — | — | — | — | — | — | — | — | — |
72
+ | **charts** (`./charts`) | — | phone tier, `curve`, formatters, `state`, log axis (§ preamble) | `ChartState.empty: string`, `AxisBottomNumeric` (§ Additions) | responsive `height`, `legend.maxRows` (§ Charts) | `height` reaches the kinds; `maxRows` beats the `fill` fit (§ Charts) | — | container-class tier + `height` keys (§ 1.31.0) | — | `ResponsiveChartHeight` `sm` → key (§ Container recipes) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
73
+ | **shell** (`.`) | — | scrollport, region seams, **the brand left the sidebar** (§§ scrollport, brand, Shell) | `PageTitle`, `BasaltDevDock`, `useBreakpoint` (§ Additions) | More popover, aside pill, the responsive gutter, `useBasaltSpacing` (§ Chrome) | — | — | `theme.breakpoints` derived, `useSizeClass`, `data-basalt-host`, `useBreakpoint` on notice (§ 1.31.0) | `useBreakpoint` removal → 1.33.0; `useSizeClass` is shell-only (§ 1.32.0) | shell-less `shell/` gutter, `AppShell` breakpoints, `useSizeClass` for overlays (§§ Shell-less, `useSizeClass`) | — | `useBreakpoint` removed (§ 1.34.0) | — | — | `useCoarsePointer()` (§ `useCoarsePointer()`) | — | — | — | — | | content gutter 16, block 8 from `sm` (§ 1.38.1) | — | — | — | — | — | — | — | — | — |
74
+ | **controls** (`./controls`) | `NumberFilter`, `field.number` (§ Controls) | sheet renders panel rows; `PanelRow`, `SliderControl` (§ `basalt-ui/controls`) | — | — | — | — | — | — | — | `NumberFilter clearable`; `createSearchStore` `resets` (§ State and controls) | — | — | — | `FormSection`/`FormRow`/`FormGroup`/`FormActions` move here (§ The form layout moves) | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
75
+ | **forms** (`./forms`) | — | `field` → `inputProps`, **`inputProps` drops `key`**, the layout tier (§ `inputProps`) | `field` DELETED — and the `--fix` recipe no longer works (§ Consolidation) | — | — | — | — | — | `FormGroup` row no longer stretches (§ Fixes) | — | — | — | — | the layout + `useForm`/`schemaResolver` deprecated, removed 1.37.0 (§ The form layout moves) | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
76
+ | **data** (`./data/table`) | — | root is a `<div>`, row selection, `getItemKey` required (§ `basalt-ui/data`) | `./data` + `./query` + `./connectivity` dropped (§ Consolidation) | — | — | — | — | — | `renderCard` documented (§ `renderCard`) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
77
+ | **dashboard** (`.`) | `StatCard`: `unit`, `breakdown`, delta format (§ `StatCard`) | `StatGroup`, `WidgetGrid`, query-aware `StatCard` — additive | — | **the column law keys on the CONTAINER; `cols={4}` is 2-up at `sm`** (§ Dashboard) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
78
+ | **toolchain** (`./configs/*`) | — | — | the shipped lefthook preset's `check-theme` BROKE — the CLI resolver stopped ascending (§ CLI) | the preset's default bin, and the `root:` recipe (§ Toolchain) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
79
+ | **CLI** (`basalt-ui`) | — | — | **resolver stops relocating; `doctor` loses 6 of 10 checks** (§ CLI) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | the managed block moves `CLAUDE.md` → `AGENTS.md` (§ 1.41.0) | — | — | — |
80
+ | **guards** | five rules → `error` (§ Guards) | six new ids at `warn` (§ preamble) | `query-dual-import` retired in 1.29.1 (§ Guards) | four rules → `error` (§ Guards) | — | — | — | `raw-breakpoint`: the container-grid answer, `error` at 1.33.0 (§ 1.32.0) | `FormRow`/`FormGroup` homes in both lanes; `basalt-main` dropped (§ Fixes) | C1 + breakpoint pairs → `error`; `unknown-vx-token` `warn` (§ Guards) | — | — | — | `unknown-vx-token` → `error`; the token factories throw (§ Guards) | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
81
81
 
82
82
  ---
83
83
 
@@ -88,8 +88,10 @@ Contract: `docs/V2-SPEC.md`. Every removal and move is one row, grouped by area.
88
88
  ### Upgrading from 1.x
89
89
 
90
90
  1. Go to `1.41.0` first and clear its deprecations: 2.0 retires the lifecycle, so nothing deprecated survives.
91
- 2. Install `basalt-ui@2` and the now-required peers (§ Subpaths, peers and gates); delete your
92
- `@mantine/*/styles.layer.css` imports, because `basalt-ui/styles.css` carries them.
91
+ 2. Install `basalt-ui@2` and the now-required peers (§ Subpaths, peers and gates); delete only the
92
+ `@mantine/{core,notifications,spotlight}/styles.layer.css` imports, because `basalt-ui/styles.css`
93
+ `@import`s exactly those three. Keep every extension package's layer CSS (`@mantine/dates`,
94
+ `@mantine/schedule`, …) imported AFTER `basalt-ui/styles.css`.
93
95
  3. Replace the hand-built root (providers, `QueryClient`, router) with `BasaltProvider` +
94
96
  `createBasaltRouter` (§ Provider and wiring).
95
97
  4. Rewrite imports by area, using the tables below. A `move` changes only the specifier.
@@ -990,6 +992,23 @@ optional, subpath-required peers.
990
992
  _Nothing yet. Author the next section under this heading as `## Unreleased — <title>`; the
991
993
  release renames it and opens a new one here._
992
994
 
995
+ ## 2.0.1
996
+
997
+ _Nothing to migrate in this release._
998
+
999
+ ## Next patch — doctor reads the peers and the config
1000
+
1001
+ - `doctor` gains `exact-peers`: for each exact-pinned peer of the installed basalt-ui (the three
1002
+ `@fontsource-variable/*`, `motion`, `remend`, …) it compares your installed version and prints a
1003
+ one-line `bun add <pkg>@<version>` fix on a mismatch. `bun add` accepts the mismatch silently.
1004
+ The three font peers now pin `5.3.0`.
1005
+ - `doctor` gains `config-keys`: a key in the package.json `basalt` object that the config does not
1006
+ read fails with its fix. `basalt.chartMissingAriaLabel` is gone with its guard kind; `ariaLabel`
1007
+ is required on a chart now, so delete the key.
1008
+ - Corrected the § 2.0 "Upgrading from 1.x" step 2: delete only the core, notifications and
1009
+ spotlight layer imports; extension packages (`@mantine/dates`, `@mantine/schedule`) stay, after
1010
+ `basalt-ui/styles.css`.
1011
+
993
1012
  ## 2.0.0
994
1013
 
995
1014
  _Nothing to migrate in this release._
@@ -26,7 +26,7 @@ declare module '@tanstack/react-router' {
26
26
  createRoot(document.getElementById('root')!).render(<BasaltProvider router={router} />)
27
27
  ```
28
28
 
29
- 3. If you use `basalt-ui/controls-dates`, import `@mantine/dates/styles.layer.css` after `basalt-ui/styles.css` (core must precede dates in `@layer mantine`).
29
+ 3. Import extension layer CSS (`@mantine/dates` for `basalt-ui/controls-dates`, `@mantine/schedule`) after `basalt-ui/styles.css` (core must precede them in `@layer mantine`).
30
30
  4. Install the peers of each subpath you import: read its `Install: bun add …` line in `node_modules/basalt-ui/llms.txt`; a subpath with no `Install:` line needs only the required peers listed at the top of that file.
31
31
  5. In `vite.config.ts` spread `...basaltViteConfig({ port, apiTarget })` and register, in this order, `plugins: [tanstackRouter({ target: 'react', autoCodeSplitting: true }), react(), ...basaltAppPlugin({ name, description })]`. Run `bun add -d @tanstack/router-plugin` and import `tanstackRouter` from `@tanstack/router-plugin/vite`.
32
32
  6. Run `vite build` once so the plugin writes `src/routeTree.gen.ts`; commit it (`tsc -b` fails without it).
@@ -165,4 +165,8 @@ export declare const DEFAULT_ROOTS: string[];
165
165
  * "matches zero files" break this derivation exists to prevent.
166
166
  */
167
167
  export declare function resolveRoots(cfg: BasaltConfig): string[];
168
+ /** Every `BasaltConfig` key, as data for `doctor`'s `config-keys` (a test pins it to the type). */
169
+ export declare const BASALT_CONFIG_KEYS: (keyof BasaltConfig)[];
170
+ /** Keys removed from `BasaltConfig`, with the reason `doctor` prints. */
171
+ export declare const RETIRED_CONFIG_KEYS: Readonly<Record<string, string>>;
168
172
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExD,yEAAyE;AACzE,MAAM,MAAM,YAAY,GAAG;IACzB,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IACnC;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;wFACoF;IACpF,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAA;IACpD,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;;;;;;OAeG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC,CAAA;IACzD;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,EAAE,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5E,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,EAAE,CAG5E;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGlF;AAED,eAAO,MAAM,YAAY,QAAQ,CAAA;AACjC,eAAO,MAAM,aAAa,UAAiB,CAAA;AAE3C;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,EAAE,CAExD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExD,yEAAyE;AACzE,MAAM,MAAM,YAAY,GAAG;IACzB,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IACnC;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;wFACoF;IACpF,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAA;IACpD,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;;;;;;OAeG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC,CAAA;IACzD;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,EAAE,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5E,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,EAAE,CAG5E;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGlF;AAED,eAAO,MAAM,YAAY,QAAQ,CAAA;AACjC,eAAO,MAAM,aAAa,UAAiB,CAAA;AAE3C;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,EAAE,CAExD;AAED,mGAAmG;AACnG,eAAO,MAAM,kBAAkB,EAIf,CAAC,MAAM,YAAY,CAAC,EAAE,CAAA;AAEtC,yEAAyE;AACzE,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEhE,CAAA"}
@@ -11,9 +11,15 @@ const DEFAULT_ROOTS = [DEFAULT_ROOT];
11
11
  function resolveRoots(cfg) {
12
12
  return cfg.roots?.length ? cfg.roots : DEFAULT_ROOTS;
13
13
  }
14
+ const BASALT_CONFIG_KEYS = "roots exempt profile include spacingSteps severity forbiddenAccents accentHue rawRadius rawSurface cardWithBorder offSystemSurfaceVar mantineShadeIndex rawHtmlLayout inlineSpacing inlineDisplay rawMotionValue rawFormControl sub16InputFont seriesModulePath exemptRules aiMajorSkewReason".split(" ");
15
+ const RETIRED_CONFIG_KEYS = {
16
+ chartMissingAriaLabel: "its guard kind is gone in 2.0 (`ariaLabel` is required on a chart now)"
17
+ };
14
18
  export {
19
+ BASALT_CONFIG_KEYS,
15
20
  DEFAULT_ROOT,
16
21
  DEFAULT_ROOTS,
22
+ RETIRED_CONFIG_KEYS,
17
23
  exemptRulePaths,
18
24
  exemptRuleReason,
19
25
  resolveRoots
@@ -28,4 +28,16 @@ export declare function seedTsconfigExtends(cwd: string): string | null;
28
28
  * proves nothing.
29
29
  */
30
30
  export declare function appWiringCheck(cwd: string): Check;
31
+ /**
32
+ * `config-keys`: a key in package.json's `basalt` object that `BasaltConfig` does not declare is
33
+ * silently ignored, so a key retired in 2.0 keeps looking configured. Reads the raw file, not
34
+ * `readBasaltConfig`, which is typed and would hide the very keys this looks for.
35
+ */
36
+ export declare function unknownConfigKeysCheck(cwd: string): Check;
37
+ /**
38
+ * `exact-peers`: basalt-ui pins some peers to one exact version, and `bun add` accepts any other
39
+ * version without a word. Compares each installed one against the install's declared pin; a peer
40
+ * that is not installed is not this check's business (optional peers are legitimately absent).
41
+ */
42
+ export declare function exactPeersCheck(cwd: string, install: BasaltInstall): Check;
31
43
  //# sourceMappingURL=doctor-upgrade.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor-upgrade.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-upgrade.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C,MAAM,MAAM,KAAK,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA;AAgB9D,wEAAwE;AACxE,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,KAAG,MACc,CAAA;AA+CvD,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAqBpD;AA+CD,4FAA4F;AAC5F,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,KAAK,CAyBhF;AA4CD,mGAAmG;AACnG,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,aAAa,GACrB,KAAK,CA8BP;AAUD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkC9D;AAoDD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CA+BjD"}
1
+ {"version":3,"file":"doctor-upgrade.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-upgrade.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C,MAAM,MAAM,KAAK,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA;AAgB9D,wEAAwE;AACxE,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,KAAG,MACc,CAAA;AA+CvD,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAqBpD;AA+CD,4FAA4F;AAC5F,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,KAAK,CAyBhF;AA4CD,mGAAmG;AACnG,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,aAAa,GACrB,KAAK,CA8BP;AAUD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkC9D;AAoDD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CA+BjD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAezD;AAaD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,KAAK,CAwB1E"}
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { createRequire } from "node:module";
3
3
  import { dirname, relative, resolve } from "node:path";
4
+ import { BASALT_CONFIG_KEYS, RETIRED_CONFIG_KEYS } from "./config.js";
4
5
  import { parseJsonc, readIfExists, scannableFiles } from "./index.js";
5
6
  const guarded = (id, run) => {
6
7
  try {
@@ -259,11 +260,55 @@ function appWiringCheck(cwd) {
259
260
  };
260
261
  });
261
262
  }
263
+ function unknownConfigKeysCheck(cwd) {
264
+ return guarded("config-keys", () => {
265
+ const raw = readIfExists(resolve(cwd, "package.json"));
266
+ const block = raw === null ? null : JSON.parse(raw).basalt;
267
+ if (typeof block !== "object" || block === null || Array.isArray(block)) return null;
268
+ const known = new Set(BASALT_CONFIG_KEYS);
269
+ const unknown = Object.keys(block).filter((k) => !known.has(k));
270
+ if (unknown.length === 0) return null;
271
+ const named = unknown.map(
272
+ (k) => `basalt.${k} (${RETIRED_CONFIG_KEYS[k] ?? "not a basalt key"})`
273
+ );
274
+ return {
275
+ fail: `config-keys: package.json sets keys basalt-ui does not read: ${named.join("; ")}. Fix: delete each key.`
276
+ };
277
+ });
278
+ }
279
+ const EXACT_VERSION = /^\d+\.\d+\.\d+$/;
280
+ function installedVersion(cwd, pkg) {
281
+ for (let dir = cwd; ; dir = dirname(dir)) {
282
+ const raw = readIfExists(resolve(dir, "node_modules", pkg, "package.json"));
283
+ if (raw !== null) return JSON.parse(raw).version ?? null;
284
+ if (dirname(dir) === dir) return null;
285
+ }
286
+ }
287
+ function exactPeersCheck(cwd, install) {
288
+ const id = "exact-peers";
289
+ if (install.dir === null)
290
+ return { skip: `${id}: basalt-ui does not resolve, so its peers cannot be read.` };
291
+ const dir = install.dir;
292
+ return guarded(id, () => {
293
+ const raw = readIfExists(resolve(dir, "package.json"));
294
+ const peers = (raw === null ? {} : JSON.parse(raw).peerDependencies) ?? {};
295
+ const off = Object.entries(peers).filter(([, range]) => EXACT_VERSION.test(range)).flatMap(([pkg, pin]) => {
296
+ const have = installedVersion(cwd, pkg);
297
+ return have === null || have === pin ? [] : [`${pkg}@${have} (basalt-ui pins ${pin}): \`bun add ${pkg}@${pin}\``];
298
+ });
299
+ if (off.length === 0) return null;
300
+ return {
301
+ fail: `${id}: installed peers differ from basalt-ui's exact pins. Fix: ${off.join("; ")}.`
302
+ };
303
+ });
304
+ }
262
305
  export {
263
306
  appWiringCheck,
264
307
  droppedSubpathsCheck,
308
+ exactPeersCheck,
265
309
  retiredLintRulesCheck,
266
310
  seedTsconfigExtends,
267
311
  tsconfigBaseCheck,
268
- tsconfigFile
312
+ tsconfigFile,
313
+ unknownConfigKeysCheck
269
314
  };
@@ -22,7 +22,8 @@ export type DoctorResult = {
22
22
  * 4. `oxlint-preset`: `.oxlintrc.json` extends the shipped preset AND that path resolves.
23
23
  * 7. `ai-major-parity`: the `ai` major agrees across this package.json's dependency fields
24
24
  * (exempt via `basalt.aiMajorSkewReason`; a stale reason warns).
25
- * 8-11. `tsconfig-base`, `retired-lint-rules`, `dropped-subpaths`, `app-wiring` (./doctor-upgrade);
25
+ * 8-13. `tsconfig-base`, `retired-lint-rules`, `dropped-subpaths`, `app-wiring`, `exact-peers`,
26
+ * `config-keys` (./doctor-upgrade);
26
27
  * a check that cannot run is SKIPPED and exits non-zero.
27
28
  *
28
29
  * Warning (non-fatal):
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAqDA,MAAM,MAAM,YAAY,GAAG;IACzB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAA;IACpB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AA8CD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,CAAC,aAAa,GAAE,MAAsB,EAAE,KAAK,GAAE,MAAM,EAAO,GAAG,MAAM,CAiS1F"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAuDA,MAAM,MAAM,YAAY,GAAG;IACzB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAA;IACpB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AA8CD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,aAAa,GAAE,MAAsB,EAAE,KAAK,GAAE,MAAM,EAAO,GAAG,MAAM,CAsS1F"}
@@ -29,8 +29,10 @@ import { resolveRoots } from "./config.js";
29
29
  import {
30
30
  appWiringCheck,
31
31
  droppedSubpathsCheck,
32
+ exactPeersCheck,
32
33
  retiredLintRulesCheck,
33
- tsconfigBaseCheck
34
+ tsconfigBaseCheck,
35
+ unknownConfigKeysCheck
34
36
  } from "./doctor-upgrade.js";
35
37
  import { findManifestAbove, parentInstallAdvice } from "./sync.js";
36
38
  function isSameFile(a, b) {
@@ -265,6 +267,11 @@ basalt-ui doctor \u2014 ${cwd}
265
267
  [
266
268
  "app-wiring: the entry mounts BasaltProvider and the router tree is generated",
267
269
  appWiringCheck(cwd)
270
+ ],
271
+ ["exact-peers: installed exact-pinned peers match basalt-ui", exactPeersCheck(cwd, install)],
272
+ [
273
+ "config-keys: the basalt config block sets only keys basalt-ui reads",
274
+ unknownConfigKeysCheck(cwd)
268
275
  ]
269
276
  ];
270
277
  for (const [ok, check] of upgrade) {
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # basalt-ui v2.0.0
1
+ # basalt-ui v2.0.1
2
2
  # Opinionated framework for Mantine-based React apps — theme, app shell, TanStack Charts, toolchain presets
3
3
  #
4
4
  # Entry points: use the exact import specifier below; do not guess.
@@ -13,7 +13,7 @@
13
13
  # A subpath's `Install:` line is its required peers; `OptionalPeers:` are import()-only, install on use.
14
14
 
15
15
  Package: basalt-ui
16
- Version: 2.0.0
16
+ Version: 2.0.1
17
17
 
18
18
  ## basalt-ui
19
19
  Import: import { ... } from 'basalt-ui'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "basalt-ui",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Opinionated framework for Mantine-based React apps — theme, app shell, TanStack Charts, toolchain presets",
5
5
  "keywords": [
6
6
  "charts",
@@ -165,9 +165,9 @@
165
165
  },
166
166
  "peerDependencies": {
167
167
  "@ai-sdk/provider": "^4.0.26",
168
- "@fontsource-variable/hubot-sans": "5.2.8",
169
- "@fontsource-variable/jetbrains-mono": "5.2.8",
170
- "@fontsource-variable/nunito-sans": "5.2.7",
168
+ "@fontsource-variable/hubot-sans": "5.3.0",
169
+ "@fontsource-variable/jetbrains-mono": "5.3.0",
170
+ "@fontsource-variable/nunito-sans": "5.3.0",
171
171
  "@mantine/core": "^9.7.0",
172
172
  "@mantine/dates": "^9.7.0",
173
173
  "@mantine/form": "^9.7.0",
package/src/cli/config.ts CHANGED
@@ -178,3 +178,15 @@ export const DEFAULT_ROOTS = [DEFAULT_ROOT]
178
178
  export function resolveRoots(cfg: BasaltConfig): string[] {
179
179
  return cfg.roots?.length ? cfg.roots : DEFAULT_ROOTS
180
180
  }
181
+
182
+ /** Every `BasaltConfig` key, as data for `doctor`'s `config-keys` (a test pins it to the type). */
183
+ export const BASALT_CONFIG_KEYS = (
184
+ 'roots exempt profile include spacingSteps severity forbiddenAccents accentHue rawRadius rawSurface ' +
185
+ 'cardWithBorder offSystemSurfaceVar mantineShadeIndex rawHtmlLayout inlineSpacing inlineDisplay ' +
186
+ 'rawMotionValue rawFormControl sub16InputFont seriesModulePath exemptRules aiMajorSkewReason'
187
+ ).split(' ') as (keyof BasaltConfig)[]
188
+
189
+ /** Keys removed from `BasaltConfig`, with the reason `doctor` prints. */
190
+ export const RETIRED_CONFIG_KEYS: Readonly<Record<string, string>> = {
191
+ chartMissingAriaLabel: 'its guard kind is gone in 2.0 (`ariaLabel` is required on a chart now)',
192
+ }
@@ -6,6 +6,7 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs'
6
6
  import { createRequire } from 'node:module'
7
7
  import { dirname, relative, resolve } from 'node:path'
8
8
 
9
+ import { BASALT_CONFIG_KEYS, RETIRED_CONFIG_KEYS } from './config'
9
10
  import type { BasaltConfig } from './config'
10
11
  import { parseJsonc, readIfExists, scannableFiles } from './index'
11
12
  import type { BasaltInstall } from './index'
@@ -390,3 +391,67 @@ export function appWiringCheck(cwd: string): Check {
390
391
  }
391
392
  })
392
393
  }
394
+
395
+ /**
396
+ * `config-keys`: a key in package.json's `basalt` object that `BasaltConfig` does not declare is
397
+ * silently ignored, so a key retired in 2.0 keeps looking configured. Reads the raw file, not
398
+ * `readBasaltConfig`, which is typed and would hide the very keys this looks for.
399
+ */
400
+ export function unknownConfigKeysCheck(cwd: string): Check {
401
+ return guarded('config-keys', () => {
402
+ const raw = readIfExists(resolve(cwd, 'package.json'))
403
+ const block = raw === null ? null : (JSON.parse(raw) as { basalt?: unknown }).basalt
404
+ if (typeof block !== 'object' || block === null || Array.isArray(block)) return null
405
+ const known = new Set<string>(BASALT_CONFIG_KEYS)
406
+ const unknown = Object.keys(block).filter((k) => !known.has(k))
407
+ if (unknown.length === 0) return null
408
+ const named = unknown.map(
409
+ (k) => `basalt.${k} (${RETIRED_CONFIG_KEYS[k] ?? 'not a basalt key'})`,
410
+ )
411
+ return {
412
+ fail: `config-keys: package.json sets keys basalt-ui does not read: ${named.join('; ')}. Fix: delete each key.`,
413
+ }
414
+ })
415
+ }
416
+
417
+ const EXACT_VERSION = /^\d+\.\d+\.\d+$/
418
+
419
+ /** `node_modules/<pkg>/package.json`'s version, searching `cwd` then every ancestor. */
420
+ function installedVersion(cwd: string, pkg: string): string | null {
421
+ for (let dir = cwd; ; dir = dirname(dir)) {
422
+ const raw = readIfExists(resolve(dir, 'node_modules', pkg, 'package.json'))
423
+ if (raw !== null) return (JSON.parse(raw) as { version?: string }).version ?? null
424
+ if (dirname(dir) === dir) return null
425
+ }
426
+ }
427
+
428
+ /**
429
+ * `exact-peers`: basalt-ui pins some peers to one exact version, and `bun add` accepts any other
430
+ * version without a word. Compares each installed one against the install's declared pin; a peer
431
+ * that is not installed is not this check's business (optional peers are legitimately absent).
432
+ */
433
+ export function exactPeersCheck(cwd: string, install: BasaltInstall): Check {
434
+ const id = 'exact-peers'
435
+ if (install.dir === null)
436
+ return { skip: `${id}: basalt-ui does not resolve, so its peers cannot be read.` }
437
+ const dir = install.dir
438
+ return guarded(id, () => {
439
+ const raw = readIfExists(resolve(dir, 'package.json'))
440
+ const peers =
441
+ (raw === null
442
+ ? {}
443
+ : (JSON.parse(raw) as { peerDependencies?: Record<string, string> }).peerDependencies) ?? {}
444
+ const off = Object.entries(peers)
445
+ .filter(([, range]) => EXACT_VERSION.test(range))
446
+ .flatMap(([pkg, pin]) => {
447
+ const have = installedVersion(cwd, pkg)
448
+ return have === null || have === pin
449
+ ? []
450
+ : [`${pkg}@${have} (basalt-ui pins ${pin}): \`bun add ${pkg}@${pin}\``]
451
+ })
452
+ if (off.length === 0) return null
453
+ return {
454
+ fail: `${id}: installed peers differ from basalt-ui's exact pins. Fix: ${off.join('; ')}.`,
455
+ }
456
+ })
457
+ }
package/src/cli/doctor.ts CHANGED
@@ -36,8 +36,10 @@ import { resolveRoots } from './config'
36
36
  import {
37
37
  appWiringCheck,
38
38
  droppedSubpathsCheck,
39
+ exactPeersCheck,
39
40
  retiredLintRulesCheck,
40
41
  tsconfigBaseCheck,
42
+ unknownConfigKeysCheck,
41
43
  } from './doctor-upgrade'
42
44
  import type { Check } from './doctor-upgrade'
43
45
  import { findManifestAbove, parentInstallAdvice } from './sync'
@@ -120,7 +122,8 @@ function resolveAiMajorSkewReason(cfg: BasaltConfig): {
120
122
  * 4. `oxlint-preset`: `.oxlintrc.json` extends the shipped preset AND that path resolves.
121
123
  * 7. `ai-major-parity`: the `ai` major agrees across this package.json's dependency fields
122
124
  * (exempt via `basalt.aiMajorSkewReason`; a stale reason warns).
123
- * 8-11. `tsconfig-base`, `retired-lint-rules`, `dropped-subpaths`, `app-wiring` (./doctor-upgrade);
125
+ * 8-13. `tsconfig-base`, `retired-lint-rules`, `dropped-subpaths`, `app-wiring`, `exact-peers`,
126
+ * `config-keys` (./doctor-upgrade);
124
127
  * a check that cannot run is SKIPPED and exits non-zero.
125
128
  *
126
129
  * Warning (non-fatal):
@@ -370,7 +373,7 @@ export function doctor(invocationCwd: string = process.cwd(), flags: string[] =
370
373
  pass('ai-major-parity: this package.json declares no ai dependency — nothing to compare')
371
374
  }
372
375
 
373
- // ── Hard checks 8-11: strictness and the 1.x → 2.0 upgrade path (./doctor-upgrade) ──────────
376
+ // ── Hard checks 8-13: strictness and the 1.x → 2.0 upgrade path (./doctor-upgrade) ──────────
374
377
  if (profile === 'tokens-only') {
375
378
  pass(
376
379
  'tsconfig-base / retired-lint-rules / dropped-subpaths / app-wiring: n/a — a tokens-only consumer',
@@ -390,6 +393,11 @@ export function doctor(invocationCwd: string = process.cwd(), flags: string[] =
390
393
  'app-wiring: the entry mounts BasaltProvider and the router tree is generated',
391
394
  appWiringCheck(cwd),
392
395
  ],
396
+ ['exact-peers: installed exact-pinned peers match basalt-ui', exactPeersCheck(cwd, install)],
397
+ [
398
+ 'config-keys: the basalt config block sets only keys basalt-ui reads',
399
+ unknownConfigKeysCheck(cwd),
400
+ ],
393
401
  ]
394
402
  for (const [ok, check] of upgrade) {
395
403
  if (check === null) pass(ok)