@hozu/cli 0.25.0 → 0.26.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +1516 -0
  2. package/dist/commands/browse-page.d.ts.map +1 -1
  3. package/dist/commands/browse-page.js +7 -1
  4. package/dist/commands/browse-page.js.map +1 -1
  5. package/dist/commands/browse-tab.d.ts +1 -1
  6. package/dist/commands/browse-tab.d.ts.map +1 -1
  7. package/dist/commands/browse-tab.js +20 -9
  8. package/dist/commands/browse-tab.js.map +1 -1
  9. package/dist/commands/browse-world.d.ts.map +1 -1
  10. package/dist/commands/browse-world.js +73 -9
  11. package/dist/commands/browse-world.js.map +1 -1
  12. package/dist/commands/browse.d.ts +2 -0
  13. package/dist/commands/browse.d.ts.map +1 -1
  14. package/dist/commands/browse.js +4 -3
  15. package/dist/commands/browse.js.map +1 -1
  16. package/dist/commands/explain.d.ts.map +1 -1
  17. package/dist/commands/explain.js +17 -3
  18. package/dist/commands/explain.js.map +1 -1
  19. package/dist/commands/request.d.ts +1 -0
  20. package/dist/commands/request.d.ts.map +1 -1
  21. package/dist/commands/request.js +14 -4
  22. package/dist/commands/request.js.map +1 -1
  23. package/dist/commands/scaffold.js +1 -1
  24. package/dist/commands/scaffold.js.map +1 -1
  25. package/dist/commands/target.d.ts +17 -0
  26. package/dist/commands/target.d.ts.map +1 -0
  27. package/dist/commands/target.js +172 -0
  28. package/dist/commands/target.js.map +1 -0
  29. package/dist/commands/validate.d.ts.map +1 -1
  30. package/dist/commands/validate.js +10 -3
  31. package/dist/commands/validate.js.map +1 -1
  32. package/dist/contract.d.ts +2 -0
  33. package/dist/contract.d.ts.map +1 -1
  34. package/dist/main.d.ts.map +1 -1
  35. package/dist/main.js +17 -1
  36. package/dist/main.js.map +1 -1
  37. package/dist/migrate/steps.d.ts.map +1 -1
  38. package/dist/migrate/steps.js +20 -0
  39. package/dist/migrate/steps.js.map +1 -1
  40. package/package.json +9 -8
  41. package/schema/inspect.schema.json +0 -7
  42. package/schema/why.schema.json +6 -1
  43. package/skill/example/features/bookmarks/views.ts +3 -1
  44. package/skill/topics/components.md +2 -2
  45. package/skill/topics/deploy.md +14 -12
  46. package/skill/topics/machine.md +3 -2
  47. package/skill/topics/testing.md +3 -1
  48. package/skill/topics/views.md +7 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hozu/cli",
3
- "version": "0.25.0",
3
+ "version": "0.26.1",
4
4
  "description": "The hozu command: check, map, why, show, get, browse, call, plan, add, build, export, gen, dev, serve, docs, migrate and skill (all with --json)",
5
5
  "keywords": [
6
6
  "hozu",
@@ -36,6 +36,7 @@
36
36
  },
37
37
  "files": [
38
38
  "bin",
39
+ "CHANGELOG.md",
39
40
  "dist",
40
41
  "schema",
41
42
  "skill",
@@ -48,14 +49,14 @@
48
49
  "access": "public"
49
50
  },
50
51
  "dependencies": {
51
- "@hozu/core": "0.25.0",
52
- "@hozu/devtools": "0.25.0",
53
- "@hozu/machine": "0.25.0",
54
- "@hozu/compiler": "0.25.0",
55
- "@hozu/transform": "0.25.0",
56
- "@hozu/validator": "0.25.0"
52
+ "@hozu/core": "0.26.1",
53
+ "@hozu/validator": "0.26.1",
54
+ "@hozu/devtools": "0.26.1",
55
+ "@hozu/machine": "0.26.1",
56
+ "@hozu/compiler": "0.26.1",
57
+ "@hozu/transform": "0.26.1"
57
58
  },
58
59
  "devDependencies": {
59
- "create-hozu": "0.25.0"
60
+ "create-hozu": "0.26.1"
60
61
  }
