@kerfjs/ui 5.0.0-beta.94 → 5.0.0-beta.95

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/README.md CHANGED
@@ -382,8 +382,10 @@ relationships from that extension; `kerf-catalog-demo --write` emits a typed
382
382
  module and `--check` catches drift. See the [catalog guide](./docs/catalog.md)
383
383
  for repository base and source path configuration. To offer the catalog's
384
384
  **Capture region** link to UX Review on a local server, add the Node-only
385
- `uxReviewPlugin` from `@kerfjs/ui/vite-ux-review` to the Vite config and pass
386
- `wireCatalog`'s `onUxReviewProject` result to `Catalog`'s `uxReview` (the
385
+ `uxReviewPlugin` from `@kerfjs/ui/vite-ux-review` to the Vite config (or, for a
386
+ catalog your own backend serves, mount its `uxReviewProjectResponse` /
387
+ `uxReviewMiddleware` on the development server) and pass `wireCatalog`'s
388
+ `onUxReviewProject` result to `Catalog`'s `uxReview` (the
387
389
  [catalog guide](./docs/catalog.md#ux-review-capture) has the wiring).
388
390
 
389
391
  Composition-aware consumers use
@@ -1,6 +1,6 @@
1
1
  # Public API signatures for the UI authoring corpus
2
2
 
3
- Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.94` and `kerfjs@5.0.0-beta.94`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
3
+ Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.95` and `kerfjs@5.0.0-beta.95`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
4
4
 
5
5
  ## `@kerfjs/ui/css-values`
6
6
 
package/ai/skill.md CHANGED
@@ -143,7 +143,9 @@ components, derive demo rows with `@kerfjs/ui/catalog-projection` or
143
143
  `kerf-catalog-demo --write`, then guard drift with `--check`; see
144
144
  `../docs/catalog.md`. For a local catalog's UX Review **Capture region** link,
145
145
  add `uxReviewPlugin()` from the Node-only `@kerfjs/ui/vite-ux-review` to the
146
- Vite config, report the project with `wireCatalog`'s `onUxReviewProject`, and
146
+ Vite config (a catalog served by the app's own backend mounts that subpath's
147
+ `uxReviewProjectResponse` or `uxReviewMiddleware` on its development server
148
+ only), report the project with `wireCatalog`'s `onUxReviewProject`, and
147
149
  render `Catalog` with `uxReview={{ projectDirectory }}`; never hand-roll the
148
150
  endpoint or the `uxreview://` link. For per-entry variant/state controls, pass
149
151
  them as `Catalog`'s `settings` (with `settingsCollapsed` and the same signal as
@@ -1,6 +1,6 @@
1
1
  # Web Awesome JSX signatures for the UI authoring corpus
2
2
 
3
- Generated from the emitted `@kerfjs/ui@5.0.0-beta.94` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
3
+ Generated from the emitted `@kerfjs/ui@5.0.0-beta.95` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
4
4
 
5
5
  ```ts
6
6
  import { KerfCustomElement } from 'kerfjs/jsx-runtime';
package/docs/catalog.md CHANGED
@@ -542,6 +542,47 @@ wireCatalog(app, {
542
542
  The browser helpers behind it, `uxReviewCaptureUrl` and `loadUxReviewProject`,
543
543
  are exported from `@kerfjs/ui/catalog` for a capture link placed elsewhere.
544
544
 
545
+ ### Catalogs served without Vite
546
+
547
+ A catalog served by the app's own backend (an esbuild bundle behind Hono,
548
+ Express, or a plain Node server) mounts the same endpoint from the same
549
+ Node-only subpath, on its development server only. The catalog requests
550
+ `__ux-review/project.json` relative to its page, so a page at `/ux-demo` asks
551
+ for `/__ux-review/project.json`; both helpers answer at any base path.
552
+
553
+ ```ts
554
+ import {
555
+ resolveUxReviewProject,
556
+ uxReviewMiddleware,
557
+ uxReviewProjectResponse,
558
+ } from "@kerfjs/ui/vite-ux-review";
559
+
560
+ // Resolve once at startup: the repository holding the working directory, or
561
+ // resolveUxReviewProject({ root, projectDirectory }) like the Vite plugin.
562
+ const projectDirectory = resolveUxReviewProject();
563
+
564
+ // Fetch API servers (Hono, Bun, Deno): null means "not this endpoint".
565
+ if (isDev)
566
+ app.use("*", async (c, next) =>
567
+ uxReviewProjectResponse(c.req.raw, projectDirectory) ?? next(),
568
+ );
569
+
570
+ // Node / connect servers (Express, node:http):
571
+ if (isDev) expressApp.use(uxReviewMiddleware(projectDirectory));
572
+ ```
573
+
574
+ - **`resolveUxReviewProject({ root?, projectDirectory? })`** returns the real
575
+ path the Vite plugin would serve: `projectDirectory` resolved against `root`,
576
+ or the repository holding `root` (default `process.cwd()`).
577
+ - **`uxReviewProjectResponse(request, projectDirectory)`** answers a Fetch API
578
+ `Request`: the JSON for a GET, `405` for another method, or `null` for any
579
+ other path.
580
+ - **`uxReviewMiddleware(projectDirectory)`** is the same endpoint as a
581
+ `(request, response, next)` handler; `uxReviewPlugin` mounts it on Vite.
582
+
583
+ Never register the endpoint in production: it reveals a local path, and a
584
+ deployed catalog has no local project to capture into.
585
+
545
586
  ## Custom action names
546
587
 
547
588
  The shell emits `data-action="catalog-select"` (sidebar items),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kerfjs/ui",
3
- "version": "5.0.0-beta.94",
3
+ "version": "5.0.0-beta.95",
4
4
  "description": "Accessible, composable UI primitives for kerf applications.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -38,6 +38,45 @@ export interface UxReviewPlugin {
38
38
  /** The nearest directory at or above `start` that holds `.git`, else `start`. */
39
39
  export declare function findProjectDirectory(start: string): string;
40
40
 
41
+ export interface ResolveUxReviewProjectOptions {
42
+ /** The directory to start from. Default: `process.cwd()`. */
43
+ root?: string;
44
+ /**
45
+ * An explicit project directory, resolved against `root`. Default: the
46
+ * nearest ancestor of `root` holding `.git`, or `root` itself.
47
+ */
48
+ projectDirectory?: string;
49
+ }
50
+
51
+ /** The real path of the UX Review project directory for a server. */
52
+ export declare function resolveUxReviewProject(
53
+ options?: ResolveUxReviewProjectOptions,
54
+ ): string;
55
+
56
+ /**
57
+ * Answer a Fetch API request for `…/__ux-review/project.json` at any base
58
+ * path: the project JSON for a GET, 405 for another method, or `null` for any
59
+ * other path so the server handles it as usual. Mount it on a development
60
+ * server only.
61
+ */
62
+ export declare function uxReviewProjectResponse(
63
+ request: Request,
64
+ projectDirectory: string,
65
+ ): Response | null;
66
+
67
+ /** A Node / connect `(request, response, next)` handler for a server's middleware stack. */
68
+ export type UxReviewMiddleware = Parameters<
69
+ UxReviewMiddlewareServer['middlewares']['use']
70
+ >[0];
71
+
72
+ /**
73
+ * A Node / connect-style handler for `…/__ux-review/project.json` at any base
74
+ * path; other requests go to `next`. Mount it on a development server only.
75
+ */
76
+ export declare function uxReviewMiddleware(
77
+ projectDirectory: string,
78
+ ): UxReviewMiddleware;
79
+
41
80
  /** Serve the UX Review project directory to catalog pages on dev and preview servers. */
42
81
  export declare function uxReviewPlugin(
43
82
  options?: UxReviewPluginOptions,
@@ -1,15 +1,21 @@
1
1
  /**
2
- * `@kerfjs/ui/vite-ux-review` — a Node-only Vite plugin that tells a local
3
- * catalog page which project it belongs to, so the catalog can offer a UX
4
- * Review "Capture region" link (see `wireCatalog`'s `onUxReviewProject` and
5
- * the Catalog's `uxReview` prop in `@kerfjs/ui/catalog`).
2
+ * `@kerfjs/ui/vite-ux-review` — Node-only helpers that tell a local catalog
3
+ * page which project it belongs to, so the catalog can offer a UX Review
4
+ * "Capture region" link (see `wireCatalog`'s `onUxReviewProject` and the
5
+ * Catalog's `uxReview` prop in `@kerfjs/ui/catalog`).
6
6
  *
7
- * It answers `GET …/__ux-review/project.json` with `{ projectDirectory }` on
8
- * the dev and preview servers. It imports nothing from Vite and never reaches
9
- * a browser bundle; a static build has no endpoint, so the link stays hidden.
7
+ * The endpoint answers `GET …/__ux-review/project.json` with
8
+ * `{ projectDirectory }`. `uxReviewPlugin()` mounts it on Vite's dev and
9
+ * preview servers; an app that serves its catalog from its own backend mounts
10
+ * `uxReviewProjectResponse()` (Fetch API servers) or `uxReviewMiddleware()`
11
+ * (Node / connect servers) instead, on its development server only. Nothing
12
+ * here imports Vite or reaches a browser bundle; a static build has no
13
+ * endpoint, so the link stays hidden.
10
14
  */
11
15
  import { existsSync, realpathSync } from 'node:fs';
12
16
  import { dirname, join, resolve } from 'node:path';
17
+ import process from 'node:process';
18
+ import { URL } from 'node:url';
13
19
 
14
20
  /** Where the endpoint lives, relative to the catalog page. */
15
21
  export const UX_REVIEW_PROJECT_PATH = '__ux-review/project.json';
@@ -24,15 +30,53 @@ export function findProjectDirectory(start) {
24
30
  }
25
31
 
26
32
  /**
27
- * Serve the UX Review project directory to catalog pages. By default it is
28
- * the repository holding Vite's `root` (the nearest ancestor with `.git`, or
29
- * `root` itself); `projectDirectory` overrides it, resolved against `root`.
33
+ * The real path of the UX Review project directory: `projectDirectory`
34
+ * resolved against `root` when given, otherwise the repository holding `root`
35
+ * (the nearest ancestor with `.git`, or `root` itself). `root` defaults to the
36
+ * current working directory.
30
37
  */
31
- export function uxReviewPlugin(options = {}) {
32
- let projectDirectory = '';
33
- const serve = (request, response, next) => {
38
+ export function resolveUxReviewProject({
39
+ root = process.cwd(),
40
+ projectDirectory,
41
+ } = {}) {
42
+ return realpathSync(
43
+ projectDirectory
44
+ ? resolve(root, projectDirectory)
45
+ : findProjectDirectory(root),
46
+ );
47
+ }
48
+
49
+ const isEndpoint = (path) => path.endsWith(`/${UX_REVIEW_PROJECT_PATH}`);
50
+ const JSON_HEADERS = {
51
+ 'Content-Type': 'application/json; charset=utf-8',
52
+ 'Cache-Control': 'no-store',
53
+ };
54
+
55
+ /**
56
+ * Answer a Fetch API request for the endpoint, at any base path: the project
57
+ * JSON for a GET, 405 for another method, or `null` when the request is for
58
+ * some other path so the server handles it as usual.
59
+ */
60
+ export function uxReviewProjectResponse(request, projectDirectory) {
61
+ if (!isEndpoint(new URL(request.url).pathname)) return null;
62
+ if (request.method !== 'GET')
63
+ return new globalThis.Response(null, {
64
+ status: 405,
65
+ headers: { Allow: 'GET' },
66
+ });
67
+ return new globalThis.Response(JSON.stringify({ projectDirectory }), {
68
+ headers: JSON_HEADERS,
69
+ });
70
+ }
71
+
72
+ /**
73
+ * A Node / connect-style `(request, response, next)` handler for the
74
+ * endpoint, at any base path; other requests go to `next`.
75
+ */
76
+ export function uxReviewMiddleware(projectDirectory) {
77
+ return (request, response, next) => {
34
78
  const path = (request.url ?? '').split(/[?#]/)[0];
35
- if (!path.endsWith(`/${UX_REVIEW_PROJECT_PATH}`)) {
79
+ if (!isEndpoint(path)) {
36
80
  next();
37
81
  return;
38
82
  }
@@ -42,17 +86,28 @@ export function uxReviewPlugin(options = {}) {
42
86
  response.end();
43
87
  return;
44
88
  }
45
- response.setHeader('Content-Type', 'application/json; charset=utf-8');
46
- response.setHeader('Cache-Control', 'no-store');
89
+ for (const [name, value] of Object.entries(JSON_HEADERS))
90
+ response.setHeader(name, value);
47
91
  response.end(JSON.stringify({ projectDirectory }));
48
92
  };
93
+ }
94
+
95
+ /**
96
+ * Serve the UX Review project directory to catalog pages on Vite's dev and
97
+ * preview servers. By default it is the repository holding Vite's `root`;
98
+ * `projectDirectory` overrides it, resolved against `root`.
99
+ */
100
+ export function uxReviewPlugin(options = {}) {
101
+ let handler = uxReviewMiddleware('');
102
+ const serve = (request, response, next) => handler(request, response, next);
49
103
  return {
50
104
  name: 'kerf-ux-review',
51
105
  configResolved(config) {
52
- projectDirectory = realpathSync(
53
- options.projectDirectory
54
- ? resolve(config.root, options.projectDirectory)
55
- : findProjectDirectory(config.root),
106
+ handler = uxReviewMiddleware(
107
+ resolveUxReviewProject({
108
+ root: config.root,
109
+ projectDirectory: options.projectDirectory,
110
+ }),
56
111
  );
57
112
  },
58
113
  configureServer(server) {