@madojs/mado 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/AGENTS.md +24 -26
  2. package/CHANGELOG.md +98 -0
  3. package/README.md +18 -45
  4. package/TODO.md +52 -48
  5. package/dist/src/component.d.ts +2 -1
  6. package/dist/src/component.js +5 -2
  7. package/dist/src/component.js.map +1 -1
  8. package/dist/src/each.d.ts +1 -1
  9. package/dist/src/each.js +1 -1
  10. package/dist/src/each.js.map +1 -1
  11. package/dist/src/html/template.js +10 -0
  12. package/dist/src/html/template.js.map +1 -1
  13. package/dist/src/index.d.ts +11 -6
  14. package/dist/src/index.js +5 -3
  15. package/dist/src/index.js.map +1 -1
  16. package/dist/src/lazy.d.ts +1 -1
  17. package/dist/src/lazy.js +1 -1
  18. package/dist/src/lazy.js.map +1 -1
  19. package/dist/src/page.d.ts +17 -21
  20. package/dist/src/page.js +7 -12
  21. package/dist/src/page.js.map +1 -1
  22. package/dist/src/router/manifest.d.ts +1 -1
  23. package/dist/src/router/manifest.js +21 -13
  24. package/dist/src/router/manifest.js.map +1 -1
  25. package/dist/src/router/match.d.ts +2 -2
  26. package/dist/src/router/match.js +3 -3
  27. package/dist/src/router/match.js.map +1 -1
  28. package/dist/src/router/navigation.js +1 -1
  29. package/dist/src/router/navigation.js.map +1 -1
  30. package/dist/src/vite/index.d.ts +10 -0
  31. package/dist/src/vite/index.js +33 -0
  32. package/dist/src/vite/index.js.map +1 -0
  33. package/docs/en/00-the-mado-way.md +25 -12
  34. package/docs/en/01-routing.md +90 -142
  35. package/docs/en/02-project-layout.md +59 -52
  36. package/docs/en/03-static-bake.md +5 -6
  37. package/docs/en/05-why-mado.md +6 -6
  38. package/docs/en/06-for-backenders.md +18 -22
  39. package/docs/en/08-llm-zero-history-test.md +9 -14
  40. package/docs/en/09-shadow-vs-light-dom.md +28 -36
  41. package/docs/en/10-app-architecture.md +158 -96
  42. package/docs/en/11-layouts.md +22 -24
  43. package/docs/en/12-auth-and-api.md +89 -182
  44. package/docs/en/13-deployment.md +25 -26
  45. package/docs/en/14-testing.md +4 -4
  46. package/docs/en/16-bake-cookbook.md +17 -10
  47. package/docs/en/18-api-freeze-map.md +6 -4
  48. package/docs/en/20-v1-stability.md +1 -1
  49. package/docs/fr/00-the-mado-way.md +55 -90
  50. package/docs/fr/01-routing.md +70 -152
  51. package/docs/fr/02-project-layout.md +74 -48
  52. package/docs/fr/03-static-bake.md +1 -1
  53. package/docs/fr/05-why-mado.md +6 -6
  54. package/docs/fr/06-for-backenders.md +7 -7
  55. package/docs/fr/08-llm-zero-history-test.md +21 -48
  56. package/docs/fr/09-shadow-vs-light-dom.md +43 -162
  57. package/docs/fr/10-app-architecture.md +110 -33
  58. package/docs/fr/11-layouts.md +24 -12
  59. package/docs/fr/12-auth-and-api.md +63 -22
  60. package/docs/fr/13-deployment.md +30 -12
  61. package/docs/fr/14-testing.md +1 -1
  62. package/docs/fr/16-bake-cookbook.md +57 -4
  63. package/docs/fr/18-api-freeze-map.md +1 -1
  64. package/docs/fr/20-v1-stability.md +1 -1
  65. package/docs/recipes/nginx/README.md +13 -0
  66. package/docs/ru/00-the-mado-way.md +53 -75
  67. package/docs/ru/01-routing.md +68 -143
  68. package/docs/ru/02-project-layout.md +75 -48
  69. package/docs/ru/03-static-bake.md +2 -2
  70. package/docs/ru/05-why-mado.md +6 -6
  71. package/docs/ru/06-for-backenders.md +7 -7
  72. package/docs/ru/08-llm-zero-history-test.md +9 -14
  73. package/docs/ru/09-shadow-vs-light-dom.md +43 -178
  74. package/docs/ru/10-app-architecture.md +115 -63
  75. package/docs/ru/11-layouts.md +24 -24
  76. package/docs/ru/12-auth-and-api.md +57 -35
  77. package/docs/ru/13-deployment.md +19 -13
  78. package/docs/ru/14-testing.md +1 -1
  79. package/docs/ru/16-bake-cookbook.md +48 -8
  80. package/docs/ru/18-api-freeze-map.md +5 -3
  81. package/docs/ru/20-v1-stability.md +1 -1
  82. package/docs/uk/00-the-mado-way.md +70 -44
  83. package/docs/uk/01-routing.md +41 -47
  84. package/docs/uk/02-project-layout.md +68 -41
  85. package/docs/uk/03-static-bake.md +1 -2
  86. package/docs/uk/06-for-backenders.md +3 -3
  87. package/docs/uk/08-llm-zero-history-test.md +22 -24
  88. package/docs/uk/09-shadow-vs-light-dom.md +37 -86
  89. package/docs/uk/10-app-architecture.md +72 -31
  90. package/docs/uk/11-layouts.md +25 -12
  91. package/docs/uk/12-auth-and-api.md +58 -22
  92. package/docs/uk/13-deployment.md +4 -3
  93. package/docs/uk/14-testing.md +1 -1
  94. package/docs/uk/18-api-freeze-map.md +1 -1
  95. package/docs/uk/20-v1-stability.md +1 -1
  96. package/llms.txt +14 -15
  97. package/package.json +18 -11
  98. package/scripts/_config.mjs +15 -161
  99. package/scripts/bake.mjs +71 -58
  100. package/scripts/cli/generate.mjs +348 -0
  101. package/scripts/cli/help.mjs +27 -0
  102. package/scripts/cli/index.mjs +79 -0
  103. package/scripts/cli/init.mjs +153 -0
  104. package/scripts/cli/release.mjs +152 -0
  105. package/scripts/cli/run.mjs +96 -0
  106. package/scripts/cli.mjs +2 -560
  107. package/scripts/package-smoke.mjs +4 -1
  108. package/scripts/preview.mjs +17 -61
  109. package/scripts/size-budget.mjs +5 -2
  110. package/scripts/vite.default.mjs +11 -0
  111. package/starters/default/.editorconfig +12 -0
  112. package/starters/default/README.md +74 -0
  113. package/starters/default/eslint.config.mjs +256 -0
  114. package/starters/default/index.html +13 -0
  115. package/starters/default/package.json +30 -0
  116. package/starters/default/public/favicon.svg +4 -0
  117. package/starters/default/src/app.routes.ts +39 -0
  118. package/starters/default/src/layouts/app-shell.layout.ts +35 -0
  119. package/starters/default/src/layouts/auth-shell.layout.ts +17 -0
  120. package/starters/default/src/main.ts +16 -0
  121. package/starters/default/src/modules/auth/_contracts/auth-api.types.ts +17 -0
  122. package/starters/default/src/modules/auth/auth.connector.ts +45 -0
  123. package/starters/default/src/modules/auth/auth.guard.ts +22 -0
  124. package/starters/default/src/modules/auth/auth.public.ts +9 -0
  125. package/starters/default/src/modules/auth/auth.routes.ts +8 -0
  126. package/starters/default/src/modules/auth/auth.service.ts +71 -0
  127. package/starters/default/src/modules/auth/auth.types.ts +15 -0
  128. package/starters/default/src/modules/auth/login.page.ts +62 -0
  129. package/starters/default/src/modules/billing/_contracts/stripe.types.ts +17 -0
  130. package/starters/default/src/modules/billing/api/stripe.connector.ts +71 -0
  131. package/starters/default/src/modules/billing/billing.public.ts +5 -0
  132. package/starters/default/src/modules/billing/billing.routes.ts +9 -0
  133. package/starters/default/src/modules/billing/billing.types.ts +15 -0
  134. package/starters/default/src/modules/billing/components/invoice-status-badge.component.ts +43 -0
  135. package/starters/default/src/modules/billing/data/invoices.resource.ts +35 -0
  136. package/starters/default/src/modules/billing/pages/invoice-detail.page.ts +70 -0
  137. package/starters/default/src/modules/billing/pages/invoices-list.page.ts +73 -0
  138. package/starters/default/src/modules/home/home.page.ts +34 -0
  139. package/starters/default/src/modules/home/not-found.page.ts +11 -0
  140. package/starters/default/src/shared/http/http-client.ts +86 -0
  141. package/starters/default/src/shared/http/http-error.ts +37 -0
  142. package/starters/default/src/shared/http/interceptors.ts +59 -0
  143. package/starters/default/src/shared/lib/format-date.ts +19 -0
  144. package/starters/default/src/shared/styles/content.css +70 -0
  145. package/starters/default/src/shared/styles/reset.css +32 -0
  146. package/starters/default/src/shared/styles/shell.css +57 -0
  147. package/starters/default/src/shared/styles/tokens.css +44 -0
  148. package/starters/default/src/shared/ui/x-button.component.ts +49 -0
  149. package/starters/default/src/shared/ui/x-spinner.component.ts +22 -0
  150. package/starters/default/src/styles.d.ts +1 -0
  151. package/starters/default/src/vite-env.d.ts +1 -0
  152. package/starters/default/tsconfig.json +24 -0
  153. package/starters/default/vite.config.ts +9 -0
  154. package/MADO_V1_PLAN.md +0 -179
  155. package/ROADMAP.md +0 -178
  156. package/dist/src/html.d.ts +0 -18
  157. package/dist/src/html.js +0 -17
  158. package/dist/src/html.js.map +0 -1
  159. package/dist/src/router.d.ts +0 -13
  160. package/dist/src/router.js +0 -13
  161. package/dist/src/router.js.map +0 -1
  162. package/scripts/bundle.mjs +0 -212
  163. package/scripts/llm-zero-history-smoke.mjs +0 -93
  164. package/scripts/new.mjs +0 -80
  165. package/scripts/showcase-regression.mjs +0 -392
  166. package/server/serve.mjs +0 -455
  167. package/starters/admin/README.md +0 -63
  168. package/starters/admin/index.html +0 -28
  169. package/starters/admin/mado.config.json +0 -22
  170. package/starters/admin/package.json +0 -24
  171. package/starters/admin/public/favicon.svg +0 -4
  172. package/starters/admin/src/components/x-button.ts +0 -82
  173. package/starters/admin/src/components/x-input.ts +0 -105
  174. package/starters/admin/src/layouts/app.ts +0 -101
  175. package/starters/admin/src/layouts/auth.ts +0 -41
  176. package/starters/admin/src/lib/api.ts +0 -184
  177. package/starters/admin/src/lib/auth.ts +0 -83
  178. package/starters/admin/src/main.ts +0 -15
  179. package/starters/admin/src/pages/admin/dashboard.ts +0 -48
  180. package/starters/admin/src/pages/admin/order-detail.ts +0 -80
  181. package/starters/admin/src/pages/admin/orders.ts +0 -117
  182. package/starters/admin/src/pages/home.ts +0 -34
  183. package/starters/admin/src/pages/login.ts +0 -70
  184. package/starters/admin/src/pages/not-found.ts +0 -12
  185. package/starters/admin/src/routes.ts +0 -40
  186. package/starters/admin/src/styles/global.ts +0 -86
  187. package/starters/admin/tsconfig.json +0 -15
  188. package/starters/crud/README.md +0 -33
  189. package/starters/crud/index.html +0 -28
  190. package/starters/crud/mado.config.json +0 -20
  191. package/starters/crud/package.json +0 -24
  192. package/starters/crud/src/components/app-shell.ts +0 -56
  193. package/starters/crud/src/components/ticket-detail.ts +0 -33
  194. package/starters/crud/src/components/ticket-form.ts +0 -69
  195. package/starters/crud/src/components/ticket-list.ts +0 -66
  196. package/starters/crud/src/lib/api.ts +0 -76
  197. package/starters/crud/src/main.ts +0 -9
  198. package/starters/crud/src/pages/home.ts +0 -34
  199. package/starters/crud/src/pages/not-found.ts +0 -12
  200. package/starters/crud/src/pages/ticket-detail.ts +0 -7
  201. package/starters/crud/src/pages/ticket-new.ts +0 -7
  202. package/starters/crud/src/pages/tickets.ts +0 -7
  203. package/starters/crud/src/routes.ts +0 -11
  204. package/starters/crud/src/styles/global.ts +0 -155
  205. package/starters/crud/tsconfig.json +0 -15
  206. package/starters/minimal/README.md +0 -21
  207. package/starters/minimal/index.html +0 -28
  208. package/starters/minimal/mado.config.json +0 -20
  209. package/starters/minimal/package.json +0 -24
  210. package/starters/minimal/src/components/app-counter.ts +0 -31
  211. package/starters/minimal/src/main.ts +0 -9
  212. package/starters/minimal/src/pages/home.ts +0 -35
  213. package/starters/minimal/src/pages/not-found.ts +0 -14
  214. package/starters/minimal/src/routes.ts +0 -8
  215. package/starters/minimal/src/styles/global.ts +0 -60
  216. package/starters/minimal/tsconfig.json +0 -15
  217. package/templates/page-detail.ts +0 -63
  218. package/templates/page-form.ts +0 -94
  219. package/templates/page-list.ts +0 -79
