@madojs/mado 0.11.1 → 0.13.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.
- package/AGENTS.md +116 -32
- package/CHANGELOG.md +216 -1
- package/README.md +158 -173
- package/dist/src/component.d.ts +6 -3
- package/dist/src/component.d.ts.map +1 -0
- package/dist/src/component.js +163 -36
- package/dist/src/component.js.map +1 -1
- package/dist/src/context.d.ts +13 -32
- package/dist/src/context.d.ts.map +1 -0
- package/dist/src/context.js +73 -48
- package/dist/src/context.js.map +1 -1
- package/dist/src/css.d.ts +1 -0
- package/dist/src/css.d.ts.map +1 -0
- package/dist/src/css.js +34 -5
- package/dist/src/css.js.map +1 -1
- package/dist/src/devtools-hook.d.ts +14 -0
- package/dist/src/devtools-hook.d.ts.map +1 -0
- package/dist/src/devtools-hook.js +27 -0
- package/dist/src/devtools-hook.js.map +1 -0
- package/dist/src/devtools.d.ts +24 -22
- package/dist/src/devtools.d.ts.map +1 -0
- package/dist/src/devtools.js +230 -58
- package/dist/src/devtools.js.map +1 -1
- package/dist/src/diagnostics.d.ts +12 -0
- package/dist/src/diagnostics.d.ts.map +1 -0
- package/dist/src/diagnostics.js +68 -8
- package/dist/src/diagnostics.js.map +1 -1
- package/dist/src/each.d.ts +1 -5
- package/dist/src/each.d.ts.map +1 -0
- package/dist/src/each.js +0 -8
- package/dist/src/each.js.map +1 -1
- package/dist/src/forms.d.ts +27 -96
- package/dist/src/forms.d.ts.map +1 -0
- package/dist/src/forms.js +184 -394
- package/dist/src/forms.js.map +1 -1
- package/dist/src/head.d.ts +7 -6
- package/dist/src/head.d.ts.map +1 -0
- package/dist/src/head.js +8 -7
- package/dist/src/head.js.map +1 -1
- package/dist/src/html/bindings.d.ts +1 -0
- package/dist/src/html/bindings.d.ts.map +1 -0
- package/dist/src/html/bindings.js.map +1 -1
- package/dist/src/html/parser.d.ts +4 -5
- package/dist/src/html/parser.d.ts.map +1 -0
- package/dist/src/html/parser.js +64 -15
- package/dist/src/html/parser.js.map +1 -1
- package/dist/src/html/template-types.d.ts +1 -0
- package/dist/src/html/template-types.d.ts.map +1 -0
- package/dist/src/html/template-types.js.map +1 -1
- package/dist/src/html/template.d.ts +5 -1
- package/dist/src/html/template.d.ts.map +1 -0
- package/dist/src/html/template.js +102 -13
- package/dist/src/html/template.js.map +1 -1
- package/dist/src/index.d.ts +15 -12
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +18 -10
- package/dist/src/index.js.map +1 -1
- package/dist/src/json.d.ts +3 -0
- package/dist/src/json.d.ts.map +1 -0
- package/dist/src/json.js +14 -0
- package/dist/src/json.js.map +1 -0
- package/dist/src/lifecycle.d.ts +1 -0
- package/dist/src/lifecycle.d.ts.map +1 -0
- package/dist/src/lifecycle.js +3 -2
- package/dist/src/lifecycle.js.map +1 -1
- package/dist/src/page.d.ts +63 -28
- package/dist/src/page.d.ts.map +1 -0
- package/dist/src/page.js +5 -0
- package/dist/src/page.js.map +1 -1
- package/dist/src/persisted.d.ts +6 -1
- package/dist/src/persisted.d.ts.map +1 -0
- package/dist/src/persisted.js +17 -11
- package/dist/src/persisted.js.map +1 -1
- package/dist/src/resource.d.ts +2 -1
- package/dist/src/resource.d.ts.map +1 -0
- package/dist/src/resource.js +128 -50
- package/dist/src/resource.js.map +1 -1
- package/dist/src/router/base.d.ts +66 -0
- package/dist/src/router/base.d.ts.map +1 -0
- package/dist/src/router/base.js +168 -0
- package/dist/src/router/base.js.map +1 -0
- package/dist/src/router/manifest.d.ts +1 -0
- package/dist/src/router/manifest.d.ts.map +1 -0
- package/dist/src/router/manifest.js +156 -171
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +17 -2
- package/dist/src/router/match.d.ts.map +1 -0
- package/dist/src/router/match.js +60 -6
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.d.ts +1 -0
- package/dist/src/router/navigation.d.ts.map +1 -0
- package/dist/src/router/navigation.js +58 -9
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/signal.d.ts +1 -0
- package/dist/src/signal.d.ts.map +1 -0
- package/dist/src/signal.js +46 -17
- package/dist/src/signal.js.map +1 -1
- package/dist/src/static-runtime.d.ts +82 -0
- package/dist/src/static-runtime.d.ts.map +1 -0
- package/dist/src/static-runtime.js +209 -0
- package/dist/src/static-runtime.js.map +1 -0
- package/dist/src/vite/index.d.ts +32 -3
- package/dist/src/vite/index.d.ts.map +1 -0
- package/dist/src/vite/index.js +49 -17
- package/dist/src/vite/index.js.map +1 -1
- package/docs/README.md +5 -8
- package/docs/architecture/adr/0001-browser-static-snapshots.md +132 -0
- package/docs/architecture/v1-roadmap.md +19 -0
- package/docs/en/00-the-mado-way.md +1 -1
- package/docs/en/01-quickstart.md +183 -0
- package/docs/en/10-pages-and-components.md +279 -0
- package/docs/en/11-templates-and-signals.md +211 -0
- package/docs/en/12-routing.md +229 -0
- package/docs/en/13-data.md +227 -0
- package/docs/en/14-forms.md +91 -0
- package/docs/en/15-static-snapshots.md +181 -0
- package/docs/en/{10-app-architecture.md → 16-app-architecture.md} +56 -4
- package/docs/en/{13-deployment.md → 20-deployment.md} +79 -17
- package/docs/en/{15-error-handling.md → 21-error-handling.md} +9 -6
- package/docs/en/{14-testing.md → 22-testing.md} +6 -5
- package/docs/en/23-cookbook.md +292 -0
- package/docs/en/24-devtools-and-diagnostics.md +45 -0
- package/docs/en/{18-api-freeze-map.md → 30-api-freeze-map.md} +17 -9
- package/docs/en/{20-v1-stability.md → 32-v1-stability.md} +5 -5
- package/docs/en/33-migration-0.12-0.13.md +56 -0
- package/docs/en/40-llm-guide.md +774 -0
- package/docs/en/{06-for-backenders.md → 41-for-backenders.md} +28 -25
- package/docs/en/{05-why-mado.md → 42-why-mado.md} +3 -3
- package/docs/en/README.md +99 -27
- package/docs/recipes/nginx/Containerfile +26 -0
- package/docs/recipes/nginx/nginx.conf +42 -0
- package/llms.txt +263 -210
- package/package.json +51 -23
- package/scripts/clean.mjs +8 -0
- package/scripts/cli/generate.mjs +57 -10
- package/scripts/cli/help.mjs +13 -5
- package/scripts/cli/index.mjs +31 -7
- package/scripts/cli/init.mjs +19 -19
- package/scripts/cli/release.mjs +38 -83
- package/scripts/cli/run.mjs +5 -3
- package/scripts/logger.mjs +117 -0
- package/scripts/output-guard.mjs +128 -0
- package/scripts/preview.mjs +105 -24
- package/scripts/static/browser.mjs +695 -0
- package/scripts/static/discover.mjs +284 -0
- package/scripts/static/output.mjs +141 -0
- package/scripts/static/serialize.mjs +212 -0
- package/scripts/static/server.mjs +167 -0
- package/scripts/static.mjs +205 -0
- package/starters/default/README.md +57 -55
- package/starters/default/package.json +6 -12
- package/starters/default/src/app.routes.ts +20 -33
- package/starters/default/src/components/feature-card.component.ts +40 -0
- package/starters/default/src/components/live-counter.component.ts +57 -0
- package/starters/default/src/content/guides.ts +55 -0
- package/starters/default/src/main.ts +9 -13
- package/starters/default/src/pages/app.page.ts +41 -0
- package/starters/default/src/pages/guide.page.ts +40 -0
- package/starters/default/src/pages/home.page.ts +49 -0
- package/starters/default/src/pages/not-found.page.ts +18 -0
- package/starters/default/src/styles/document.css +39 -0
- package/starters/default/src/styles/reset.css +37 -0
- package/starters/default/src/styles/tokens.css +30 -0
- package/starters/default/src/styles.d.ts +3 -1
- package/starters/default/src/vite-env.d.ts +1 -1
- package/starters/default/tsconfig.node.json +13 -0
- package/starters/default/vite.config.ts +14 -4
- package/starters/modular/README.md +142 -0
- package/starters/modular/index.html +13 -0
- package/starters/modular/package.json +30 -0
- package/starters/modular/src/app.routes.ts +39 -0
- package/starters/{default → modular}/src/layouts/app-shell.layout.ts +9 -7
- package/starters/{default → modular}/src/layouts/auth-shell.layout.ts +3 -3
- package/starters/modular/src/main.ts +16 -0
- package/starters/{default → modular}/src/modules/auth/login.page.ts +5 -12
- package/starters/{default → modular}/src/modules/billing/pages/invoice-detail.page.ts +3 -4
- package/starters/{default → modular}/src/modules/billing/pages/invoices-list.page.ts +4 -4
- package/starters/{default → modular}/src/modules/home/home.page.ts +5 -7
- package/starters/modular/src/modules/home/not-found.page.ts +14 -0
- package/starters/{default → modular}/src/shared/styles/reset.css +21 -1
- package/starters/modular/src/styles.d.ts +1 -0
- package/starters/modular/src/vite-env.d.ts +1 -0
- package/starters/modular/tsconfig.json +33 -0
- package/starters/modular/tsconfig.node.json +13 -0
- package/starters/modular/vite.config.ts +132 -0
- package/TODO.md +0 -79
- package/dist/src/lazy.d.ts +0 -38
- package/dist/src/lazy.js +0 -73
- package/dist/src/lazy.js.map +0 -1
- package/docs/en/01-routing.md +0 -152
- package/docs/en/02-project-layout.md +0 -124
- package/docs/en/03-static-bake.md +0 -249
- package/docs/en/04-ide-setup.md +0 -162
- package/docs/en/07-llm-pitfalls.md +0 -724
- package/docs/en/08-llm-zero-history-test.md +0 -56
- package/docs/en/09-shadow-vs-light-dom.md +0 -174
- package/docs/en/11-layouts.md +0 -113
- package/docs/en/12-auth-and-api.md +0 -124
- package/docs/en/16-bake-cookbook.md +0 -100
- package/docs/en/17-shadow-dom-forms.md +0 -192
- package/docs/fr/00-the-mado-way.md +0 -85
- package/docs/fr/01-routing.md +0 -120
- package/docs/fr/02-project-layout.md +0 -84
- package/docs/fr/03-static-bake.md +0 -289
- package/docs/fr/04-ide-setup.md +0 -162
- package/docs/fr/05-why-mado.md +0 -193
- package/docs/fr/06-for-backenders.md +0 -438
- package/docs/fr/07-llm-pitfalls.md +0 -625
- package/docs/fr/08-llm-zero-history-test.md +0 -38
- package/docs/fr/09-shadow-vs-light-dom.md +0 -65
- package/docs/fr/10-app-architecture.md +0 -138
- package/docs/fr/11-layouts.md +0 -47
- package/docs/fr/12-auth-and-api.md +0 -76
- package/docs/fr/13-deployment.md +0 -57
- package/docs/fr/14-testing.md +0 -41
- package/docs/fr/15-error-handling.md +0 -50
- package/docs/fr/16-bake-cookbook.md +0 -88
- package/docs/fr/17-shadow-dom-forms.md +0 -196
- package/docs/fr/18-api-freeze-map.md +0 -63
- package/docs/fr/19-reactivity-ordering.md +0 -97
- package/docs/fr/20-v1-stability.md +0 -88
- package/docs/fr/README.md +0 -27
- package/docs/ru/00-the-mado-way.md +0 -84
- package/docs/ru/01-routing.md +0 -119
- package/docs/ru/02-project-layout.md +0 -84
- package/docs/ru/03-static-bake.md +0 -250
- package/docs/ru/04-ide-setup.md +0 -144
- package/docs/ru/05-why-mado.md +0 -193
- package/docs/ru/06-for-backenders.md +0 -428
- package/docs/ru/07-llm-pitfalls.md +0 -624
- package/docs/ru/08-llm-zero-history-test.md +0 -57
- package/docs/ru/09-shadow-vs-light-dom.md +0 -63
- package/docs/ru/10-app-architecture.md +0 -152
- package/docs/ru/11-layouts.md +0 -47
- package/docs/ru/12-auth-and-api.md +0 -75
- package/docs/ru/13-deployment.md +0 -66
- package/docs/ru/14-testing.md +0 -50
- package/docs/ru/15-error-handling.md +0 -56
- package/docs/ru/16-bake-cookbook.md +0 -95
- package/docs/ru/17-shadow-dom-forms.md +0 -193
- package/docs/ru/18-api-freeze-map.md +0 -64
- package/docs/ru/19-reactivity-ordering.md +0 -95
- package/docs/ru/20-v1-stability.md +0 -82
- package/docs/ru/README.md +0 -25
- package/docs/uk/00-the-mado-way.md +0 -82
- package/docs/uk/01-routing.md +0 -76
- package/docs/uk/02-project-layout.md +0 -73
- package/docs/uk/03-static-bake.md +0 -48
- package/docs/uk/04-ide-setup.md +0 -26
- package/docs/uk/05-why-mado.md +0 -34
- package/docs/uk/06-for-backenders.md +0 -55
- package/docs/uk/07-llm-pitfalls.md +0 -145
- package/docs/uk/08-llm-zero-history-test.md +0 -34
- package/docs/uk/09-shadow-vs-light-dom.md +0 -58
- package/docs/uk/10-app-architecture.md +0 -97
- package/docs/uk/11-layouts.md +0 -47
- package/docs/uk/12-auth-and-api.md +0 -70
- package/docs/uk/13-deployment.md +0 -40
- package/docs/uk/14-testing.md +0 -34
- package/docs/uk/15-error-handling.md +0 -32
- package/docs/uk/16-bake-cookbook.md +0 -36
- package/docs/uk/17-shadow-dom-forms.md +0 -193
- package/docs/uk/18-api-freeze-map.md +0 -61
- package/docs/uk/19-reactivity-ordering.md +0 -95
- package/docs/uk/20-v1-stability.md +0 -83
- package/docs/uk/README.md +0 -27
- package/scripts/bake.mjs +0 -570
- package/scripts/package-smoke.mjs +0 -77
- package/scripts/release-notes.mjs +0 -66
- package/scripts/size-budget.mjs +0 -91
- package/starters/default/src/modules/home/not-found.page.ts +0 -11
- package/starters/default/src/shared/ui/x-button.component.ts +0 -49
- /package/docs/en/{19-reactivity-ordering.md → 31-reactivity-ordering.md} +0 -0
- /package/starters/{default → modular}/.editorconfig +0 -0
- /package/starters/{default → modular}/eslint.config.mjs +0 -0
- /package/starters/{default → modular}/public/favicon.svg +0 -0
- /package/starters/{default → modular}/src/modules/auth/_contracts/auth-api.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.connector.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.guard.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.public.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.routes.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.service.ts +0 -0
- /package/starters/{default → modular}/src/modules/auth/auth.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/_contracts/stripe.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/api/stripe.connector.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.public.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.routes.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/billing.types.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/components/invoice-status-badge.component.ts +0 -0
- /package/starters/{default → modular}/src/modules/billing/data/invoices.resource.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/http-client.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/http-error.ts +0 -0
- /package/starters/{default → modular}/src/shared/http/interceptors.ts +0 -0
- /package/starters/{default → modular}/src/shared/lib/format-date.ts +0 -0
- /package/starters/{default → modular}/src/shared/styles/content.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/shell.css +0 -0
- /package/starters/{default → modular}/src/shared/styles/tokens.css +0 -0
- /package/starters/{default → modular}/src/shared/ui/x-spinner.component.ts +0 -0
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# Структура проєкту
|
|
2
|
-
|
|
3
|
-
Кожен Mado-застосунок використовує одну канонічну форму. Це потрібно, щоб люди
|
|
4
|
-
й AI-асистенти однаково розуміли, де живе код.
|
|
5
|
-
|
|
6
|
-
```txt
|
|
7
|
-
my-app/
|
|
8
|
-
├── package.json # runtime dep: @madojs/mado
|
|
9
|
-
├── tsconfig.json # strict TS, ES2022, Bundler resolution
|
|
10
|
-
├── vite.config.ts # mado() from @madojs/mado/vite
|
|
11
|
-
├── index.html # Vite entry + SPA shell
|
|
12
|
-
├── public/ # static assets: favicon, images, robots.txt
|
|
13
|
-
└── src/
|
|
14
|
-
├── main.ts # imports CSS and mounts router into #app
|
|
15
|
-
├── app.routes.ts # app map: manifest + default routes(...)
|
|
16
|
-
├── layouts/ # app-zone layouts
|
|
17
|
-
├── shared/ # ui, http, lib, styles
|
|
18
|
-
└── modules/ # bounded contexts
|
|
19
|
-
└── billing/
|
|
20
|
-
├── billing.routes.ts
|
|
21
|
-
├── billing.public.ts
|
|
22
|
-
├── billing.types.ts
|
|
23
|
-
├── pages/
|
|
24
|
-
├── data/
|
|
25
|
-
├── api/
|
|
26
|
-
└── _contracts/
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Artifact States
|
|
30
|
-
|
|
31
|
-
| Folder | Що це | Хто пише | Deploy? |
|
|
32
|
-
| --- | --- | --- | --- |
|
|
33
|
-
| `src/` | TypeScript sources | ви | no |
|
|
34
|
-
| `public/` | static assets copied as-is | ви | via `out/` |
|
|
35
|
-
| `out/` | deploy artifact: SPA shell + assets + baked HTML | `mado release` | yes |
|
|
36
|
-
|
|
37
|
-
`mado release` = `typecheck` + Vite build (`out/index.html`, `out/assets/`,
|
|
38
|
-
`public/*`) + `bake` directly into route paths + `sitemap.xml` + precompression.
|
|
39
|
-
|
|
40
|
-
## Where To Put Files
|
|
41
|
-
|
|
42
|
-
| What | Where |
|
|
43
|
-
| --- | --- |
|
|
44
|
-
| Page for a new URL | `src/modules/<module>/pages/<name>.page.ts` + module routes |
|
|
45
|
-
| Module route map | `src/modules/<module>/<module>.routes.ts` |
|
|
46
|
-
| App shell/layout | `src/layouts/<zone>.layout.ts` |
|
|
47
|
-
| Shared UI widget | `src/shared/ui/<x-name>.component.ts` |
|
|
48
|
-
| Module-only UI widget | `src/modules/<module>/components/<name>.component.ts` |
|
|
49
|
-
| API connector | `src/modules/<module>/api/<provider>.connector.ts` |
|
|
50
|
-
| Data resource/mutation | `src/modules/<module>/data/<name>.resource.ts` |
|
|
51
|
-
| Auth/session | `src/modules/auth/` |
|
|
52
|
-
| Public module surface | `src/modules/<module>/<module>.public.ts` |
|
|
53
|
-
| Pure function without UI | `src/shared/lib/<name>.ts` |
|
|
54
|
-
| Static image / favicon | `public/<file>` |
|
|
55
|
-
| App-zone shell CSS | `src/shared/styles/shell.css` |
|
|
56
|
-
| Page-level CSS | `src/shared/styles/content.css` |
|
|
57
|
-
|
|
58
|
-
## Vite Config
|
|
59
|
-
|
|
60
|
-
```ts
|
|
61
|
-
import { defineConfig } from "vite";
|
|
62
|
-
import { mado } from "@madojs/mado/vite";
|
|
63
|
-
|
|
64
|
-
export default defineConfig({
|
|
65
|
-
plugins: [mado()],
|
|
66
|
-
css: {
|
|
67
|
-
transformer: "lightningcss",
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Starter uses Vite's Lightning CSS transformer. Mado does not own prefixing, CSS
|
|
73
|
-
lowering or minification.
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# Static Bake & SEO
|
|
2
|
-
|
|
3
|
-
Mado навмисно не робить SSR із hydration. Для SEO-сторінок є `bake`: build-time
|
|
4
|
-
prerender у статичний HTML із meta-тегами, JSON-LD і baked data.
|
|
5
|
-
|
|
6
|
-
## Коли підходить
|
|
7
|
-
|
|
8
|
-
- Блог, документація, landing pages.
|
|
9
|
-
- Невеликі каталоги з кінцевим набором сторінок.
|
|
10
|
-
- Сторінки, де crawlers мають одразу побачити контент.
|
|
11
|
-
- Контент не персоналізований для конкретного користувача.
|
|
12
|
-
|
|
13
|
-
## Коли не підходить
|
|
14
|
-
|
|
15
|
-
- Авторизовані dashboards.
|
|
16
|
-
- Персоналізований HTML.
|
|
17
|
-
- Real-time дані.
|
|
18
|
-
- Каталоги, які потребують справжнього server rendering на кожен запит.
|
|
19
|
-
|
|
20
|
-
## Сторінка з bake
|
|
21
|
-
|
|
22
|
-
```ts
|
|
23
|
-
import { page, html } from "@madojs/mado";
|
|
24
|
-
|
|
25
|
-
export default page<{ slug: string }, Product>({
|
|
26
|
-
title: ({ slug }) => `Product ${slug}`,
|
|
27
|
-
head: ({ slug }, data) => ({
|
|
28
|
-
description: data?.description ?? `Product ${slug}`,
|
|
29
|
-
}),
|
|
30
|
-
bake: {
|
|
31
|
-
paths: () => products.map((p) => ({ slug: p.slug })),
|
|
32
|
-
data: ({ slug }) => findProduct(slug),
|
|
33
|
-
revalidate: 3600,
|
|
34
|
-
},
|
|
35
|
-
view: ({ params }) => html`<x-product data-slug=${params.slug}></x-product>`,
|
|
36
|
-
});
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
`bake.paths()` повертає всі params, `bake.data()` повертає дані для конкретної
|
|
40
|
-
сторінки, `head()` формує SEO-метадані.
|
|
41
|
-
|
|
42
|
-
## Edge prerender
|
|
43
|
-
|
|
44
|
-
Для великих наборів сторінок можна робити той самий підхід на edge: Cloudflare
|
|
45
|
-
Worker генерує HTML на cache miss, кладе в KV і віддає з TTL.
|
|
46
|
-
|
|
47
|
-
Це не hydration. Клієнтський Mado-застосунок все одно стартує нормально і
|
|
48
|
-
перерендерює сторінку після завантаження.
|
package/docs/uk/04-ide-setup.md
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# Налаштування IDE
|
|
2
|
-
|
|
3
|
-
Mado використовує tagged templates `html`` і `css``. Для TypeScript це звичайні
|
|
4
|
-
рядки, тому підсвітка HTML/CSS залежить від IDE.
|
|
5
|
-
|
|
6
|
-
## VS Code
|
|
7
|
-
|
|
8
|
-
Рекомендовано встановити `lit-plugin` або інше розширення, яке розуміє
|
|
9
|
-
`html``/`css`` tagged templates.
|
|
10
|
-
|
|
11
|
-
## WebStorm
|
|
12
|
-
|
|
13
|
-
WebStorm зазвичай розуміє tagged templates без додаткового налаштування.
|
|
14
|
-
|
|
15
|
-
## Neovim / Helix
|
|
16
|
-
|
|
17
|
-
Можна використовувати LSP/плагіни для lit-html або inline HTML у template
|
|
18
|
-
strings.
|
|
19
|
-
|
|
20
|
-
## Що IDE не перевірить
|
|
21
|
-
|
|
22
|
-
- Чи правильно ти обгорнув `count()` у `() => count()`.
|
|
23
|
-
- Чи варто використовувати `?disabled` замість `disabled`.
|
|
24
|
-
- Чи є `.js` у browser ESM imports.
|
|
25
|
-
|
|
26
|
-
Для цього потрібні правила з `AGENTS.md`, тести та уважний review.
|
package/docs/uk/05-why-mado.md
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# Чому Mado
|
|
2
|
-
|
|
3
|
-
Mado не намагається “вбити React” або довести, що всі інші помиляються. Це
|
|
4
|
-
інструмент для людей, які хочуть маленький, читабельний frontend runtime поверх
|
|
5
|
-
нативного браузера.
|
|
6
|
-
|
|
7
|
-
## Lit
|
|
8
|
-
|
|
9
|
-
Lit чудовий для дизайн-систем і компонентів, які мають жити всередині будь-якого
|
|
10
|
-
framework. Mado краще підходить, коли ти пишеш увесь застосунок і хочеш router,
|
|
11
|
-
data, forms, context і static bake в одному маленькому пакеті.
|
|
12
|
-
|
|
13
|
-
## Solid
|
|
14
|
-
|
|
15
|
-
Solid швидший і зріліший. Якщо тобі комфортно з JSX transform, Vite і mature
|
|
16
|
-
ecosystem — Solid може бути кращим вибором. Mado обирає інше: browser ESM,
|
|
17
|
-
`tsc`, tagged templates і код, який можна прочитати.
|
|
18
|
-
|
|
19
|
-
## htmx
|
|
20
|
-
|
|
21
|
-
htmx сильний, коли backend володіє HTML fragments. Mado потрібен тоді, коли все
|
|
22
|
-
ж хочеться SPA: локальний UI state, optimistic updates, query params, cached
|
|
23
|
-
resources, persisted state і lazy modules.
|
|
24
|
-
|
|
25
|
-
## React / Vue / Svelte
|
|
26
|
-
|
|
27
|
-
Вони мають більші ecosystem, більше відповідей у Google/LLM і більше людей на
|
|
28
|
-
ринку. Mado виграє не масштабом ecosystem, а ownership: маленький runtime,
|
|
29
|
-
нуль runtime-залежностей і зрозумілі правила.
|
|
30
|
-
|
|
31
|
-
## Головна теза
|
|
32
|
-
|
|
33
|
-
Mado — не найшвидший у synthetic benchmarks і не найбагатший ecosystem. Його
|
|
34
|
-
сенс у тому, що frontend знову можна тримати в голові.
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Mado для бекендерів
|
|
2
|
-
|
|
3
|
-
Якщо ти пишеш Go, Rust, .NET, Java, Python або Node backend, Mado можна уявляти
|
|
4
|
-
як маленький HTTP-сервер у браузері.
|
|
5
|
-
|
|
6
|
-
| Backend mental model | Mado |
|
|
7
|
-
|---|---|
|
|
8
|
-
| router | `routes()` |
|
|
9
|
-
| handler | `page().view` |
|
|
10
|
-
| middleware/layout | `layout()` route group |
|
|
11
|
-
| cache get-or-set | `resource()` |
|
|
12
|
-
| POST/PUT/DELETE | `mutation()` |
|
|
13
|
-
| cache invalidation | `invalidates` / `invalidate()` |
|
|
14
|
-
| dependency injection | `createContext/provide/inject` |
|
|
15
|
-
|
|
16
|
-
## CRUD
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
const users = resource(
|
|
20
|
-
() => "/api/users",
|
|
21
|
-
jsonFetcher<User[]>(),
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const save = mutation(api.saveUser, {
|
|
25
|
-
invalidates: ["/api/users*"],
|
|
26
|
-
});
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Ключі `resource()` — це identity кешу. Додавайте endpoint, query params і форму
|
|
30
|
-
даних у ключ: два живі `resource()` з однаковим ключем ділять cache та
|
|
31
|
-
in-flight request. Якщо той самий ключ використано з іншим fetcher, Mado
|
|
32
|
-
попереджає, бо зазвичай це означає, що ключ кешу занадто широкий.
|
|
33
|
-
|
|
34
|
-
## Форми
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
const form = useForm({
|
|
38
|
-
email: { required: true, type: "email" },
|
|
39
|
-
});
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Mado використовує schema-based validation, близьку до HTML constraints, а не
|
|
43
|
-
окрему validation ecosystem.
|
|
44
|
-
|
|
45
|
-
## Auth
|
|
46
|
-
|
|
47
|
-
Auth зазвичай живе в `modules/auth` як signal/context. Protected area зручно
|
|
48
|
-
робити через `layout()` group, який перевіряє session і показує dashboard або
|
|
49
|
-
redirect/login.
|
|
50
|
-
|
|
51
|
-
## Правило
|
|
52
|
-
|
|
53
|
-
Не тягни backend-архітектуру в frontend дослівно. Тримай UI state локальним,
|
|
54
|
-
data fetching через `resource()`, mutation через `mutation()`, а shared services
|
|
55
|
-
через context.
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# Типові помилки LLM у Mado-коді
|
|
2
|
-
|
|
3
|
-
Цей файл потрібен, щоб AI-агенти не писали React/Vue-код у Mado-проєкті.
|
|
4
|
-
|
|
5
|
-
## JSX
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
// Ні
|
|
9
|
-
const view = <button>{count}</button>;
|
|
10
|
-
|
|
11
|
-
// Так
|
|
12
|
-
const view = html`<button>${count}</button>`;
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## `useState` / `useEffect`
|
|
16
|
-
|
|
17
|
-
```ts
|
|
18
|
-
// Ні
|
|
19
|
-
const [count, setCount] = useState(0);
|
|
20
|
-
|
|
21
|
-
// Так
|
|
22
|
-
const count = signal(0);
|
|
23
|
-
count.set(1);
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Signal `.value`
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
// Ні
|
|
30
|
-
count.value;
|
|
31
|
-
|
|
32
|
-
// Так
|
|
33
|
-
count();
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Нереактивний child binding
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
// Нереактивно: count() прочитано один раз
|
|
40
|
-
html`<div>${count() * 2}</div>`;
|
|
41
|
-
|
|
42
|
-
// Реактивно
|
|
43
|
-
html`<div>${() => count() * 2}</div>`;
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Boolean attributes
|
|
47
|
-
|
|
48
|
-
```ts
|
|
49
|
-
// Ні
|
|
50
|
-
html`<button disabled=${loading}>Save</button>`;
|
|
51
|
-
|
|
52
|
-
// Так
|
|
53
|
-
html`<button ?disabled=${loading}>Save</button>`;
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Списки
|
|
57
|
-
|
|
58
|
-
```ts
|
|
59
|
-
// Не для динамічного reorder
|
|
60
|
-
items().map((item) => html`<li>${item.name}</li>`);
|
|
61
|
-
|
|
62
|
-
// Так
|
|
63
|
-
each(
|
|
64
|
-
items(),
|
|
65
|
-
(item) => item.id,
|
|
66
|
-
(item) => html`<li>${item.name}</li>`,
|
|
67
|
-
);
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Imports
|
|
71
|
-
|
|
72
|
-
У browser ESM локальні imports мають мати `.js`:
|
|
73
|
-
|
|
74
|
-
```ts
|
|
75
|
-
import "./components/x-card.js";
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## `resource()` lifecycle
|
|
79
|
-
|
|
80
|
-
`resource()` створюй у component setup або всередині явного lifecycle, щоб cleanup
|
|
81
|
-
відбувався автоматично.
|
|
82
|
-
|
|
83
|
-
## Component names
|
|
84
|
-
|
|
85
|
-
Custom element name має містити дефіс. `x-*` — демо-конвенція; production може
|
|
86
|
-
мати `app-*`, `crm-*`, `ticket-*`, `admin-*`.
|
|
87
|
-
|
|
88
|
-
## `host.getAttribute()` у render — не реактивно
|
|
89
|
-
|
|
90
|
-
```ts
|
|
91
|
-
// Ні: читається один раз, не оновлюється
|
|
92
|
-
component("x-badge", ({ host }) => () => {
|
|
93
|
-
const variant = host.getAttribute("variant") ?? "default";
|
|
94
|
-
return html`<span class=${variant}>...</span>`;
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Так: ctx.attr() — реактивний Signal
|
|
98
|
-
component("x-badge", ({ attr }) => {
|
|
99
|
-
const variant = attr("variant", "default");
|
|
100
|
-
return () => html`<span class=${() => `badge-${variant()}`}>...</span>`;
|
|
101
|
-
});
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Shadow DOM кнопка і submit форми
|
|
105
|
-
|
|
106
|
-
`<button type="submit">` всередині Shadow DOM не тригерить submit `<form>` у Light DOM.
|
|
107
|
-
Використовуйте `form.requestSubmit()` у click handler.
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
const handleClick = () => {
|
|
111
|
-
const form = host.closest("form");
|
|
112
|
-
if (form && !host.hasAttribute("disabled")) form.requestSubmit();
|
|
113
|
-
};
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## `useForm()` + Shadow DOM input
|
|
117
|
-
|
|
118
|
-
Після ретаргетингу `e.target` — це `<x-input>`, у якого немає `.name`/`.value`.
|
|
119
|
-
Додайте proxy-властивості на host:
|
|
120
|
-
|
|
121
|
-
```ts
|
|
122
|
-
Object.defineProperty(host, "name", {
|
|
123
|
-
get: () => host.getAttribute("name") ?? "",
|
|
124
|
-
});
|
|
125
|
-
Object.defineProperty(host, "value", {
|
|
126
|
-
get: () => host.shadowRoot?.querySelector("input")?.value ?? "",
|
|
127
|
-
});
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
Детальніше: [`17-shadow-dom-forms.md`](./17-shadow-dom-forms.md).
|
|
131
|
-
|
|
132
|
-
## Шпаргалка
|
|
133
|
-
|
|
134
|
-
| React/інше | Mado |
|
|
135
|
-
| --------------------------- | ---------------------------- |
|
|
136
|
-
| `useState(0)` | `signal(0)` |
|
|
137
|
-
| `useEffect(() => {...})` | `effect(() => {...})` |
|
|
138
|
-
| `useMemo(() => x)` | `computed(() => x)` |
|
|
139
|
-
| `useQuery(['key'], fn)` | `resource(() => 'key', fn)` |
|
|
140
|
-
| `useRouter().push('/')` | `navigate('/')` |
|
|
141
|
-
| `class extends HTMLElement` | `component('x-name', setup)` |
|
|
142
|
-
| `host.getAttribute('x')` | `ctx.attr('x', default)` |
|
|
143
|
-
| `jsonFetcher()` з auth | `apiFetcher()` |
|
|
144
|
-
| `setInterval` в page view | `onDispose(() => clearInterval(id))` |
|
|
145
|
-
| читання сигналу в async init | `untracked(() => cursor())` |
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# LLM Zero-History Test
|
|
2
|
-
|
|
3
|
-
This document defines a manual validation test: can a fresh LLM write idiomatic
|
|
4
|
-
Mado without falling back to React-shaped code?
|
|
5
|
-
|
|
6
|
-
## Allowed Context
|
|
7
|
-
|
|
8
|
-
- `AGENTS.md`
|
|
9
|
-
- `README.md`
|
|
10
|
-
- `docs/uk/07-llm-pitfalls.md` or the English version
|
|
11
|
-
- files from the external `madojs-examples` workspace only when the agent asks
|
|
12
|
-
for a larger app pattern
|
|
13
|
-
|
|
14
|
-
## Task
|
|
15
|
-
|
|
16
|
-
Build a small ticket-admin SPA:
|
|
17
|
-
|
|
18
|
-
- routes: `/`, `/tickets`, `/tickets/new`, `/tickets/:id`, `*`;
|
|
19
|
-
- in-memory mock API with realistic async delays;
|
|
20
|
-
- list page with `resource()`, `queryParam()`, `computed()` and keyed `each()`;
|
|
21
|
-
- create/edit flows with `useForm()` + `mutation()` + `invalidates`;
|
|
22
|
-
- local UI state with `signal()`.
|
|
23
|
-
|
|
24
|
-
## Failure Checklist
|
|
25
|
-
|
|
26
|
-
- JSX, `useState`, `useEffect`, `ref`, `$state`, class-style components;
|
|
27
|
-
- `${signal()}` where a reactive child thunk is required;
|
|
28
|
-
- `disabled=${...}` instead of `?disabled=${...}`;
|
|
29
|
-
- unkeyed `.map()` for dynamic lists;
|
|
30
|
-
- `resource()` created outside lifecycle-aware context;
|
|
31
|
-
- new runtime dependencies or new public APIs.
|
|
32
|
-
|
|
33
|
-
The historical tickets implementation lives in the external examples workspace.
|
|
34
|
-
The core repository no longer ships that artifact.
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# Shadow DOM vs Light DOM
|
|
2
|
-
|
|
3
|
-
Mado components use Shadow DOM by default. This is good for self-contained
|
|
4
|
-
widgets, but app zones and pages usually stay as light DOM templates.
|
|
5
|
-
|
|
6
|
-
## Rule
|
|
7
|
-
|
|
8
|
-
Use route layouts for app zones:
|
|
9
|
-
|
|
10
|
-
```ts
|
|
11
|
-
export default page({
|
|
12
|
-
view: ({ child }) => html`<main class="app-main">${child}</main>`,
|
|
13
|
-
});
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
These files live in `src/layouts/` and are composed from `src/app.routes.ts`
|
|
17
|
-
with `layout()`. They are styled by `src/shared/styles/shell.css`.
|
|
18
|
-
|
|
19
|
-
Use page files for screens:
|
|
20
|
-
|
|
21
|
-
```ts
|
|
22
|
-
export default page({
|
|
23
|
-
view: () => html`<section><h1>Users</h1></section>`,
|
|
24
|
-
});
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Page-level tables, forms, prose and simple states are styled by
|
|
28
|
-
`src/shared/styles/content.css`.
|
|
29
|
-
|
|
30
|
-
Use Shadow DOM components for leaf widgets:
|
|
31
|
-
|
|
32
|
-
- buttons, badges, cards, metrics;
|
|
33
|
-
- spinners, modals, toasts;
|
|
34
|
-
- widgets that should own their CSS.
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
component("x-status-badge", ({ attr }) => {
|
|
38
|
-
const status = attr("status", "draft");
|
|
39
|
-
return () => html`<span>${status}</span>`;
|
|
40
|
-
}, {
|
|
41
|
-
styles: css`
|
|
42
|
-
:host { display: inline-block; }
|
|
43
|
-
span { color: var(--color-text-muted); }
|
|
44
|
-
`,
|
|
45
|
-
});
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Style Behavior
|
|
49
|
-
|
|
50
|
-
- `tokens.css` defines CSS custom properties; `var(...)` crosses Shadow DOM.
|
|
51
|
-
- `reset.css`, `shell.css`, `content.css` apply only to document/light DOM.
|
|
52
|
-
- Class selectors like `.data`, `.app-main`, `.error` do not cross Shadow DOM.
|
|
53
|
-
- Component-local styles live in ``css`...` `` inside `component()` options.
|
|
54
|
-
- If a Shadow component accepts children, use `<slot>` and style the frame in
|
|
55
|
-
component styles.
|
|
56
|
-
|
|
57
|
-
If a page looks unstyled, you probably used global classes inside a Shadow DOM
|
|
58
|
-
component. Move markup into a page/layout or move CSS into component styles.
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# Архітектура застосунку
|
|
2
|
-
|
|
3
|
-
Офіційний starter — канонічна production-форма Mado-застосунку. Це не
|
|
4
|
-
framework всередині framework: лише files, imports, Mado primitives та ESLint
|
|
5
|
-
boundaries.
|
|
6
|
-
|
|
7
|
-
```txt
|
|
8
|
-
src/
|
|
9
|
-
├── main.ts
|
|
10
|
-
├── app.routes.ts
|
|
11
|
-
├── layouts/
|
|
12
|
-
│ ├── app-shell.layout.ts
|
|
13
|
-
│ └── auth-shell.layout.ts
|
|
14
|
-
├── shared/
|
|
15
|
-
│ ├── http/
|
|
16
|
-
│ ├── lib/
|
|
17
|
-
│ ├── styles/
|
|
18
|
-
│ └── ui/
|
|
19
|
-
└── modules/
|
|
20
|
-
├── auth/
|
|
21
|
-
│ ├── auth.routes.ts
|
|
22
|
-
│ ├── auth.public.ts
|
|
23
|
-
│ ├── auth.service.ts
|
|
24
|
-
│ ├── auth.connector.ts
|
|
25
|
-
│ ├── auth.guard.ts
|
|
26
|
-
│ ├── login.page.ts
|
|
27
|
-
│ └── _contracts/
|
|
28
|
-
└── billing/
|
|
29
|
-
├── billing.routes.ts
|
|
30
|
-
├── billing.public.ts
|
|
31
|
-
├── billing.types.ts
|
|
32
|
-
├── api/
|
|
33
|
-
├── data/
|
|
34
|
-
├── pages/
|
|
35
|
-
├── components/
|
|
36
|
-
└── _contracts/
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
`src/app.routes.ts` is the app map. Modules export plain route maps; app routes
|
|
40
|
-
decide which shell and guard wrap each zone.
|
|
41
|
-
|
|
42
|
-
```ts
|
|
43
|
-
import { layout, routes } from "@madojs/mado";
|
|
44
|
-
import { requireAuth } from "./modules/auth/auth.public";
|
|
45
|
-
import { authRoutes } from "./modules/auth/auth.routes";
|
|
46
|
-
import { billingRoutes } from "./modules/billing/billing.routes";
|
|
47
|
-
|
|
48
|
-
export const manifest = {
|
|
49
|
-
"/": () => import("./modules/home/home.page"),
|
|
50
|
-
"/login": layout({
|
|
51
|
-
layout: () => import("./layouts/auth-shell.layout"),
|
|
52
|
-
routes: authRoutes,
|
|
53
|
-
}),
|
|
54
|
-
"/billing": layout({
|
|
55
|
-
layout: () => import("./layouts/app-shell.layout"),
|
|
56
|
-
guard: requireAuth,
|
|
57
|
-
routes: billingRoutes,
|
|
58
|
-
}),
|
|
59
|
-
"*": () => import("./modules/home/not-found.page"),
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export default routes(manifest);
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Page-local signals, resources and forms live inside `view()`. Module-wide state
|
|
66
|
-
lives in `*.service.ts`.
|
|
67
|
-
|
|
68
|
-
## Styles
|
|
69
|
-
|
|
70
|
-
| File | Role |
|
|
71
|
-
| --- | --- |
|
|
72
|
-
| `src/shared/styles/tokens.css` | design tokens as CSS custom properties |
|
|
73
|
-
| `src/shared/styles/reset.css` | document/light DOM reset |
|
|
74
|
-
| `src/shared/styles/shell.css` | app-zone layouts from `src/layouts/` |
|
|
75
|
-
| `src/shared/styles/content.css` | page-level forms, tables, prose and states |
|
|
76
|
-
|
|
77
|
-
Leaf components keep their own styles in ``css`...` `` inside `component()`
|
|
78
|
-
options and depend on tokens, not global classes.
|
|
79
|
-
|
|
80
|
-
`vite.config.ts` uses Vite's Lightning CSS transformer. Mado does not own
|
|
81
|
-
prefixing, CSS lowering or minification.
|
|
82
|
-
|
|
83
|
-
## CLI
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
mado new module billing
|
|
87
|
-
mado new page billing/pages/invoices-list
|
|
88
|
-
mado new connector billing/api/stripe
|
|
89
|
-
mado new resource billing/data/invoices
|
|
90
|
-
mado new service billing/cart
|
|
91
|
-
mado new form billing/invoice
|
|
92
|
-
mado new component billing/components/invoice-status-badge
|
|
93
|
-
mado new guard billing/billing
|
|
94
|
-
mado new layout app-shell
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
The generator writes files only and does not edit `app.routes.ts`.
|
package/docs/uk/11-layouts.md
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# Layouts
|
|
2
|
-
|
|
3
|
-
The blessed layout recipe in Mado is a route group in `src/app.routes.ts`.
|
|
4
|
-
Do not put a single global shell in `main.ts` when the app has multiple zones:
|
|
5
|
-
public, auth, app, embed.
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
import { layout, routes } from "@madojs/mado";
|
|
9
|
-
import { requireAuth } from "./modules/auth/auth.public";
|
|
10
|
-
import { authRoutes } from "./modules/auth/auth.routes";
|
|
11
|
-
import { billingRoutes } from "./modules/billing/billing.routes";
|
|
12
|
-
|
|
13
|
-
export const manifest = {
|
|
14
|
-
"/": () => import("./modules/home/home.page.js"),
|
|
15
|
-
"/login": layout({
|
|
16
|
-
layout: () => import("./layouts/auth-shell.layout.js"),
|
|
17
|
-
routes: authRoutes,
|
|
18
|
-
}),
|
|
19
|
-
"/billing": layout({
|
|
20
|
-
layout: () => import("./layouts/app-shell.layout.js"),
|
|
21
|
-
guard: requireAuth,
|
|
22
|
-
routes: billingRoutes,
|
|
23
|
-
}),
|
|
24
|
-
"*": () => import("./modules/home/not-found.page.js"),
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export default routes(manifest);
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
A layout is a normal `page({ view })` that renders `child`:
|
|
31
|
-
|
|
32
|
-
```ts
|
|
33
|
-
export default page({
|
|
34
|
-
view: ({ child }) => html`
|
|
35
|
-
<div class="layout layout--app">
|
|
36
|
-
<main class="app-main">${child}</main>
|
|
37
|
-
</div>
|
|
38
|
-
`,
|
|
39
|
-
});
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Rules:
|
|
43
|
-
|
|
44
|
-
- one shell per route group, not per page;
|
|
45
|
-
- modules export plain route maps and do not call `layout()`;
|
|
46
|
-
- guard on a group protects the whole subtree;
|
|
47
|
-
- layout view stays stateless; page-local state lives in pages/components/resources.
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Auth and API
|
|
2
|
-
|
|
3
|
-
The default starter is the blessed recipe. HTTP mechanics live in
|
|
4
|
-
`src/shared/http/`, auth state lives in `src/modules/auth/`.
|
|
5
|
-
|
|
6
|
-
```txt
|
|
7
|
-
src/shared/http/
|
|
8
|
-
http-client.ts
|
|
9
|
-
http-error.ts
|
|
10
|
-
interceptors.ts
|
|
11
|
-
|
|
12
|
-
src/modules/auth/
|
|
13
|
-
auth.connector.ts
|
|
14
|
-
auth.service.ts
|
|
15
|
-
auth.guard.ts
|
|
16
|
-
auth.routes.ts
|
|
17
|
-
auth.public.ts
|
|
18
|
-
_contracts/
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Business module flow:
|
|
22
|
-
|
|
23
|
-
```txt
|
|
24
|
-
connector -> resource/mutation -> page
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Pages do not import DTOs and do not call `fetch()` directly. Connectors do not
|
|
28
|
-
import Mado reactivity or UI.
|
|
29
|
-
|
|
30
|
-
## Auth Service
|
|
31
|
-
|
|
32
|
-
```ts
|
|
33
|
-
const _user = signal<User | null>(null);
|
|
34
|
-
const _token = signal<string | null>(null);
|
|
35
|
-
|
|
36
|
-
export const user = () => _user();
|
|
37
|
-
export const isAuthed = computed(() => _user() !== null);
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Expose only what other modules need through `auth.public.ts`.
|
|
41
|
-
|
|
42
|
-
## Guards
|
|
43
|
-
|
|
44
|
-
```ts
|
|
45
|
-
export function requireAuth(): boolean | string {
|
|
46
|
-
if (isAuthed()) return true;
|
|
47
|
-
return "/login";
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Use in `src/app.routes.ts`:
|
|
52
|
-
|
|
53
|
-
```ts
|
|
54
|
-
"/billing": layout({
|
|
55
|
-
layout: () => import("./layouts/app-shell.layout"),
|
|
56
|
-
guard: requireAuth,
|
|
57
|
-
routes: billingRoutes,
|
|
58
|
-
}),
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Dev Proxy
|
|
62
|
-
|
|
63
|
-
```ts
|
|
64
|
-
export default defineConfig({
|
|
65
|
-
plugins: [mado()],
|
|
66
|
-
server: {
|
|
67
|
-
proxy: { "/api": "http://localhost:3000" },
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
```
|