@madojs/mado 0.10.0 → 0.11.0

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 (219) hide show
  1. package/AGENTS.md +24 -26
  2. package/CHANGELOG.md +98 -0
  3. package/README.md +18 -45
  4. package/TODO.md +52 -48
  5. package/dist/src/component.d.ts +2 -1
  6. package/dist/src/component.js +5 -2
  7. package/dist/src/component.js.map +1 -1
  8. package/dist/src/each.d.ts +1 -1
  9. package/dist/src/each.js +1 -1
  10. package/dist/src/each.js.map +1 -1
  11. package/dist/src/html/template.js +10 -0
  12. package/dist/src/html/template.js.map +1 -1
  13. package/dist/src/index.d.ts +11 -6
  14. package/dist/src/index.js +5 -3
  15. package/dist/src/index.js.map +1 -1
  16. package/dist/src/lazy.d.ts +1 -1
  17. package/dist/src/lazy.js +1 -1
  18. package/dist/src/lazy.js.map +1 -1
  19. package/dist/src/page.d.ts +17 -21
  20. package/dist/src/page.js +7 -12
  21. package/dist/src/page.js.map +1 -1
  22. package/dist/src/router/manifest.d.ts +1 -1
  23. package/dist/src/router/manifest.js +21 -13
  24. package/dist/src/router/manifest.js.map +1 -1
  25. package/dist/src/router/match.d.ts +2 -2
  26. package/dist/src/router/match.js +3 -3
  27. package/dist/src/router/match.js.map +1 -1
  28. package/dist/src/router/navigation.js +1 -1
  29. package/dist/src/router/navigation.js.map +1 -1
  30. package/dist/src/vite/index.d.ts +10 -0
  31. package/dist/src/vite/index.js +33 -0
  32. package/dist/src/vite/index.js.map +1 -0
  33. package/docs/en/00-the-mado-way.md +25 -12
  34. package/docs/en/01-routing.md +90 -142
  35. package/docs/en/02-project-layout.md +59 -52
  36. package/docs/en/03-static-bake.md +5 -6
  37. package/docs/en/05-why-mado.md +6 -6
  38. package/docs/en/06-for-backenders.md +18 -22
  39. package/docs/en/08-llm-zero-history-test.md +9 -14
  40. package/docs/en/09-shadow-vs-light-dom.md +28 -36
  41. package/docs/en/10-app-architecture.md +158 -96
  42. package/docs/en/11-layouts.md +22 -24
  43. package/docs/en/12-auth-and-api.md +89 -182
  44. package/docs/en/13-deployment.md +25 -26
  45. package/docs/en/14-testing.md +4 -4
  46. package/docs/en/16-bake-cookbook.md +17 -10
  47. package/docs/en/18-api-freeze-map.md +6 -4
  48. package/docs/en/20-v1-stability.md +1 -1
  49. package/docs/fr/00-the-mado-way.md +55 -90
  50. package/docs/fr/01-routing.md +70 -152
  51. package/docs/fr/02-project-layout.md +74 -48
  52. package/docs/fr/03-static-bake.md +1 -1
  53. package/docs/fr/05-why-mado.md +6 -6
  54. package/docs/fr/06-for-backenders.md +7 -7
  55. package/docs/fr/08-llm-zero-history-test.md +21 -48
  56. package/docs/fr/09-shadow-vs-light-dom.md +43 -162
  57. package/docs/fr/10-app-architecture.md +110 -33
  58. package/docs/fr/11-layouts.md +24 -12
  59. package/docs/fr/12-auth-and-api.md +63 -22
  60. package/docs/fr/13-deployment.md +30 -12
  61. package/docs/fr/14-testing.md +1 -1
  62. package/docs/fr/16-bake-cookbook.md +57 -4
  63. package/docs/fr/18-api-freeze-map.md +1 -1
  64. package/docs/fr/20-v1-stability.md +1 -1
  65. package/docs/recipes/nginx/README.md +13 -0
  66. package/docs/ru/00-the-mado-way.md +53 -75
  67. package/docs/ru/01-routing.md +68 -143
  68. package/docs/ru/02-project-layout.md +75 -48
  69. package/docs/ru/03-static-bake.md +2 -2
  70. package/docs/ru/05-why-mado.md +6 -6
  71. package/docs/ru/06-for-backenders.md +7 -7
  72. package/docs/ru/08-llm-zero-history-test.md +9 -14
  73. package/docs/ru/09-shadow-vs-light-dom.md +43 -178
  74. package/docs/ru/10-app-architecture.md +115 -63
  75. package/docs/ru/11-layouts.md +24 -24
  76. package/docs/ru/12-auth-and-api.md +57 -35
  77. package/docs/ru/13-deployment.md +19 -13
  78. package/docs/ru/14-testing.md +1 -1
  79. package/docs/ru/16-bake-cookbook.md +48 -8
  80. package/docs/ru/18-api-freeze-map.md +5 -3
  81. package/docs/ru/20-v1-stability.md +1 -1
  82. package/docs/uk/00-the-mado-way.md +70 -44
  83. package/docs/uk/01-routing.md +41 -47
  84. package/docs/uk/02-project-layout.md +68 -41
  85. package/docs/uk/03-static-bake.md +1 -2
  86. package/docs/uk/06-for-backenders.md +3 -3
  87. package/docs/uk/08-llm-zero-history-test.md +22 -24
  88. package/docs/uk/09-shadow-vs-light-dom.md +37 -86
  89. package/docs/uk/10-app-architecture.md +72 -31
  90. package/docs/uk/11-layouts.md +25 -12
  91. package/docs/uk/12-auth-and-api.md +58 -22
  92. package/docs/uk/13-deployment.md +4 -3
  93. package/docs/uk/14-testing.md +1 -1
  94. package/docs/uk/18-api-freeze-map.md +1 -1
  95. package/docs/uk/20-v1-stability.md +1 -1
  96. package/llms.txt +14 -15
  97. package/package.json +18 -11
  98. package/scripts/_config.mjs +15 -161
  99. package/scripts/bake.mjs +71 -58
  100. package/scripts/cli/generate.mjs +348 -0
  101. package/scripts/cli/help.mjs +27 -0
  102. package/scripts/cli/index.mjs +79 -0
  103. package/scripts/cli/init.mjs +153 -0
  104. package/scripts/cli/release.mjs +152 -0
  105. package/scripts/cli/run.mjs +96 -0
  106. package/scripts/cli.mjs +2 -560
  107. package/scripts/package-smoke.mjs +4 -1
  108. package/scripts/preview.mjs +17 -61
  109. package/scripts/size-budget.mjs +5 -2
  110. package/scripts/vite.default.mjs +11 -0
  111. package/starters/default/.editorconfig +12 -0
  112. package/starters/default/README.md +74 -0
  113. package/starters/default/eslint.config.mjs +256 -0
  114. package/starters/default/index.html +13 -0
  115. package/starters/default/package.json +30 -0
  116. package/starters/default/public/favicon.svg +4 -0
  117. package/starters/default/src/app.routes.ts +39 -0
  118. package/starters/default/src/layouts/app-shell.layout.ts +35 -0
  119. package/starters/default/src/layouts/auth-shell.layout.ts +17 -0
  120. package/starters/default/src/main.ts +16 -0
  121. package/starters/default/src/modules/auth/_contracts/auth-api.types.ts +17 -0
  122. package/starters/default/src/modules/auth/auth.connector.ts +45 -0
  123. package/starters/default/src/modules/auth/auth.guard.ts +22 -0
  124. package/starters/default/src/modules/auth/auth.public.ts +9 -0
  125. package/starters/default/src/modules/auth/auth.routes.ts +8 -0
  126. package/starters/default/src/modules/auth/auth.service.ts +71 -0
  127. package/starters/default/src/modules/auth/auth.types.ts +15 -0
  128. package/starters/default/src/modules/auth/login.page.ts +62 -0
  129. package/starters/default/src/modules/billing/_contracts/stripe.types.ts +17 -0
  130. package/starters/default/src/modules/billing/api/stripe.connector.ts +71 -0
  131. package/starters/default/src/modules/billing/billing.public.ts +5 -0
  132. package/starters/default/src/modules/billing/billing.routes.ts +9 -0
  133. package/starters/default/src/modules/billing/billing.types.ts +15 -0
  134. package/starters/default/src/modules/billing/components/invoice-status-badge.component.ts +43 -0
  135. package/starters/default/src/modules/billing/data/invoices.resource.ts +35 -0
  136. package/starters/default/src/modules/billing/pages/invoice-detail.page.ts +70 -0
  137. package/starters/default/src/modules/billing/pages/invoices-list.page.ts +73 -0
  138. package/starters/default/src/modules/home/home.page.ts +34 -0
  139. package/starters/default/src/modules/home/not-found.page.ts +11 -0
  140. package/starters/default/src/shared/http/http-client.ts +86 -0
  141. package/starters/default/src/shared/http/http-error.ts +37 -0
  142. package/starters/default/src/shared/http/interceptors.ts +59 -0
  143. package/starters/default/src/shared/lib/format-date.ts +19 -0
  144. package/starters/default/src/shared/styles/content.css +70 -0
  145. package/starters/default/src/shared/styles/reset.css +32 -0
  146. package/starters/default/src/shared/styles/shell.css +57 -0
  147. package/starters/default/src/shared/styles/tokens.css +44 -0
  148. package/starters/default/src/shared/ui/x-button.component.ts +49 -0
  149. package/starters/default/src/shared/ui/x-spinner.component.ts +22 -0
  150. package/starters/default/src/styles.d.ts +1 -0
  151. package/starters/default/src/vite-env.d.ts +1 -0
  152. package/starters/default/tsconfig.json +24 -0
  153. package/starters/default/vite.config.ts +9 -0
  154. package/MADO_V1_PLAN.md +0 -179
  155. package/ROADMAP.md +0 -178
  156. package/dist/src/html.d.ts +0 -18
  157. package/dist/src/html.js +0 -17
  158. package/dist/src/html.js.map +0 -1
  159. package/dist/src/router.d.ts +0 -13
  160. package/dist/src/router.js +0 -13
  161. package/dist/src/router.js.map +0 -1
  162. package/scripts/bundle.mjs +0 -212
  163. package/scripts/llm-zero-history-smoke.mjs +0 -93
  164. package/scripts/new.mjs +0 -80
  165. package/scripts/showcase-regression.mjs +0 -392
  166. package/server/serve.mjs +0 -455
  167. package/starters/admin/README.md +0 -63
  168. package/starters/admin/index.html +0 -28
  169. package/starters/admin/mado.config.json +0 -22
  170. package/starters/admin/package.json +0 -24
  171. package/starters/admin/public/favicon.svg +0 -4
  172. package/starters/admin/src/components/x-button.ts +0 -82
  173. package/starters/admin/src/components/x-input.ts +0 -105
  174. package/starters/admin/src/layouts/app.ts +0 -101
  175. package/starters/admin/src/layouts/auth.ts +0 -41
  176. package/starters/admin/src/lib/api.ts +0 -184
  177. package/starters/admin/src/lib/auth.ts +0 -83
  178. package/starters/admin/src/main.ts +0 -15
  179. package/starters/admin/src/pages/admin/dashboard.ts +0 -48
  180. package/starters/admin/src/pages/admin/order-detail.ts +0 -80
  181. package/starters/admin/src/pages/admin/orders.ts +0 -117
  182. package/starters/admin/src/pages/home.ts +0 -34
  183. package/starters/admin/src/pages/login.ts +0 -70
  184. package/starters/admin/src/pages/not-found.ts +0 -12
  185. package/starters/admin/src/routes.ts +0 -40
  186. package/starters/admin/src/styles/global.ts +0 -86
  187. package/starters/admin/tsconfig.json +0 -15
  188. package/starters/crud/README.md +0 -33
  189. package/starters/crud/index.html +0 -28
  190. package/starters/crud/mado.config.json +0 -20
  191. package/starters/crud/package.json +0 -24
  192. package/starters/crud/src/components/app-shell.ts +0 -56
  193. package/starters/crud/src/components/ticket-detail.ts +0 -33
  194. package/starters/crud/src/components/ticket-form.ts +0 -69
  195. package/starters/crud/src/components/ticket-list.ts +0 -66
  196. package/starters/crud/src/lib/api.ts +0 -76
  197. package/starters/crud/src/main.ts +0 -9
  198. package/starters/crud/src/pages/home.ts +0 -34
  199. package/starters/crud/src/pages/not-found.ts +0 -12
  200. package/starters/crud/src/pages/ticket-detail.ts +0 -7
  201. package/starters/crud/src/pages/ticket-new.ts +0 -7
  202. package/starters/crud/src/pages/tickets.ts +0 -7
  203. package/starters/crud/src/routes.ts +0 -11
  204. package/starters/crud/src/styles/global.ts +0 -155
  205. package/starters/crud/tsconfig.json +0 -15
  206. package/starters/minimal/README.md +0 -21
  207. package/starters/minimal/index.html +0 -28
  208. package/starters/minimal/mado.config.json +0 -20
  209. package/starters/minimal/package.json +0 -24
  210. package/starters/minimal/src/components/app-counter.ts +0 -31
  211. package/starters/minimal/src/main.ts +0 -9
  212. package/starters/minimal/src/pages/home.ts +0 -35
  213. package/starters/minimal/src/pages/not-found.ts +0 -14
  214. package/starters/minimal/src/routes.ts +0 -8
  215. package/starters/minimal/src/styles/global.ts +0 -60
  216. package/starters/minimal/tsconfig.json +0 -15
  217. package/templates/page-detail.ts +0 -63
  218. package/templates/page-form.ts +0 -94
  219. package/templates/page-list.ts +0 -79
