@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,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,64 +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
- Публичные subpaths — side-effect модуль devtools и Vite integration:
13
-
14
- ```ts
15
- import "@madojs/mado/devtools.js";
16
- import { mado } from "@madojs/mado/vite";
17
- ```
18
-
19
- Все остальное под `dist/src/` — деталь реализации, даже если файл виден в
20
- репозитории.
21
-
22
- ## Стабильный публичный API
23
-
24
- Эти имена публичны и будут защищены SemVer после v1:
25
-
26
- - Reactivity: `signal`, `computed`, `effect`, `untracked`, `batch`,
27
- `flushSync`.
28
- - Templates и directives: `html`, `render`, `each`, `list`, `unsafeHTML`,
29
- `ref`, `classMap`, `styleMap`.
30
- - Components и CSS: `component`, `css`, `cssVars`.
31
- - Routing и pages: `routes`, `router`, `page`, `layout`,
32
- `navigate`, `queryParam`, `prefetchPath`.
33
- - Data: `resource`, `mutation`, `invalidate`, `jsonFetcher`, `HttpError`.
34
- - Forms: `useForm`.
35
- - Head и persistence: `applyHead`, `persisted`.
36
- - Context: `createContext`, `provide`, `inject`.
37
- - Advanced lifecycle helpers: `createLifecycle`, `runInLifecycle`,
38
- `getCurrentLifecycle`.
39
- - Публичные TypeScript-типы, экспортируемые из `@madojs/mado`.
40
-
41
- ## Внутреннее или нестабильное
42
-
43
- Это не публичный API:
44
-
45
- - Package subpaths кроме `@madojs/mado`, `@madojs/mado/devtools.js` и
46
- `@madojs/mado/vite`.
47
- - Internals парсера/биндингов: `html/parser.js`, `html/bindings.js`,
48
- `ChildState`, `EachEntry`.
49
- - Internals роутера: `router/match.js`, `router/navigation.js`,
50
- `router/manifest.js`.
51
- - Diagnostics internals и все `_testHooks`.
52
- - Точный текст bundle, имена chunks и внутренняя структура файлов.
53
-
54
- Тесты репозитория могут импортировать внутренние файлы через относительные
55
- пути `dist/`. Код приложений так делать не должен.
56
-
57
- ## Что может меняться
58
-
59
- Patch и minor релизы могут добавлять root exports, опции, diagnostics, docs или
60
- starter files. Они также могут менять internals, форму bundle и детали
61
- реализации, если стабильный API и задокументированное поведение остаются
62
- совместимыми.
63
-
64
- Ломающие изменения стабильного API требуют major version.
@@ -1,95 +0,0 @@
1
- # Порядок reactivity
2
-
3
- > Небольшой набор ordering-гарантий, которые Mado считает публичным поведением.
4
-
5
- Reactivity в Mado синхронна для чтения и планирует side effects. Цель —
6
- предсказуемые UI-обновления без большой 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` помечается до запуска effects, поэтому effect, читающий computed,
20
- видит актуальные dependencies, а не старый кеш.
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 предпочитайте `ctx.onDispose()` /
41
- page `onDispose()`. Cleanup effect — это cleanup между запусками.
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 на полностью примененном state.
58
- Они не должны видеть половинчатый batch вроде `(new x, old y)`.
59
-
60
- ## DOM updates
61
-
62
- `render(result, container)` переиспользует существующий template instance, когда
63
- следующий render имеет те же template strings. Для child bindings, возвращающих
64
- вложенный `html```, действует то же правило: те же strings обновляются на
65
- месте, другие strings пересобирают ветку.
66
-
67
- Это значит, что несвязанные изменения signals не пересоздают `<input>` внутри
68
- стабильного вложенного template, поэтому focus, DOM state и listeners
69
- сохраняются.
70
-
71
- Списки должны использовать `each(items, key, renderItem)`. Keys задают DOM
72
- identity. Duplicate keys предупреждают в development и получают positional
73
- suffix, чтобы все элементы все равно отрендерились, но duplicate keys — это
74
- ошибка данных.
75
-
76
- ## Teardown компонентов
77
-
78
- Custom elements могут получить `disconnectedCallback()`, а затем
79
- `connectedCallback()` во время same-tick move. Mado откладывает teardown
80
- компонента до microtask и отменяет его при reconnect, поэтому keyed reorders
81
- сохраняют state компонента. Настоящее удаление все равно запускает cleanup на
82
- следующей microtask.
83
-
84
- ## Не гарантируется
85
-
86
- Mado не гарантирует точное число внутренних scheduler microtasks, порядок
87
- независимых effects без общих dependencies, форму generated bundle или
88
- внутреннюю структуру modules. Это детали реализации.
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,82 +0,0 @@
1
- # Стабильность v1
2
-
3
- > Что Mado обещает после v1, и что остается свободным для развития.
4
-
5
- Mado v1 означает, что публичный app-facing contract достаточно стабилен для
6
- реальных business apps. Это не значит, что каждый внутренний файл, generated
7
- byte, 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`,
28
- `bake`, `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 и внутренние 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 и новое поведение сохраняет
49
- reactivity ordering 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
- - Точный JavaScript output, chunk names, sourcemap content и bundle byte layout.
59
- - Internal parser, binding, router и resource cache data structures.
60
- - Visual copy и demo data в starters.
61
-
62
- Apps не должны импортировать internal files или проверять точный bundle output.
63
-
64
- ## Bundle и release output
65
-
66
- Mado держит size budget и deterministic release tests, но v1 stability не
67
- замораживает byte-for-byte bundler output. Hashes, chunk boundaries и asset
68
- names могут меняться, если задокументированный deployment contract продолжает
69
- работать.
70
-
71
- ## Если релиз вас сломал
72
-
73
- Если update ломает код, который использует только public exports и
74
- задокументированное поведение, считайте это bug. Откройте issue и укажите:
75
-
76
- - версию Mado до и после;
77
- - задействованный public API;
78
- - минимальную репродукцию;
79
- - это runtime behaviour, TypeScript types, CLI output или docs.
80
-
81
- Если поломка зависит от internal subpath или точного generated output, ее все
82
- равно можно зарепортить, но это не считается SemVer break.
package/docs/ru/README.md DELETED
@@ -1,25 +0,0 @@
1
- # Mado docs — Русский
2
-
3
- | Раздел | Файл |
4
- | --------------------------- | ------------------------------------------------------------ |
5
- | Принципы | [00-the-mado-way.md](./00-the-mado-way.md) |
6
- | Маршрутизация | [01-routing.md](./01-routing.md) |
7
- | Структура проекта | [02-project-layout.md](./02-project-layout.md) |
8
- | Статический bake и SEO | [03-static-bake.md](./03-static-bake.md) |
9
- | Настройка IDE | [04-ide-setup.md](./04-ide-setup.md) |
10
- | Почему Mado | [05-why-mado.md](./05-why-mado.md) |
11
- | Для бекендеров | [06-for-backenders.md](./06-for-backenders.md) |
12
- | Типичные ошибки LLM | [07-llm-pitfalls.md](./07-llm-pitfalls.md) |
13
- | Тест LLM с нулевой историей | [08-llm-zero-history-test.md](./08-llm-zero-history-test.md) |
14
- | Shadow DOM vs Light DOM | [09-shadow-vs-light-dom.md](./09-shadow-vs-light-dom.md) |
15
- | Архитектура приложения | [10-app-architecture.md](./10-app-architecture.md) |
16
- | Макеты (layouts) | [11-layouts.md](./11-layouts.md) |
17
- | Авторизация и API | [12-auth-and-api.md](./12-auth-and-api.md) |
18
- | Развёртывание | [13-deployment.md](./13-deployment.md) |
19
- | Тестирование | [14-testing.md](./14-testing.md) |
20
- | Обработка ошибок | [15-error-handling.md](./15-error-handling.md) |
21
- | Рецепты bake | [16-bake-cookbook.md](./16-bake-cookbook.md) |
22
- | Shadow DOM + формы | [17-shadow-dom-forms.md](./17-shadow-dom-forms.md) |
23
- | Карта заморозки API | [18-api-freeze-map.md](./18-api-freeze-map.md) |
24
- | Порядок reactivity | [19-reactivity-ordering.md](./19-reactivity-ordering.md) |
25
- | Стабильность v1 | [20-v1-stability.md](./20-v1-stability.md) |
@@ -1,82 +0,0 @@
1
- # Шлях Mado
2
-
3
- > Один зрозумілий шлях. Жорсткі контракти. Мінімум магії.
4
-
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
24
- src/
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
- });
52
- ```
53
-
54
- Importing the component file registers the element. Import it where the tag is
55
- used.
56
-
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
- ```
70
-
71
- Page-local signals, resources and forms live inside `view()`. Module-wide state
72
- lives in `*.service.ts`.
73
-
74
- ## What We Do Not Do
75
-
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.
81
-
82
- When in doubt, document one clear recipe instead of adding a new core primitive.
@@ -1,76 +0,0 @@
1
- # Routing
2
-
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`.
7
-
8
- ```ts
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";
13
-
14
- export const manifest = {
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"),
26
- };
27
-
28
- export default routes(manifest);
29
- ```
30
-
31
- Export `manifest` so `mado bake` can read it.
32
-
33
- ## Module Routes
34
-
35
- Modules export plain route maps. They do not call `layout()`.
36
-
37
- ```ts
38
- export const billingRoutes = {
39
- "/invoices": () => import("./pages/invoices-list.page.js"),
40
- "/invoices/:id": () => import("./pages/invoice-detail.page.js"),
41
- };
42
- ```
43
-
44
- The prefix is applied by `src/app.routes.ts`.
45
-
46
- ## Page
47
-
48
- ```ts
49
- import { html, page } from "@madojs/mado";
50
-
51
- export default page<{ id: string }>({
52
- title: ({ id }) => `User ${id}`,
53
- view: ({ params }) => html`<h1>${params.id}</h1>`,
54
- });
55
- ```
56
-
57
- ## Navigation
58
-
59
- ```ts
60
- import appRoutes from "./app.routes.js";
61
-
62
- appRoutes.navigate("/billing/invoices");
63
- appRoutes.navigate("/billing/invoices?page=2");
64
- appRoutes.navigate("/login", { replace: true });
65
- ```
66
-
67
- ## Query Params
68
-
69
- ```ts
70
- import { queryParam } from "@madojs/mado";
71
-
72
- const search = queryParam("q", "");
73
- search.set("mado");
74
- ```
75
-
76
- `queryParam()` returns a signal-like API and syncs state with the URL.