@@ -1,35 +1,76 @@
1
1
  # Auth et API
2
2
 
3
- Le starter `admin` contient la recette recommandée :
3
+ Le starter par défaut est la recette recommandée. La mécanique HTTP vit dans
4
+ `src/shared/http/`, l'état auth dans `src/modules/auth/`.
4
5
 
5
- - `src/lib/api.ts` — un seul client HTTP, `ApiError`, refresh après 401 ;
6
- - `src/lib/auth.ts` — `accessToken`, `restoreSession()`, `login()`,
7
- `logout()`, `requireAuth`.
6
+ ```txt
7
+ src/shared/http/
8
+ http-client.ts
9
+ http-error.ts
10
+ interceptors.ts
8
11
 
9
- Modèle :
12
+ src/modules/auth/
13
+ auth.connector.ts
14
+ auth.service.ts
15
+ auth.guard.ts
16
+ auth.routes.ts
17
+ auth.public.ts
18
+ _contracts/
19
+ ```
20
+
21
+ Flow pour un business module :
22
+
23
+ ```txt
24
+ connector -> resource/mutation -> page
25
+ ```
26
+
27
+ Les pages n'importent pas de DTOs et n'appellent pas `fetch()` directement. Les
28
+ connectors n'importent pas la réactivité Mado ou l'UI.
10
29
 