@@ -1,36 +1,34 @@
1
1
  # LLM Zero-History Test
2
2
 
3
- Мета тесту: перевірити, чи може LLM без попередньої історії написати ідіоматичний
4
- Mado CRUD, не перетворюючи його на React у tagged templates.
3
+ This document defines a manual validation test: can a fresh LLM write idiomatic
4
+ Mado without falling back to React-shaped code?
5
5
 
6
- ## Дозволений контекст
6
+ ## Allowed Context
7
7
 
8
8
  - `AGENTS.md`
9
9
  - `README.md`
10
- - `docs/uk/07-llm-pitfalls.md` або відповідна англійська версія
11
- - `examples/basic/README.md`
12
- - конкретні файли прикладів тільки за потреби
10
+ - `docs/uk/07-llm-pitfalls.md` or the English version
11
+ - files from the external `madojs-examples` workspace only when the agent asks
12
+ for a larger app pattern
13
13
 
14
- ## Що перевіряти
14
+ ## Task
15
15
 
16
- - Немає JSX, `useState`, `useEffect`.
17
- - Немає signal `.value`.
18
- - Reactive child bindings з `count()` обгорнуті у `() =>`.
19
- - Boolean attributes пишуться як `?disabled`, `?checked`.
20
- - Динамічні списки використовують `each()`.
21
- - Imports мають `.js`.
22
- - `resource()` створюється в lifecycle-aware контексті.
16
+ Build a small ticket-admin SPA:
23
17
 
