@madojs/mado 0.10.1 → 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 (217) hide show
  1. package/AGENTS.md +24 -26
  2. package/CHANGELOG.md +68 -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/index.d.ts +11 -6
  12. package/dist/src/index.js +5 -3
  13. package/dist/src/index.js.map +1 -1
  14. package/dist/src/lazy.d.ts +1 -1
  15. package/dist/src/lazy.js +1 -1
  16. package/dist/src/lazy.js.map +1 -1
  17. package/dist/src/page.d.ts +17 -21
  18. package/dist/src/page.js +7 -12
  19. package/dist/src/page.js.map +1 -1
  20. package/dist/src/router/manifest.d.ts +1 -1
  21. package/dist/src/router/manifest.js +21 -13
  22. package/dist/src/router/manifest.js.map +1 -1
  23. package/dist/src/router/match.d.ts +2 -2
  24. package/dist/src/router/match.js +3 -3
  25. package/dist/src/router/match.js.map +1 -1
  26. package/dist/src/router/navigation.js +1 -1
  27. package/dist/src/router/navigation.js.map +1 -1
  28. package/dist/src/vite/index.d.ts +10 -0
  29. package/dist/src/vite/index.js +33 -0
  30. package/dist/src/vite/index.js.map +1 -0
  31. package/docs/en/00-the-mado-way.md +25 -12
  32. package/docs/en/01-routing.md +90 -142
  33. package/docs/en/02-project-layout.md +59 -53
  34. package/docs/en/03-static-bake.md +5 -6
  35. package/docs/en/05-why-mado.md +6 -6
  36. package/docs/en/06-for-backenders.md +18 -22
  37. package/docs/en/08-llm-zero-history-test.md +9 -14
  38. package/docs/en/09-shadow-vs-light-dom.md +28 -36
  39. package/docs/en/10-app-architecture.md +158 -96
  40. package/docs/en/11-layouts.md +22 -24
  41. package/docs/en/12-auth-and-api.md +89 -182
  42. package/docs/en/13-deployment.md +18 -22
  43. package/docs/en/14-testing.md +4 -4
  44. package/docs/en/16-bake-cookbook.md +11 -12
  45. package/docs/en/18-api-freeze-map.md +6 -4
  46. package/docs/en/20-v1-stability.md +1 -1
  47. package/docs/fr/00-the-mado-way.md +55 -90
  48. package/docs/fr/01-routing.md +70 -152
  49. package/docs/fr/02-project-layout.md +61 -42
  50. package/docs/fr/03-static-bake.md +1 -1
  51. package/docs/fr/05-why-mado.md +6 -6
  52. package/docs/fr/06-for-backenders.md +7 -7
  53. package/docs/fr/08-llm-zero-history-test.md +21 -48
  54. package/docs/fr/09-shadow-vs-light-dom.md +43 -162
  55. package/docs/fr/10-app-architecture.md +110 -33
  56. package/docs/fr/11-layouts.md +24 -12
  57. package/docs/fr/12-auth-and-api.md +63 -22
  58. package/docs/fr/13-deployment.md +7 -10
  59. package/docs/fr/14-testing.md +1 -1
  60. package/docs/fr/16-bake-cookbook.md +2 -2
  61. package/docs/fr/18-api-freeze-map.md +1 -1
  62. package/docs/fr/20-v1-stability.md +1 -1
  63. package/docs/recipes/nginx/README.md +13 -0
  64. package/docs/ru/00-the-mado-way.md +53 -75
  65. package/docs/ru/01-routing.md +68 -143
  66. package/docs/ru/02-project-layout.md +61 -41
  67. package/docs/ru/03-static-bake.md +2 -2
  68. package/docs/ru/05-why-mado.md +6 -6
  69. package/docs/ru/06-for-backenders.md +7 -7
  70. package/docs/ru/08-llm-zero-history-test.md +9 -14
  71. package/docs/ru/09-shadow-vs-light-dom.md +43 -178
  72. package/docs/ru/10-app-architecture.md +115 -63
  73. package/docs/ru/11-layouts.md +24 -24
  74. package/docs/ru/12-auth-and-api.md +57 -35
  75. package/docs/ru/13-deployment.md +7 -11
  76. package/docs/ru/14-testing.md +1 -1
  77. package/docs/ru/16-bake-cookbook.md +12 -6
  78. package/docs/ru/18-api-freeze-map.md +5 -3
  79. package/docs/ru/20-v1-stability.md +1 -1
  80. package/docs/uk/00-the-mado-way.md +70 -44
  81. package/docs/uk/01-routing.md +41 -47
  82. package/docs/uk/02-project-layout.md +68 -41
  83. package/docs/uk/03-static-bake.md +1 -2
  84. package/docs/uk/06-for-backenders.md +3 -3
  85. package/docs/uk/08-llm-zero-history-test.md +22 -24
  86. package/docs/uk/09-shadow-vs-light-dom.md +37 -86
  87. package/docs/uk/10-app-architecture.md +72 -31
  88. package/docs/uk/11-layouts.md +25 -12
  89. package/docs/uk/12-auth-and-api.md +58 -22
  90. package/docs/uk/13-deployment.md +4 -3
  91. package/docs/uk/14-testing.md +1 -1
  92. package/docs/uk/18-api-freeze-map.md +1 -1
  93. package/docs/uk/20-v1-stability.md +1 -1
  94. package/llms.txt +14 -15
  95. package/package.json +18 -11
  96. package/scripts/_config.mjs +15 -161
  97. package/scripts/bake.mjs +67 -57
  98. package/scripts/cli/generate.mjs +348 -0
  99. package/scripts/cli/help.mjs +27 -0
  100. package/scripts/cli/index.mjs +79 -0
  101. package/scripts/cli/init.mjs +153 -0
  102. package/scripts/cli/release.mjs +152 -0
  103. package/scripts/cli/run.mjs +96 -0
  104. package/scripts/cli.mjs +2 -621
  105. package/scripts/package-smoke.mjs +4 -1
  106. package/scripts/preview.mjs +13 -37
  107. package/scripts/size-budget.mjs +5 -2
  108. package/scripts/vite.default.mjs +11 -0
  109. package/starters/default/.editorconfig +12 -0
  110. package/starters/default/README.md +74 -0
  111. package/starters/default/eslint.config.mjs +256 -0
  112. package/starters/default/index.html +13 -0
  113. package/starters/default/package.json +30 -0
  114. package/starters/default/public/favicon.svg +4 -0
  115. package/starters/default/src/app.routes.ts +39 -0
  116. package/starters/default/src/layouts/app-shell.layout.ts +35 -0
  117. package/starters/default/src/layouts/auth-shell.layout.ts +17 -0
  118. package/starters/default/src/main.ts +16 -0
  119. package/starters/default/src/modules/auth/_contracts/auth-api.types.ts +17 -0
  120. package/starters/default/src/modules/auth/auth.connector.ts +45 -0
  121. package/starters/default/src/modules/auth/auth.guard.ts +22 -0
  122. package/starters/default/src/modules/auth/auth.public.ts +9 -0
  123. package/starters/default/src/modules/auth/auth.routes.ts +8 -0
  124. package/starters/default/src/modules/auth/auth.service.ts +71 -0
  125. package/starters/default/src/modules/auth/auth.types.ts +15 -0
  126. package/starters/default/src/modules/auth/login.page.ts +62 -0
  127. package/starters/default/src/modules/billing/_contracts/stripe.types.ts +17 -0
  128. package/starters/default/src/modules/billing/api/stripe.connector.ts +71 -0
  129. package/starters/default/src/modules/billing/billing.public.ts +5 -0
  130. package/starters/default/src/modules/billing/billing.routes.ts +9 -0
  131. package/starters/default/src/modules/billing/billing.types.ts +15 -0
  132. package/starters/default/src/modules/billing/components/invoice-status-badge.component.ts +43 -0
  133. package/starters/default/src/modules/billing/data/invoices.resource.ts +35 -0
  134. package/starters/default/src/modules/billing/pages/invoice-detail.page.ts +70 -0
  135. package/starters/default/src/modules/billing/pages/invoices-list.page.ts +73 -0
  136. package/starters/default/src/modules/home/home.page.ts +34 -0
  137. package/starters/default/src/modules/home/not-found.page.ts +11 -0
  138. package/starters/default/src/shared/http/http-client.ts +86 -0
  139. package/starters/default/src/shared/http/http-error.ts +37 -0
  140. package/starters/default/src/shared/http/interceptors.ts +59 -0
  141. package/starters/default/src/shared/lib/format-date.ts +19 -0
  142. package/starters/default/src/shared/styles/content.css +70 -0
  143. package/starters/default/src/shared/styles/reset.css +32 -0
  144. package/starters/default/src/shared/styles/shell.css +57 -0
  145. package/starters/default/src/shared/styles/tokens.css +44 -0
  146. package/starters/default/src/shared/ui/x-button.component.ts +49 -0
  147. package/starters/default/src/shared/ui/x-spinner.component.ts +22 -0
  148. package/starters/default/src/styles.d.ts +1 -0
  149. package/starters/default/src/vite-env.d.ts +1 -0
  150. package/starters/default/tsconfig.json +24 -0
  151. package/starters/default/vite.config.ts +9 -0
  152. package/MADO_V1_PLAN.md +0 -179
  153. package/ROADMAP.md +0 -178
  154. package/dist/src/html.d.ts +0 -18
  155. package/dist/src/html.js +0 -17
  156. package/dist/src/html.js.map +0 -1
  157. package/dist/src/router.d.ts +0 -13
  158. package/dist/src/router.js +0 -13
  159. package/dist/src/router.js.map +0 -1
  160. package/scripts/bundle.mjs +0 -212
  161. package/scripts/llm-zero-history-smoke.mjs +0 -93
  162. package/scripts/new.mjs +0 -80
  163. package/scripts/showcase-regression.mjs +0 -392
  164. package/server/serve.mjs +0 -455
  165. package/starters/admin/README.md +0 -63
  166. package/starters/admin/index.html +0 -28
  167. package/starters/admin/mado.config.json +0 -22
  168. package/starters/admin/package.json +0 -24
  169. package/starters/admin/public/favicon.svg +0 -4
  170. package/starters/admin/src/components/x-button.ts +0 -82
  171. package/starters/admin/src/components/x-input.ts +0 -105
  172. package/starters/admin/src/layouts/app.ts +0 -101
  173. package/starters/admin/src/layouts/auth.ts +0 -41
  174. package/starters/admin/src/lib/api.ts +0 -184
  175. package/starters/admin/src/lib/auth.ts +0 -83
  176. package/starters/admin/src/main.ts +0 -15
  177. package/starters/admin/src/pages/admin/dashboard.ts +0 -48
  178. package/starters/admin/src/pages/admin/order-detail.ts +0 -80
  179. package/starters/admin/src/pages/admin/orders.ts +0 -117
  180. package/starters/admin/src/pages/home.ts +0 -34
  181. package/starters/admin/src/pages/login.ts +0 -70
  182. package/starters/admin/src/pages/not-found.ts +0 -12
  183. package/starters/admin/src/routes.ts +0 -40
  184. package/starters/admin/src/styles/global.ts +0 -86
  185. package/starters/admin/tsconfig.json +0 -15
  186. package/starters/crud/README.md +0 -33
  187. package/starters/crud/index.html +0 -28
  188. package/starters/crud/mado.config.json +0 -20
  189. package/starters/crud/package.json +0 -24
  190. package/starters/crud/src/components/app-shell.ts +0 -56
  191. package/starters/crud/src/components/ticket-detail.ts +0 -33
  192. package/starters/crud/src/components/ticket-form.ts +0 -69
  193. package/starters/crud/src/components/ticket-list.ts +0 -66
  194. package/starters/crud/src/lib/api.ts +0 -76
  195. package/starters/crud/src/main.ts +0 -9
  196. package/starters/crud/src/pages/home.ts +0 -34
  197. package/starters/crud/src/pages/not-found.ts +0 -12
  198. package/starters/crud/src/pages/ticket-detail.ts +0 -7
  199. package/starters/crud/src/pages/ticket-new.ts +0 -7
  200. package/starters/crud/src/pages/tickets.ts +0 -7
  201. package/starters/crud/src/routes.ts +0 -11
  202. package/starters/crud/src/styles/global.ts +0 -155
  203. package/starters/crud/tsconfig.json +0 -15
  204. package/starters/minimal/README.md +0 -21
  205. package/starters/minimal/index.html +0 -28
  206. package/starters/minimal/mado.config.json +0 -20
  207. package/starters/minimal/package.json +0 -24
  208. package/starters/minimal/src/components/app-counter.ts +0 -31
  209. package/starters/minimal/src/main.ts +0 -9
  210. package/starters/minimal/src/pages/home.ts +0 -35
  211. package/starters/minimal/src/pages/not-found.ts +0 -14
  212. package/starters/minimal/src/routes.ts +0 -8
  213. package/starters/minimal/src/styles/global.ts +0 -60
  214. package/starters/minimal/tsconfig.json +0 -15
  215. package/templates/page-detail.ts +0 -63
  216. package/templates/page-form.ts +0 -94
  217. package/templates/page-list.ts +0 -79