11
- - access token en mémoire via `signal`, pas dans `localStorage` ;
12
- - refresh cookie HttpOnly pour restaurer la session ;
13
- - toutes les requêtes passent par le client API ;
14
- - les routes protégées utilisent un group guard.
30
+ ## Auth Service
31
+
32
+ Auth state est un ES module singleton :
15
33
 
16
34
  ```ts
17
- export const requireAuth: Guard = async ({ path }) => {
18
- if (accessToken()) return;
19
- if (await restoreSession()) return;
20
- return { redirect: `/login?return=${encodeURIComponent(path)}`, replace: true };
21
- };
35
+ const _user = signal<User | null>(null);
36
+ const _token = signal<string | null>(null);
37
+
38
+ export const user = () => _user();
39
+ export const isAuthed = computed(() => _user() !== null);
22
40
  ```
23
41
 
24
- Dev proxy :
42
+ Exposez seulement la surface nécessaire via `auth.public.ts`.
25
43
 
26
- ```jsonc
27
- {
28
- "dev": {
29
- "proxy": { "/api": "http://localhost:3000" }
30
- }
44
+ ## Guards
45
+
46
+ ```ts
47
+ export function requireAuth(): boolean | string {
48
+ if (isAuthed()) return true;
49
+ return "/login";
31
50
  }