24
- ## Артефакт
18
+ - routes: `/`, `/tickets`, `/tickets/new`, `/tickets/:id`, `*`;
19
+ - in-memory mock API with realistic async delays;
20
+ - list page with `resource()`, `queryParam()`, `computed()` and keyed `each()`;
21
+ - create/edit flows with `useForm()` + `mutation()` + `invalidates`;
22
+ - local UI state with `signal()`.
25
23
 
26
- `examples/tickets` маленький ticket-admin SPA з routes, mock API, forms,
27
- resources, mutations, invalidation, `queryParam`, `computed`, `signal` і
28
- keyed lists.
24
+ ## Failure Checklist
29
25
 
30
- CI запускає `npm run llm:smoke` як детермінований proxy для цієї задачі:
31
- перевіряє, що `llms.txt` містить ключові правила, звіряє закомічений артефакт
32
- `examples/tickets` з потрібною Mado API surface та failure patterns, потім
33
- збирає проєкт і запускає `test/tickets-smoke.test.mjs`.
26
+ - JSX, `useState`, `useEffect`, `ref`, `$state`, class-style components;
27
+ - `${signal()}` where a reactive child thunk is required;
28
+ - `disabled=${...}` instead of `?disabled=${...}`;
29
+ - unkeyed `.map()` for dynamic lists;
30
+ - `resource()` created outside lifecycle-aware context;
31
+ - new runtime dependencies or new public APIs.
34
32
 
