@madojs/mado 0.5.1 → 0.6.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 (107) hide show
  1. package/AGENTS.md +26 -0
  2. package/CHANGELOG.md +265 -0
  3. package/MADO_V1_PLAN.md +179 -0
  4. package/README.md +31 -13
  5. package/ROADMAP.md +28 -7
  6. package/TODO.md +72 -0
  7. package/dist/src/forms.d.ts +37 -4
  8. package/dist/src/forms.js +331 -57
  9. package/dist/src/forms.js.map +1 -1
  10. package/dist/src/html/bindings.d.ts +41 -0
  11. package/dist/src/html/bindings.js +163 -6
  12. package/dist/src/html/bindings.js.map +1 -1
  13. package/dist/src/html.d.ts +2 -0
  14. package/dist/src/html.js +1 -0
  15. package/dist/src/html.js.map +1 -1
  16. package/dist/src/index.d.ts +6 -6
  17. package/dist/src/index.js +2 -2
  18. package/dist/src/index.js.map +1 -1
  19. package/dist/src/page.d.ts +56 -0
  20. package/dist/src/page.js +17 -0
  21. package/dist/src/page.js.map +1 -1
  22. package/dist/src/resource.js +11 -0
  23. package/dist/src/resource.js.map +1 -1
  24. package/dist/src/router/manifest.d.ts +16 -1
  25. package/dist/src/router/manifest.js +210 -40
  26. package/dist/src/router/manifest.js.map +1 -1
  27. package/dist/src/router/match.d.ts +7 -2
  28. package/dist/src/router/match.js +14 -4
  29. package/dist/src/router/match.js.map +1 -1
  30. package/dist/src/router/navigation.d.ts +10 -0
  31. package/dist/src/router/navigation.js +71 -3
  32. package/dist/src/router/navigation.js.map +1 -1
  33. package/dist/src/signal.d.ts +15 -1
  34. package/dist/src/signal.js +112 -16
  35. package/dist/src/signal.js.map +1 -1
  36. package/docs/en/02-project-layout.md +99 -40
  37. package/docs/en/10-app-architecture.md +141 -0
  38. package/docs/en/11-layouts.md +115 -0
  39. package/docs/en/12-auth-and-api.md +217 -0
  40. package/docs/en/13-deployment.md +192 -0
  41. package/docs/en/14-testing.md +82 -0
  42. package/docs/en/15-error-handling.md +100 -0
  43. package/docs/en/16-bake-cookbook.md +93 -0
  44. package/docs/en/README.md +7 -0
  45. package/docs/fr/10-app-architecture.md +61 -0
  46. package/docs/fr/11-layouts.md +35 -0
  47. package/docs/fr/12-auth-and-api.md +35 -0
  48. package/docs/fr/13-deployment.md +39 -0
  49. package/docs/fr/14-testing.md +41 -0
  50. package/docs/fr/15-error-handling.md +50 -0
  51. package/docs/fr/16-bake-cookbook.md +35 -0
  52. package/docs/fr/README.md +7 -0
  53. package/docs/ru/10-app-architecture.md +100 -0
  54. package/docs/ru/11-layouts.md +47 -0
  55. package/docs/ru/12-auth-and-api.md +53 -0
  56. package/docs/ru/13-deployment.md +60 -0
  57. package/docs/ru/14-testing.md +50 -0
  58. package/docs/ru/15-error-handling.md +56 -0
  59. package/docs/ru/16-bake-cookbook.md +55 -0
  60. package/docs/ru/README.md +7 -0
  61. package/docs/uk/10-app-architecture.md +56 -0
  62. package/docs/uk/11-layouts.md +34 -0
  63. package/docs/uk/12-auth-and-api.md +34 -0
  64. package/docs/uk/13-deployment.md +39 -0
  65. package/docs/uk/14-testing.md +34 -0
  66. package/docs/uk/15-error-handling.md +32 -0
  67. package/docs/uk/16-bake-cookbook.md +36 -0
  68. package/docs/uk/README.md +7 -0
  69. package/llms.txt +9 -1
  70. package/package.json +3 -1
  71. package/scripts/_config.mjs +224 -0
  72. package/scripts/bake.mjs +266 -121
  73. package/scripts/bundle.mjs +133 -67
  74. package/scripts/cli.mjs +195 -27
  75. package/scripts/preview.mjs +125 -21
  76. package/server/serve.mjs +161 -10
  77. package/starters/admin/README.md +63 -0
  78. package/starters/admin/index.html +28 -0
  79. package/starters/admin/mado.config.json +22 -0
  80. package/starters/admin/package.json +24 -0
  81. package/starters/admin/public/favicon.svg +4 -0
  82. package/starters/admin/src/components/x-button.ts +55 -0
  83. package/starters/admin/src/components/x-input.ts +74 -0
  84. package/starters/admin/src/layouts/app.ts +101 -0
  85. package/starters/admin/src/layouts/auth.ts +41 -0
  86. package/starters/admin/src/lib/api.ts +133 -0
  87. package/starters/admin/src/lib/auth.ts +83 -0
  88. package/starters/admin/src/main.ts +15 -0
  89. package/starters/admin/src/pages/admin/dashboard.ts +48 -0
  90. package/starters/admin/src/pages/admin/order-detail.ts +80 -0
  91. package/starters/admin/src/pages/admin/orders.ts +117 -0
  92. package/starters/admin/src/pages/home.ts +34 -0
  93. package/starters/admin/src/pages/login.ts +70 -0
  94. package/starters/admin/src/pages/not-found.ts +12 -0
  95. package/starters/admin/src/routes.ts +40 -0
  96. package/starters/admin/src/styles/global.ts +86 -0
  97. package/starters/admin/tsconfig.json +15 -0
  98. package/starters/crud/index.html +12 -4
  99. package/starters/crud/mado.config.json +20 -0
  100. package/starters/crud/package.json +9 -3
  101. package/starters/crud/src/pages/home.ts +16 -0
  102. package/starters/crud/src/routes.ts +4 -2
  103. package/starters/minimal/index.html +12 -4
  104. package/starters/minimal/mado.config.json +20 -0
  105. package/starters/minimal/package.json +9 -3
  106. package/starters/minimal/src/pages/home.ts +17 -0
  107. package/starters/minimal/src/routes.ts +4 -2
