kerfjs 5.0.0-beta.18 → 5.0.0-beta.19

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
@@ -6,6 +6,33 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ - Added `npx kerfjs setup` and the `kerfjs/setup` automation API for safe,
10
+ AI-first initialization and migration of core and UI projects. The command
11
+ provides bounded dry-run diffs, explicit per-conflict keep/Kerf choices,
12
+ package-scoped hash-backed upgrades, monorepo selection, transactional
13
+ rollback, shipped guidance, strict TypeScript/ESLint, and UI catalog/profile
14
+ and doctor discovery. Empty application component metadata now emits a valid
15
+ empty v2 catalog. Existing JSONC TypeScript configuration is structurally
16
+ merged without discarding comments, trailing commas, authored fields, or line
17
+ endings; malformed roots and compiler-option containers require an explicit
18
+ keep/Kerf decision. Workspace selection now evaluates ordered globstar,
19
+ brace, exclusion, and re-inclusion patterns deterministically across npm,
20
+ Yarn, and pnpm declarations, rejects path/symlink/name ambiguity, and uses
21
+ manager-correct offline commands with explicit Yarn Classic/Berry detection
22
+ and network-disabled Berry installs.
23
+ - Added `@kerfjs/ui/doctor` and the `kerf-ui-doctor` command: a versioned, redacted repair-loop report that merges catalog/profile validation, TypeScript, Kerf UI ESLint, static analysis, and opt-in browser evaluation with changed/full modes, monorepo selection, reasoned suppressions, caching, and deterministic exits.
24
+ - Added the public Playwright-backed `kerf-ui-evaluate` CLI and `@kerfjs/ui/evaluator` Node API. It resolves application profiles and composition catalogs, exercises wide/intermediate/narrow/200%-zoom, light/dark, and reduced-motion contexts across Chromium, Firefox, and WebKit, and emits versioned repair-oriented `KUI-B###` diagnostics plus focused DOM/style evidence and hashed screenshots. Subjective visual quality remains an explicitly separate unrecorded reviewer rubric.
25
+ - Added `kerf-ui-analyze`, a profile- and catalog-aware static evaluator for Kerf UI integrations. It resolves directory policy per source, follows project-local CSS import graphs without cross-package class leakage, and evaluates shared stylesheets against every consuming source policy before deduplicating findings. Stable portable text, JSON, or SARIF diagnostics cover private selectors, unknown tokens, competing geometry/scroll owners, repeated insets, forced dimensions, off-scale spacing, and dynamic-class review, with exact scoped profile exceptions, a public report schema, and configurable CI review failure.
26
+ - Added catalog/profile-aware `recommended-ui` and `strict-ui` flat presets to `eslint-plugin-kerfjs`. Toolbar zone acceptance/cardinality and valid wiring imports come from the shipped catalogs; intrinsic or unknown zone children, root/subpath/namespace wiring, and the non-colliding `KUI-L090` load diagnostic are covered by real packed downstream installs on every supported ESLint major.
27
+ - **Breaking (`@kerfjs/ui` TypeScript declarations):** strengthened invalid-integration contracts for ListHeader modes, Select naming and literal choice identity, SegmentedControl/TabScaffold literal identity, collapsible TokenSearchField props, protected editor metadata, PanelHeader summary ids, ToolbarText line caps, and token-removal callbacks. Added named public unions for previously anonymous finite variants plus source and packed-tarball positive/negative compilation gates. See `ui/docs/type-contracts.md` for the compatibility review and dynamic-data widening syntax.
28
+ - `create-kerf-component` now scaffolds explicit `kerf.components.json` AI metadata and a deterministic `kerf-component-catalog` generator/checker. Generated v2 entries carry package identity, purpose, verified named exports, composition and geometry ownership, tokens, accessibility, and source links; publishing rejects missing author decisions, schema-invalid source/output fields, deleted sources, renamed exports, duplicate ids, or output drift, including multi-package workspaces. Syntax-aware TypeScript/TSX export discovery prevents JSX text, nested scopes, comments, and literals from impersonating public exports.
29
+ - Added a versioned application UI profile for AI tools and static evaluators. Shipped package defaults, schema, TypeScript types, Node discovery/merge/validation APIs, and a workspace example now make catalog locations, preferred components/recipes, theme/density policy, semantic token overrides, responsive/layout conventions, and narrow source-located exceptions explicit. Composition-only generated consumer catalogs load without a fabricated v1 selection artifact, while Kerf retains its required v1 selection catalog. Deterministic package → workspace → parent-to-child directory precedence validates every raw layer before merge, attributes resolved diagnostics through field provenance, and rejects stale catalogs, unknown components/tokens/rules, conflicts, malformed value shapes, and broad exemptions.
30
+ - Fixed `CatalogExampleStack` accessible labels: the helper now renders a semantic `section`, so a supplied `label` exposes the documented named region to assistive technology while an unlabeled stack remains an ordinary grouping.
31
+ - Added first-class `Pane` and `SunkenPanel` primitives to `@kerfjs/ui`. `Pane` standardizes semantic header/content/footer columns, explicit logical-edge separators, and one content scroll owner; `SunkenPanel` supplies one lowered surface with an 8px inset and vertical rhythm. The Catalog shell now dogfoods `Pane`, and both components ship focused demos, catalog metadata, documentation, and three-engine browser coverage.
32
+ - Added the package-qualified component catalog v2 alongside the compatible v1 artifact. Its schema and generated metadata formalize child zones, cardinality, exclusivity, wiring, state, responsive behavior, layout and accessibility boundaries, stable validation diagnostics, provenance, and consumer extension catalogs, so AI tools can join Kerf and application components without guessing composition contracts.
33
+ - Added an AST-based Catalog demo conformance gate. It checks every first-party demo for public helper usage, package imports, focused specimen structure, geometry-overlay metadata, and reviewed schema-checked exceptions, with adversarial fixtures and authoring guidance that makes the same contract available to downstream AI tools.
34
+ - Made `ui/docs/catalog.md` the authoritative Catalog demo authoring contract, covering focused versus composition previews, public row/group nesting, exact specimen selection, conditional geometry overlays, skip-marker behavior, metadata ownership, and an explicit overlay legend. A shipped `ai/catalog-authoring.json` discovery artifact now routes AI tools to that contract and exact signatures, with schema, link-integrity, and browser-consumer fixture coverage.
35
+ - Extended `CatalogExample` and `CatalogExampleStack` with typed, runtime-filtered `rootAttributes` for catalog-authoring `data-*` metadata such as `data-demo` and `data-catalog-geometry-overlay-skip`, while preserving helper-owned structural and alignment markers. The UX catalog now composes its example stacks through these public helpers instead of copying private `kui-catalog-*` markup.
9
36
  - Added `revealCatalogEntry` and opt-in `wireCatalog({ revealSelection })` support to the exported Catalog tool. Sidebar or related-entry selection can now reveal the matching row after the controlled render settles, using a desktop-safe media guard, exact id matching, configurable scroll alignment/behavior, focus-preserving scrolling, and cancellation of stale rapid selections; the Kerf UX demo now uses the public behavior.