35
- Критерій успіху: код виглядає як Mado, а не як React/Vue, переодягнений у
36
- template strings.
33
+ The historical tickets implementation lives in the external examples workspace.
34
+ The core repository no longer ships that artifact.
@@ -1,107 +1,58 @@
1
1
  # Shadow DOM vs Light DOM
2
2
 
3
- Mado використовує Shadow DOM за замовчуванням. Це хороший default для
4
- самодостатніх widgets, але не для кожного компонента в app.
3
+ Mado components use Shadow DOM by default. This is good for self-contained
4
+ widgets, but app zones and pages usually stay as light DOM templates.
5
5
 
6
- ## Практичне правило
6
+ ## Rule
7
7
 
8
- У Mado layout це теж component. Якщо файл описує видиму reusable частину
9
- UI-дерева — app shell, sidebar, modal, table, page section — за замовчуванням
10
- робіть Web Component через `component()`.
11
-
12
- Звичайні функції залишайте для маленьких inline helpers:
13
-
14
- ```ts
15
- const money = (value: number) => html`<span>${formatMoney(value)}</span>`;
16
- ```
17
-
18
- Не варто робити app shell функцією в public examples. Це працює, але ховає
19
- browser model замість того, щоб її пояснювати.
20
-
21
- Використовуйте **Shadow DOM** для leaf widgets:
22
-
23
- - buttons, badges, cards, metrics;
24
- - modals, toasts, small visual components;
25
- - embedded widgets, які не мають випадково успадковувати app CSS;
26
- - components, чиї styles належать самому component.
27
-
28
- Використовуйте **Light DOM** (`{ shadow: false }`) для app structure, якій
29
- потрібні global CSS utilities:
30
-
31
- - route/page components;
32
- - admin screens з dense table/form layouts;
33
- - data-heavy screens з tables and forms;
34
- - місця, де children мають залишатися normal document DOM.
35
-
36
- Використовуйте **Shadow DOM** для slot-based layouts:
37
-
38
- - app shells, які render `<slot>`;
39
- - sidebar/content wrappers;
40
- - reusable layout frames, які володіють своїм grid/header/sidebar CSS.
41
-
42
- `<slot>` — це feature Shadow DOM. У component з `shadow: false` тег `<slot>` є
43
- звичайним DOM element і не переносить children у це місце layout.
44
-
45
- ## Як працює import
8
+ Use route layouts for app zones:
46
9
 
47
10
  ```ts
48
- import "./components/app-layout.js";
49
-
50
- render(html`<x-app-layout>${router.view}</x-app-layout>`, app);
11
+ export default page({
12
+ view: ({ child }) => html`<main class="app-main">${child}</main>`,
13
+ });
51
14
  ```
52
15
 
53
- Import реєструє custom element через `customElements.define()`. Template створює
54
- `<x-app-layout>` element. Далі browser сам з'єднує tag з component class. Це не
55
- React-style component value, який передається як function.
56
-
57
- ## Routing and links
16
+ These files live in `src/layouts/` and are composed from `src/app.routes.ts`
17
+ with `layout()`. They are styled by `src/shared/styles/shell.css`.
58
18
 
59
- `data-link` працює всередині Shadow DOM, бо router використовує
60
- `event.composedPath()`.
19
+ Use page files for screens:
61
20
 
62
21
  ```ts
63
- component("x-card-link", () => () => html`
64
- <a href="/app/accounts" data-link>Accounts</a>
65
- `);
22
+ export default page({
23
+ view: () => html`<section><h1>Users</h1></section>`,
24
+ });
66
25
  ```