@@ -2,55 +2,81 @@
2
2
 
3
3
  > Один зрозумілий шлях. Жорсткі контракти. Мінімум магії.
4
4
 
5
- Mado — фреймворк для команд, що будують адмін-панелі, внутрішні інструменти
6
- та бізнес-SPA застосунки, які мають бути простими у розробці та нудними в
7
- підтримці. Для цього він задає **набір домовленостей**. Якщо їх дотримуватись,
8
- проєкт залишається читабельним навіть тоді, коли в ньому десятки сторінок і
9
- кілька розробників.
10
-
11
- ## Принципи
12
-
13
- 1. **Один спосіб.** Для типової задачі має бути один канонічний шлях, а не п’ять
14
- рівноправних стилів.
15
- 2. **Явність замість магії.** Немає сканерів файлів, прихованих глобалів і
16
- неочевидних side effects.
17
- 3. **Платформа спочатку.** Якщо браузер уже має потрібну можливість, Mado дає
18
- тонку обгортку, а не переписує платформу.
19
- 4. **Суворий TypeScript.** `tsc --strict` — базовий контракт.
20
- 5. **Нуль runtime-залежностей.** Кожна залежність — це довгострокове
21
- зобов’язання.
22
-
23
- ## Базова структура
24
-
25
- ```text
5
+ Mado — framework для команд, що будують admin panels, internal tools і
6
+ business SPA. Такі apps мають бути простими у розробці та нудними в підтримці,
7
+ тому Mado обирає чіткі conventions замість п'яти рівноправних стилів.
8
+
9
+ ## Principles
10
+
11
+ 1. **One way.** If code feels unusual, first check whether a canonical helper/API
12
+ already exists.
13
+ 2. **Explicit over magic.** No file-system scanners, implicit globals or hidden
14
+ side effects.
15
+ 3. **Platform first.** Web Components, History API, `<form>`, `fetch` and Shadow
16
+ DOM stay visible.
17
+ 4. **Strict types.** `tsc --strict --noUncheckedIndexedAccess` always.
18
+ 5. **No runtime dependencies.** Dev/build tooling is fine; Mado runtime stays
19
+ native.
20
+
21
+ ## Project Structure
22
+
23
+ ```txt
26
24
  src/