10
37
  - Added a reusable consumer component-catalog extension schema and checked app-component example. Downstream packages can now publish the same selection facts and margin/border/padding ownership vocabulary as Kerf, and the README, component contract, Catalog guide, `llms.txt`, and AI skill explain how to combine package-qualified app entries with Kerf's shipped catalog for alignment decisions.
11
38
  - Added an opt-in geometry inspector to the exported `@kerfjs/ui/catalog` tool. `geometryOverlay` plus `wireCatalogGeometryOverlay` now marks transparent component bounds with a dashed outline and positive intrinsic margins with devtools-style bands, excludes example labels/alignment scaffolding, stays synchronized across preview changes, and replaces the Kerf UX demo's private implementation.
package/README.md CHANGED
@@ -51,6 +51,19 @@ Here's the whole development loop — write a component, run the dev server, cli
51
51
  npm install kerfjs
52
52
  ```
53
53
 
54
+ For an existing project, install the AI guidance, strict TypeScript/ESLint
55
+ feedback, and package scripts with a reviewed dry run:
56
+
57
+ ```bash
58
+ npx kerfjs setup # inspect a value-level plan
59
+ npx kerfjs setup --write --yes # apply it
60
+ ```
61
+
62
+ The command detects core versus `@kerfjs/ui`, supports deterministic npm,
63
+ pnpm, and Yarn workspace selection plus manager-correct offline installs, and
64
+ never adds UI to a core-only project. See
65
+ [`docs/24-ai-first-setup.md`](./docs/24-ai-first-setup.md).
66
+
54
67
  ```jsonc
55
68
  // tsconfig.json — point JSX at kerf
56
69
  { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "kerfjs" } }
