@madojs/mado 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +24 -26
- package/CHANGELOG.md +68 -0
- package/README.md +18 -45
- package/TODO.md +52 -48
- package/dist/src/component.d.ts +2 -1
- package/dist/src/component.js +5 -2
- package/dist/src/component.js.map +1 -1
- package/dist/src/each.d.ts +1 -1
- package/dist/src/each.js +1 -1
- package/dist/src/each.js.map +1 -1
- package/dist/src/index.d.ts +11 -6
- package/dist/src/index.js +5 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/lazy.d.ts +1 -1
- package/dist/src/lazy.js +1 -1
- package/dist/src/lazy.js.map +1 -1
- package/dist/src/page.d.ts +17 -21
- package/dist/src/page.js +7 -12
- package/dist/src/page.js.map +1 -1
- package/dist/src/router/manifest.d.ts +1 -1
- package/dist/src/router/manifest.js +21 -13
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +2 -2
- package/dist/src/router/match.js +3 -3
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.js +1 -1
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/vite/index.d.ts +10 -0
- package/dist/src/vite/index.js +33 -0
- package/dist/src/vite/index.js.map +1 -0
- package/docs/en/00-the-mado-way.md +25 -12
- package/docs/en/01-routing.md +90 -142
- package/docs/en/02-project-layout.md +59 -53
- package/docs/en/03-static-bake.md +5 -6
- package/docs/en/05-why-mado.md +6 -6
- package/docs/en/06-for-backenders.md +18 -22
- package/docs/en/08-llm-zero-history-test.md +9 -14
- package/docs/en/09-shadow-vs-light-dom.md +28 -36
- package/docs/en/10-app-architecture.md +158 -96
- package/docs/en/11-layouts.md +22 -24
- package/docs/en/12-auth-and-api.md +89 -182
- package/docs/en/13-deployment.md +18 -22
- package/docs/en/14-testing.md +4 -4
- package/docs/en/16-bake-cookbook.md +11 -12
- package/docs/en/18-api-freeze-map.md +6 -4
- package/docs/en/20-v1-stability.md +1 -1
- package/docs/fr/00-the-mado-way.md +55 -90
- package/docs/fr/01-routing.md +70 -152
- package/docs/fr/02-project-layout.md +61 -42
- package/docs/fr/03-static-bake.md +1 -1
- package/docs/fr/05-why-mado.md +6 -6
- package/docs/fr/06-for-backenders.md +7 -7
- package/docs/fr/08-llm-zero-history-test.md +21 -48
- package/docs/fr/09-shadow-vs-light-dom.md +43 -162
- package/docs/fr/10-app-architecture.md +110 -33
- package/docs/fr/11-layouts.md +24 -12
- package/docs/fr/12-auth-and-api.md +63 -22
- package/docs/fr/13-deployment.md +7 -10
- package/docs/fr/14-testing.md +1 -1
- package/docs/fr/16-bake-cookbook.md +2 -2
- package/docs/fr/18-api-freeze-map.md +1 -1
- package/docs/fr/20-v1-stability.md +1 -1
- package/docs/recipes/nginx/README.md +13 -0
- package/docs/ru/00-the-mado-way.md +53 -75
- package/docs/ru/01-routing.md +68 -143
- package/docs/ru/02-project-layout.md +61 -41
- package/docs/ru/03-static-bake.md +2 -2
- package/docs/ru/05-why-mado.md +6 -6
- package/docs/ru/06-for-backenders.md +7 -7
- package/docs/ru/08-llm-zero-history-test.md +9 -14
- package/docs/ru/09-shadow-vs-light-dom.md +43 -178
- package/docs/ru/10-app-architecture.md +115 -63
- package/docs/ru/11-layouts.md +24 -24
- package/docs/ru/12-auth-and-api.md +57 -35
- package/docs/ru/13-deployment.md +7 -11
- package/docs/ru/14-testing.md +1 -1
- package/docs/ru/16-bake-cookbook.md +12 -6
- package/docs/ru/18-api-freeze-map.md +5 -3
- package/docs/ru/20-v1-stability.md +1 -1
- package/docs/uk/00-the-mado-way.md +70 -44
- package/docs/uk/01-routing.md +41 -47
- package/docs/uk/02-project-layout.md +68 -41
- package/docs/uk/03-static-bake.md +1 -2
- package/docs/uk/06-for-backenders.md +3 -3
- package/docs/uk/08-llm-zero-history-test.md +22 -24
- package/docs/uk/09-shadow-vs-light-dom.md +37 -86
- package/docs/uk/10-app-architecture.md +72 -31
- package/docs/uk/11-layouts.md +25 -12
- package/docs/uk/12-auth-and-api.md +58 -22
- package/docs/uk/13-deployment.md +4 -3
- package/docs/uk/14-testing.md +1 -1
- package/docs/uk/18-api-freeze-map.md +1 -1
- package/docs/uk/20-v1-stability.md +1 -1
- package/llms.txt +14 -15
- package/package.json +18 -11
- package/scripts/_config.mjs +15 -161
- package/scripts/bake.mjs +67 -57
- package/scripts/cli/generate.mjs +348 -0
- package/scripts/cli/help.mjs +27 -0
- package/scripts/cli/index.mjs +79 -0
- package/scripts/cli/init.mjs +153 -0
- package/scripts/cli/release.mjs +152 -0
- package/scripts/cli/run.mjs +96 -0
- package/scripts/cli.mjs +2 -621
- package/scripts/package-smoke.mjs +4 -1
- package/scripts/preview.mjs +13 -37
- package/scripts/size-budget.mjs +5 -2
- package/scripts/vite.default.mjs +11 -0
- package/starters/default/.editorconfig +12 -0
- package/starters/default/README.md +74 -0
- package/starters/default/eslint.config.mjs +256 -0
- package/starters/default/index.html +13 -0
- package/starters/default/package.json +30 -0
- package/starters/default/public/favicon.svg +4 -0
- package/starters/default/src/app.routes.ts +39 -0
- package/starters/default/src/layouts/app-shell.layout.ts +35 -0
- package/starters/default/src/layouts/auth-shell.layout.ts +17 -0
- package/starters/default/src/main.ts +16 -0
- package/starters/default/src/modules/auth/_contracts/auth-api.types.ts +17 -0
- package/starters/default/src/modules/auth/auth.connector.ts +45 -0
- package/starters/default/src/modules/auth/auth.guard.ts +22 -0
- package/starters/default/src/modules/auth/auth.public.ts +9 -0
- package/starters/default/src/modules/auth/auth.routes.ts +8 -0
- package/starters/default/src/modules/auth/auth.service.ts +71 -0
- package/starters/default/src/modules/auth/auth.types.ts +15 -0
- package/starters/default/src/modules/auth/login.page.ts +62 -0
- package/starters/default/src/modules/billing/_contracts/stripe.types.ts +17 -0
- package/starters/default/src/modules/billing/api/stripe.connector.ts +71 -0
- package/starters/default/src/modules/billing/billing.public.ts +5 -0
- package/starters/default/src/modules/billing/billing.routes.ts +9 -0
- package/starters/default/src/modules/billing/billing.types.ts +15 -0
- package/starters/default/src/modules/billing/components/invoice-status-badge.component.ts +43 -0
- package/starters/default/src/modules/billing/data/invoices.resource.ts +35 -0
- package/starters/default/src/modules/billing/pages/invoice-detail.page.ts +70 -0
- package/starters/default/src/modules/billing/pages/invoices-list.page.ts +73 -0
- package/starters/default/src/modules/home/home.page.ts +34 -0
- package/starters/default/src/modules/home/not-found.page.ts +11 -0
- package/starters/default/src/shared/http/http-client.ts +86 -0
- package/starters/default/src/shared/http/http-error.ts +37 -0
- package/starters/default/src/shared/http/interceptors.ts +59 -0
- package/starters/default/src/shared/lib/format-date.ts +19 -0
- package/starters/default/src/shared/styles/content.css +70 -0
- package/starters/default/src/shared/styles/reset.css +32 -0
- package/starters/default/src/shared/styles/shell.css +57 -0
- package/starters/default/src/shared/styles/tokens.css +44 -0
- package/starters/default/src/shared/ui/x-button.component.ts +49 -0
- package/starters/default/src/shared/ui/x-spinner.component.ts +22 -0
- package/starters/default/src/styles.d.ts +1 -0
- package/starters/default/src/vite-env.d.ts +1 -0
- package/starters/default/tsconfig.json +24 -0
- package/starters/default/vite.config.ts +9 -0
- package/MADO_V1_PLAN.md +0 -179
- package/ROADMAP.md +0 -178
- package/dist/src/html.d.ts +0 -18
- package/dist/src/html.js +0 -17
- package/dist/src/html.js.map +0 -1
- package/dist/src/router.d.ts +0 -13
- package/dist/src/router.js +0 -13
- package/dist/src/router.js.map +0 -1
- package/scripts/bundle.mjs +0 -212
- package/scripts/llm-zero-history-smoke.mjs +0 -93
- package/scripts/new.mjs +0 -80
- package/scripts/showcase-regression.mjs +0 -392
- package/server/serve.mjs +0 -455
- package/starters/admin/README.md +0 -63
- package/starters/admin/index.html +0 -28
- package/starters/admin/mado.config.json +0 -22
- package/starters/admin/package.json +0 -24
- package/starters/admin/public/favicon.svg +0 -4
- package/starters/admin/src/components/x-button.ts +0 -82
- package/starters/admin/src/components/x-input.ts +0 -105
- package/starters/admin/src/layouts/app.ts +0 -101
- package/starters/admin/src/layouts/auth.ts +0 -41
- package/starters/admin/src/lib/api.ts +0 -184
- package/starters/admin/src/lib/auth.ts +0 -83
- package/starters/admin/src/main.ts +0 -15
- package/starters/admin/src/pages/admin/dashboard.ts +0 -48
- package/starters/admin/src/pages/admin/order-detail.ts +0 -80
- package/starters/admin/src/pages/admin/orders.ts +0 -117
- package/starters/admin/src/pages/home.ts +0 -34
- package/starters/admin/src/pages/login.ts +0 -70
- package/starters/admin/src/pages/not-found.ts +0 -12
- package/starters/admin/src/routes.ts +0 -40
- package/starters/admin/src/styles/global.ts +0 -86
- package/starters/admin/tsconfig.json +0 -15
- package/starters/crud/README.md +0 -33
- package/starters/crud/index.html +0 -28
- package/starters/crud/mado.config.json +0 -20
- package/starters/crud/package.json +0 -24
- package/starters/crud/src/components/app-shell.ts +0 -56
- package/starters/crud/src/components/ticket-detail.ts +0 -33
- package/starters/crud/src/components/ticket-form.ts +0 -69
- package/starters/crud/src/components/ticket-list.ts +0 -66
- package/starters/crud/src/lib/api.ts +0 -76
- package/starters/crud/src/main.ts +0 -9
- package/starters/crud/src/pages/home.ts +0 -34
- package/starters/crud/src/pages/not-found.ts +0 -12
- package/starters/crud/src/pages/ticket-detail.ts +0 -7
- package/starters/crud/src/pages/ticket-new.ts +0 -7
- package/starters/crud/src/pages/tickets.ts +0 -7
- package/starters/crud/src/routes.ts +0 -11
- package/starters/crud/src/styles/global.ts +0 -155
- package/starters/crud/tsconfig.json +0 -15
- package/starters/minimal/README.md +0 -21
- package/starters/minimal/index.html +0 -28
- package/starters/minimal/mado.config.json +0 -20
- package/starters/minimal/package.json +0 -24
- package/starters/minimal/src/components/app-counter.ts +0 -31
- package/starters/minimal/src/main.ts +0 -9
- package/starters/minimal/src/pages/home.ts +0 -35
- package/starters/minimal/src/pages/not-found.ts +0 -14
- package/starters/minimal/src/routes.ts +0 -8
- package/starters/minimal/src/styles/global.ts +0 -60
- package/starters/minimal/tsconfig.json +0 -15
- package/templates/page-detail.ts +0 -63
- package/templates/page-form.ts +0 -94
- package/templates/page-list.ts +0 -79
|
@@ -2,55 +2,81 @@
|
|
|
2
2
|
|
|
3
3
|
> Один зрозумілий шлях. Жорсткі контракти. Мінімум магії.
|
|
4
4
|
|
|
5
|
-
Mado —
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```text
|
|
5
|
+
Mado — framework для команд, що будують admin panels, internal tools і
|
|
6
|
+
business SPA. Такі apps мають бути простими у розробці та нудними в підтримці,
|
|
7
|
+
тому Mado обирає чіткі conventions замість п'яти рівноправних стилів.
|
|
8
|
+
|
|
9
|
+
## Principles
|
|
10
|
+
|
|
11
|
+
1. **One way.** If code feels unusual, first check whether a canonical helper/API
|
|
12
|
+
already exists.
|
|
13
|
+
2. **Explicit over magic.** No file-system scanners, implicit globals or hidden
|
|
14
|
+
side effects.
|
|
15
|
+
3. **Platform first.** Web Components, History API, `<form>`, `fetch` and Shadow
|
|
16
|
+
DOM stay visible.
|
|
17
|
+
4. **Strict types.** `tsc --strict --noUncheckedIndexedAccess` always.
|
|
18
|
+
5. **No runtime dependencies.** Dev/build tooling is fine; Mado runtime stays
|
|
19
|
+
native.
|
|
20
|
+
|
|
21
|
+
## Project Structure
|
|
22
|
+
|
|
23
|
+
```txt
|
|
26
24
|
src/
|
|
27
|
-
├──
|
|
28
|
-
├──
|
|
29
|
-
├──
|
|
30
|
-
├──
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
├── main.ts ← boot: global CSS/providers + render router
|
|
26
|
+
├── app.routes.ts ← readable app map, exports `manifest` + default routes()
|
|
27
|
+
├── layouts/ ← app-zone wrappers (`page({ view: ({ child }) => ... })`)
|
|
28
|
+
├── shared/ ← UI bricks, http client, pure lib, global CSS
|
|
29
|
+
└── modules/ ← bounded contexts
|
|
30
|
+
└── billing/
|
|
31
|
+
├── billing.routes.ts
|
|
32
|
+
├── billing.public.ts
|
|
33
|
+
├── billing.types.ts
|
|
34
|
+
├── pages/
|
|
35
|
+
├── data/
|
|
36
|
+
├── api/
|
|
37
|
+
└── _contracts/
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The default starter is the canonical version of this shape.
|
|
41
|
+
|
|
42
|
+
## One Component = One File
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { component, css, html } from "@madojs/mado";
|
|
46
|
+
|
|
47
|
+
component("x-user-card", () => () => html`<div class="card"><slot></slot></div>`, {
|
|
48
|
+
styles: css`
|
|
49
|
+
.card { padding: 1rem; }
|
|
50
|
+
`,
|
|
51
|
+
});
|
|
34
52
|
```
|
|
35
53
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
зміни виконуються через `mutation()`, списки рендеряться через `each()`.
|
|
54
|
+
Importing the component file registers the element. Import it where the tag is
|
|
55
|
+
used.
|
|
39
56
|
|
|
40
|
-
##
|
|
57
|
+
## One Way To Describe A Page
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { html, page, resource, jsonFetcher } from "@madojs/mado";
|
|
61
|
+
|
|
62
|
+
export default page({
|
|
63
|
+
title: ({ id }) => `User #${id}`,
|
|
64
|
+
view: ({ params }) => {
|
|
65
|
+
const user = resource(() => `/api/users/${params.id}`, jsonFetcher());
|
|
66
|
+
return html`...`;
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
```
|
|
41
70
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
префікс домену: `app-*`, `crm-*`, `ticket-*`, `admin-*`.
|
|
71
|
+
Page-local signals, resources and forms live inside `view()`. Module-wide state
|
|
72
|
+
lives in `*.service.ts`.
|
|
45
73
|
|
|
46
|
-
##
|
|
74
|
+
## What We Do Not Do
|
|
47
75
|
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
-
|
|
53
|
-
стану; використовуємо `each()`.
|
|
54
|
-
- Не додаємо runtime-залежності без дуже вагомої причини.
|
|
76
|
+
- No JSX/Vue/Svelte syntax.
|
|
77
|
+
- No custom elements without a hyphen.
|
|
78
|
+
- No signal `.value`; a signal is a function.
|
|
79
|
+
- No direct `innerHTML`.
|
|
80
|
+
- No runtime packages without discussion.
|
|
55
81
|
|
|
56
|
-
|
|
82
|
+
When in doubt, document one clear recipe instead of adding a new core primitive.
|
package/docs/uk/01-routing.md
CHANGED
|
@@ -1,76 +1,70 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Routing
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
> One app map. No folder scanners. No magic path syntax.
|
|
4
|
+
|
|
5
|
+
Mado uses an explicit route manifest. Route composition should be readable in
|
|
6
|
+
one place: `src/app.routes.ts`.
|
|
5
7
|
|
|
6
8
|
```ts
|
|
7
|
-
import { routes } from "@madojs/mado";
|
|
9
|
+
import { layout, routes } from "@madojs/mado";
|
|
10
|
+
import { requireAuth } from "./modules/auth/auth.public";
|
|
11
|
+
import { authRoutes } from "./modules/auth/auth.routes";
|
|
12
|
+
import { billingRoutes } from "./modules/billing/billing.routes";
|
|
8
13
|
|
|
9
14
|
export const manifest = {
|
|
10
|
-
"/": () => import("./
|
|
11
|
-
"/
|
|
12
|
-
|
|
15
|
+
"/": () => import("./modules/home/home.page.js"),
|
|
16
|
+
"/login": layout({
|
|
17
|
+
layout: () => import("./layouts/auth-shell.layout.js"),
|
|
18
|
+
routes: authRoutes,
|
|
19
|
+
}),
|
|
20
|
+
"/billing": layout({
|
|
21
|
+
layout: () => import("./layouts/app-shell.layout.js"),
|
|
22
|
+
guard: requireAuth,
|
|
23
|
+
routes: billingRoutes,
|
|
24
|
+
}),
|
|
25
|
+
"*": () => import("./modules/home/not-found.page.js"),
|
|
13
26
|
};
|
|
14
27
|
|
|
15
28
|
export default routes(manifest);
|
|
16
29
|
```
|
|
17
30
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
```ts
|
|
21
|
-
import { page, html } from "@madojs/mado";
|
|
22
|
-
|
|
23
|
-
export default page<{ id: string }>({
|
|
24
|
-
title: ({ id }) => `User ${id}`,
|
|
25
|
-
view: ({ params }) => html`<x-user data-id=${params.id}></x-user>`,
|
|
26
|
-
});
|
|
27
|
-
```
|
|
31
|
+
Export `manifest` so `mado bake` can read it.
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
Маршрут може бути lazy import або готовим `page({...})`.
|
|
33
|
+
## Module Routes
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
Modules export plain route maps. They do not call `layout()`.
|
|
33
36
|
|
|
34
37
|
```ts
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"/": () => import("./pages/home.js"),
|
|
39
|
-
"/app": nested({
|
|
40
|
-
layout: () => import("./layouts/app-layout.js"),
|
|
41
|
-
routes: {
|
|
42
|
-
"/dashboard": () => import("./pages/dashboard.js"),
|
|
43
|
-
"/settings": () => import("./pages/settings.js"),
|
|
44
|
-
},
|
|
45
|
-
}),
|
|
38
|
+
export const billingRoutes = {
|
|
39
|
+
"/invoices": () => import("./pages/invoices-list.page.js"),
|
|
40
|
+
"/invoices/:id": () => import("./pages/invoice-detail.page.js"),
|
|
46
41
|
};
|
|
47
|
-
|
|
48
|
-
export default routes(manifest);
|
|
49
42
|
```
|
|
50
43
|
|
|
51
|
-
|
|
52
|
-
notifications.
|
|
44
|
+
The prefix is applied by `src/app.routes.ts`.
|
|
53
45
|
|
|
54
|
-
##
|
|
46
|
+
## Page
|
|
55
47
|
|
|
56
|
-
|
|
48
|
+
```ts
|
|
49
|
+
import { html, page } from "@madojs/mado";
|
|
57
50
|
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
export default page<{ id: string }>({
|
|
52
|
+
title: ({ id }) => `User ${id}`,
|
|
53
|
+
view: ({ params }) => html`<h1>${params.id}</h1>`,
|
|
54
|
+
});
|
|
60
55
|
```
|
|
61
56
|
|
|
62
|
-
|
|
57
|
+
## Navigation
|
|
63
58
|
|
|
64
59
|
```ts
|
|
65
|
-
import
|
|
60
|
+
import appRoutes from "./app.routes.js";
|
|
66
61
|
|
|
67
|
-
navigate("/
|
|
62
|
+
appRoutes.navigate("/billing/invoices");
|
|
63
|
+
appRoutes.navigate("/billing/invoices?page=2");
|
|
64
|
+
appRoutes.navigate("/login", { replace: true });
|
|
68
65
|
```
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
навігації та `dispose()` для тестів/dev overlay.
|
|
72
|
-
|
|
73
|
-
## Query params
|
|
67
|
+
## Query Params
|
|
74
68
|
|
|
75
69
|
```ts
|
|
76
70
|
import { queryParam } from "@madojs/mado";
|
|
@@ -79,4 +73,4 @@ const search = queryParam("q", "");
|
|
|
79
73
|
search.set("mado");
|
|
80
74
|
```
|
|
81
75
|
|
|
82
|
-
`queryParam()`
|
|
76
|
+
`queryParam()` returns a signal-like API and syncs state with the URL.
|
|
@@ -1,46 +1,73 @@
|
|
|
1
1
|
# Структура проєкту
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
├──
|
|
9
|
-
├──
|
|
10
|
-
├──
|
|
11
|
-
├──
|
|
12
|
-
├──
|
|
13
|
-
└──
|
|
3
|
+
Кожен Mado-застосунок використовує одну канонічну форму. Це потрібно, щоб люди
|
|
4
|
+
й AI-асистенти однаково розуміли, де живе код.
|
|
5
|
+
|
|
6
|
+
```txt
|
|
7
|
+
my-app/
|
|
8
|
+
├── package.json # runtime dep: @madojs/mado
|
|
9
|
+
├── tsconfig.json # strict TS, ES2022, Bundler resolution
|
|
10
|
+
├── vite.config.ts # mado() from @madojs/mado/vite
|
|
11
|
+
├── index.html # Vite entry + SPA shell
|
|
12
|
+
├── public/ # static assets: favicon, images, robots.txt
|
|
13
|
+
└── src/
|
|
14
|
+
├── main.ts # imports CSS and mounts router into #app
|
|
15
|
+
├── app.routes.ts # app map: manifest + default routes(...)
|
|
16
|
+
├── layouts/ # app-zone layouts
|
|
17
|
+
├── shared/ # ui, http, lib, styles
|
|
18
|
+
└── modules/ # bounded contexts
|
|
19
|
+
└── billing/
|
|
20
|
+
├── billing.routes.ts
|
|
21
|
+
├── billing.public.ts
|
|
22
|
+
├── billing.types.ts
|
|
23
|
+
├── pages/
|
|
24
|
+
├── data/
|
|
25
|
+
├── api/
|
|
26
|
+
└── _contracts/
|
|
14
27
|
```
|
|
15
28
|
|
|
16
|
-
##
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
29
|
+
## Artifact States
|
|
30
|
+
|
|
31
|
+
| Folder | Що це | Хто пише | Deploy? |
|
|
32
|
+
| --- | --- | --- | --- |
|
|
33
|
+
| `src/` | TypeScript sources | ви | no |
|
|
34
|
+
| `public/` | static assets copied as-is | ви | via `out/` |
|
|
35
|
+
| `out/` | deploy artifact: SPA shell + assets + baked HTML | `mado release` | yes |
|
|
36
|
+
|
|
37
|
+
`mado release` = `typecheck` + Vite build (`out/index.html`, `out/assets/`,
|
|
38
|
+
`public/*`) + `bake` directly into route paths + `sitemap.xml` + precompression.
|
|
39
|
+
|
|
40
|
+
## Where To Put Files
|
|
41
|
+
|
|
42
|
+
| What | Where |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| Page for a new URL | `src/modules/<module>/pages/<name>.page.ts` + module routes |
|
|
45
|
+
| Module route map | `src/modules/<module>/<module>.routes.ts` |
|
|
46
|
+
| App shell/layout | `src/layouts/<zone>.layout.ts` |
|
|
47
|
+
| Shared UI widget | `src/shared/ui/<x-name>.component.ts` |
|
|
48
|
+
| Module-only UI widget | `src/modules/<module>/components/<name>.component.ts` |
|
|
49
|
+
| API connector | `src/modules/<module>/api/<provider>.connector.ts` |
|
|
50
|
+
| Data resource/mutation | `src/modules/<module>/data/<name>.resource.ts` |
|
|
51
|
+
| Auth/session | `src/modules/auth/` |
|
|
52
|
+
| Public module surface | `src/modules/<module>/<module>.public.ts` |
|
|
53
|
+
| Pure function without UI | `src/shared/lib/<name>.ts` |
|
|
54
|
+
| Static image / favicon | `public/<file>` |
|
|
55
|
+
| App-zone shell CSS | `src/shared/styles/shell.css` |
|
|
56
|
+
| Page-level CSS | `src/shared/styles/content.css` |
|
|
57
|
+
|
|
58
|
+
## Vite Config
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { defineConfig } from "vite";
|
|
62
|
+
import { mado } from "@madojs/mado/vite";
|
|
63
|
+
|
|
64
|
+
export default defineConfig({
|
|
65
|
+
plugins: [mado()],
|
|
66
|
+
css: {
|
|
67
|
+
transformer: "lightningcss",
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
```
|
|
44
71
|
|
|
45
|
-
|
|
46
|
-
|
|
72
|
+
Starter uses Vite's Lightning CSS transformer. Mado does not own prefixing, CSS
|
|
73
|
+
lowering or minification.
|
|
@@ -42,8 +42,7 @@ export default page<{ slug: string }, Product>({
|
|
|
42
42
|
## Edge prerender
|
|
43
43
|
|
|
44
44
|
Для великих наборів сторінок можна робити той самий підхід на edge: Cloudflare
|
|
45
|
-
Worker генерує HTML на cache miss, кладе в KV і віддає з TTL.
|
|
46
|
-
`examples/cloudflare`.
|
|
45
|
+
Worker генерує HTML на cache miss, кладе в KV і віддає з TTL.
|
|
47
46
|
|
|
48
47
|
Це не hydration. Клієнтський Mado-застосунок все одно стартує нормально і
|
|
49
48
|
перерендерює сторінку після завантаження.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|---|---|
|
|
8
8
|
| router | `routes()` |
|
|
9
9
|
| handler | `page().view` |
|
|
10
|
-
| middleware/layout | `
|
|
10
|
+
| middleware/layout | `layout()` route group |
|
|
11
11
|
| cache get-or-set | `resource()` |
|
|
12
12
|
| POST/PUT/DELETE | `mutation()` |
|
|
13
13
|
| cache invalidation | `invalidates` / `invalidate()` |
|
|
@@ -44,8 +44,8 @@ Mado використовує schema-based validation, близьку до HTML
|
|
|
44
44
|
|
|
45
45
|
## Auth
|
|
46
46
|
|
|
47
|
-
Auth зазвичай живе в `
|
|
48
|
-
робити через
|
|
47
|
+
Auth зазвичай живе в `modules/auth` як signal/context. Protected area зручно
|
|
48
|
+
робити через `layout()` group, який перевіряє session і показує dashboard або
|
|
49
49
|
redirect/login.
|
|
50
50
|
|
|
51
51
|
## Правило
|
|
@@ -1,36 +1,34 @@
|
|
|
1
1
|
# LLM Zero-History Test
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Mado
|
|
3
|
+
This document defines a manual validation test: can a fresh LLM write idiomatic
|
|
4
|
+
Mado without falling back to React-shaped code?
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Allowed Context
|
|
7
7
|
|
|
8
8
|
- `AGENTS.md`
|
|
9
9
|
- `README.md`
|
|
10
|
-
- `docs/uk/07-llm-pitfalls.md`
|
|
11
|
-
- `examples
|
|
12
|
-
|
|
10
|
+
- `docs/uk/07-llm-pitfalls.md` or the English version
|
|
11
|
+
- files from the external `madojs-examples` workspace only when the agent asks
|
|
12
|
+
for a larger app pattern
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Task
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
- Немає signal `.value`.
|
|
18
|
-
- Reactive child bindings з `count()` обгорнуті у `() =>`.
|
|
19
|
-
- Boolean attributes пишуться як `?disabled`, `?checked`.
|
|
20
|
-
- Динамічні списки використовують `each()`.
|
|
21
|
-
- Imports мають `.js`.
|
|
22
|
-
- `resource()` створюється в lifecycle-aware контексті.
|
|
16
|
+
Build a small ticket-admin SPA:
|
|
23
17
|
|
|
24
|
-
|
|
18
|
+
- routes: `/`, `/tickets`, `/tickets/new`, `/tickets/:id`, `*`;
|
|
19
|
+
- in-memory mock API with realistic async delays;
|
|
20
|
+
- list page with `resource()`, `queryParam()`, `computed()` and keyed `each()`;
|
|
21
|
+
- create/edit flows with `useForm()` + `mutation()` + `invalidates`;
|
|
22
|
+
- local UI state with `signal()`.
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
resources, mutations, invalidation, `queryParam`, `computed`, `signal` і
|
|
28
|
-
keyed lists.
|
|
24
|
+
## Failure Checklist
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
`
|
|
33
|
-
|
|
26
|
+
- JSX, `useState`, `useEffect`, `ref`, `$state`, class-style components;
|
|
27
|
+
- `${signal()}` where a reactive child thunk is required;
|
|
28
|
+
- `disabled=${...}` instead of `?disabled=${...}`;
|
|
29
|
+
- unkeyed `.map()` for dynamic lists;
|
|
30
|
+
- `resource()` created outside lifecycle-aware context;
|
|
31
|
+
- new runtime dependencies or new public APIs.
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
The historical tickets implementation lives in the external examples workspace.
|
|
34
|
+
The core repository no longer ships that artifact.
|
|
@@ -1,107 +1,58 @@
|
|
|
1
1
|
# Shadow DOM vs Light DOM
|
|
2
2
|
|
|
3
|
-
Mado
|
|
4
|
-
|
|
3
|
+
Mado components use Shadow DOM by default. This is good for self-contained
|
|
4
|
+
widgets, but app zones and pages usually stay as light DOM templates.
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Rule
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
UI-дерева — app shell, sidebar, modal, table, page section — за замовчуванням
|
|
10
|
-
робіть Web Component через `component()`.
|
|
11
|
-
|
|
12
|
-
Звичайні функції залишайте для маленьких inline helpers:
|
|
13
|
-
|
|
14
|
-
```ts
|
|
15
|
-
const money = (value: number) => html`<span>${formatMoney(value)}</span>`;
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Не варто робити app shell функцією в public examples. Це працює, але ховає
|
|
19
|
-
browser model замість того, щоб її пояснювати.
|
|
20
|
-
|
|
21
|
-
Використовуйте **Shadow DOM** для leaf widgets:
|
|
22
|
-
|
|
23
|
-
- buttons, badges, cards, metrics;
|
|
24
|
-
- modals, toasts, small visual components;
|
|
25
|
-
- embedded widgets, які не мають випадково успадковувати app CSS;
|
|
26
|
-
- components, чиї styles належать самому component.
|
|
27
|
-
|
|
28
|
-
Використовуйте **Light DOM** (`{ shadow: false }`) для app structure, якій
|
|
29
|
-
потрібні global CSS utilities:
|
|
30
|
-
|
|
31
|
-
- route/page components;
|
|
32
|
-
- admin screens з dense table/form layouts;
|
|
33
|
-
- data-heavy screens з tables and forms;
|
|
34
|
-
- місця, де children мають залишатися normal document DOM.
|
|
35
|
-
|
|
36
|
-
Використовуйте **Shadow DOM** для slot-based layouts:
|
|
37
|
-
|
|
38
|
-
- app shells, які render `<slot>`;
|
|
39
|
-
- sidebar/content wrappers;
|
|
40
|
-
- reusable layout frames, які володіють своїм grid/header/sidebar CSS.
|
|
41
|
-
|
|
42
|
-
`<slot>` — це feature Shadow DOM. У component з `shadow: false` тег `<slot>` є
|
|
43
|
-
звичайним DOM element і не переносить children у це місце layout.
|
|
44
|
-
|
|
45
|
-
## Як працює import
|
|
8
|
+
Use route layouts for app zones:
|
|
46
9
|
|
|
47
10
|
```ts
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
11
|
+
export default page({
|
|
12
|
+
view: ({ child }) => html`<main class="app-main">${child}</main>`,
|
|
13
|
+
});
|
|
51
14
|
```
|
|
52
15
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
React-style component value, який передається як function.
|
|
56
|
-
|
|
57
|
-
## Routing and links
|
|
16
|
+
These files live in `src/layouts/` and are composed from `src/app.routes.ts`
|
|
17
|
+
with `layout()`. They are styled by `src/shared/styles/shell.css`.
|
|
58
18
|
|
|
59
|
-
|
|
60
|
-
`event.composedPath()`.
|
|
19
|
+
Use page files for screens:
|
|
61
20
|
|
|
62
21
|
```ts
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
22
|
+
export default page({
|
|
23
|
+
view: () => html`<section><h1>Users</h1></section>`,
|
|
24
|
+
});
|
|
66
25
|
```
|
|
67
26
|
|
|
68
|
-
|
|
27
|
+
Page-level tables, forms, prose and simple states are styled by
|
|
28
|
+
`src/shared/styles/content.css`.
|
|
69
29
|
|
|
70
|
-
|
|
30
|
+
Use Shadow DOM components for leaf widgets:
|
|
71
31
|
|
|
72
|
-
|
|
73
|
-
|
|
32
|
+
- buttons, badges, cards, metrics;
|
|
33
|
+
- spinners, modals, toasts;
|
|
34
|
+
- widgets that should own their CSS.
|
|
74
35
|
|
|
75
36
|
```ts
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
37
|
+
component("x-status-badge", ({ attr }) => {
|
|
38
|
+
const status = attr("status", "draft");
|
|
39
|
+
return () => html`<span>${status}</span>`;
|
|
40
|
+
}, {
|
|
41
|
+
styles: css`
|
|
42
|
+
:host { display: inline-block; }
|
|
43
|
+
span { color: var(--color-text-muted); }
|
|
44
|
+
`,
|
|
45
|
+
});
|
|
81
46
|
```
|
|
82
47
|
|
|
83
|
-
|
|
84
|
-
`<ticket-list>`. File має бути imported десь першим. Після import він викликає
|
|
85
|
-
`customElements.define(...)`, і tag стає відомим у поточному document.
|
|
86
|
-
|
|
87
|
-
Не робіть bulk-import усіх components у `main.ts` "just in case". Це працює в
|
|
88
|
-
tiny demos, але ховає ownership і ламає lazy route loading. Краще:
|
|
89
|
-
|
|
90
|
-
- global app shell/providers імпортувати в `main.ts`;
|
|
91
|
-
- components, якими володіє одна page, імпортувати в цій page;
|
|
92
|
-
- shared feature components імпортувати у feature entry page;
|
|
93
|
-
- truly global leaf components імпортувати в `main.ts` лише якщо вони реально
|
|
94
|
-
використовуються всюди.
|
|
95
|
-
|
|
96
|
-
## Showcase lesson
|
|
97
|
-
|
|
98
|
-
`examples/showcase` використовує цей split навмисно:
|
|
48
|
+
## Style Behavior
|
|
99
49
|
|
|
100
|
-
- `
|
|
101
|
-
- `
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
|
|
50
|
+
- `tokens.css` defines CSS custom properties; `var(...)` crosses Shadow DOM.
|
|
51
|
+
- `reset.css`, `shell.css`, `content.css` apply only to document/light DOM.
|
|
52
|
+
- Class selectors like `.data`, `.app-main`, `.error` do not cross Shadow DOM.
|
|
53
|
+
- Component-local styles live in ``css`...` `` inside `component()` options.
|
|
54
|
+
- If a Shadow component accepts children, use `<slot>` and style the frame in
|
|
55
|
+
component styles.
|
|
105
56
|
|
|
106
|
-
|
|
107
|
-
|
|
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.
|