32
51
  ```
33
52
 
34
- Si ton backend a une autre forme, modifie `api.ts` et `auth.ts`. Les pages ne
35
- doivent pas connaître les détails d'authentification.
53
+ Use in `src/app.routes.ts`:
54
+
55
+ ```ts
56
+ "/billing": layout({
57
+ layout: () => import("./layouts/app-shell.layout"),
58
+ guard: requireAuth,
59
+ routes: billingRoutes,
60
+ }),
61
+ ```
62
+
63
+ ## Dev Proxy
64
+
65
+ ```ts
66
+ export default defineConfig({
67
+ plugins: [mado()],
68
+ server: {
69
+ proxy: { "/api": "http://localhost:3000" },
70
+ },
71
+ });
72
+ ```
73
+
74
+ Rule: `shared/http` connaît HTTP, connectors connaissent un système externe,
75
+ resources connaissent les cache keys, pages connaissent l'UI, `*.public.ts` est
76
+ la surface cross-module.
@@ -1,6 +1,6 @@
1
1
  # Déploiement
2
2
 
3
- Une commande, un artefact :
3
+ Une commande, un artefact déployable :
4
4
 
5
5
  ```bash
6
6
  mado release
@@ -10,30 +10,48 @@ Résultat :
10
10
 
11
11
  ```txt
12
12
  out/
13
- ├── index.html
14
- ├── assets/
15
- ├── baked/
16
- ├── _redirects
17
- └── _headers
13
+ ├── index.html ← shell SPA ou HTML baked pour /
14
+ ├── assets/ ← assets Vite hashés
15
+ ├── *.gz ← gzip précompressé
16
+ │ └── *.br ← brotli précompressé
17
+ ├── <route>/index.html ← HTML baked pour les hébergeurs statiques
18
+ ├── sitemap.xml ← sitemap à la racine du site
19
+ ├── _redirects ← fallback SPA Cloudflare Pages / Netlify
20
+ └── _headers ← règles de cache
18
21
  ```