@@ -287,7 +300,7 @@ Full docs at [brianwestphal.github.io/kerf/docs/eslint-plugin/](https://brianwes
287
300
 
288
301
  ### Optional: `create-kerf-component`
289
302
 
290
- Building a reusable component package? Scaffold one that already follows kerf's hard packaging rules (kerfjs as a peer dependency and `external` in the build, ESM + `.d.ts`, `jsxImportSource: "kerfjs"`, subpath exports) plus an example component showing per-instance state via a factory and a `wire(root)` delegation disposer:
303
+ Building a reusable component package? Scaffold one that already follows kerf's hard packaging rules (kerfjs as a peer dependency and `external` in the build, ESM + `.d.ts`, `jsxImportSource: "kerfjs"`, subpath exports), includes an example component showing per-instance state via a factory and a `wire(root)` delegation disposer, and generates checked package-qualified AI component metadata from explicit author decisions:
291
304
 
292
305
  ```bash
293
306
  npm create kerf-component@latest my-widgets
@@ -302,7 +315,7 @@ See [`docs/13-component-packages.md`](./docs/13-component-packages.md) for the f
302
315
  - **Migrating:** [coming from another framework?](https://brianwestphal.github.io/kerf/migrating/) — side-by-side TodoMVC translations + per-framework gotchas
303
316
  - **AI guide:** [`docs/ai/usage-guide.md`](./docs/ai/usage-guide.md) — reference for AI tools fetching kerf docs (linked from `llms.txt`)
304
317
  - **ESLint plugin:** [brianwestphal.github.io/kerf/docs/eslint-plugin/](https://brianwestphal.github.io/kerf/docs/eslint-plugin/) — `eslint-plugin-kerfjs`; eight rules (four hard-rule errors + four warns: `require-delegate-disposer`, `prefer-attr-selector`, `no-raw-with-dynamic-arg`, `ai-assistant-configs`) at edit time (source: [`eslint-plugin/`](./eslint-plugin/))
305
- - **Component scaffold:** `npm create kerf-component@latest <dir>` — `create-kerf-component`; generates a publishable component package with the hard packaging rules pre-wired (source: [`create-kerf-component/`](./create-kerf-component/))
318
+ - **Component scaffold:** `npm create kerf-component@latest <dir>` — `create-kerf-component`; generates a publishable component package with packaging rules plus deterministic, drift-checked AI metadata pre-wired (source: [`create-kerf-component/`](./create-kerf-component/))
306
319
  - **Demo:** [live demo](https://brianwestphal.github.io/kerf/demo/) — nine sections exercising every primitive (counter, store-backed cart, focus survival, keyed list, morph-skip, SVG render, Tier-2 capture, `arraySignal` patches, fine-grained signal bindings)
307
320
  - **Repo:** [github.com/brianwestphal/kerf](https://github.com/brianwestphal/kerf)
308
321
 
package/ai/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "kerfjsVersion": "5.0.0-beta.18",
2
+ "kerfjsVersion": "5.0.0-beta.19",
3
3
  "files": [
4
4
  {
5
5
  "name": "skill",
package/llms.txt CHANGED
@@ -10,13 +10,17 @@ kerf renders JSX to a structured `SafeHtml` (string for static content; tagged "
10
10
 
11
11
  ## For AI assistants
12
12
 
13
+ - One-step project setup: run `npx kerfjs setup` for a safe dry-run, then
14
+ `npx kerfjs setup --write --yes`; see
15
+ [AI-first setup](https://github.com/brianwestphal/kerf/blob/main/docs/24-ai-first-setup.md).
16
+
13
17
  - [AI usage guide](https://github.com/brianwestphal/kerf/blob/main/docs/ai/usage-guide.md): when to recommend kerf, the full public API, the four core patterns, hard rules, and a common-errors table.
14
18
  - [Code summary](https://github.com/brianwestphal/kerf/blob/main/docs/ai/code-summary.md): directory tree + reverse index of every public export.
15
19
  - [Requirements summary](https://github.com/brianwestphal/kerf/blob/main/docs/ai/requirements-summary.md): synthesized view of the numbered design docs.
16
20
  - [`kerf.cursorrules`](https://github.com/brianwestphal/kerf/blob/main/kerf.cursorrules): drop-in [Cursor](https://cursor.sh) rules for projects that import `kerfjs`. Copy into your project as `.cursorrules` — or use the bundled mirror at `node_modules/kerfjs/ai/cursorrules` once you've `npm install`ed kerfjs.
17
21
  - [`kerf.claude-skill.md`](https://github.com/brianwestphal/kerf/blob/main/kerf.claude-skill.md): drop-in [Claude Code](https://claude.com/claude-code) skill. Copy into `~/.claude/skills/kerf-app/SKILL.md` (or your project's `.claude/skills/kerf-app/SKILL.md`) — or use the bundled mirror at `node_modules/kerfjs/ai/skill.md` once you've `npm install`ed kerfjs.
18
22
  - [`eslint-plugin-kerfjs`](https://github.com/brianwestphal/kerf/blob/main/eslint-plugin/README.md): companion ESLint plugin enforcing the hard rules at edit time — eight rules: `no-inline-jsx-event-handlers`, `require-data-key-in-each`, `no-nested-mount`, `prefer-module-jsx-augmentation` (error) plus `require-delegate-disposer`, `prefer-attr-selector`, `no-raw-with-dynamic-arg`, `ai-assistant-configs` (warn). AST-only, no `parserServices` dependency. Install with `npm install --save-dev eslint-plugin-kerfjs` and add `kerfjs.configs.recommended` to your eslint config. Recommended when authoring kerf code with an AI assistant — eslint feedback surfaces in the IDE before `tsc` or runtime warns ever run.
19
- - [`create-kerf-component`](https://github.com/brianwestphal/kerf/blob/main/create-kerf-component/README.md): companion initializer that scaffolds a publishable kerf component package with the hard packaging rules already wired (kerfjs as a peer dependency + `external` in the build, ESM + `.d.ts`, `jsxImportSource: "kerfjs"`, subpath exports) plus an example component (per-instance state via a factory, a `wire(root)` delegation disposer). Run `npm create kerf-component@latest <dir>`.
23
+ - [`create-kerf-component`](https://github.com/brianwestphal/kerf/blob/main/create-kerf-component/README.md): companion initializer that scaffolds a publishable kerf component package with the hard packaging rules, an example component, and explicit `kerf.components.json` source metadata plus deterministic `component-catalog-v2.json` generation/checking. Run `npm create kerf-component@latest <dir>`.
20
24
  - [`@kerfjs/ui` AI guide](https://github.com/brianwestphal/kerf/blob/main/ui/ai/skill.md): component-selection, styling, accessibility, and event-wiring contract for the optional first-party UI package.
21
25
 
22
26
  ## Reference docs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kerfjs",
3
- "version": "5.0.0-beta.18",
3
+ "version": "5.0.0-beta.19",
4
4
  "description": "Tiny reactive UI framework — fine-grained signals + DOM morphing + JSX. Apply the smallest possible cut to update your DOM.",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -35,6 +35,9 @@
35
35
  },
36
36
  "main": "./dist/index.js",
37
37
  "types": "./dist/index.d.ts",
38
+ "bin": {
39
+ "kerfjs": "./setup/cli.mjs"
40
+ },
38
41
  "exports": {
39
42
  ".": {
40
43
  "types": "./dist/index.d.ts",
@@ -100,11 +103,17 @@
100
103
  "types": "./dist/attach.d.ts",
101
104
  "import": "./dist/attach.js"
102
105
  },
103
- "./ai/*": "./ai/*"
106
+ "./ai/*": "./ai/*",
107
+ "./setup": {
108
+ "types": "./setup/index.d.mts",
109
+ "import": "./setup/index.mjs"
110
+ },
111
+ "./setup/state.schema.json": "./setup/state.schema.json"
104
112
  },
105
113
  "files": [
106
114
  "dist",
107
115
  "ai",
116
+ "setup",
108
117
  "llms.txt",
109
118
  "README.md",
110
119
  "CHANGELOG.md",
@@ -165,7 +174,9 @@
165
174
  "check:audit": "npm audit --omit=dev --audit-level=high"
166
175
  },
167
176
  "dependencies": {
168
- "@preact/signals-core": "^1.14.1"
177
+ "@preact/signals-core": "^1.14.1",
178
+ "minimatch": "^9.0.5",
179
+ "yaml": "^2.8.1"
169
180
  },
170
181
  "devDependencies": {
171
182
  "@eslint/js": "^10.0.1",
package/setup/cli.mjs ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+
3
+ import console from 'node:console';
4
+ import process from 'node:process';
5
+
6
+ import { applyKerfSetup, formatSetupPlan, planKerfSetup } from './index.mjs';
7
+
8
+ const args = process.argv.slice(2);
9
+ const command = args[0] && !args[0].startsWith('-') ? args.shift() : 'setup';
10
+ const take = (name) => {
11
+ const index = args.indexOf(name);
12
+ if (index < 0) return undefined;
13
+ const value = args[index + 1];
14
+ if (!value || value.startsWith('--'))
15
+ throw new Error(`${name} requires a value.`);
16
+ args.splice(index, 2);
17
+ return value;
18
+ };
19
+ const takeAll = (name) => {
20
+ const values = [];
21
+ while (args.includes(name)) values.push(take(name));
22
+ return values;
23
+ };
24
+
25
+ try {
26
+ if (args.includes('--help') || args.includes('-h')) {
27
+ console.log(`Usage: kerfjs setup [options]
28
+
29
+ Options:
30
+ --root <path> Workspace root (default: current directory)
31
+ --package <name> Select a workspace package by name or relative path
32
+ --core | --ui Choose a mode when it cannot be detected
33
+ --resolve <id>=<choice>
34
+ Resolve one conflict with keep or kerf (repeatable)
35
+ --write Apply the displayed plan (default is dry-run)
36
+ --yes Required with --write in non-interactive use
37
+ --offline Forbid registry access during dependency installation
38
+ --no-install Write configuration without running the package manager`);
39
+ process.exit(0);
40
+ }
41
+ if (command !== 'setup') throw new Error(`Unknown command ${command}.`);
42
+ const core = args.includes('--core');
43
+ const ui = args.includes('--ui');
44
+ if (core && ui) throw new Error('--core and --ui are mutually exclusive.');
45
+ const write = args.includes('--write');
46
+ const yes = args.includes('--yes');
47
+ const offline = args.includes('--offline');
48
+ const noInstall = args.includes('--no-install');
49
+ const root = take('--root');
50
+ const packageSelector = take('--package');
51
+ const resolutions = {};
52
+ for (const entry of takeAll('--resolve')) {
53
+ const index = entry.lastIndexOf('=');
54
+ if (index < 1) throw new Error('--resolve requires <id>=keep|kerf.');
55
+ const id = entry.slice(0, index);
56
+ const choice = entry.slice(index + 1);
57
+ if (id in resolutions && resolutions[id] !== choice)
58
+ throw new Error(`Conflicting resolutions for ${id}.`);
59
+ resolutions[id] = choice;
60
+ }
61
+ const known = new Set([
62
+ '--core',
63
+ '--ui',
64
+ '--write',
65
+ '--yes',
66
+ '--offline',
67
+ '--no-install',
68
+ ]);
69
+ const unknown = args.filter((arg) => !known.has(arg));
70
+ if (unknown.length)
71
+ throw new Error(`Unknown arguments: ${unknown.join(' ')}`);
72
+ const plan = await planKerfSetup({
73
+ root,
74
+ package: packageSelector,
75
+ mode: ui ? 'ui' : core ? 'core' : undefined,
76
+ resolutions,
77
+ });
78
+ console.log(formatSetupPlan(plan));
79
+ if (plan.conflicts.length) {
80
+ process.exitCode = 2;
81
+ } else if (write) {
82
+ if (!yes)
83
+ throw new Error('--write requires --yes after reviewing the plan.');
84
+ await applyKerfSetup(plan, { install: !noInstall, offline });
85
+ console.log(`Applied ${plan.actions.length} change(s).`);
86
+ }
87
+ } catch (error) {
88
+ console.error(`kerfjs setup: ${error.message}`);
89
+ process.exitCode = 2;
90
+ }
@@ -0,0 +1,37 @@
1
+ export interface SetupConflict {
2
+ id: string;
3
+ message: string;
4
+ }
5
+ export interface SetupAction {
6
+ path: string;
7
+ absolutePath: string;
8
+ before: string | null;
9
+ after: string;
10
+ kind: string;
11
+ }
12
+ export interface KerfSetupPlan {
13
+ schemaVersion: 1;
14
+ root: string;
15
+ packageRoot: string;
16
+ packageName: string;
17
+ packageManager: 'npm' | 'pnpm' | 'yarn';
18
+ packageManagerVersion?: string;
19
+ packageManagerVariant?: 'classic' | 'berry';
20
+ mode: 'core' | 'ui';
21
+ setupVersion: string;
22
+ actions: SetupAction[];
23
+ conflicts: SetupConflict[];
24
+ generatedPaths: string[];
25
+ }
26
+ export function planKerfSetup(options?: {
27
+ root?: string;
28
+ package?: string;
29
+ mode?: 'core' | 'ui';
30
+ version?: string;
31
+ resolutions?: Record<string, 'keep' | 'kerf'>;
32
+ }): Promise<KerfSetupPlan>;
33
+ export function formatSetupPlan(plan: KerfSetupPlan): string;
34
+ export function applyKerfSetup(
35
+ plan: KerfSetupPlan,
36
+ options?: { install?: boolean; offline?: boolean; runner?: Function },
37
+ ): Promise<{ changed: string[] }>;