67
26
 
68
- Link може бути в Shadow DOM; navigation все одно залишається SPA.
27
+ Page-level tables, forms, prose and simple states are styled by
28
+ `src/shared/styles/content.css`.
69
29
 
70
- ## Де імпортувати компоненти
30
+ Use Shadow DOM components for leaf widgets:
71
31
 
72
- Custom elements стають global після registration, але registration все одно є
73
- явним JavaScript import.
32
+ - buttons, badges, cards, metrics;
33
+ - spinners, modals, toasts;
34
+ - widgets that should own their CSS.
74
35
 
75
36
  ```ts
76
- // main.ts: global app frame
77
- import "./components/app-shell.js";
78
-
79
- // pages/tickets.ts: component, яким володіє ця page
80
- import "../components/ticket-list.js";
37
+ component("x-status-badge", ({ attr }) => {
38
+ const status = attr("status", "draft");
39
+ return () => html`<span>${status}</span>`;
40
+ }, {
41
+ styles: css`
42
+ :host { display: inline-block; }
43
+ span { color: var(--color-text-muted); }
44
+ `,
45
+ });
81
46
  ```
82
47
 
83
- Browser **не** завантажує `ticket-list.js` лише тому, що побачив
84
- `<ticket-list>`. File має бути imported десь першим. Після import він викликає
85
- `customElements.define(...)`, і tag стає відомим у поточному document.
86
-
87
- Не робіть bulk-import усіх components у `main.ts` "just in case". Це працює в
88
- tiny demos, але ховає ownership і ламає lazy route loading. Краще:
89
-
90
- - global app shell/providers імпортувати в `main.ts`;
91
- - components, якими володіє одна page, імпортувати в цій page;
92
- - shared feature components імпортувати у feature entry page;
93
- - truly global leaf components імпортувати в `main.ts` лише якщо вони реально
94
- використовуються всюди.
95
-
96
- ## Showcase lesson
97
-
98
- `examples/showcase` використовує цей split навмисно:
48
+ ## Style Behavior
99
49
 
100
- - `x-app` і CRM route pages Light DOM;
101
- - `x-app-layout` Shadow DOM, бо він володіє slot-based sidebar/content shell;
102
- - table/form/page utilities живуть у `styles/global.ts`;
103
- - leaf components на кшталт `x-stat-card`, `x-status-badge`, `x-modal`,
104
- `x-toast-stack` залишають Shadow DOM.
50
+ - `tokens.css` defines CSS custom properties; `var(...)` crosses Shadow DOM.
51
+ - `reset.css`, `shell.css`, `content.css` apply only to document/light DOM.
52
+ - Class selectors like `.data`, `.app-main`, `.error` do not cross Shadow DOM.
53
+ - Component-local styles live in ``css`...` `` inside `component()` options.
54
+ - If a Shadow component accepts children, use `<slot>` and style the frame in
55
+ component styles.
105
56
 
106
- Якщо page раптом виглядає unstyled, перевірте, чи не використовує вона global
107
- classes всередині Shadow DOM component. Зазвичай проблема саме в цьому.
57
+ If a page looks unstyled, you probably used global classes inside a Shadow DOM
58
+ component. Move markup into a page/layout or move CSS into component styles.
@@ -1,56 +1,97 @@
1
1
  # Архітектура застосунку
2
2
 
3
- Production-застосунок на Mado має бути простим: один manifest маршрутів, один
4
- shell, один API-клієнт, один auth-модуль і сторінки, які імпортують власні
5
- компоненти.
3
+ Офіційний starter канонічна production-форма Mado-застосунку. Це не
4
+ framework всередині framework: лише files, imports, Mado primitives та ESLint
5
+ boundaries.
6
6
 
7
7
  ```txt
8
8
  src/
9
9
  ├── main.ts
10
- ├── routes.ts
10
+ ├── app.routes.ts
11
11
  ├── layouts/
12
- ├── pages/
13
- ├── components/
14
- ├── lib/
15
- └── styles/
12
+ ├── app-shell.layout.ts
13
+ │ └── auth-shell.layout.ts
14
+ ├── shared/
15
+ │ ├── http/
16
+ │ ├── lib/
17
+ │ ├── styles/
18
+ │ └── ui/
19
+ └── modules/
20
+ ├── auth/
21
+ │ ├── auth.routes.ts
22
+ │ ├── auth.public.ts
23
+ │ ├── auth.service.ts
24
+ │ ├── auth.connector.ts
25
+ │ ├── auth.guard.ts
26
+ │ ├── login.page.ts
27
+ │ └── _contracts/
28
+ └── billing/
29
+ ├── billing.routes.ts
30
+ ├── billing.public.ts
31
+ ├── billing.types.ts
32
+ ├── api/
33
+ ├── data/
34
+ ├── pages/
35
+ ├── components/
36
+ └── _contracts/
16
37
  ```
17
38
 