19
22
 
20
23
  Déploie `out/` sur nginx, Cloudflare Pages, Netlify, S3/CloudFront ou GitHub
21
- Pages.
24
+ Pages. Ne déploie pas `dist/` : c'est un output interne.
25
+
26
+ ## Preview
22
27
 
23
28
  ```bash
24
29
  mado release
25
30
  mado preview
26
31
  ```
27
32
 
28
- `mado preview` sert `out/` comme un hébergeur statique : HTML baked d'abord,
29
- fallback SPA ensuite.
33
+ `mado preview` sert le `out/` final comme un hébergeur statique : fichiers réels
34
+ d'abord (`/<route>/index.html` si la route est baked), fallback SPA ensuite.
35
+ Preview vérifie exactement ce qui sera déployé.
30
36
 
31
- Pour VPS + nginx :
37
+ ## VPS + nginx
32
38
 
33
39
  ```bash
34
40
  mado release
35
41
  rsync -avz --delete out/ user@server:/var/www/myapp/
36
42
  ```
37
43
 
38
- Le `nginx.conf` fourni gère cache immutable pour les bundles hashés, no-cache
39
- pour HTML, et fallback SPA pour les deep links.
44
+ La recette nginx optionnelle vit dans `docs/recipes/nginx/`. Elle gère le cache
45
+ immutable pour `/assets/*`, no-cache pour HTML, et le fallback SPA pour les deep
46
+ links.
47
+
48
+ ## Cloudflare / Netlify
49
+
50
+ ```bash
51
+ mado release
52
+ npx wrangler pages deploy out --project-name=myapp
53
+ ```
54
+
55
+ `_redirects` et `_headers` sont générés automatiquement si tu n'en fournis pas.
56
+ Les routes baked sont promues en vrais fichiers (`out/<route>/index.html`), donc
57
+ l'hébergeur statique les sert avant le fallback SPA.
@@ -22,7 +22,7 @@ globalThis.document = window.document;
22
22
  globalThis.Node = window.Node;
23
23
  globalThis.HTMLElement = window.HTMLElement;
24
24
 
25
- const { html, render } = await import("../dist/src/html.js");
25
+ const { html, render } = await import("../dist/src/html/template.js");
26
26
 