27
- ├── routes.ts
28
- ├── main.ts
29
- ├── pages/
30
- ├── components/
31
- ├── layouts/
32
- ├── lib/
33
- └── styles/
25
+ ├── main.ts ← boot: global CSS/providers + render router
26
+ ├── app.routes.ts ← readable app map, exports `manifest` + default routes()
27
+ ├── layouts/ ← app-zone wrappers (`page({ view: ({ child }) => ... })`)
28
+ ├── shared/ ← UI bricks, http client, pure lib, global CSS
29
+ └── modules/ ← bounded contexts
30
+ └── billing/
31
+ ├── billing.routes.ts
32
+ ├── billing.public.ts
33
+ ├── billing.types.ts
34
+ ├── pages/
35
+ ├── data/
36
+ ├── api/
37
+ └── _contracts/
38
+ ```
39
+
40
+ The default starter is the canonical version of this shape.
41
+
42
+ ## One Component = One File
43
+
44
+ ```ts
45
+ import { component, css, html } from "@madojs/mado";
46
+
47
+ component("x-user-card", () => () => html`<div class="card"><slot></slot></div>`, {
48
+ styles: css`
49
+ .card { padding: 1rem; }
50
+ `,
51
+ });
34
52
  ```
35
53
 
36
- Кожна сторінка живе в окремому файлі та експортує `page({...})`.
37
- Компоненти реєструються через `component()`. Дані читаються через `resource()`,
38
- зміни виконуються через `mutation()`, списки рендеряться через `each()`.
54
+ Importing the component file registers the element. Import it where the tag is
55
+ used.
39
56
 
40
- ## Імена компонентів
57
+ ## One Way To Describe A Page
58
+
59
+ ```ts
60
+ import { html, page, resource, jsonFetcher } from "@madojs/mado";
61
+
62
+ export default page({
63
+ title: ({ id }) => `User #${id}`,
64
+ view: ({ params }) => {
65
+ const user = resource(() => `/api/users/${params.id}`, jsonFetcher());
66
+ return html`...`;
67
+ },
68
+ });
69
+ ```
41
70
 
42
- Єдине правило браузера: ім’я custom element має містити дефіс. `x-*` у прикладах
43
- Mado це демо-конвенція, а не вимога фреймворку. У production краще брати
44
- префікс домену: `app-*`, `crm-*`, `ticket-*`, `admin-*`.
71
+ Page-local signals, resources and forms live inside `view()`. Module-wide state
72
+ lives in `*.service.ts`.
45
73
 
46
- ## Чого не робимо
74
+ ## What We Do Not Do
47
75
 
48
- - Не пишемо JSX або Vue-шаблони.
49
- - Не читаємо сигнал через `.value`; сигнал читається як функція: `count()`.
50
- - Не використовуємо `disabled=${...}` для булевих атрибутів; треба
51
- `?disabled=${...}`.
52
- - Не рендеримо динамічні списки через `.map()` там, де потрібне збереження DOM
53
- стану; використовуємо `each()`.
54
- - Не додаємо runtime-залежності без дуже вагомої причини.
76
+ - No JSX/Vue/Svelte syntax.
77
+ - No custom elements without a hyphen.
78
+ - No signal `.value`; a signal is a function.
79
+ - No direct `innerHTML`.
80
+ - No runtime packages without discussion.
55
81
 
56
- Коли є сумнів, краще додати рецепт у документацію, ніж новий primitive в core.
82
+ When in doubt, document one clear recipe instead of adding a new core primitive.
@@ -1,76 +1,70 @@
1
- # Маршрутизація
1
+ # Routing
2
2
 
3
- Mado використовує явний route manifest: один файл показує весь URL-граф
4
- застосунку.
3
+ > One app map. No folder scanners. No magic path syntax.
4
+
5
+ Mado uses an explicit route manifest. Route composition should be readable in
6
+ one place: `src/app.routes.ts`.
5
7
 
6
8
  ```ts