18
- `lib/` бізнес-логіка, `layouts/` обгортки груп маршрутів, `components/`
19
- повторні UI-теги, `pages/` один файл на сторінку.
20
-
21
- ```ts
22
- import { html, render } from "@madojs/mado";
23
- import "./styles/global.js";
24
- import routesApi from "./routes.js";
25
-
26
- render(html`${routesApi.view}`, document.getElementById("app")!);
27
- ```
28
-
29
- Feature-компоненти імпортує сторінка, яка їх рендерить.
39
+ `src/app.routes.ts` is the app map. Modules export plain route maps; app routes
40
+ decide which shell and guard wrap each zone.
30
41
 
31
42
  ```ts
32
43
  import { layout, routes } from "@madojs/mado";
33
- import { requireAuth } from "./lib/auth.js";
44
+ import { requireAuth } from "./modules/auth/auth.public";
45
+ import { authRoutes } from "./modules/auth/auth.routes";
46
+ import { billingRoutes } from "./modules/billing/billing.routes";
34
47
 
35
48
  export const manifest = {
36
- "/": () => import("./pages/home.js"),
37
- "/admin": layout({
38
- layout: () => import("./layouts/app.js"),
49
+ "/": () => import("./modules/home/home.page"),
50
+ "/login": layout({
51
+ layout: () => import("./layouts/auth-shell.layout"),
52
+ routes: authRoutes,
53
+ }),
54
+ "/billing": layout({
55
+ layout: () => import("./layouts/app-shell.layout"),
39
56
  guard: requireAuth,
40
- routes: { "/": () => import("./pages/admin/dashboard.js") },
57
+ routes: billingRoutes,
41
58
  }),
42
- "*": () => import("./pages/not-found.js"),
59
+ "*": () => import("./modules/home/not-found.page"),
43
60
  };
44
61
 
45
62
  export default routes(manifest);
46
63
  ```
47
64
 
48
- `manifest` потрібен для `mado bake`. Для читання використовуй `resource()`, для
49
- запису `mutation(..., { invalidates })`, для форм `useForm()`.
65
+ Page-local signals, resources and forms live inside `view()`. Module-wide state
66
+ lives in `*.service.ts`.
67
+
68
+ ## Styles
69
+
70
+ | File | Role |
71
+ | --- | --- |
72
+ | `src/shared/styles/tokens.css` | design tokens as CSS custom properties |
73
+ | `src/shared/styles/reset.css` | document/light DOM reset |
74
+ | `src/shared/styles/shell.css` | app-zone layouts from `src/layouts/` |
75
+ | `src/shared/styles/content.css` | page-level forms, tables, prose and states |
76
+
77
+ Leaf components keep their own styles in ``css`...` `` inside `component()`
78
+ options and depend on tokens, not global classes.
79
+
80
+ `vite.config.ts` uses Vite's Lightning CSS transformer. Mado does not own
81
+ prefixing, CSS lowering or minification.
82
+
83
+ ## CLI
50
84
 
51
85
  ```bash
52
- mado dev
53
- mado release
86
+ mado new module billing
87
+ mado new page billing/pages/invoices-list
88
+ mado new connector billing/api/stripe
89
+ mado new resource billing/data/invoices
90
+ mado new service billing/cart
91
+ mado new form billing/invoice
92
+ mado new component billing/components/invoice-status-badge
93
+ mado new guard billing/billing
94
+ mado new layout app-shell
54
95
  ```
55
96
 
56
- Деплоїться `out/`, а не `dist/`.
97
+ The generator writes files only and does not edit `app.routes.ts`.
@@ -1,34 +1,47 @@
1
1
  # Layouts
2
2
 
3
- Рекомендований спосіб layout у Mado вкладена група маршрутів у `routes.ts`.
3
+ The blessed layout recipe in Mado is a route group in `src/app.routes.ts`.
4
+ Do not put a single global shell in `main.ts` when the app has multiple zones:
5
+ public, auth, app, embed.
4
6
 
5
7
  ```ts
6
8
  import { layout, routes } from "@madojs/mado";
7
- import { requireAuth } from "./lib/auth.js";
9
+ import { requireAuth } from "./modules/auth/auth.public";
10
+ import { authRoutes } from "./modules/auth/auth.routes";
11
+ import { billingRoutes } from "./modules/billing/billing.routes";
8
12
 
9
13
  export const manifest = {
10
- "/": () => import("./pages/home.js"),
14
+ "/": () => import("./modules/home/home.page.js"),
11
15
  "/login": layout({
12
- layout: () => import("./layouts/auth.js"),
13
- routes: { "/": () => import("./pages/login.js") },
16
+ layout: () => import("./layouts/auth-shell.layout.js"),
17
+ routes: authRoutes,
14
18
  }),
15
- "/admin": layout({
16
- layout: () => import("./layouts/app.js"),
19
+ "/billing": layout({
20
+ layout: () => import("./layouts/app-shell.layout.js"),
17
21
  guard: requireAuth,
18
- routes: { "/": () => import("./pages/admin/dashboard.js") },
22
+ routes: billingRoutes,
19
23
  }),
24
+ "*": () => import("./modules/home/not-found.page.js"),
20
25
  };
21
26
 
22
27
  export default routes(manifest);
23
28
  ```
