@madojs/mado 0.11.1 → 0.12.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 +102 -25
  2. package/CHANGELOG.md +158 -1
  3. package/README.md +135 -172
  4. package/dist/src/component.d.ts +3 -2
  5. package/dist/src/component.js +98 -6
  6. package/dist/src/component.js.map +1 -1
  7. package/dist/src/css.js +34 -5
  8. package/dist/src/css.js.map +1 -1
  9. package/dist/src/head.d.ts +6 -6
  10. package/dist/src/head.js +6 -6
  11. package/dist/src/html/parser.d.ts +1 -1
  12. package/dist/src/html/parser.js +1 -1
  13. package/dist/src/html/template.js +24 -10
  14. package/dist/src/html/template.js.map +1 -1
  15. package/dist/src/index.d.ts +8 -4
  16. package/dist/src/index.js +13 -3
  17. package/dist/src/index.js.map +1 -1
  18. package/dist/src/page.d.ts +62 -28
  19. package/dist/src/page.js +5 -0
  20. package/dist/src/page.js.map +1 -1
  21. package/dist/src/resource.js +2 -1
  22. package/dist/src/resource.js.map +1 -1
  23. package/dist/src/router/base.d.ts +65 -0
  24. package/dist/src/router/base.js +168 -0
  25. package/dist/src/router/base.js.map +1 -0
  26. package/dist/src/router/manifest.js +75 -31
  27. package/dist/src/router/manifest.js.map +1 -1
  28. package/dist/src/router/match.d.ts +16 -2
  29. package/dist/src/router/match.js +41 -1
  30. package/dist/src/router/match.js.map +1 -1
  31. package/dist/src/router/navigation.js +58 -9
  32. package/dist/src/router/navigation.js.map +1 -1
  33. package/dist/src/static-runtime.d.ts +81 -0
  34. package/dist/src/static-runtime.js +209 -0
  35. package/dist/src/static-runtime.js.map +1 -0
  36. package/dist/src/vite/index.d.ts +31 -3
  37. package/dist/src/vite/index.js +49 -18
  38. package/dist/src/vite/index.js.map +1 -1
  39. package/docs/README.md +5 -8
  40. package/docs/architecture/adr/0001-browser-static-snapshots.md +132 -0
  41. package/docs/en/00-the-mado-way.md +1 -1
  42. package/docs/en/01-quickstart.md +183 -0
  43. package/docs/en/10-pages-and-components.md +271 -0
  44. package/docs/en/11-templates-and-signals.md +211 -0
  45. package/docs/en/12-routing.md +229 -0
  46. package/docs/en/13-data.md +225 -0
  47. package/docs/en/14-forms.md +244 -0
  48. package/docs/en/15-static-snapshots.md +181 -0
  49. package/docs/en/{10-app-architecture.md → 16-app-architecture.md} +53 -2
  50. package/docs/en/{13-deployment.md → 20-deployment.md} +59 -10
  51. package/docs/en/{14-testing.md → 22-testing.md} +6 -5
  52. package/docs/en/23-cookbook.md +292 -0
  53. package/docs/en/{18-api-freeze-map.md → 30-api-freeze-map.md} +12 -3
  54. package/docs/en/{20-v1-stability.md → 32-v1-stability.md} +3 -3
  55. package/docs/en/40-llm-guide.md +776 -0
  56. package/docs/en/{06-for-backenders.md → 41-for-backenders.md} +15 -16
  57. package/docs/en/{05-why-mado.md → 42-why-mado.md} +1 -1
  58. package/docs/en/README.md +97 -27
  59. package/docs/recipes/nginx/Containerfile +26 -0
  60. package/docs/recipes/nginx/nginx.conf +42 -0
  61. package/llms.txt +246 -211
  62. package/package.json +17 -10
  63. package/scripts/bake.mjs +4 -569
  64. package/scripts/cli/generate.mjs +55 -5
  65. package/scripts/cli/help.mjs +11 -5
  66. package/scripts/cli/index.mjs +20 -4
  67. package/scripts/cli/init.mjs +7 -2
  68. package/scripts/cli/release.mjs +20 -68
  69. package/scripts/docs-lint.mjs +170 -0
  70. package/scripts/package-smoke.mjs +67 -9
  71. package/scripts/preview.mjs +95 -6
  72. package/scripts/size-budget.mjs +5 -1
  73. package/scripts/static/browser.mjs +654 -0
  74. package/scripts/static/discover.mjs +281 -0
  75. package/scripts/static/output.mjs +141 -0
  76. package/scripts/static/serialize.mjs +212 -0
  77. package/scripts/static/server.mjs +167 -0
  78. package/scripts/static.mjs +191 -0
  79. package/starters/default/README.md +57 -55
  80. package/starters/default/package.json +3 -9
  81. package/starters/default/src/app.routes.ts +20 -33
  82. package/starters/default/src/components/feature-card.component.ts +40 -0
  83. package/starters/default/src/components/live-counter.component.ts +57 -0
  84. package/starters/default/src/content/guides.ts +55 -0
  85. package/starters/default/src/main.ts +9 -13
  86. package/starters/default/src/pages/app.page.ts +41 -0
  87. package/starters/default/src/pages/guide.page.ts +40 -0
  88. package/starters/default/src/pages/home.page.ts +49 -0
  89. package/starters/default/src/pages/not-found.page.ts +18 -0
  90. package/starters/default/src/styles/document.css +39 -0
  91. package/starters/default/src/styles/reset.css +37 -0
  92. package/starters/default/src/styles/tokens.css +30 -0
  93. package/starters/default/src/styles.d.ts +3 -1
  94. package/starters/default/src/vite-env.d.ts +1 -1
  95. package/starters/default/vite.config.ts +14 -1
  96. package/starters/modular/README.md +142 -0
  97. package/starters/modular/index.html +13 -0
  98. package/starters/modular/package.json +30 -0
  99. package/starters/modular/src/app.routes.ts +39 -0
  100. package/starters/{default → modular}/src/layouts/app-shell.layout.ts +8 -5
  101. package/starters/{default → modular}/src/layouts/auth-shell.layout.ts +3 -3
  102. package/starters/modular/src/main.ts +16 -0
  103. package/starters/{default → modular}/src/modules/billing/pages/invoices-list.page.ts +3 -3
  104. package/starters/{default → modular}/src/modules/home/home.page.ts +5 -7
  105. package/starters/modular/src/modules/home/not-found.page.ts +14 -0
  106. package/starters/modular/src/styles.d.ts +1 -0
  107. package/starters/modular/src/vite-env.d.ts +1 -0
  108. package/starters/modular/tsconfig.json +24 -0
  109. package/starters/modular/vite.config.ts +131 -0
  110. package/TODO.md +0 -79
  111. package/docs/en/01-routing.md +0 -152
  112. package/docs/en/02-project-layout.md +0 -124
  113. package/docs/en/03-static-bake.md +0 -249
  114. package/docs/en/04-ide-setup.md +0 -162
  115. package/docs/en/07-llm-pitfalls.md +0 -724
  116. package/docs/en/08-llm-zero-history-test.md +0 -56
  117. package/docs/en/09-shadow-vs-light-dom.md +0 -174
  118. package/docs/en/11-layouts.md +0 -113
  119. package/docs/en/12-auth-and-api.md +0 -124
  120. package/docs/en/16-bake-cookbook.md +0 -100
  121. package/docs/en/17-shadow-dom-forms.md +0 -192
  122. package/docs/fr/00-the-mado-way.md +0 -85
  123. package/docs/fr/01-routing.md +0 -120
  124. package/docs/fr/02-project-layout.md +0 -84
  125. package/docs/fr/03-static-bake.md +0 -289
  126. package/docs/fr/04-ide-setup.md +0 -162
  127. package/docs/fr/05-why-mado.md +0 -193
  128. package/docs/fr/06-for-backenders.md +0 -438
  129. package/docs/fr/07-llm-pitfalls.md +0 -625
  130. package/docs/fr/08-llm-zero-history-test.md +0 -38
  131. package/docs/fr/09-shadow-vs-light-dom.md +0 -65
  132. package/docs/fr/10-app-architecture.md +0 -138
  133. package/docs/fr/11-layouts.md +0 -47
  134. package/docs/fr/12-auth-and-api.md +0 -76
  135. package/docs/fr/13-deployment.md +0 -57
  136. package/docs/fr/14-testing.md +0 -41
  137. package/docs/fr/15-error-handling.md +0 -50
  138. package/docs/fr/16-bake-cookbook.md +0 -88
  139. package/docs/fr/17-shadow-dom-forms.md +0 -196
  140. package/docs/fr/18-api-freeze-map.md +0 -63
  141. package/docs/fr/19-reactivity-ordering.md +0 -97
  142. package/docs/fr/20-v1-stability.md +0 -88
  143. package/docs/fr/README.md +0 -27
  144. package/docs/ru/00-the-mado-way.md +0 -84
  145. package/docs/ru/01-routing.md +0 -119
  146. package/docs/ru/02-project-layout.md +0 -84
  147. package/docs/ru/03-static-bake.md +0 -250
  148. package/docs/ru/04-ide-setup.md +0 -144
  149. package/docs/ru/05-why-mado.md +0 -193
  150. package/docs/ru/06-for-backenders.md +0 -428
  151. package/docs/ru/07-llm-pitfalls.md +0 -624
  152. package/docs/ru/08-llm-zero-history-test.md +0 -57
  153. package/docs/ru/09-shadow-vs-light-dom.md +0 -63
  154. package/docs/ru/10-app-architecture.md +0 -152
  155. package/docs/ru/11-layouts.md +0 -47
  156. package/docs/ru/12-auth-and-api.md +0 -75
  157. package/docs/ru/13-deployment.md +0 -66
  158. package/docs/ru/14-testing.md +0 -50
  159. package/docs/ru/15-error-handling.md +0 -56
  160. package/docs/ru/16-bake-cookbook.md +0 -95
  161. package/docs/ru/17-shadow-dom-forms.md +0 -193
  162. package/docs/ru/18-api-freeze-map.md +0 -64
  163. package/docs/ru/19-reactivity-ordering.md +0 -95
  164. package/docs/ru/20-v1-stability.md +0 -82
  165. package/docs/ru/README.md +0 -25
  166. package/docs/uk/00-the-mado-way.md +0 -82
  167. package/docs/uk/01-routing.md +0 -76
  168. package/docs/uk/02-project-layout.md +0 -73
  169. package/docs/uk/03-static-bake.md +0 -48
  170. package/docs/uk/04-ide-setup.md +0 -26
  171. package/docs/uk/05-why-mado.md +0 -34
  172. package/docs/uk/06-for-backenders.md +0 -55
  173. package/docs/uk/07-llm-pitfalls.md +0 -145
  174. package/docs/uk/08-llm-zero-history-test.md +0 -34
  175. package/docs/uk/09-shadow-vs-light-dom.md +0 -58
  176. package/docs/uk/10-app-architecture.md +0 -97
  177. package/docs/uk/11-layouts.md +0 -47
  178. package/docs/uk/12-auth-and-api.md +0 -70
  179. package/docs/uk/13-deployment.md +0 -40
  180. package/docs/uk/14-testing.md +0 -34
  181. package/docs/uk/15-error-handling.md +0 -32
  182. package/docs/uk/16-bake-cookbook.md +0 -36
  183. package/docs/uk/17-shadow-dom-forms.md +0 -193
  184. package/docs/uk/18-api-freeze-map.md +0 -61
  185. package/docs/uk/19-reactivity-ordering.md +0 -95
  186. package/docs/uk/20-v1-stability.md +0 -83
  187. package/docs/uk/README.md +0 -27
  188. package/starters/default/src/modules/home/not-found.page.ts +0 -11
  189. /package/docs/en/{15-error-handling.md → 21-error-handling.md} +0 -0
  190. /package/docs/en/{19-reactivity-ordering.md → 31-reactivity-ordering.md} +0 -0
  191. /package/starters/{default → modular}/.editorconfig +0 -0
  192. /package/starters/{default → modular}/eslint.config.mjs +0 -0
  193. /package/starters/{default → modular}/public/favicon.svg +0 -0
  194. /package/starters/{default → modular}/src/modules/auth/_contracts/auth-api.types.ts +0 -0
  195. /package/starters/{default → modular}/src/modules/auth/auth.connector.ts +0 -0
  196. /package/starters/{default → modular}/src/modules/auth/auth.guard.ts +0 -0
  197. /package/starters/{default → modular}/src/modules/auth/auth.public.ts +0 -0
  198. /package/starters/{default → modular}/src/modules/auth/auth.routes.ts +0 -0
  199. /package/starters/{default → modular}/src/modules/auth/auth.service.ts +0 -0
  200. /package/starters/{default → modular}/src/modules/auth/auth.types.ts +0 -0
  201. /package/starters/{default → modular}/src/modules/auth/login.page.ts +0 -0
  202. /package/starters/{default → modular}/src/modules/billing/_contracts/stripe.types.ts +0 -0
  203. /package/starters/{default → modular}/src/modules/billing/api/stripe.connector.ts +0 -0
  204. /package/starters/{default → modular}/src/modules/billing/billing.public.ts +0 -0
  205. /package/starters/{default → modular}/src/modules/billing/billing.routes.ts +0 -0
  206. /package/starters/{default → modular}/src/modules/billing/billing.types.ts +0 -0
  207. /package/starters/{default → modular}/src/modules/billing/components/invoice-status-badge.component.ts +0 -0
  208. /package/starters/{default → modular}/src/modules/billing/data/invoices.resource.ts +0 -0
  209. /package/starters/{default → modular}/src/modules/billing/pages/invoice-detail.page.ts +0 -0
  210. /package/starters/{default → modular}/src/shared/http/http-client.ts +0 -0
  211. /package/starters/{default → modular}/src/shared/http/http-error.ts +0 -0
  212. /package/starters/{default → modular}/src/shared/http/interceptors.ts +0 -0
  213. /package/starters/{default → modular}/src/shared/lib/format-date.ts +0 -0
  214. /package/starters/{default → modular}/src/shared/styles/content.css +0 -0
  215. /package/starters/{default → modular}/src/shared/styles/reset.css +0 -0
  216. /package/starters/{default → modular}/src/shared/styles/shell.css +0 -0
  217. /package/starters/{default → modular}/src/shared/styles/tokens.css +0 -0
  218. /package/starters/{default → modular}/src/shared/ui/x-button.component.ts +0 -0
  219. /package/starters/{default → modular}/src/shared/ui/x-spinner.component.ts +0 -0