7
- import { routes } from "@madojs/mado";
9
+ import { layout, routes } from "@madojs/mado";
10
+ import { requireAuth } from "./modules/auth/auth.public";
11
+ import { authRoutes } from "./modules/auth/auth.routes";
12
+ import { billingRoutes } from "./modules/billing/billing.routes";
8
13
 
9
14
  export const manifest = {
10
- "/": () => import("./pages/home.js"),
11
- "/users/:id": () => import("./pages/user-detail.js"),
12
- "*": () => import("./pages/not-found.js"),
15
+ "/": () => import("./modules/home/home.page.js"),
16
+ "/login": layout({
17
+ layout: () => import("./layouts/auth-shell.layout.js"),
18
+ routes: authRoutes,
19
+ }),
20
+ "/billing": layout({
21
+ layout: () => import("./layouts/app-shell.layout.js"),
22
+ guard: requireAuth,
23
+ routes: billingRoutes,
24
+ }),
25
+ "*": () => import("./modules/home/not-found.page.js"),
13
26
  };
14
27
 
15
28
  export default routes(manifest);
16
29
  ```
17
30
 
18
- ## Сторінка
19
-
20
- ```ts
21
- import { page, html } from "@madojs/mado";
22
-
23
- export default page<{ id: string }>({
24
- title: ({ id }) => `User ${id}`,
25
- view: ({ params }) => html`<x-user data-id=${params.id}></x-user>`,
26
- });
27
- ```
31
+ Export `manifest` so `mado bake` can read it.
28
32
 
29
- Сторінка може мати `title`, `head`, `load`, `view`, `errorView` і `bake`.
30
- Маршрут може бути lazy import або готовим `page({...})`.
33
+ ## Module Routes
31
34
 
32
- ## Nested routes
35
+ Modules export plain route maps. They do not call `layout()`.
33
36
 
34
37
  ```ts