61
62
  }
@@ -1429,13 +1429,6 @@
1429
1429
  },
1430
1430
  "payload": {
1431
1431
  "$ref": "#/definitions/ValueExpr"
1432
- },
1433
- "keys": {
1434
- "type": "array",
1435
- "items": {
1436
- "type": "string"
1437
- },
1438
- "description": "Only these key presses send it, and they do not reach the browser's own shortcuts (ADR 0072 B)."
1439
1432
  }
1440
1433
  },
1441
1434
  "required": [
@@ -224,6 +224,10 @@
224
224
  "items": {
225
225
  "type": "string"
226
226
  }
227
+ },
228
+ "decides": {
229
+ "type": "boolean",
230
+ "description": "Whether it decides, so a contract must cover it; otherwise the lock reviews it (ADR 0037)."
227
231
  }
228
232
  },
229
233
  "required": [
@@ -234,7 +238,8 @@
234
238
  "guard",
235
239
  "assign",
236
240
  "navigate",
237
- "coveredBy"
241
+ "coveredBy",
242
+ "decides"
238
243
  ],
239
244
  "additionalProperties": false
240
245
  },
@@ -55,7 +55,9 @@ export const Board = ui.view({
55
55
  { name: 'kind', 'aria-label': 'Kind', class: 'rounded border px-2 py-2' },
56
56
  kinds.map((k) => ui.option({ value: k, selected: ctx.kind === k }, [k])),
57
57
  ),
58
- ui.use(Button, { props: { type: 'submit', disabled: is(['adding']) } }, ['Add']),
58
+ ui.use(Button, { props: { type: 'submit', disabled: is(['adding']) }, keys: ['Mod+Enter'] }, [
59
+ 'Add',
60
+ ]),
59
61
  ],
60
62
  ),
61
63
  ctx.error !== null && ui.p({ role: 'alert', class: 'text-rose-600' }, [ctx.error]),