27
27
  test("renders", () => {
28
28
  const root = document.createElement("div");
@@ -1,8 +1,10 @@
1
- # Bake cookbook
1
+ # Bake Cookbook
2
2
 
3
3
  `mado bake` rend certaines routes en HTML statique. C'est pour le SEO et un
4
4
  premier rendu rapide, pas pour SSR + hydration.
5
5
 
6
+ ## Page Minimale
7
+
6
8
  ```ts
7
9
  export default page({
8
10
  head: () => ({ title: "Products", description: "Catalog" }),
@@ -21,15 +23,66 @@ export default page({
21
23
  ```
22
24
 
23
25
  Dans les vues baked, préfère les tableaux simples (`items.map(...)`). Les
24
- directives runtime comme `each()` sont pour le navigateur.
26
+ directives runtime comme keyed `each()` sont pour le navigateur.
27
+
28
+ ## Routes Dynamiques
29
+
30
+ ```ts
31
+ export default page<{ slug: string }>({
32
+ head: ({ slug }, data) => ({ title: data.title, canonical: `/blog/${slug}` }),
33
+ view: ({ data }) => html`<article>${unsafeHTML(data.html)}</article>`,
34
+ bake: {
35
+ paths: async () => (await api.posts()).map((p) => ({ slug: p.slug })),
36
+ data: ({ slug }) => api.post(slug),
37
+ },
38
+ });
39
+ ```
40
+
41
+ Utilise `unsafeHTML()` seulement pour du HTML fiable ou déjà nettoyé.
42
+
43
+ ## Route Manifest
44
+
45
+ `mado bake` a besoin du manifest source :
25
46
 
26
47
  ```ts
27
48
  export const manifest = {
28
49
  "/": () => import("./pages/home.js"),
29
50
  "/blog/:slug": () => import("./pages/blog-post.js"),
30
51
  };
52
+
31
53
  export default routes(manifest);
32
54
  ```
33
55
 
34
- `mado release` produit l'artefact déployable dans `out/`. Si bake signale une
35
- valeur non supportée, remplace la partie runtime-only ou rends-la côté client.
56
+ ## Output
57
+
58
+ Standalone `mado bake` écrit les pages baked dans `out/baked/` par défaut.
59
+ `mado release` utilise le shell de production bundlé, garde cette copie
60
+ `out/baked/` pour inspection, promeut le HTML dans les vrais chemins de route
61
+ dans `out/`, et copie le sitemap vers `out/sitemap.xml`.
62
+
63
+ ```bash
64
+ mado release
65
+ tree out
66
+ ```
67
+
68
+ Le dossier déployable est `out/`, pas `dist/`.
69
+
70
+ ## Client Boot
71
+
72
+ Le HTML baked marque `#app` avec `data-mado-baked`. Ce n'est pas de
73
+ l'hydration : au démarrage, `render()` remplace le DOM baked par les bindings
74
+ vivants de l'application.
75
+
76
+ ## Valeurs Non Supportées
77
+
78
+ Bake échoue volontairement au lieu d'écrire `[object Object]`. Si une vue baked
79
+ signale une directive non supportée :
80
+
81
+ - remplace `each()` par `items.map(...)` dans le markup baked ;
82
+ - garde les widgets interactifs dans des routes client-only ;
83
+ - assure-toi que chaque valeur peut être sérialisée en HTML statique.
84
+
85
+ ## Canonical Links
86
+
87
+ Passe `--base-url` pour que les liens canonical et le sitemap pointent vers la
88
+ production.
@@ -27,7 +27,7 @@ Ces noms sont publics et protégés par SemVer une fois v1 publiée :
27
27
  - Templates et directives : `html`, `render`, `each`, `list`, `unsafeHTML`,
28
28
  `ref`, `classMap`, `styleMap`.
29
29
  - Composants et CSS : `component`, `css`, `cssVars`.
30
- - Routage et pages : `routes`, `router`, `page`, `layout`, `nested`,
30
+ - Routage et pages : `routes`, `router`, `page`, `layout`,
31
31
  `navigate`, `queryParam`, `prefetchPath`.
32
32
  - Data : `resource`, `mutation`, `invalidate`, `jsonFetcher`, `HttpError`.
33
33
  - Formulaires : `useForm`.
@@ -26,7 +26,7 @@ Après v1, Mado considère comme protégés par SemVer :
26
26
  `ctx.onDispose`.
27
27
  - Les contrats router/page/resource/form documentés dans les docs anglaises.
28
28
  - Les noms de commandes CLI et leur intention générale (`build`, `dev`,
29
- `release`, `bake`, `bundle`, `preview`, `init`, `new`).
29
+ `release`, `bake`, `preview`, `init`, `new`).
30
30
 
31
31
  Casser cela nécessite une version majeure.
32
32
 
@@ -0,0 +1,13 @@
1
+ # Nginx Container Recipe
2
+
3
+ Optional static deployment recipe for a generated Mado app.
4
+
5
+ Copy this directory into your app as `docker/`, then build from the app root:
6
+
7
+ ```bash
8
+ docker build -f docker/Containerfile -t myapp .
9
+ docker run --rm -p 8080:80 myapp
10
+ ```
11
+
12
+ The container runs `npm run release` and serves the resulting `out/` directory
13
+ with nginx. This is a recipe, not a framework runtime requirement.
@@ -2,105 +2,83 @@
2
2
 
3
3
  > Один правильный путь. Жёсткие контракты. Никакой магии.
4
4
 
5
- Mado — фреймворк для команд, которые строят админки, внутренние инструменты
6
- и бизнес-SPA приложения, которые должны быть просты в разработке и скучны
7
- в поддержке. Для этого он задаёт **набор соглашений**. Если ты следуешь им,
8
- проект остаётся понятным даже когда в нём 200 экранов и 5 разработчиков. Если
9
- нарушаешь — типы и линтер скажут об этом сразу.
5
+ Mado — фреймворк для команд, которые строят админки, внутренние инструменты и
6
+ business SPA. Такие приложения должны быть простыми в разработке и скучными в
7
+ поддержке. Поэтому Mado задает соглашения, а не предлагает пять равноценных
8
+ стилей.
10
9
 
11
10
  ## Принципы
12
11
 
13
- 1. **Один способ.** Для каждой задачи — один правильный путь, не пять. Если ты пишешь странное спроси себя, не существует ли уже идиоматичный хелпер.
14
- 2. **Явность над магией.** Никаких сканеров файлов, неявных глобалов, скрытых side-effects. Всё, что делает фреймворк, можно прочитать в одном файле.
15
- 3. **Платформа сначала.** Если в браузере уже есть фича — используем её напрямую. Своих абстракций над `fetch`, `<form>`, History API, Shadow DOM не плодим.
16
- 4. **Жёсткие типы.** `tsc --strict --noUncheckedIndexedAccess` всегда. Если что-то не типизируется — это сигнал что API кривой.
17
- 5. **Никаких рантайм-зависимостей.** Любая зависимость обязательство на годы; экосистема Web Components этого не требует.
12
+ 1. **Один способ.** Если пишешь что-то необычное, сначала проверь, нет ли уже
13
+ каноничного helper/API.
14
+ 2. **Явность над магией.** Никаких file-system scanners, implicit globals и
15
+ скрытых side effects.
16
+ 3. **Платформа сначала.** Web Components, History API, `<form>`, `fetch` и
17
+ Shadow DOM остаются платформой, а не прячутся под тяжелыми абстракциями.
18
+ 4. **Strict types.** `tsc --strict --noUncheckedIndexedAccess` всегда.
19
+ 5. **No runtime dependencies.** Dev/build tooling допустим, runtime Mado
20
+ остается нативным.
18
21
 
19
- ## Соглашения
22
+ ## Структура проекта
20
23
 
21
- ### Структура проекта
22
-
23
- ```
24
+ ```txt
24
25
  src/
25
- ├── routes.ts манифест маршрутов, один файл на проект
26
- ├── main.ts точка входа: провайдеры + монтаж <x-app>
27
- ├── pages/ одна страница = один файл = `export default page({...})`
28
- ├── components/ переиспользуемые компоненты, side-effect-регистрация
29
- ├── lib/ контексты, API-клиенты, бизнес-логика без UI
30
- └── styles/ ← общие стили (если нужны), .ts с css``
31
- ```
32
-
33
- Это **обязательно**, не "по желанию". Если у проекта будет 10 разработчиков — они все должны писать одинаково.
34
-
35
- ### Один компонент = один файл
36
-
37
- ```ts
38
- // src/components/user-card.ts
39
- import { component, html, css } from "@madojs/mado";
40
-
41
- component(
42
- "x-user-card",
43
- () => {
44
- return () => html`<div class="card"><slot /></div>`;
45
- },
46
- {
47
- styles: css`
48
- .card {
49
- padding: 1rem;
50
- }
51
- `,
52
- },
53
- );
26
+ ├── main.ts boot: global CSS/providers + render router
27
+ ├── app.routes.ts readable app map, exports `manifest` + default routes()
28
+ ├── layouts/ app-zone wrappers (`page({ view: ({ child }) => ... })`)
29
+ ├── shared/ UI bricks, http client, pure lib, global CSS
30
+ └── modules/ bounded contexts
31
+ └── billing/
32
+ ├── billing.routes.ts
33
+ ├── billing.public.ts
34
+ ├── billing.types.ts
35
+ ├── pages/
36
+ ├── data/
37
+ ├── api/
38
+ └── _contracts/
54
39
  ```
55
40
 
56
- Импорт `import './components/user-card.js'` **регистрирует** компонент через `customElements.define`. Это side-effect. Где компонент нужен — там и импортируем.
57
-
58
- ### Один способ загрузки данных
41
+ Default starter каноничная версия этой формы. Если docs и старые примеры
42
+ расходятся, starter и `docs/10-app-architecture.md` главнее.
59
43
 
60
- Не зовём `fetch()` напрямую из компонента. Всегда через:
44
+ ## Один компонент = один файл
61
45
 
62
46
  ```ts
63
- // чтение resource
64
- const user = resource(() => `/api/users/${id()}`, jsonFetcher());
47
+ import { component, css, html } from "@madojs/mado";
65
48
 
66
- // запись mutation
67
- const save = mutation(api.save, { invalidates: ["/api/users*"] });
49
+ component("x-user-card", () => () => html`<div class="card"><slot></slot></div>`, {
50
+ styles: css`
51
+ .card { padding: 1rem; }
52
+ `,
53
+ });
68
54
  ```
69
55
 
70
- Это даёт кеш, отмену, обработку ошибок, авто-инвалидацию.
56
+ Import component file registers the element. Import it where the tag is used.
71
57
 
72
- ### Один способ описать страницу
58
+ ## Один способ описать страницу
73
59
 
74
60
  ```ts
75
- // src/pages/user-profile.ts
76
- import { page, html, resource, jsonFetcher } from "@madojs/mado";
61
+ import { html, page, resource, jsonFetcher } from "@madojs/mado";
77
62
 
78
63
  export default page({
79
64
  title: ({ id }) => `User #${id}`,
80
- view: ({ params }) => html`...`,
65
+ view: ({ params }) => {
66
+ const user = resource(() => `/api/users/${params.id}`, jsonFetcher());
67
+ return html`...`;
68
+ },
81
69
  });
82
70
  ```
83
71
 
84
- Три слота `title`, `load`, `view`. Других нет. Хочешь что-то ещё — это уже компонент или хелпер.
85
-
86
- ### Один способ объявить роуты
87
-
88
- См. [`01-routing.md`](./01-routing.md).
72
+ Page-local signals, resources and forms live inside `view()`. Module-wide state
73
+ belongs in `*.service.ts`.
89
74
 
90
75
  ## Чего НЕ делаем
91
76
 
92
- - Не пишем компоненты без дефиса. Это правило браузера для custom elements: `user-card` ок, `usercard` нет.
93
- - `x-*` только convention для примеров и тестов Mado, не брендовый стандарт. В production лучше брать префикс домена: `app-*`, `crm-*`, `ticket-*`, `admin-*`.
94
- - Не используем `innerHTML` напрямую. Только через `html\`\``.
95
- - Не вызываем `setTimeout`/`setInterval` без cleanup. Только внутри `effect()`.
96
- - Не храним глобальный мутируемый state. Используем сигналы и `context`.
97
- - ❌ Не подключаем pkg без обсуждения. Каждая зависимость — обязательство.
98
-
99
- ## Когда сомневаешься
100
-
101
- Если ты задаёшься вопросом "а как тут лучше?" — это сигнал, что:
102
-
103
- 1. Либо есть встроенный хелпер, который ты не знаешь (загляни в `docs/`).
104
- 2. Либо это новая ситуация — её надо обсудить и **зафиксировать** в этом документе как ещё одно соглашение.
77
+ - Не используем JSX/Vue/Svelte syntax.
78
+ - Не пишем custom elements без дефиса.
79
+ - Не читаем signals через `.value`; signal читается как function.
80
+ - Не используем `innerHTML` напрямую.
81
+ - Не добавляем runtime packages без обсуждения.
105
82
 
106
- «Лучше единый-окей, чем разный-идеальный.»
83
+ Когда сомневаешься, лучше записать один честный рецепт в docs, чем добавить
84
+ новый primitive в core.