35
- import { nested, routes } from "@madojs/mado";
36
-
37
- export const manifest = {
38
- "/": () => import("./pages/home.js"),
39
- "/app": nested({
40
- layout: () => import("./layouts/app-layout.js"),
41
- routes: {
42
- "/dashboard": () => import("./pages/dashboard.js"),
43
- "/settings": () => import("./pages/settings.js"),
44
- },
45
- }),
38
+ export const billingRoutes = {
39
+ "/invoices": () => import("./pages/invoices-list.page.js"),
40
+ "/invoices/:id": () => import("./pages/invoice-detail.page.js"),
46
41
  };
47
-
48
- export default routes(manifest);
49
42
  ```
50
43
 
51
- Layout отримує дочірній view і може рендерити shell: nav, sidebar, toolbar,
52
- notifications.
44
+ The prefix is applied by `src/app.routes.ts`.
53
45
 
54
- ## Навігація
46
+ ## Page
55
47
 
56
- Посилання з `data-link` перехоплюються роутером:
48
+ ```ts
49
+ import { html, page } from "@madojs/mado";
57
50
 
58
- ```html
59
- <a href="/users/42" data-link>Open</a>
51
+ export default page<{ id: string }>({
52
+ title: ({ id }) => `User ${id}`,
53
+ view: ({ params }) => html`<h1>${params.id}</h1>`,
54
+ });
60
55
  ```
61
56
 
62
- Програмна навігація:
57
+ ## Navigation
63
58
 
64
59
  ```ts
65
- import { navigate } from "@madojs/mado";
60
+ import appRoutes from "./app.routes.js";
66
61
 
67
- navigate("/users/42");
62
+ appRoutes.navigate("/billing/invoices");
63
+ appRoutes.navigate("/billing/invoices?page=2");
64
+ appRoutes.navigate("/login", { replace: true });
68
65
  ```
69
66
 
70
- Router підтримує hover-prefetch, stale async guard, scroll-to-top для нової
71
- навігації та `dispose()` для тестів/dev overlay.
72
-
73
- ## Query params
67
+ ## Query Params
74
68
 
75
69
  ```ts
76
70
  import { queryParam } from "@madojs/mado";
@@ -79,4 +73,4 @@ const search = queryParam("q", "");
79
73
  search.set("mado");
80
74
  ```
81
75
 
82
- `queryParam()` повертає signal-like API і синхронізує стан із URL.
76
+ `queryParam()` returns a signal-like API and syncs state with the URL.
@@ -1,46 +1,73 @@
1
1
  # Структура проєкту
2
2
 
3
- Рекомендована структура Mado-застосунку:
4
-
5
- ```text
6
- src/
7
- ├── main.ts
8
- ├── routes.ts
9
- ├── pages/
10
- ├── components/
11
- ├── layouts/
12
- ├── lib/
13
- └── styles/
3
+ Кожен Mado-застосунок використовує одну канонічну форму. Це потрібно, щоб люди
4
+ й AI-асистенти однаково розуміли, де живе код.
5
+
6
+ ```txt
7
+ my-app/
8
+ ├── package.json # runtime dep: @madojs/mado
9
+ ├── tsconfig.json # strict TS, ES2022, Bundler resolution
10
+ ├── vite.config.ts # mado() from @madojs/mado/vite
11
+ ├── index.html # Vite entry + SPA shell
12
+ ├── public/ # static assets: favicon, images, robots.txt
13
+ └── src/
14
+ ├── main.ts # imports CSS and mounts router into #app
15
+ ├── app.routes.ts # app map: manifest + default routes(...)
16
+ ├── layouts/ # app-zone layouts
17
+ ├── shared/ # ui, http, lib, styles
18
+ └── modules/ # bounded contexts
19
+ └── billing/
20
+ ├── billing.routes.ts
21
+ ├── billing.public.ts
22
+ ├── billing.types.ts
23
+ ├── pages/
24
+ ├── data/
25
+ ├── api/
26
+ └── _contracts/
14
27
  ```
15
28
 
16
- ## `main.ts`
17
-
18
- Точка входу: встановлює глобальні стилі, провайдери контексту та монтує кореневий
19
- компонент у `#app`.
20
-
21
- ## `routes.ts`
22
-
23
- Єдиний manifest маршрутів. Немає file-system routing, груп у назвах папок або
24
- прихованих conventions.
25
-
26
- ## `pages/`
27
-
28
- Одна сторінка — один файл — `export default page({...})`.
29
-
30
- ## `components/`
31
-
32
- Повторно використовувані Web Components. Імпорт файлу реєструє компонент як side
33
- effect.
34
-
35
- ## `layouts/`
36
-
37
- Shell для nested routes: admin layout, marketing layout, authenticated area.
38
-
39
- ## `lib/`
40
-
41
- API-клієнти, contexts, чиста бізнес-логіка без UI.
42
-
43
- ## `styles/`
29
+ ## Artifact States
30
+
31
+ | Folder | Що це | Хто пише | Deploy? |
32
+ | --- | --- | --- | --- |
33
+ | `src/` | TypeScript sources | ви | no |
34
+ | `public/` | static assets copied as-is | ви | via `out/` |
35
+ | `out/` | deploy artifact: SPA shell + assets + baked HTML | `mado release` | yes |
36
+
37
+ `mado release` = `typecheck` + Vite build (`out/index.html`, `out/assets/`,
38
+ `public/*`) + `bake` directly into route paths + `sitemap.xml` + precompression.
39
+
40
+ ## Where To Put Files
41
+
42
+ | What | Where |
43
+ | --- | --- |
44
+ | Page for a new URL | `src/modules/<module>/pages/<name>.page.ts` + module routes |
45
+ | Module route map | `src/modules/<module>/<module>.routes.ts` |
46
+ | App shell/layout | `src/layouts/<zone>.layout.ts` |
47
+ | Shared UI widget | `src/shared/ui/<x-name>.component.ts` |
48
+ | Module-only UI widget | `src/modules/<module>/components/<name>.component.ts` |
49
+ | API connector | `src/modules/<module>/api/<provider>.connector.ts` |
50
+ | Data resource/mutation | `src/modules/<module>/data/<name>.resource.ts` |
51
+ | Auth/session | `src/modules/auth/` |
52
+ | Public module surface | `src/modules/<module>/<module>.public.ts` |
53
+ | Pure function without UI | `src/shared/lib/<name>.ts` |
54
+ | Static image / favicon | `public/<file>` |
55
+ | App-zone shell CSS | `src/shared/styles/shell.css` |
56
+ | Page-level CSS | `src/shared/styles/content.css` |
57
+
58
+ ## Vite Config
59
+
60
+ ```ts
61
+ import { defineConfig } from "vite";
62
+ import { mado } from "@madojs/mado/vite";
63
+
64
+ export default defineConfig({
65
+ plugins: [mado()],
66
+ css: {
67
+ transformer: "lightningcss",
68
+ },
69
+ });
70
+ ```
44
71
 
45
- Глобальні tokens або shared CSS через `css```. Для app-shell часто зручно
46
- використовувати Light DOM, для leaf-компонентів — Shadow DOM.
72
+ Starter uses Vite's Lightning CSS transformer. Mado does not own prefixing, CSS
73
+ lowering or minification.
@@ -42,8 +42,7 @@ export default page<{ slug: string }, Product>({
42
42
  ## Edge prerender
43
43
 
44
44
  Для великих наборів сторінок можна робити той самий підхід на edge: Cloudflare
45
- Worker генерує HTML на cache miss, кладе в KV і віддає з TTL. Приклад лежить в
46
- `examples/cloudflare`.
45
+ Worker генерує HTML на cache miss, кладе в KV і віддає з TTL.
47
46
 
48
47
  Це не hydration. Клієнтський Mado-застосунок все одно стартує нормально і
49
48
  перерендерює сторінку після завантаження.
@@ -7,7 +7,7 @@
7
7
  |---|---|
8
8
  | router | `routes()` |
9
9
  | handler | `page().view` |
10
- | middleware/layout | `nested()` + layout |
10
+ | middleware/layout | `layout()` route group |
11
11
  | cache get-or-set | `resource()` |
12
12
  | POST/PUT/DELETE | `mutation()` |
13
13
  | cache invalidation | `invalidates` / `invalidate()` |
@@ -44,8 +44,8 @@ Mado використовує schema-based validation, близьку до HTML
44
44
 
45
45
  ## Auth
46
46
 
47
- Auth зазвичай живе в `lib/auth.ts` як signal/context. Protected area зручно
48
- робити через nested layout, який перевіряє session і показує dashboard або
47
+ Auth зазвичай живе в `modules/auth` як signal/context. Protected area зручно
48
+ робити через `layout()` group, який перевіряє session і показує dashboard або
49
49
  redirect/login.
50
50
 
51
51
  ## Правило
@@ -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.