@@ -20,8 +20,8 @@ export const Button = ui.component({
20
20
  ui.use(Button, { variant: { tone: 'ghost' }, props: { busy: ctx.saving }, slots: { icon: ui.span({}, ['+']) },
21
21
  on: { press: ui.send(Save, {}) }, class: 'w-full' }, ['Save']) // in a view; its id is ui.Button
22
22
  ```
23
- - **`ui.use` keys** (all optional): `variant` (literals only, HZ071), `props` (anything that changes while the page
24
- runs), `slots`, `on`, `class`; children only with `children: true`.
23
+ - **`ui.use` keys** (all optional): `variant` (literals only, HZ071), `props` (what changes at run time), `slots`,
24
+ `on`, `class`, `keys` (a control root); children only with `children: true`.
25
25
  - **Render** reads only `props`, `slots`, `children`, `on` and `classes`; the caller passes sends, links and text
26
26
  in (HZ070).
27
27
  - **`class`** may only add classes that set none of the component's properties (`w-full`, `md:hidden`); to change
@@ -4,10 +4,9 @@
4
4
  `app({ resolvers, session?, components?, … })` from `@hozu/runtime-server`. `hozu serve`, `hozu check`, `hozu get`,
5
5
  `hozu browse` and `testApp(app)` all run it, so what the tools verify is what production serves.
6
6
  - **Which host:** nobody marks pages static. `npx hozu export` writes every page for a static host (GitHub Pages,
7
- Netlify, Cloudflare Pages, Vercel) to `dist/` and exits 1 naming each page or effect that needs a server; then
8
- use Node (`npm start`, a Docker image) or Cloudflare Workers.
9
- - **Node:** `npm start` is `hozu serve` (adapter-node on `PORT`, `HOST`). Docker: `node:22-slim`, `npm ci
10
- --omit=dev`, `CMD ["npx", "hozu", "serve"]`.
7
+ Netlify, Cloudflare Pages) to `dist/` and exits 1 naming each page or effect that needs a server; then
8
+ `npx hozu build --target node | workers | vercel` writes what that platform deploys as is and lists what it needs.
9
+ - **Node:** `npm start` is `hozu serve` (`PORT`, `HOST`); `--target node` writes its `Dockerfile`.
11
10
  - Set `SESSION_SECRET` when the app has sessions: `npm start` (`hozu serve`) runs as production unless `NODE_ENV` is set, and production refuses to start without it. `hozu dev`, `get`, `browse` and `call` do not need it.
12
11
  - Workers, several instances, sessions in KV, upgrading: see --more.
13
12
 
@@ -24,14 +23,16 @@
24
23
  `dist/public/`, `dist/manifest.json` and `dist/server/render.js`. It compresses answers as they stream (gzip),
25
24
  and framework files with brotli or gzip from the `.br` / `.gz` that `hozu build` writes. Live streams are not
26
25
  compressed. The edge handler leaves compression to the platform.
27
- - **Edge (Cloudflare Workers, Bun, Deno):** `hozu build --out build` (git-ignore `build/`), then bundle an entry with esbuild and
28
- `hozuTransform()` from `@hozu/transform/esbuild` (it gives each app file its own `import.meta.url`, which a
29
- Worker lacks). The entry creates the handler on the first request, when the platform's `env` is known:
30
- `handler ??= createHandler(app, { manifest, render, env })` from `@hozu/runtime-server`, with
31
- `import * as render from './build/server/render.js'`. Serve `build/public` as static assets (wrangler
32
- `[assets] directory`) and let other requests reach the Worker (`/_hozu/f/…` fn modules come from the handler).
33
- Build and bundle from the same source on every deploy: the manifest holds the fingerprints. Workers keep no memory
34
- between requests: data goes in a database.
26
+ - **Workers and Vercel:** `npx hozu build --target workers` writes `dist/workers/` (`worker.mjs`, `assets/`,
27
+ `wrangler.jsonc`; then `npx wrangler deploy` there); `--target vercel` writes `.vercel/output/` (an Edge Function;
28
+ then `npx vercel deploy --prebuilt`). Both need `@hozu/bundle`, bundle the app with its resolvers (a resolver that
29
+ imports Node-only code fails the build there) and print what the platform needs: env, `SESSION_SECRET`, a KV
30
+ namespace bound as `SESSIONS` on Workers, a shared session store on Vercel. Workers keep no memory between
31
+ requests: data goes in a database. Check the bundle before deploying: `npx hozu browse / --build dist/workers`
32
+ (env from your shell; `--session` signs in through its KV).
33
+ - **Another edge (Bun, Deno):** `hozu build --out build`, then an entry that calls
34
+ `createHandler(app, { manifest, render, env })` with `import * as render from './build/server/render.js'`,
35
+ bundled with `hozuTransform()` from `@hozu/transform/esbuild` (the target's entry is the model).
35
36
  - **Static host:** `npx hozu export [--out dist]` (`@hozu/adapter-static`, in new apps) empties the folder, writes
36
37
  every page without per-request server data plus `.nojekyll`, and prints what it skipped. Pages whose data runs in
37
38
  the browser (`runs: 'browser'` / `'either'`) export completely; a page that calls a server effect is listed
@@ -75,3 +76,4 @@
75
76
  ## Upgrading Hozu
76
77
  `npx -p @hozu/cli@latest hozu migrate` (preview with `--dry-run`), then run the `next:` lines it prints: install, and
77
78
  `npx hozu migrate` again, which checks the IR is unchanged and runs `hozu check`. Never raise `@hozu/*` by hand.
79
+ Each release's notes are in `node_modules/@hozu/cli/CHANGELOG.md`.
@@ -71,8 +71,9 @@ export const m = machine({
71
71
  - **Across pages:** the calm state (the last state without `invoke`, with its context) is kept in `sessionStorage`
72
72
  and resumed on a page that shows the same machine view, or on the same address; a reload or a page without that
73
73
  view starts from `initialContext` (or `seed`).
74
- - `Unexpected`'s `message` is `Internal error (call <id>)` in production (the real one reaches `onError`): store a
75
- code or a fixed text, never `e.message`.
74
+ - `Unexpected`'s `message` is `Internal error (call <id>)` in production (the real one reaches `onError`). A page
75
+ for customers stores a code or a fixed text; a staff tool may show `e.message`, whose call id matches the server
76
+ log line.
76
77
  - **assign** values are event (`e`), result (`r`) or error fields, context, literals, operators and `fn()` calls.
77
78
  - **guard** conditions: a field (`() => ctx.auto`), comparisons, `&&`, `||`, `!`, or a boolean `fn()`.
78
79
  - **navigate** sends the browser to `ui.link(route, params, search?)` after the transition. It returns one link: to
@@ -29,6 +29,7 @@
29
29
 
30
30
  <!-- more -->
31
31
 
32
+ - `press Mod+s` (with `Mod`, `Ctrl`, `Meta`, `Alt`, `Shift`) presses the control whose `keys` match, as a person would; with `--js off` it reports that a shortcut needs JavaScript.
32
33
  - **Server errors:** what the app's `onError` receives (a resolver that threw, an invalid input) is listed under the
33
34
  step or the `get` request that caused it, `server error: <message> (<feature.effect>)`; `--json` `serverErrors`.
34
35
  - **A calm page:** a step that rebuilds elements unchanged says `N elements rebuilt unchanged (a flash: main > form >
@@ -83,7 +84,8 @@
83
84
  - **More checks in a step:**
84
85
  - A click that would land on another element fails the step: `the click would land on <h3>, which contains it, above
85
86
  <a href="/x">: a person cannot click it` (an overlay, a card covering its link).
86
- - A navigation shows how it arrived: `→ /x (loaded, 32 ms)` or `(prerendered, 4 ms)`; with `--js both`, per mode.
87
+ - A navigation shows how it arrived: `→ /x (loaded, 32 ms)`; with `--js both`, per mode. Browse always shows
88
+ `loaded`: Chrome turns prerendering off under DevTools request interception, so this is the worst case.
87
89
  - `--select` prints each element's `class` too. An element moved to another parent is not a flash.
88
90
  - A server error that `get` or `browse` lists is noted once with `a production server shows "Internal error" here`:
89
91
  the visitor sees that text and the call id; `onError` gets the message.
@@ -55,10 +55,13 @@ export const Board = ui.view({
55
55
  `row(item)`; it is inlined, so the IR equals the inline form. A plain function that receives data is HZ059.
56
56
  - **Shared UI:** use the kit component, not a styled `ui.button` per page (`example/` uses a kit).
57
57
  - **More events:** any DOM event name plus `visible` (entered the viewport). Payload fields also:
58
- `ui.dom.formAll('name')`, `ui.dom.checked`, `ui.dom.valueAsNumber`, `ui.dom.key`. Keyboard shortcuts:
59
- `ui.window({ on: { keydown: ui.send(Open, {}, { keys: ['Mod+k', '/'] }) } })` sends only on those presses and
60
- stops the browser's own (`Mod` is ⌘ on Apple, Ctrl elsewhere; also `Ctrl`, `Meta`, `Alt`, `Shift`); a printable
61
- key without a modifier waits while the person types in a field (`Escape` does not). `ui.dom.value` / `ui.dom.form`
58
+ `ui.dom.formAll('name')`, `ui.dom.checked`, `ui.dom.valueAsNumber`, `ui.dom.key`. Keyboard shortcuts
59
+ belong to the control they press: `ui.input({ name: 'q', keys: ['/'] })` focuses the field, `ui.button({ type:
60
+ 'submit', keys: ['Mod+s'] }, ['Save'])` clicks it (so the form submits; no machine needed); a kit control takes
61
+ them too: `ui.use(Button, { props, keys: ['Mod+Enter'] }, ['Add'])`. `Mod` is ⌘ on Apple,
62
+ Ctrl elsewhere; also `Ctrl`, `Meta`, `Alt`, `Shift`. A printable key without a modifier waits while the person types
63
+ in another field (`Escape` does not); inside an open modal only its controls count. The page loads a small module
64
+ for it and writes `aria-keyshortcuts`; two controls always shown together with one key is HZ014. `ui.dom.value` / `ui.dom.form`
62
65
  fill an enum field only from a `<select>`, radios or submit buttons whose literal values are all members (HZ033).
63
66
  - **Search in links:** the third argument of `ui.link` is optional and exists only when the route declares `search`:
64
67
  omitted means every default, and a search lists only the fields that differ: `ui.link(home, null, { show: 'done' })`.