@@ -1,40 +0,0 @@
1
- # Deployment
2
-
3
- Одна команда, один artifact:
4
-
5
- ```bash
6
- mado release
7
- ```
8
-
9
- Результат:
10
-
11
- ```txt
12
- out/
13
- ├── index.html
14
- ├── assets/
15
- ├── <route>/index.html
16
- ├── sitemap.xml
17
- ├── _redirects
18
- └── _headers
19
- ```
20
-
21
- `out/` можна деплоїти на nginx, Cloudflare Pages, Netlify, S3/CloudFront або
22
- GitHub Pages.
23
-
24
- ```bash
25
- mado release
26
- mado preview
27
- ```
28
-
29
- `mado preview` показує `out/` як статичний хост: baked HTML має пріоритет,
30
- потім SPA fallback.
31
-
32
- Для VPS + nginx:
33
-
34
- ```bash
35
- mado release
36
- rsync -avz --delete out/ user@server:/var/www/myapp/
37
- ```
38
-
39
- Опційний nginx recipe живе в `docs/recipes/nginx/`: immutable cache для
40
- `/assets/*`, no-cache для HTML та fallback для deep links.
@@ -1,34 +0,0 @@
1
- # Тестування
2
-
3
- Mado — це TypeScript і browser APIs. У репозиторії фреймворка тести йдуть через
4
- Node test runner та `linkedom`.
5
-
6
- ```bash
7
- npm run typecheck
8
- npm run build
9
- npm test
10
- ```
11
-
12
- DOM-тест:
13
-
14
- ```js
15
- import test from "node:test";
16
- import assert from "node:assert/strict";
17
-
18
- const { parseHTML } = await import("linkedom");
19
- const { window } = parseHTML("<!doctype html><html><body></body></html>");
20
- globalThis.window = window;
21
- globalThis.document = window.document;
22
-
23
- const { html, render } = await import("../dist/src/html/template.js");
24
-
25
- test("renders", () => {
26
- const root = document.createElement("div");
27
- render(html`<p>${"hello"}</p>`, root);
28
- assert.equal(root.querySelector("p").textContent, "hello");
29
- });
30
- ```
31
-
32
- Покривай signals/computed/effect, HTML bindings, guards/redirects, scroll/focus,
33
- error boundaries, forms, resources/mutations і CLI-команди `mado release`,
34
- `mado bake`, `mado preview`.
@@ -1,32 +0,0 @@
1
- # Обробка помилок
2
-
3
- Обробляй помилки там, де користувач може відновитися: маршрути, дані, дії
4
- користувача.
5
-
6
- ```ts
7
- export default routes(manifest, {
8
- errorPage: (err) => html`
9
- <main>
10
- <h1>Щось пішло не так</h1>
11
- <pre>${err.message}</pre>
12
- <a data-link href="/">На головну</a>
13
- </main>
14
- `,
15
- });
16
- ```
17
-
18
- `page({ errorView })` має пріоритет над глобальною boundary.
19
-
20
- ```ts
21
- const users = resource(() => "/api/users", jsonFetcher<User[]>());
22
-
23
- html`
24
- ${() => users.error()
25
- ? html`<p role="alert">${users.error()!.message}</p>
26
- <button @click=${users.refresh}>Повторити</button>`
27
- : null}
28
- `;
29
- ```
30
-
31
- Валідація належить `useForm()`, помилки запису — поруч із submit-кнопкою.
32
- Зовнішні browser subscriptions очищай через `ctx.onDispose()`.
@@ -1,36 +0,0 @@
1
- # Bake cookbook
2
-
3
- `mado bake` рендерить вибрані маршрути у статичний HTML. Це для SEO та швидкого
4
- першого рендеру, не SSR з hydration.
5
-
6
- ```ts
7
- export default page({
8
- head: () => ({ title: "Products", description: "Catalog" }),
9
- view: ({ data }) => html`
10
- <main>
11
- <h1>Products</h1>
12
- ${data.products.map((p) => html`<article><h2>${p.name}</h2></article>`)}
13
- </main>
14
- `,
15
- bake: {
16
- paths: () => [{}],
17
- data: async () => ({ products: await api.products() }),
18
- revalidate: 3600,
19
- },
20
- });
21
- ```
22
-
23
- У baked views краще використовувати звичайні масиви (`items.map(...)`).
24
- Runtime-директиви на кшталт `each()` потрібні браузеру.
25
-
26
- ```ts
27
- export const manifest = {
28
- "/": () => import("./pages/home.js"),
29
- "/blog/:slug": () => import("./pages/blog-post.js"),
30
- };
31
- export default routes(manifest);
32
- ```
33
-
34
- `mado release` створює deploy artifact у `out/`. Якщо bake повідомляє про
35
- unsupported value, винеси runtime-only частину в клієнт або заміни її на
36
- серіалізований HTML.
@@ -1,193 +0,0 @@
1
- # Shadow DOM + форми
2
-
3
- Використання `useForm()` з кастомними input-компонентами у Shadow DOM потребує
4
- знання двох поведінок на рівні браузера:
5
-
6
- 1. **Ретаргетинг подій** — події, що спливають із Shadow DOM, мають
7
- `e.target`, перенаправлений на host-елемент. `useForm().onInput` читає
8
- `e.target.name` та `e.target.value`, але host-елемент `<x-input>`
9
- не має цих властивостей нативно.
10
-
11
- 2. **Асоціація з формою** — `<button type="submit">` всередині Shadow Root
12
- НЕ бере участі в алгоритмі form-owner для `<form>` у Light DOM. Клік по ній
13
- не тригерить submit форми.
14
-
15
- Обидва обмеження — на рівні специфікації, не баги Mado. Але фреймворк надає
16
- патерни, що роблять їх безболісними.
17
-
18
- ## Патерн: Proxy-властивості на input-компонентах
19
-
20
- Обгортаючи `<input>` у Shadow DOM компонент, експонуйте `name` та `value`
21
- як DOM-властивості на host, щоб `useForm().onInput` працював після ретаргетингу:
22
-
23
- ```ts
24
- import { component, css, html } from "@madojs/mado";
25
-
26
- component("x-input", ({ host, attr }) => {
27
- const name = attr("name", "");
28
- const type = attr("type", "text");
29
- const value = attr("value", "");
30
-
31
- // Proxy-властивості для сумісності з useForm().
32
- // Після ретаргетингу Shadow DOM e.target з <input> → <x-input>,
33
- // useForm читає e.target.name / e.target.value — ці геттери будують міст.
34
- Object.defineProperty(host, "name", {
35
- get: () => host.getAttribute("name") ?? "",
36
- configurable: true,
37
- });
38
- Object.defineProperty(host, "value", {
39
- get: () => host.shadowRoot?.querySelector("input")?.value ?? "",
40
- set: (v: string) => {
41
- const input = host.shadowRoot?.querySelector("input");
42
- if (input) input.value = v;
43
- },
44
- configurable: true,
45
- });
46
-
47
- return () => html`<input name=${name} type=${type} .value=${value} />`;
48
- });
49
- ```
50
-
51
- Подія `input` від внутрішнього `<input>` має `composed: true` за замовчуванням,
52
- тому вона спливає через межу shadow. Після ретаргетингу `e.target` —
53
- це `<x-input>`, але тепер у нього є геттери `.name` та `.value` → `useForm`
54
- працює.
55
-
56
- ## Патерн: Submit форми з Shadow DOM кнопок
57
-
58
- `<button type="submit">` всередині Shadow DOM не може тригерити submit `<form>`
59
- у Light DOM. Міст через `requestSubmit()`:
60
-
61
- ```ts
62
- import { component, css, html } from "@madojs/mado";
63
-
64
- component("x-button", ({ host, attr }) => {
65
- const disabled = attr("disabled");
66
-
67
- const handleClick = () => {
68
- const typeAttr = host.getAttribute("type");
69
- if (typeAttr === "button" || typeAttr === "reset") return;
70
- const form = host.closest("form");
71
- if (form && !host.hasAttribute("disabled")) form.requestSubmit();
72
- };
73
-
74
- return () => html`
75
- <button ?disabled=${() => disabled() !== ""} @click=${handleClick}>
76
- <slot></slot>
77
- </button>
78
- `;
79
- });
80
- ```
81
-
82
- `host.closest("form")` працює, бо сам host-елемент живе в Light DOM
83
- (тільки його внутрішні елементи у тіні). `requestSubmit()` тригерить валідацію
84
- та подію `submit` точно так, ніби користувач клікнув нативну submit-кнопку
85
- всередині форми.
86
-
87
- ## Патерн: Реактивні атрибути через ctx.attr()
88
-
89
- З версії 0.7 `ctx.attr(name, defaultValue?)` повертає `Signal<string>`,
90
- який автоматично оновлюється при зміні атрибута на host. Ніякого
91
- `MutationObserver` бойлерплейту:
92
-
93
- ```ts
94
- component("x-badge", ({ attr }) => {
95
- const variant = attr("variant", "default"); // Signal<string>
96
-
97
- return () =>
98
- html`<span class=${() => `badge badge-${variant()}`}>
99
- <slot></slot>
100
- </span>`;
101
- });
102
- ```
103
-
104
- Батько може використовувати `?disabled=${() => !form.isValid()}` (boolean атрибут)
105
- або `.variant=${"danger"}` — компонент перерендерюється реактивно в обох випадках.
106
-
107
- ## Повний приклад форми
108
-
109
- ```ts
110
- import { page, html, useForm, navigate } from "@madojs/mado";
111
- import "../components/x-input.js";
112
- import "../components/x-button.js";
113
-
114
- export default page({
115
- title: "Вхід",
116
- view: () => {
117
- const form = useForm({
118
- email: { required: true, type: "email" },
119
- password: { required: true, minLength: 6 },
120
- });
121
-
122
- const handleLogin = async (values) => {
123
- await api("/auth/login", { method: "POST", json: values });
124
- navigate("/admin");
125
- };
126
-
127
- return html`
128
- <form @submit=${form.onSubmit(handleLogin)}>
129
- <x-input
130
- name="email"
131
- type="email"
132
- label="Email"
133
- required
134
- @input=${form.onInput}
135
- @blur=${form.onBlur}
136
- ></x-input>
137
- ${() =>
138
- form.errors().email
139
- ? html`<small class="err">${form.errors().email}</small>`
140
- : null}
141
-
142
- <x-input
143
- name="password"
144
- type="password"
145
- label="Пароль"
146
- required
147
- @input=${form.onInput}
148
- @blur=${form.onBlur}
149
- ></x-input>
150
-
151
- <x-button type="submit" ?disabled=${() => !form.isValid()}>
152
- Увійти
153
- </x-button>
154
- </form>
155
- `;
156
- },
157
- });
158
- ```
159
-
160
- ## Коли використовувати Light DOM
161
-
162
- Якщо ваш input-компонент — це просто стилізована обгортка без потреби в
163
- інкапсуляції, `shadow: false` уникає обох проблем (ретаргетинг та
164
- form-association):
165
-
166
- ```ts
167
- component(
168
- "x-field",
169
- ({ attr }) => {
170
- const label = attr("label", "");
171
- return () => html`
172
- <label>
173
- <span>${label}</span>
174
- <slot></slot>
175
- </label>
176
- `;
177
- },
178
- { shadow: false },
179
- );
180
- ```
181
-
182
- З Light DOM нативний `<input>` — частина дерева документа, події не
183
- ретаргетуються, і submit форми працює нативно. Компроміс: стилі не
184
- інкапсульовані (треба скоупити самостійно).
185
-
186
- ## Підсумок
187
-
188
- | Задача | Рішення Shadow DOM | Альтернатива Light DOM |
189
- | --------------------------- | -------------------------------------- | -------------------------- |
190
- | `useForm` + кастомний input | Proxy `name`/`value` на host | Нативний `<input>` у slot |
191
- | Submit форми | `form.requestSubmit()` у click handler | Нативна кнопка працює |
192
- | Реактивні атрибути | `ctx.attr()` → авто-сигнал | `ctx.attr()` працює скрізь |
193
- | Інкапсуляція стилів | Так (автоматично) | Ручний `@scope` або BEM |
@@ -1,61 +0,0 @@
1
- # Карта замороження API
2
-
3
- > Що є публічним, що внутрішнім, і що SemVer захищатиме у v1.
4
-
5
- Контракт Mado v1 навмисно невеликий. Код застосунку імпортує API з кореня
6
- пакета:
7
-
8
- ```ts
9
- import { component, html, resource, routes, signal } from "@madojs/mado";
10
- ```
11
-
12
- Єдиний публічний subpath — side-effect модуль devtools:
13
-
14
- ```ts
15
- import "@madojs/mado/devtools.js";
16
- ```
17
-
18
- Усе інше під `dist/src/` — деталь реалізації, навіть якщо файл видно в
19
- репозиторії.
20
-
21
- ## Стабільний публічний API
22
-
23
- Ці імена публічні й захищаються SemVer після v1:
24
-
25
- - Reactivity: `signal`, `computed`, `effect`, `untracked`, `batch`,
26
- `flushSync`.
27
- - Templates і directives: `html`, `render`, `each`, `list`, `unsafeHTML`,
28
- `ref`, `classMap`, `styleMap`.
29
- - Components і CSS: `component`, `css`, `cssVars`.
30
- - Routing і pages: `routes`, `router`, `page`, `layout`,
31
- `navigate`, `queryParam`, `prefetchPath`.
32
- - Data: `resource`, `mutation`, `invalidate`, `jsonFetcher`, `HttpError`.
33
- - Forms: `useForm`.
34
- - Head і persistence: `applyHead`, `persisted`.
35
- - Context: `createContext`, `provide`, `inject`.
36
- - Advanced lifecycle helpers: `createLifecycle`, `runInLifecycle`,
37
- `getCurrentLifecycle`.
38
- - Публічні TypeScript-типи, експортовані з `@madojs/mado`.
39
-
40
- ## Внутрішнє або нестабільне
41
-
42
- Це не публічний API:
43
-
44
- - Package subpaths крім `@madojs/mado` і `@madojs/mado/devtools.js`.
45
- - Internals parser/binding: `html/parser.js`, `html/bindings.js`,
46
- `ChildState`, `EachEntry`.
47
- - Internals router: `router/match.js`, `router/navigation.js`,
48
- `router/manifest.js`.
49
- - Diagnostics internals і всі `_testHooks`.
50
- - Точний текст bundle, назви chunks і внутрішня структура файлів.
51
-
52
- Тести репозиторію можуть імпортувати internal files через відносні шляхи
53
- `dist/`. Код застосунків не повинен цього робити.
54
-
55
- ## Що може змінюватися
56
-
57
- Patch і minor releases можуть додавати root exports, options, diagnostics, docs
58
- або starter files. Вони також можуть змінювати internals, форму bundle і деталі
59
- реалізації, якщо stable API та задокументована поведінка лишаються сумісними.
60
-
61
- Breaking changes стабільного API потребують major version.
@@ -1,95 +0,0 @@
1
- # Порядок reactivity
2
-
3
- > Малий набір ordering-гарантій, які Mado вважає публічною поведінкою.
4
-
5
- Reactivity у Mado синхронна для читання і планована для side effects. Мета —
6
- передбачувані UI updates без великої scheduling-моделі.
7
-
8
- ## Signals
9
-
10
- `signal(value)` повертає getter-функцію. `set(next)` змінює значення одразу,
11
- якщо `Object.is(previous, next)` не дорівнює `true`.
12
-
13
- ```ts
14
- const count = signal(0);
15
- count.set(1);
16
- count(); // 1, одразу
17
- ```
18
-
19
- Computed values позначаються до запуску effects, тому effect, який читає
20
- computed, бачить актуальні dependencies, а не застарілий cache.
21
-
22
- ## Effects
23
-
24
- `effect(fn)` запускається один раз одразу. Подальші зміни dependencies планують
25
- один запуск effect у microtask. Тести можуть викликати `flushSync()`, щоб
26
- синхронно очистити чергу.
27
-
28
- Якщо effect повертає cleanup-функцію, Mado запускає її перед наступним запуском
29
- effect і ще раз при виклику disposer.
30
-
31
- ```ts
32
- const stop = effect(() => {
33
- const id = setInterval(tick, 1000);
34
- return () => clearInterval(id);
35
- });
36
-
37
- stop();
38
- ```
39
-
40
- У компонентах і pages для unmount cleanup надавайте перевагу
41
- `ctx.onDispose()` / page `onDispose()`. Cleanup effect — це cleanup між runs.
42
-
43
- ## Batch
44
-
45
- `batch(fn)` групує записи signals в один subscriber pass. Effects не
46
- запускаються до виходу з найзовнішнього batch, включно з вкладеними batches.
47
-
48
- ```ts
49
- batch(() => {
50
- first.set("Ada");
51
- batch(() => last.set("Lovelace"));
52
- });
53
- // effects бачать тільки фінальну пару
54
- ```
55
-
56
- Спостережувані `computed({ equals })` також зберігають batch atomicity: вони
57
- перераховуються один раз після зовнішнього batch на повністю застосованому
58
- state. Вони не повинні бачити напівзастосований batch на кшталт
59
- `(new x, old y)`.
60
-
61
- ## DOM updates
62
-
63
- `render(result, container)` перевикористовує наявний template instance, коли
64
- наступний render має ті самі template strings. Для child bindings, що
65
- повертають вкладений `html```, діє те саме правило: ті самі strings оновлюються
66
- на місці, інші strings перебудовують гілку.
67
-
68
- Це означає, що непов'язані зміни signals не пересоздають `<input>` у
69
- стабільному вкладеному template, тому focus, DOM state і listeners
70
- зберігаються.
71
-
72
- Списки повинні використовувати `each(items, key, renderItem)`. Keys задають DOM
73
- identity. Duplicate keys попереджають у development і отримують positional
74
- suffix, щоб кожен item все одно рендерився, але duplicate keys — це data bug.
75
-
76
- ## Teardown компонентів
77
-
78
- Custom elements можуть отримати `disconnectedCallback()`, а потім
79
- `connectedCallback()` під час same-tick move. Mado відкладає teardown компонента
80
- до microtask і скасовує його при reconnect, тому keyed reorders зберігають
81
- state компонента. Справжнє видалення все одно запускає lifecycle cleanup на
82
- наступній microtask.
83
-
84
- ## Не гарантується
85
-
86
- Mado не гарантує точну кількість internal scheduler microtasks, порядок
87
- незалежних effects без спільних dependencies, форму generated bundle або
88
- внутрішній module layout. Це implementation details.
89
-
90
- Invariant tests для цього контракту:
91
-
92
- - `test/reactivity-ordering.test.mjs`
93
- - `test/signal-batch-equals.test.mjs`
94
- - `test/update-nested-reuse.test.mjs`
95
- - `test/each-component-state.test.mjs`
@@ -1,83 +0,0 @@
1
- # Стабільність v1
2
-
3
- > Що Mado обіцяє після v1, і що лишається вільним для розвитку.
4
-
5
- Mado v1 означає, що публічний app-facing contract достатньо стабільний для
6
- реальних business apps. Це не означає, що кожен internal file, generated byte,
7
- starter copy або diagnostic string заморожені назавжди.
8
-
9
- Читайте разом із:
10
-
11
- - [Карта замороження API](./18-api-freeze-map.md)
12
- - [Порядок reactivity](./19-reactivity-ordering.md)
13
-
14
- ## Стабільно під SemVer
15
-
16
- Після v1 Mado вважає SemVer-protected:
17
-
18
- - Public exports з `@madojs/mado`.
19
- - Public TypeScript types з `@madojs/mado`.
20
- - Side-effect subpath `@madojs/mado/devtools.js`.
21
- - Template binding syntax: child `${}`, `@event`, `.prop`, `?boolean`,
22
- attribute bindings, directives і `each()`.
23
- - Signal semantics, описані в reactivity ordering guide.
24
- - Component lifecycle semantics: setup один раз за connection lifetime,
25
- deferred teardown для same-tick moves, cleanup через `ctx.onDispose`.
26
- - Router/page/resource/form contracts, описані в English docs.
27
- - Імена CLI commands і широкий сенс команд (`build`, `dev`, `release`, `bake`,
28
- `preview`, `init`, `new`).
29
-
30
- Ламати це можна тільки в major version.
31
-
32
- ## Дозволено в minor releases
33
-
34
- Minor releases можуть додавати:
35
-
36
- - New root exports.
37
- - New options на наявних API.
38
- - New diagnostics і warnings.
39
- - New starters, examples, docs і CLI flags.
40
- - Performance improvements і internal implementation rewrites.
41
-
42
- Minor release не має вимагати змін у вже коректних apps.
43
-
44
- ## Дозволено в patch releases
45
-
46
- Patch releases можуть виправляти bugs, посилювати diagnostics, покращувати docs
47
- і робити сумісні implementation changes. Patch може змінити timing тільки якщо
48
- старий timing був незадокументованим bug і зміна зберігає reactivity ordering
49
- contract.
50
-
51
- ## Нестабільно
52
-
53
- Це навмисно не захищено SemVer:
54
-
55
- - Internal package subpaths крім `@madojs/mado/devtools.js`.
56
- - Файли під `src/`, `dist/src/` і implementation module boundaries.
57
- - `_testHooks`, diagnostics internals і warning codes.
58
- - Точний generated JavaScript text, chunk names, sourcemap content і bundle
59
- byte layout.
60
- - Internal parser, binding, router і resource cache data structures.
61
- - Starter app visual copy і demo data.
62
-
63
- Apps не повинні імпортувати internal files або перевіряти точний bundle output.
64
-
65
- ## Bundle і release output
66
-
67
- Mado триматиме size budget і deterministic release tests, але v1 stability не
68
- заморожує byte-for-byte bundler output. Hashes, chunk boundaries і generated
69
- asset names можуть змінюватися, якщо задокументований deployment contract
70
- продовжує працювати.
71
-
72
- ## Якщо release вас зламав
73
-
74
- Якщо update ламає код, який використовує тільки public exports і
75
- задокументовану поведінку, вважайте це bug. Відкрийте issue з:
76
-
77
- - версією Mado до і після;
78
- - задіяним public API;
79
- - мінімальною репродукцією;
80
- - чи це runtime behaviour, TypeScript types, CLI output або docs.
81
-
82
- Якщо поломка залежить від internal subpath або точного generated output, про це
83
- все одно варто повідомити, але це не вважається SemVer break.
package/docs/uk/README.md DELETED
@@ -1,27 +0,0 @@
1
- # Документація Mado — Українська
2
-
3
- Український комплект документації.
4
-
5
- | Розділ | Файл |
6
- | ----------------------- | ------------------------------------------------------------ |
7
- | Шлях Mado | [00-the-mado-way.md](./00-the-mado-way.md) |
8
- | Маршрутизація | [01-routing.md](./01-routing.md) |
9
- | Структура проєкту | [02-project-layout.md](./02-project-layout.md) |
10
- | Статичний bake & SEO | [03-static-bake.md](./03-static-bake.md) |
11
- | Налаштування IDE | [04-ide-setup.md](./04-ide-setup.md) |
12
- | Чому Mado | [05-why-mado.md](./05-why-mado.md) |
13
- | Для бекендерів | [06-for-backenders.md](./06-for-backenders.md) |
14
- | Типові помилки LLM | [07-llm-pitfalls.md](./07-llm-pitfalls.md) |
15
- | LLM zero-history тест | [08-llm-zero-history-test.md](./08-llm-zero-history-test.md) |
16
- | Shadow DOM vs Light DOM | [09-shadow-vs-light-dom.md](./09-shadow-vs-light-dom.md) |
17
- | Архітектура застосунку | [10-app-architecture.md](./10-app-architecture.md) |
18
- | Макети (layouts) | [11-layouts.md](./11-layouts.md) |
19
- | Auth та API | [12-auth-and-api.md](./12-auth-and-api.md) |
20
- | Розгортання | [13-deployment.md](./13-deployment.md) |
21
- | Тестування | [14-testing.md](./14-testing.md) |
22
- | Обробка помилок | [15-error-handling.md](./15-error-handling.md) |
23
- | Рецепти bake | [16-bake-cookbook.md](./16-bake-cookbook.md) |
24
- | Shadow DOM + форми | [17-shadow-dom-forms.md](./17-shadow-dom-forms.md) |
25
- | Карта замороження API | [18-api-freeze-map.md](./18-api-freeze-map.md) |
26
- | Порядок reactivity | [19-reactivity-ordering.md](./19-reactivity-ordering.md) |
27
- | Стабільність v1 | [20-v1-stability.md](./20-v1-stability.md) |
@@ -1,11 +0,0 @@
1
- import { html, page } from "@madojs/mado";
2
-
3
- export default page({
4
- title: "Not Found",
5
- view: () => html`
6
- <section>
7
- <h1>404</h1>
8
- <p>This route does not exist. <a href="/">Go home</a>.</p>
9
- </section>
10
- `,
11
- });
File without changes
File without changes