24
29
 
25
- Layout це `page({ view })`, яка рендерить `child`:
30
+ A layout is a normal `page({ view })` that renders `child`:
26
31
 
27
32
  ```ts
28
33
  export default page({
29
- view: ({ child }) => html`<x-app-shell>${child}</x-app-shell>`,
34
+ view: ({ child }) => html`
35
+ <div class="layout layout--app">
36
+ <main class="app-main">${child}</main>
37
+ </div>
38
+ `,
30
39
  });
31
40
  ```
32
41
 
33
- Один shell на групу, не на кожну сторінку. Guard на групі захищає все
34
- піддерево.
42
+ Rules:
43
+
44
+ - one shell per route group, not per page;
45
+ - modules export plain route maps and do not call `layout()`;
46
+ - guard on a group protects the whole subtree;
47
+ - layout view stays stateless; page-local state lives in pages/components/resources.
@@ -1,34 +1,70 @@
1
- # Auth та API
1
+ # Auth and API
2
2
 
3
- Starter `admin` містить рекомендований рецепт:
3
+ The default starter is the blessed recipe. HTTP mechanics live in
4
+ `src/shared/http/`, auth state lives in `src/modules/auth/`.
4
5
 
5
- - `src/lib/api.ts` — один HTTP-клієнт, `ApiError`, refresh після 401;
6
- - `src/lib/auth.ts` — `accessToken`, `restoreSession()`, `login()`,
7
- `logout()`, `requireAuth`.
6
+ ```txt
7
+ src/shared/http/
8
+ http-client.ts
9
+ http-error.ts
10
+ interceptors.ts
8
11
 
9
- Модель:
12
+ src/modules/auth/
13
+ auth.connector.ts
14
+ auth.service.ts
15
+ auth.guard.ts
16
+ auth.routes.ts
17
+ auth.public.ts
18
+ _contracts/
19
+ ```
20
+
21
+ Business module flow:
22
+
23
+ ```txt
24
+ connector -> resource/mutation -> page
25
+ ```
10
26
 
11
- - access token у пам'яті через `signal`, не в `localStorage`;
12
- - HttpOnly refresh cookie відновлює сесію;
13
- - усі запити проходять через API-клієнт;
14
- - захищені routes використовують group guard.
27
+ Pages do not import DTOs and do not call `fetch()` directly. Connectors do not
28
+ import Mado reactivity or UI.
29
+
30
+ ## Auth Service
15
31
 
16
32
  ```ts
17
- export const requireAuth: Guard = async ({ path }) => {
18
- if (accessToken()) return;
19
- if (await restoreSession()) return;
20
- return { redirect: `/login?return=${encodeURIComponent(path)}`, replace: true };
21
- };
33
+ const _user = signal<User | null>(null);
34
+ const _token = signal<string | null>(null);
35
+
36
+ export const user = () => _user();
37
+ export const isAuthed = computed(() => _user() !== null);
22
38
  ```
23
39
 
24
- Dev proxy:
40
+ Expose only what other modules need through `auth.public.ts`.
25
41
 
26
- ```jsonc
27
- {
28
- "dev": {
29
- "proxy": { "/api": "http://localhost:3000" }
30
- }
42
+ ## Guards
43
+
44
+ ```ts
45
+ export function requireAuth(): boolean | string {
46
+ if (isAuthed()) return true;
47
+ return "/login";
31
48
  }
32
49
  ```
33
50
 
34
- Якщо backend має іншу auth-схему, змінюй `api.ts`/`auth.ts`, а не сторінки.
51
+ Use in `src/app.routes.ts`:
52
+
53
+ ```ts
54
+ "/billing": layout({
55
+ layout: () => import("./layouts/app-shell.layout"),
56
+ guard: requireAuth,
57
+ routes: billingRoutes,
58
+ }),
59
+ ```
60
+
61
+ ## Dev Proxy
62
+
63
+ ```ts
64
+ export default defineConfig({
65
+ plugins: [mado()],
66
+ server: {
67
+ proxy: { "/api": "http://localhost:3000" },
68
+ },
69
+ });
70
+ ```
@@ -12,7 +12,8 @@ mado release
12
12
  out/
13
13
  ├── index.html
14
14
  ├── assets/
15
- ├── baked/
15
+ ├── <route>/index.html
16
+ ├── sitemap.xml
16
17
  ├── _redirects
17
18
  └── _headers
18
19
  ```
@@ -35,5 +36,5 @@ mado release
35
36
  rsync -avz --delete out/ user@server:/var/www/myapp/