package/AGENTS.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  > This file is read by AI agents in IDEs (Cursor, Cline, Copilot, Continue, etc.).
4
4
  > Goal: prevent them from generating React-like code where Mado should be used.
5
+ >
6
+ > The v0.6 product-surface push is archived in
7
+ > [`MADO_V1_PLAN.md`](./MADO_V1_PLAN.md). New work should follow `ROADMAP.md`
8
+ > / `TODO.md` unless the user explicitly resumes that tracker.
5
9
 
6
10
  ## Project at a glance
7
11
 
@@ -289,6 +293,25 @@ src/
289
293
  └── lib/ ← API client, contexts, pure logic
290
294
  ```
291
295
 
296
+ ## App architecture for LLM
297
+
298
+ When generating an app, prefer the blessed production shape from
299
+ `docs/en/10-app-architecture.md` and the `starters/admin/` example:
300
+
301
+ - `src/main.ts` mounts `routesApi.view` and imports only global styles,
302
+ providers, and tiny shared components.
303
+ - `src/routes.ts` exports both `manifest` and `default routes(manifest, ...)`.
304
+ - Put route wrappers in `src/layouts/` via `layout()`, not ad-hoc shell logic
305
+ inside every page.
306
+ - Put backend access in `src/lib/api.ts` and auth/session logic in
307
+ `src/lib/auth.ts`; guards call auth helpers, pages call API helpers.
308
+ - Put one page per file under `src/pages/`; a page imports the feature
309
+ components it renders.
310
+ - Use `resource()` for reads, `mutation(..., { invalidates })` for writes,
311
+ and `useForm()` for form state/validation.
312
+ - Use `mado release` as the production path. `out/` is the only deploy
313
+ artifact; `dist/` is internal build output.
314
+
292
315
  ## Where to find specific answers
293
316
 
294
317
  | Question | File |
@@ -298,6 +321,9 @@ src/
298
321
  | How does the router work? | `src/router.ts` (~530 lines) |
299
322
  | How does resource + cache work? | `src/resource.ts` (297 lines) |
300
323
  | How do forms work? | `src/forms.ts` (212 lines) |
324
+ | How should an app be structured? | `docs/en/10-app-architecture.md` |
325
+ | How should errors be handled? | `docs/en/15-error-handling.md` |
326
+ | How should bake be used? | `docs/en/16-bake-cookbook.md` |
301
327
  | When something goes wrong | `docs/en/07-llm-pitfalls.md` |
302
328
 
303
329
  ## Before committing
package/CHANGELOG.md CHANGED
@@ -1,5 +1,270 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ Nothing yet.
6
+
7
+ ## 0.6.1
8
+
9
+ Starter & release-pipeline hardening pass. No public API breaks.
10
+ Identified from a lab pressure-test on `/admin/lab` plus a deep audit of the
11
+ starter / bundle / bake / dev-server contour. All fixes verified by
12
+ regression tests added in this release.
13
+
14
+ ### Fixed
15
+ - **Starters**: every `index.html` in `starters/{admin,crud,minimal}/` now
16
+ uses root-absolute paths in the importmap and entry `<script>` tag
17
+ (`/node_modules/@madojs/mado/...`, `/dist/main.js`). Relative paths
18
+ (`./node_modules/...`, `./dist/main.js`) broke hard-refresh of any nested
19
+ route (`/admin/orders/42` → browser fetched
20
+ `/admin/orders/dist/main.js` → 404 → blank page). Inline comments in each
21
+ file explain the trap so it does not get reverted.
22
+ - **Starters/admin**: `pages/admin/order-detail.ts` now uses `each(items,
23
+ key, render)` instead of `o.items.map(...)`, matching `llms.txt` rule #3
24
+ and the framework's own pitfalls documentation.
25
+ - **`scripts/bundle.mjs`**: cleans stale hashed assets before every build.
26
+ Previously each `mado bundle` / `mado release` left old `main-<hash>.js`
27
+ and `chunk-<hash>.js` in `out/assets/`; the rewriter then emitted
28
+ `<link rel="modulepreload">` for every leftover `.js` it found, so
29
+ production HTML shipped dead-code preloads without SRI. In app-mode the
30
+ whole assets directory is wiped; in repo-mode only recognisable hashed
31
+ files are removed so unrelated repo artifacts stay put.
32
+ - **`src/router/manifest.ts`**: opens a fresh component lifecycle scope
33
+ around every `page.view()` / layout `view()` call and disposes the
34
+ previous one on navigation (and on `router.dispose()`). `resource()`,
35
+ `effect()` and `persisted()` created inside `page.view()` now register
36
+ cleanup with that scope automatically — no more
37
+ `[mado:resource-outside-lifecycle]` warnings on the framework's own
38
+ canonical examples, and no more invalidator-subscription leaks across
39
+ route changes.
40
+ - **`src/resource.ts`**: guards against stale responses overwriting fresh
41
+ data on rapid key changes. The previous `AbortController` defence worked
42
+ only if the user-supplied fetcher honored `AbortSignal` — for fetchers
43
+ that ignore cancellation, a slow stale resolution for an old key could
44
+ win over a fast fresh one. Both then/catch branches now also check
45
+ `if (key !== lastKey) return`.
46
+ - **`server/serve.mjs`**: falls back to `./public/*` when a file is not
47
+ found at the project root, mirroring what `mado release` does to `out/`.
48
+ `favicon.svg`, `robots.txt`, `og-image.png` etc. no longer 404 in dev.
49
+ - **`server/serve.mjs`**: prints an actionable hint on `EPERM`/`EACCES`
50
+ pointing at `mado dev --host 127.0.0.1` (the default host changed from
51
+ the implicit `0.0.0.0` to `localhost`, which is friendlier in sandboxed
52
+ environments).
53
+ - **`scripts/preview.mjs`**: serves prerendered HTML from `<out>/baked/`
54
+ with priority over the SPA shell. Previously `mado preview` only looked
55
+ at `out/` and never saw bake's output, so prerendered routes returned
56
+ the empty SPA shell — looking like a "blank page" bug even when bake
57
+ had succeeded.
58
+
59
+ ### Added
60
+ - **`mado dev` / `mado serve` flag pass-through**: `cli.mjs` now splits
61
+ positional arguments from flags via `splitDevArgs()`, so calls like
62
+ `mado dev --host 127.0.0.1`, `mado dev showcase --port 6000` and
63
+ `mado dev -- --host 0.0.0.0` all work. Previously the CLI mistook the
64
+ flag for an example name and exited with `unknown example`.
65
+ - **`server/serve.mjs`**: tiny argv parser supporting `--host`, `--port`
66
+ and `--host=value` forms; HOST and PORT also fall back to environment
67
+ variables and `mado.config.json` (`dev.host`, `dev.port`).
68
+ - **`scripts/preview.mjs`**: same `--host` / `--port` flags as the dev
69
+ server, plus a startup banner showing `url:` / `out:` / `baked:` so it
70
+ is obvious which directories preview is serving from.
71
+ - **`scripts/bake.mjs`**: fails loudly when the manifest exists but no
72
+ page declares `bake: { paths, data }`. The previous behaviour produced
73
+ `0 pages + sitemap.xml` silently with exit code 0, making `mado
74
+ release` look successful while shipping only the SPA shell with no
75
+ SEO-friendly HTML. The new warning prints the skipped routes, a
76
+ worked example bake snippet, and exits non-zero. Override with
77
+ `MADO_BAKE_ALLOW_EMPTY=1` for intentional SPA-only deploys.
78
+ - **`scripts/bake.mjs`**: clearer "missing dev dep" errors — when
79
+ `linkedom` or `esbuild` is missing the message now tells the user
80
+ exactly which packages to `npm i -D`.
81
+ - **Starter landing pages**: `home.ts` in all three starters now declares
82
+ `bake: { paths: () => [{}], data: () => ({}) }` and a `head()` so
83
+ `mado release` actually prerenders the landing page out of the box.
84
+ - **Starter `devDependencies`**: `linkedom` and `esbuild` added to
85
+ `starters/{admin,crud,minimal}/package.json` so `mado release` works
86
+ immediately after `mado init <app>` + `npm install`, without manual
87
+ follow-up installs.
88
+ - **Regression tests** (`test/`):
89
+ - `starter-html-paths.test.mjs` — asserts every starter `index.html`
90
+ uses root-absolute paths in both the importmap and the entry script.
91
+ - `bundle-cleanup.test.mjs` — end-to-end: runs `mado bundle` twice on
92
+ a synthesized temp project (mutating source between runs) and
93
+ asserts there is exactly one `main-<hash>.js` in `out/assets/`
94
+ afterwards.
95
+ - `resource.test.mjs` (2 new cases) — stale-response races: a fetcher
96
+ that ignores `AbortSignal` with key=1 slower than key=2, plus a
97
+ rapid 3-way key thrash where the final key is the slowest fetch.
98
+ Both assert `data()` reflects the latest key, not the fastest
99
+ response.
100
+
101
+ ### Changed
102
+ - **`server/serve.mjs`** default host is now `localhost` (was implicitly
103
+ `0.0.0.0`). LAN exposure is opt-in via `mado dev --host 0.0.0.0` or
104
+ `HOST=0.0.0.0`. The startup banner shows both the bound host and a
105
+ click-friendly URL (`localhost` substituted when bound to `0.0.0.0`).
106
+
107
+ ### Notes
108
+ - No public API changes; no migrations required. Apps that previously
109
+ worked on a fresh-out-of-the-box `mado init` did so only because
110
+ someone manually fixed the starter's relative paths and dev deps —
111
+ this release closes those gaps so the documented "happy path" actually
112
+ is happy.
113
+ - If you intentionally deploy SPA-only (no prerendered HTML), drop
114
+ `mado bake` from your release pipeline or set
115
+ `MADO_BAKE_ALLOW_EMPTY=1`; otherwise bake will now fail your
116
+ CI with a clear pointer to the missing config.
117
+ - Test count: 137 pass, 0 fail, 3 skipped (Playwright e2e — unchanged).
118
+
119
+ ## 0.6.0
120
+
121
+ Product-surface release: app-mode defaults, blessed admin starter, release
122
+ pipeline, core hardening and v1 recipe docs.
123
+
124
+ Phase 1 — Repo-vs-app split:
125
+
126
+ ### Added
127
+ - `MADO_V1_PLAN.md` — executable tracker for the v1 push.
128
+ - `scripts/_config.mjs` — single configuration loader (defaults < `mado.config.json`
129
+ < CLI flags). Exports `loadConfig`, `detectContext`, `parseFlags`,
130
+ `resolveProjectPath`. [v1 F1.1]
131
+ - `mado release` command: one-shot `typecheck + build + bundle + bake + copy
132
+ public/ → out/` pipeline so apps have exactly one command to ship. [v1 F1.3]
133
+ - `mado.config.json` shipped in the `minimal` and `crud` starters with the
134
+ default app-mode layout (`src/routes.ts`, `index.html`, `out/`). [v1 F1.4]
135
+ - Tests: `test/config-loader.test.mjs`, `test/bake-cli.test.mjs` (11 + 3
136
+ cases covering context detection, config precedence, flag parsing, bake
137
+ flags, and the no-more-silent-`[object Object]` contract). [v1 F1.6]
138
+
139
+ ### Changed
140
+ - `scripts/bake.mjs` now reads configuration from `mado.config.json` and
141
+ accepts `--entry`, `--template`, `--out`, `--base-url` flags. In app-mode
142
+ defaults are `src/routes.ts` + `index.html` + `out/baked/`; the
143
+ `@madojs/mado → src/index.ts` alias is repo-only. [v1 F1.2]
144
+ - `scripts/bake.mjs` no longer renders unsupported values as `[object Object]`;
145
+ it raises a loud, file/route-targeted error with a hint instead. [v1 F1.2]
146
+ - `scripts/cli.mjs` detects repo-vs-app context, advertises `mado release`,
147
+ and ships a redesigned `mado help` that explains the dev / build / release
148
+ / preview pipeline. [v1 F1.3]
149
+ - Starter `package.json` scripts go through the `mado` CLI exclusively
150
+ (`mado build`, `mado dev`, `mado bundle`, `mado bake`, `mado release`,
151
+ `mado preview`). [v1 F1.4]
152
+ - `docs/en/02-project-layout.md` rewritten around the canonical `src/` /
153
+ `dist/` / `public/` / `out/` model plus a `mado.config.json` one-screen
154
+ reference. [v1 F1.5]
155
+ - `ROADMAP.md`, `TODO.md`, `AGENTS.md` now distinguish the completed
156
+ `MADO_V1_PLAN.md` archive from future roadmap/TODO work. [v1 F0]
157
+
158
+ Phase 2 — One blessed way:
159
+
160
+ ### Added
161
+ - `layout()` factory in `src/page.ts` (alias of `nested()`) plus `Guard` and
162
+ `GuardResult` types. Exported from the public API. [v1 F2.1 / F2.3]
163
+ - Route guards: nested groups and individual pages accept `guard: Guard | Guard[]`.
164
+ Verdicts: void (pass), `{ halt: true }`, or `{ redirect, replace? }`. Async-aware
165
+ with a sync fast path; throwing guards are treated as `halt`. [v1 F2.2]
166
+ - New starter `starters/admin/`: nested manifest with `/`, `/login`, `/admin`
167
+ groups; blessed `lib/api.ts` (`createApiClient`, `ApiError`, single-flight
168
+ 401-refresh) and `lib/auth.ts` (`accessToken`, `restoreSession`, `requireAuth`,
169
+ `login`, `logout`); `layouts/{app,auth}.ts` with a real admin shell; tiny
170
+ `x-button`/`x-input` design-token components; dashboard + orders + order-detail
171
+ pages; `mado.config.json` with a dev `/api` proxy; `public/favicon.svg`. [v1 F2.4]
172
+ - CLI advertises `mado init <name> --starter admin` and lists it in `mado help`.
173
+ [v1 F2.5]
174
+ - `docs/en/11-layouts.md` — the canonical layout recipe (nested routes) plus
175
+ two acceptable alternatives with caveats. [v1 F2.6]
176
+ - `docs/en/12-auth-and-api.md` — the blessed `api`/`auth` recipes, backend
177
+ contract, and dev-proxy hint. [v1 F2.7]
178
+ - `test/guards-layouts.test.mjs` — 7 cases covering the public `layout()`
179
+ alias, sync pass/halt/redirect, async guards, parent→page guard order, and
180
+ throwing-guard fallback. [v1 F2.8]
181
+
182
+ Phase 3 — Bake first-class + Release pipeline:
183
+
184
+ ### Added
185
+ - `mado release` writes `_redirects` (`/* /index.html 200`) and `_headers`
186
+ (immutable for `/assets/*`, no-cache for HTML) into `out/` when they do not
187
+ exist, so Cloudflare Pages / Netlify deploys "just work". [v1 F3.7]
188
+ - `docs/en/13-deployment.md` — VPS + nginx, Cloudflare Pages, S3/CloudFront,
189
+ Netlify and GitHub Pages recipes; cache-control matrix; GitHub Actions
190
+ release sketch; troubleshooting (deep-link 404, HTML caching, `[object Object]`).
191
+ [v1 F3.8 / F3.9]
192
+ - `test/release-pipeline.test.mjs` — end-to-end test: scaffold a temp app,
193
+ symlink the local framework, run `mado release`, assert that `out/index.html`,
194
+ `out/baked/index.html`, `out/baked/sitemap.xml`, `out/_redirects`, `out/_headers`,
195
+ and copied `public/` assets are all present. [v1 F3.10]
196
+
197
+ ### Changed
198
+ - `scripts/preview.mjs` now reads `mado.config.json` (`build.out`, `dev.port`),
199
+ refuses to auto-build by default in app-mode, and asks the user to run
200
+ `mado release` first. Legacy auto-build is opt-in via `PREVIEW_AUTOBUILD=1`
201
+ for the framework repo. [v1 F3.3]
202
+ - `scripts/bundle.mjs` is now app-mode aware: reads `mado.config.json` for
203
+ defaults, accepts `--entry/--html/--out` flags, and writes hashed bundles
204
+ into `out/assets/` so the new nginx / Cloudflare cache rules apply. The old
205
+ `examples/showcase` defaults are kept only in repo-mode for dogfooding.
206
+ [v1 F3.10]
207
+ - `server/serve.mjs` honors `dev.proxy` from `mado.config.json` and forwards
208
+ matching prefixes (e.g. `/api → http://localhost:3000`) without external
209
+ dependencies. The startup banner prints the active proxy table. [v1 F3.6]
210
+
211
+ ### Deferred to v0.7
212
+ - `mado dev` does not yet serve baked routes inline. Workaround: run
213
+ `mado release && mado preview`. [v1 F3.2]
214
+ - `mado check` (bake-safety scan over `bake:` routes) is not exposed yet.
215
+ The loud-error contract in `scripts/bake.mjs` covers the regression case.
216
+ [v1 F3.5]
217
+
218
+ Phase 4 — Core hardening:
219
+
220
+ ### Added
221
+ - `computed(fn, { equals })` option to suppress subscriber reruns when an
222
+ observed computed recomputes to an equal value. [v1 F4.3]
223
+ - HTML directives: `unsafeHTML()`, `ref()`, `classMap()` and `styleMap()` are
224
+ exported from the public API. Runtime bindings enforce valid positions and
225
+ clean up stale classes/styles/refs on updates and disposal. Bake can serialize
226
+ the static directive shapes it can safely represent. [v1 F4.4]
227
+ - `useForm()` now supports async validators via form-level `validateAsync`,
228
+ field-level `validateAsync`, `validating` / `validatingFields`, and explicit
229
+ `validate()` / `validateField()` methods. [v1 F4.5]
230
+ - `useForm().array(name)` adds a small field-array helper with dotted path names
231
+ (`items.0.title`) and wildcard schema validation (`items.*.title`). [v1 F4.5]
232
+ - `test/signal-cycle.test.mjs` covers self-triggering effect cycle detection.
233
+ [v1 F4.2]
234
+ - `test/html-directives.test.mjs` covers directive rendering, cleanup and
235
+ invalid child-position usage. [v1 F4.4]
236
+ - `test/forms.test.mjs` covers async validator races, async submit blocking,
237
+ and field-array wildcard validation. [v1 F4.5]
238
+ - Phase 4 now has an explicit coverage audit tying every core-hardening task to
239
+ its regression tests. [v1 F4.9]
240
+
241
+ ### Changed
242
+ - `computed()` now releases dependency subscriptions after unobserved reads and
243
+ after the last subscriber is disposed, avoiding long-lived stale subscriptions
244
+ in the signal graph. [v1 F4.1]
245
+ - The effect scheduler now detects runaway self-triggering cycles and emits a
246
+ clear diagnostic instead of flushing forever. [v1 F4.2]
247
+ - Runtime head management now clears Mado-managed tags on every navigation,
248
+ including pages without `head()` and pages whose `head()` throws, so stale
249
+ baked/runtime SEO tags cannot leak across routes. [v1 F4.8]
250
+ - Router navigation now saves/restores scroll positions for back/forward,
251
+ scrolls new navigations to the top, and moves focus to the main content
252
+ landmark after navigation. [v1 F4.6]
253
+ - `routes()` now supports a global `errorPage(err, params)` route boundary for
254
+ lazy loader, `load()` and `view()` errors, while local `page.errorView` still
255
+ wins. [v1 F4.7]
256
+
257
+ Phase 5 — Documentation:
258
+
259
+ ### Added
260
+ - `docs/en/10-app-architecture.md`, `14-testing.md`, `15-error-handling.md`
261
+ and `16-bake-cookbook.md` complete the v1 English recipe set. [v1 F5.1-F5.4]
262
+ - `AGENTS.md` now includes an "App architecture for LLM" section and `llms.txt`
263
+ links the v1 architecture, layout, auth/API, deployment, testing,
264
+ error-handling and bake docs. [v1 F5.5-F5.6]
265
+ - Russian, French and Ukrainian documentation now includes localized versions of
266
+ the v1 recipe docs `10` through `16`. [v1 F5.7]
267
+
3
268
  ## 0.5.1
4
269
 
5
270
  Patch release focused on first-user DX after the public npm launch.
@@ -0,0 +1,179 @@
1
+ # MadoJS — Road to v1 (completed v0.6 tracker)
2
+
3
+ > Completed tracker for the v0.6 product-surface push. The work agreed during
4
+ > the v1 audit is now closed and squash-merged into `main` as
5
+ > `feat: add v1 product surface`.
6
+ >
7
+ > Convention: every commit/PR that advances v1 references task IDs in its message,
8
+ > e.g. `[v1 F1.2] bake.mjs: app-mode defaults + --entry/--template/--out flags`.
9
+ > When a task is completed, tick its box here in the same commit.
10
+
11
+ ---
12
+
13
+ ## 0. Mental model (write this into docs first)
14
+
15
+ Three artifact states. One deploy artifact. End of story.
16
+
17
+ | Folder | What it is | Who writes | Who reads | Deployed? |
18
+ |-------------|----------------------------------------------------------------|-------------------|----------------------------|-------------------|
19
+ | `src/` | your source (TS) | you | `tsc`, `esbuild` | no |
20
+ | `dist/` | `tsc` output (native ESM JS for the browser) | `mado build` | `mado dev`, dev browser | no (internal) |
21
+ | `public/` | static assets (favicons, images, robots.txt) | you | `mado bundle` copies it | as part of `out/` |
22
+ | `out/` | **the only deploy artifact**: SPA shell + bundles + baked HTML | `mado release` | nginx / CDN / CF / VPS | ✅ yes |
23
+
24
+ One-liner for users:
25
+ > Develop with `mado dev`. To deploy: run `mado release`, then upload `out/` anywhere.
26
+
27
+ `mado release` = `typecheck` + `build` + `bundle` + `bake` + copy `public/*` → `out/`.
28
+ One command. One artifact. Zero questions about where things live.
29
+
30
+ ---
31
+
32
+ ## 1. Problem map (consolidated)
33
+
34
+ ### Layer A — Core (from the audit)
35
+ - **A1** Computed leaks: no refcount/owner; once read, computed stays subscribed to deps forever.
36
+ - **A2** No cycle-detection in `effect` (e.g. `effect(() => x.set(x()+1))` runs forever).
37
+ - **A3** No `equals` option on `computed`.
38
+ - **A4** No template directives: `unsafeHTML`, `ref`, `classMap`, `styleMap`.
39
+ - **A5** `useForm`: no async validators, no field-arrays.
40
+ - **A6** `resource`/`invalidate`: no typed key relationship; pattern miss is silent.
41
+ - **A7** Router: no scroll restoration / focus management / error boundary.
42
+ - **A8** `head()`: not obvious how dedup/cleanup happens across navigation.
43
+
44
+ ### Layer B — DX, tooling, product surface (from dogfooding)
45
+ - **B1** CLI/bake hardcoded to `examples/` and alias `@madojs/mado → src/index.ts` — repo-mode leaks into user apps.
46
+ - **B2** No single blessed way for **layouts** (LLM and humans both guess).
47
+ - **B3** No blessed way for **auth + API client + dev-proxy**.
48
+ - **B4** No single config file (`mado.config.json`); everything is env vars.
49
+ - **B5** Unclear model for `dist/` vs `out/` vs `public/`.
50
+ - **B6** No `mado release` (one deploy artifact).
51
+ - **B7** Bake silently renders `[object Object]` for `each()`.
52
+ - **B8** Bake lives "next to" the app instead of inside `mado dev`.
53
+ - **B9** No route guard API.
54
+ - **B10** `crud` starter looks like raw scaffold; no admin-template.
55
+ - **B11** Docs missing: app-architecture, layouts, auth-and-api, deployment, testing, error-handling, bake-cookbook.
56
+ - **B12** Starter scaffolds contain `examples/` legacy.
57
+
58
+ ---
59
+
60
+ ## 2. Phases (deliver in order; phase 5 runs in parallel)
61
+
62
+ ### 🟦 Phase 1 — Repo-vs-app split (P0)
63
+
64
+ Goal: kill `examples/` leakage; make app-mode the default.
65
+
66
+ ### 🟩 Phase 2 — One blessed way (P0)
67
+
68
+ Goal: conventions over flexibility. Layouts, guards, auth, api — one recipe each.
69
+
70
+ ### 🟨 Phase 3 — Bake first-class + Release pipeline (P0)
71
+
72
+ Goal: pragmatic path from `mado dev` to production on VPS / Cloudflare / static CDN.
73
+
74
+ ### 🟪 Phase 4 — Core hardening (P1)
75
+
76
+ Goal: long-term correctness of signals, html, router, forms, head.
77
+
78
+ ### 🟫 Phase 5 — Documentation (P0/P1, in parallel)
79
+
80
+ Goal: a backender can ship an admin app reading only the docs.
81
+
82
+ ---
83
+
84
+ ## 3. Acceptance scenario for v1
85
+
86
+ ```bash
87
+ npm exec --package @madojs/mado@latest -- mado init dashboard --starter admin
88
+ cd dashboard
89
+ npm install
90
+ mado dev
91
+ # / → public landing
92
+ # /admin → redirected to /login (guard works)
93
+ # log in → admin shell with sidebar / topbar / dashboard / orders table
94
+
95
+ mado release
96
+ rsync -avz out/ user@vps:/var/www/dashboard/
97
+ # done
98
+ ```
99
+
100
+ If a backend developer reaches production **without reading `bake.mjs`,
101
+ without setting env vars, without asking "where does layout live?",
102
+ and without an `examples/` folder in their app** — v1 is done.
103
+
104
+ ---
105
+
106
+ ## 4. Tracker
107
+
108
+ ### Phase 1 — Repo-vs-app split ✅
109
+ - [x] **F1.1** `mado.config.json` schema + loader (`scripts/_config.mjs`)
110
+ - [x] **F1.2** `scripts/bake.mjs`: flags `--entry/--template/--out/--base-url`, app-mode defaults, no `examples/` alias in app-mode, loud errors instead of silent `[object Object]`
111
+ - [x] **F1.3** `scripts/cli.mjs`: detect repo vs app, read `mado.config.json`, redesigned help, new `mado release` command
112
+ - [x] **F1.4** Starters cleanup: `mado.config.json` and blessed CLI scripts in `starters/minimal` and `starters/crud`; no `examples/` references
113
+ - [x] **F1.5** `docs/en/02-project-layout.md` rewritten around the canonical `src/`/`dist/`/`public/`/`out/` model and `mado.config.json`
114
+ - [x] **F1.6** `test/config-loader.test.mjs` + `test/bake-cli.test.mjs`.
115
+
116
+ ### Phase 2 — One blessed way ✅
117
+ - [x] **F2.1** `layout()` factory (alias of `nested()`) exported from `src/page.ts`; flatten propagates layouts outer → inner.
118
+ - [x] **F2.2** `Guard` type + guard chain in `src/router/manifest.ts`. Verdicts: void / `{ halt: true }` / `{ redirect, replace? }`. Async-aware with sync fast path; throwing guard is treated as `halt` with a `console.error`.
119
+ - [x] **F2.3** `layout`, `Guard`, `GuardResult`, `nested`, `navigate` exported from `src/index.ts`.
120
+ - [x] **F2.4** New starter `starters/admin/` with `layouts/{app,auth}.ts`, `lib/{api,auth}.ts`, `components/{x-button,x-input}.ts`, `pages/{home,login,not-found,admin/{dashboard,orders,order-detail}}.ts`, `styles/global.ts`, `mado.config.json`, `public/favicon.svg`. End-to-end scaffold + typecheck + build verified locally.
121
+ - [x] **F2.5** CLI advertises `--starter admin` (`mado init my-app --starter admin`) and adds it to the help screen.
122
+ - [x] **F2.6** Doc `docs/en/11-layouts.md` (one path + 2 alternatives with caveats).
123
+ - [x] **F2.7** Doc `docs/en/12-auth-and-api.md` (blessed `api`/`auth` recipes, backend contract, dev-proxy hint).
124
+ - [x] **F2.8** `test/guards-layouts.test.mjs` covers the public `layout()` alias, sync pass/halt/redirect verdicts, async guards, parent→page guard order, and throwing-guard fallback (7 tests, all green).
125
+
126
+ ### Phase 3 — Bake first-class + Release pipeline ✅ (core)
127
+ - [x] **F3.1** `mado release` command: typecheck + build + bundle + bake + copy `public/` → `out/` + write `_headers` / `_redirects` (`scripts/cli.mjs`).
128
+ - [x] **F3.2** Deferred to v0.7: `mado dev` serves baked routes inline. Current v1 path is `mado release && mado preview`.
129
+ - [x] **F3.3** `mado preview` reads `mado.config.json`, refuses to auto-build in app-mode (opt in with `PREVIEW_AUTOBUILD=1`), and emulates nginx fallback (`scripts/preview.mjs`).
130
+ - [x] **F3.4** Bake raises a loud, file/route-targeted error on unsupported render values (e.g. `each()` directive objects). Covered by `test/bake-cli.test.mjs`.
131
+ - [x] **F3.5** Deferred to v0.7: `mado check` runs bake-safety scan on `bake:` routes. Current v1 coverage is the loud-error contract from F3.4.
132
+ - [x] **F3.6** Dev-proxy in `server/serve.mjs`: reads `dev.proxy` from `mado.config.json` and forwards matching prefixes to the upstream backend without external deps.
133
+ - [x] **F3.7** `mado release` generates `out/_redirects` (`/* /index.html 200`) and `out/_headers` (immutable for `/assets/*`, no-cache for `/*.html`) when those files do not exist.
134
+ - [x] **F3.8** Doc `docs/en/13-deployment.md` (VPS + nginx, Cloudflare Pages, S3/CloudFront, Netlify, GitHub Pages, CI sketch, cache matrix, troubleshooting).
135
+ - [x] **F3.9** Production `nginx.conf` next to the docs (already shipped, with `gzip_static`, immutable hashed bundles, SPA fallback). Verified referenced from the deployment doc.
136
+ - [x] **F3.10** `test/release-pipeline.test.mjs`: end-to-end CLI test scaffolds an app, runs `mado release`, and asserts `out/index.html`, `out/baked/index.html`, `out/_headers`, `out/_redirects`, public-asset copy, and sitemap. Also `scripts/bundle.mjs` is app-mode aware (no `examples/`-leak when run from a user app).
137
+
138
+ ### Phase 4 — Core hardening
139
+ - [x] **F4.1** Refcount/GC for `computed` in `src/signal.ts` + tests (`computed` releases dep subscriptions after unobserved reads and after the last subscriber is disposed).
140
+ - [x] **F4.2** Cycle-detection in `effect` + `test/signal-cycle.test.mjs`
141
+ - [x] **F4.3** `equals` option on `computed`
142
+ - [x] **F4.4** Directives: `unsafeHTML`, `ref`, `classMap`, `styleMap` in `src/html/bindings.ts` + public exports + `test/html-directives.test.mjs`.
143
+ - [x] **F4.5** `useForm`: async validators (`validateAsync`, field-level `validateAsync`, `validating`) + field arrays (`form.array()`, dotted paths, wildcard schema) + tests.
144
+ - [x] **F4.6** Router: scroll restoration + focus management
145
+ - [x] **F4.7** Router: `errorPage:` in manifest + error boundary
146
+ - [x] **F4.8** `src/head.ts`: guaranteed cleanup on navigation (`data-mado-head` removed before re-applying)
147
+ - [x] **F4.9** Tests for everything above: `test/computed-lazy.test.mjs` (F4.1/F4.3), `test/signal-cycle.test.mjs` (F4.2), `test/html-directives.test.mjs` (F4.4), `test/forms.test.mjs` (F4.5), `test/router-isolation.test.mjs` (F4.6), `test/router-error-boundary.test.mjs` (F4.7), `test/head.test.mjs` (F4.8). Final `typecheck + build + test` pass verified.
148
+
149
+ ### Phase 5 — Documentation (parallel to phases 2-4)
150
+ English docs for project layout, layouts, auth/API and deployment exist now.
151
+ The remaining work is deeper recipes plus translation sync.
152
+
153
+ - [x] **F5.1** `docs/en/10-app-architecture.md` (end-to-end admin)
154
+ - [x] **F5.2** `docs/en/14-testing.md`
155
+ - [x] **F5.3** `docs/en/15-error-handling.md`
156
+ - [x] **F5.4** `docs/en/16-bake-cookbook.md`
157
+ - [x] **F5.5** `AGENTS.md`: section "App architecture for LLM"
158
+ - [x] **F5.6** `llms.txt`: new anchors
159
+ - [x] **F5.7** ru/fr/uk translations for new docs (`10` through `16`)
160
+
161
+ ---
162
+
163
+ ## 5. Working loop
164
+
165
+ 1. Pick the first unchecked box.
166
+ 2. Implement it (small, focused change).
167
+ 3. Run `npm test` (and `npm run typecheck` when touching `src/`).
168
+ 4. Tick the box in this file in the same commit.
169
+ 5. Add a one-line entry under "Unreleased" in `CHANGELOG.md`.
170
+ 6. After each phase finishes, pause and review.
171
+
172
+ ## 6. Context-loss safety
173
+
174
+ - This file = ground truth on disk.
175
+ - `task_progress` in chat = current phase/task.
176
+ - Git commits tagged `[v1 Fx.y]` = time machine.
177
+
178
+ If future work resumes from this plan, treat it as archive context. New tasks
179
+ belong in `ROADMAP.md`, `TODO.md`, or a fresh tracker.
package/README.md CHANGED
@@ -91,12 +91,20 @@ enough for serious admin apps while remaining small and readable.
91
91
  npm exec --package @madojs/mado@latest -- mado init my-app
92
92
  cd my-app
93
93
  npm install
94
- npm run build
95
- npm run serve
94
+ npm run dev
96
95
  ```
97
96
 
98
- Use the CRUD starter when you want a compact admin-style example with
99
- `resource()`, `mutation()`, `useForm()`, `each()` and `queryParam()`:
97
+ Use the admin starter when you want the blessed production shape out of the box:
98
+ layouts, guards, auth/API client, dev proxy, forms and a small admin shell.
99
+
100
+ ```bash
101
+ npm exec --package @madojs/mado@latest -- mado init dashboard --starter admin
102
+ cd dashboard
103
+ npm install
104
+ npm run dev
105
+ ```
106
+
107
+ Use the CRUD starter when you want a compact resource/mutation/forms example:
100
108
 
101
109
  ```bash
102
110
  npm exec --package @madojs/mado@latest -- mado init my-app --starter crud
@@ -151,17 +159,19 @@ The developer convenience CLI is available as `mado`:
151
159
 
152
160
  ```bash
153
161
  mado init my-app
162
+ mado init dashboard --starter admin
154
163
  mado init my-app --starter crud
164
+ mado dev
155
165
  mado build
156
166
  mado typecheck
157
167
  mado test
168
+ mado release
169
+ mado preview
158
170
  mado serve basic
159
171
  mado dev showcase
160
172
  mado examples
161
173
  ```
162
174
 
163
- The CLI is useful before v1, but its command polish may still change.
164
-
165
175
  ## Documentation
166
176
 
167
177
  - [Language index](./docs/README.md)
@@ -181,6 +191,13 @@ Core topics:
181
191
  - [For backenders](./docs/en/06-for-backenders.md)
182
192
  - [LLM pitfalls](./docs/en/07-llm-pitfalls.md)
183
193
  - [Shadow DOM vs Light DOM](./docs/en/09-shadow-vs-light-dom.md)
194
+ - [App architecture](./docs/en/10-app-architecture.md)
195
+ - [Layouts](./docs/en/11-layouts.md)
196
+ - [Auth and API](./docs/en/12-auth-and-api.md)
197
+ - [Deployment](./docs/en/13-deployment.md)
198
+ - [Testing](./docs/en/14-testing.md)
199
+ - [Error handling](./docs/en/15-error-handling.md)
200
+ - [Bake cookbook](./docs/en/16-bake-cookbook.md)
184
201
 
185
202
  AI-agent entrypoints:
186
203
 
@@ -327,7 +344,8 @@ html`
327
344
 
328
345
  Validation is schema-based and intentionally close to native HTML constraints:
329
346
  `required`, `min`, `max`, `pattern`, `type=email/url/number`, plus optional
330
- custom `validate(values)`.
347
+ custom `validate(values)`. Async validators and field arrays are available via
348
+ `validateAsync`, `validateField()` and `form.array("items")`.
331
349
 
332
350
  Only the root import is the stable public API:
333
351
 
@@ -347,16 +365,16 @@ Cloudflare Worker edge-prerender PoC in [`examples/cloudflare`](./examples/cloud
347
365
 
348
366
  ## Production Bundle
349
367
 
350
- Native ESM is the default development and demo path. When a bundled production
351
- artifact is useful, run:
368
+ Native ESM is the default development path. For production, use the one-command
369
+ release pipeline:
352
370
 
353
371
  ```bash
354
- npm run bundle
355
- npm run preview
372
+ mado release
373
+ mado preview
356
374
  ```
357
375
 
358
- `bundle` uses optional `esbuild` for code splitting, SRI, `.gz` and `.br`
359
- outputs. `preview` emulates the provided `nginx.conf` with `node:http`.
376
+ `release` runs typecheck, build, bundle, bake and public asset copy, then writes
377
+ the deployable artifact into `out/`. `preview` serves `out/` like a static host.
360
378
 
361
379
  ## Tests
362
380
 
package/ROADMAP.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # Roadmap
2
2
 
3
- Mado is pre-v1. The goal is not to grow sideways, but to keep the runtime small,
4
- readable and reliable under real application pressure.
3
+ Mado is still pre-1.0, but the v0.6 product-surface push is complete. The goal
4
+ from here is not to grow sideways, but to keep the runtime small, readable and
5
+ reliable under real application pressure.
5
6
 
6
7
  ## Current Focus
7
8
 
@@ -12,12 +13,31 @@ readable and reliable under real application pressure.
12
13
  - Keep English as the public/default project language, with localized docs in
13
14
  `docs/ru`, `docs/fr` and `docs/uk`.
14
15
 
15
- ## Before v1
16
+ ## Completed in v0.6
17
+
18
+ - **Phase 1 — Repo-vs-app split.** Kill `examples/`-leakage from CLI and bake;
19
+ introduce `mado.config.json`; app-mode becomes the default for `mado bake`,
20
+ `mado bundle`, `mado preview`.
21
+ - **Phase 2 — One blessed way.** First-class `layout()` + nested manifest;
22
+ `Guard` API with `redirect`/`halt`; blessed `--starter admin` starter with
23
+ blessed `api`/`auth` recipes; docs for layouts and auth-and-api.
24
+ - **Phase 3 — Bake first-class + Release pipeline.** `mado release` as the
25
+ single deploy command (`dist` internal, `out` deployed); `mado dev` serves
26
+ baked routes inline; bake stops silently rendering `[object Object]`;
27
+ deployment doc with VPS / Cloudflare Pages / static-CDN recipes.
28
+ - **Phase 4 — Core hardening.** Computed GC + cycle-detection in `effect`;
29
+ template directives (`unsafeHTML`, `ref`, `classMap`, `styleMap`); async
30
+ validators and field-arrays in `useForm`; scroll restoration, focus
31
+ management and error boundary in the router; head cleanup on navigation.
32
+ - **Phase 5 — Documentation (parallel).** `10-app-architecture.md`,
33
+ `11-layouts.md`, `12-auth-and-api.md`, `13-deployment.md`, `14-testing.md`,
34
+ `15-error-handling.md`, `16-bake-cookbook.md`; AGENTS.md app-architecture
35
+ section; ru/fr/uk translations.
36
+
37
+ The detailed completed tracker lives in [`MADO_V1_PLAN.md`](./MADO_V1_PLAN.md).
38
+
39
+ ## Remaining Before 1.0
16
40
 
17
- - Generated app DX: `mado dev`/`serve`/starter scripts should feel as polished
18
- outside the framework repo as they do inside it.
19
- - Generated app production story: `mado bundle` and `mado preview` should work
20
- from a starter app without repository-only assumptions.
21
41
  - Browser compatibility pass across current Chrome, Edge, Firefox and Safari.
22
42
  - Accessibility pass for examples and common component patterns.
23
43
  - Public API audit: names, warnings, lifecycle rules, docs coverage.
@@ -55,6 +75,7 @@ readable and reliable under real application pressure.
55
75
  | 2026-06-06 | Mado rebrand | Public package/import name `@madojs/mado`, CLI `mado`, brand/docs/examples updated, internal legacy markers cleaned. |
56
76
  | 2026-06-06 | Public polish | English public surface, localized docs, translated code comments/examples/templates/GitHub files. |
57
77
  | 2026-06-07 | First npm release | Published `@madojs/mado@0.5.0` with the `mado` CLI, minimal/crud starters, CI and release workflow. |
78
+ | 2026-06-09 | v0.6 product surface | App-mode CLI/config split, admin starter, release pipeline, core hardening, v1 recipe docs and localized docs. |
58
79
 
59
80
  ## Future Ideas
60
81