36
37
  ```
37
38
 
38
- Наданий `nginx.conf` налаштовує immutable cache для hash bundles, no-cache для
39
- HTML та fallback для deep links.
39
+ Опційний nginx recipe живе в `docs/recipes/nginx/`: immutable cache для
40
+ `/assets/*`, no-cache для HTML та fallback для deep links.
@@ -20,7 +20,7 @@ const { window } = parseHTML("<!doctype html><html><body></body></html>");
20
20
  globalThis.window = window;
21
21
  globalThis.document = window.document;
22
22
 
23
- const { html, render } = await import("../dist/src/html.js");
23
+ const { html, render } = await import("../dist/src/html/template.js");
24
24
 
25
25
  test("renders", () => {
26
26
  const root = document.createElement("div");
@@ -27,7 +27,7 @@ import "@madojs/mado/devtools.js";
27
27
  - Templates і directives: `html`, `render`, `each`, `list`, `unsafeHTML`,
28
28
  `ref`, `classMap`, `styleMap`.
29
29
  - Components і CSS: `component`, `css`, `cssVars`.
30
- - Routing і pages: `routes`, `router`, `page`, `layout`, `nested`,
30
+ - Routing і pages: `routes`, `router`, `page`, `layout`,
31
31
  `navigate`, `queryParam`, `prefetchPath`.
32
32
  - Data: `resource`, `mutation`, `invalidate`, `jsonFetcher`, `HttpError`.
33
33
  - Forms: `useForm`.
@@ -25,7 +25,7 @@ starter copy або diagnostic string заморожені назавжди.
25
25
  deferred teardown для same-tick moves, cleanup через `ctx.onDispose`.
26
26
  - Router/page/resource/form contracts, описані в English docs.
27
27
  - Імена CLI commands і широкий сенс команд (`build`, `dev`, `release`, `bake`,
28
- `bundle`, `preview`, `init`, `new`).
28
+ `preview`, `init`, `new`).
29
29
 
30
30
  Ламати це можна тільки в major version.
31
31
 
package/llms.txt CHANGED
@@ -79,7 +79,7 @@ import {
79
79
  component,
80
80
  css, cssVars,
81
81
  routes, router, navigate, queryParam, prefetchPath,
82
- page, nested,
82
+ page, layout,
83
83
  resource, mutation, invalidate, jsonFetcher,
84
84
  useForm,
85
85
  createContext, provide, inject,
@@ -147,8 +147,9 @@ Use `jsonFetcher()` for public endpoints, `apiFetcher()` for anything behind aut
147
147
 
148
148
  ## Layouts and bake
149
149
 
150
- Use `layout()` in `routes.ts` for shared shells. A layout view should be a pure
151
- wrapper around `${child}` and shared chrome:
150
+ Use `layout()` in `src/app.routes.ts` for shared shells. Modules export plain
151
+ route maps; app routes decide which layout and guard wrap each zone. A layout
152
+ view should be a pure wrapper around `${child}` and shared chrome:
152
153
 
153
154
  ```ts
154
155
  export default page({
@@ -169,17 +170,18 @@ relative `fetch`, and runtime directives like keyed `each()` during bake.
169
170
  ```ts
170
171
  // src/main.ts
171
172
  import { html, render } from "@madojs/mado";
172
- import routesApi from "./routes.js";
173
+ import routesApi from "./app.routes.js";
173
174
  render(html`${routesApi.view}`, document.getElementById("app")!);
174
175
 
175
- // src/routes.ts
176
+ // src/app.routes.ts
176
177
  import { routes } from "@madojs/mado";
177
- export default routes({
178
- "/": () => import("./pages/home.js"),
179
- "*": () => import("./pages/not-found.js"),
180
- });
178
+ export const manifest = {
179
+ "/": () => import("./modules/home/home.page.js"),
180
+ "*": () => import("./modules/home/not-found.page.js"),
181
+ };
182
+ export default routes(manifest);
181
183
 
182
- // src/pages/home.ts
184
+ // src/modules/home/home.page.ts
183
185
  import { page, component, html, css, signal } from "@madojs/mado";
184
186
 
185
187
  component("x-counter", () => {
@@ -254,10 +256,7 @@ export default page({
254
256
  - docs/en/18-api-freeze-map.md — stable public API vs internal implementation details
255
257
  - docs/en/19-reactivity-ordering.md — signal ordering, batching and teardown guarantees
256
258
  - docs/en/20-v1-stability.md — v1 SemVer contract and what remains internal
257
- - examples/basic/ — minimal API tour
258
- - examples/tickets/ — LLM zero-history CRUD validation
259
- - examples/showcase/ — flagship CRM pressure app (auth, nested routes, forms, mutations)
260
- - examples/cloudflare/ — Edge prerender PoC on Cloudflare Workers
259
+ - starters/default/ — canonical generated app shape
261
260
 
262
261
  ## What Mado does NOT do (intentionally)
263
262
 
@@ -265,7 +264,7 @@ export default page({
265
264
  - ❌ Virtual DOM → fine-grained signal updates
266
265
  - ❌ SSR with hydration → `bake` static meta-shell or edge-prerender for SEO
267
266
  - ❌ Hooks and rules of hooks → signals
268
- - ❌ Mandatory Webpack/Viteonly `tsc`
267
+ - ❌ Runtime framework dependencies generated apps use Vite as dev/build tooling
269
268
  - ❌ React-Router / TanStack → built-in 500-line `routes()`
270
269
  - ❌ React-Query / SWR → built-in `resource()`
271
270
  - ❌ Formik / RHF → built-in `useForm()` (HTML5 validation)