@madojs/mado 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +102 -25
- package/CHANGELOG.md +158 -1
- package/README.md +135 -172
- package/dist/src/component.d.ts +3 -2
- package/dist/src/component.js +98 -6
- package/dist/src/component.js.map +1 -1
- package/dist/src/css.js +34 -5
- package/dist/src/css.js.map +1 -1
- package/dist/src/head.d.ts +6 -6
- package/dist/src/head.js +6 -6
- package/dist/src/html/parser.d.ts +1 -1
- package/dist/src/html/parser.js +1 -1
- package/dist/src/html/template.js +24 -10
- package/dist/src/html/template.js.map +1 -1
- package/dist/src/index.d.ts +8 -4
- package/dist/src/index.js +13 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/page.d.ts +62 -28
- package/dist/src/page.js +5 -0
- package/dist/src/page.js.map +1 -1
- package/dist/src/resource.js +2 -1
- package/dist/src/resource.js.map +1 -1
- package/dist/src/router/base.d.ts +65 -0
- package/dist/src/router/base.js +168 -0
- package/dist/src/router/base.js.map +1 -0
- package/dist/src/router/manifest.js +75 -31
- package/dist/src/router/manifest.js.map +1 -1
- package/dist/src/router/match.d.ts +16 -2
- package/dist/src/router/match.js +41 -1
- package/dist/src/router/match.js.map +1 -1
- package/dist/src/router/navigation.js +58 -9
- package/dist/src/router/navigation.js.map +1 -1
- package/dist/src/static-runtime.d.ts +81 -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 +31 -3
- package/dist/src/vite/index.js +49 -18
- 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/en/00-the-mado-way.md +1 -1
- package/docs/en/01-quickstart.md +183 -0
- package/docs/en/10-pages-and-components.md +271 -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 +225 -0
- package/docs/en/14-forms.md +244 -0
- package/docs/en/15-static-snapshots.md +181 -0
- package/docs/en/{10-app-architecture.md → 16-app-architecture.md} +53 -2
- package/docs/en/{13-deployment.md → 20-deployment.md} +59 -10
- package/docs/en/{14-testing.md → 22-testing.md} +6 -5
- package/docs/en/23-cookbook.md +292 -0
- package/docs/en/{18-api-freeze-map.md → 30-api-freeze-map.md} +12 -3
- package/docs/en/{20-v1-stability.md → 32-v1-stability.md} +3 -3
- package/docs/en/40-llm-guide.md +776 -0
- package/docs/en/{06-for-backenders.md → 41-for-backenders.md} +15 -16
- package/docs/en/{05-why-mado.md → 42-why-mado.md} +1 -1
- package/docs/en/README.md +97 -27
- package/docs/recipes/nginx/Containerfile +26 -0
- package/docs/recipes/nginx/nginx.conf +42 -0
- package/llms.txt +246 -211
- package/package.json +17 -10
- package/scripts/bake.mjs +4 -569
- package/scripts/cli/generate.mjs +55 -5
- package/scripts/cli/help.mjs +11 -5
- package/scripts/cli/index.mjs +20 -4
- package/scripts/cli/init.mjs +7 -2
- package/scripts/cli/release.mjs +20 -68
- package/scripts/docs-lint.mjs +170 -0
- package/scripts/package-smoke.mjs +67 -9
- package/scripts/preview.mjs +95 -6
- package/scripts/size-budget.mjs +5 -1
- package/scripts/static/browser.mjs +654 -0
- package/scripts/static/discover.mjs +281 -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 +191 -0
- package/starters/default/README.md +57 -55
- package/starters/default/package.json +3 -9
- 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/vite.config.ts +14 -1
- 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 +8 -5
- 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/billing/pages/invoices-list.page.ts +3 -3
- 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/modular/src/styles.d.ts +1 -0
- package/starters/modular/src/vite-env.d.ts +1 -0
- package/starters/modular/tsconfig.json +24 -0
- package/starters/modular/vite.config.ts +131 -0
- package/TODO.md +0 -79
- 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/starters/default/src/modules/home/not-found.page.ts +0 -11
- /package/docs/en/{15-error-handling.md → 21-error-handling.md} +0 -0
- /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/auth/login.page.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/modules/billing/pages/invoice-detail.page.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/reset.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-button.component.ts +0 -0
- /package/starters/{default → modular}/src/shared/ui/x-spinner.component.ts +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// App entry point. The ONLY file allowed to import CSS at runtime.
|
|
2
|
+
import "./shared/styles/tokens.css";
|
|
3
|
+
import "./shared/styles/reset.css";
|
|
4
|
+
import "./shared/styles/shell.css";
|
|
5
|
+
import "./shared/styles/content.css";
|
|
6
|
+
|
|
7
|
+
import { html, render } from "@madojs/mado";
|
|
8
|
+
|
|
9
|
+
import { init as initAuth } from "./modules/auth/auth.service";
|
|
10
|
+
import appRoutes from "./app.routes";
|
|
11
|
+
|
|
12
|
+
// One-off boot order: init cross-cutting modules first, then render the
|
|
13
|
+
// router view into #app.
|
|
14
|
+
await initAuth();
|
|
15
|
+
|
|
16
|
+
render(html`${appRoutes.view}`, document.getElementById("app")!);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { each, html, page, signal, untracked } from "@madojs/mado";
|
|
1
|
+
import { each, html, page, routeUrl, signal, untracked } from "@madojs/mado";
|
|
2
2
|
|
|
3
3
|
import { formatDate, formatMoney } from "../../../shared/lib/format-date";
|
|
4
4
|
import "../../../shared/ui/x-spinner.component";
|
|
@@ -50,7 +50,7 @@ export default page({
|
|
|
50
50
|
(i) => html`
|
|
51
51
|
<tr>
|
|
52
52
|
<td>
|
|
53
|
-
<a href=${`/billing/invoices/${i.id}`}>${i.number}</a>
|
|
53
|
+
<a data-link href=${routeUrl(`/billing/invoices/${i.id}`)}>${i.number}</a>
|
|
54
54
|
</td>
|
|
55
55
|
<td>${i.customerEmail}</td>
|
|
56
56
|
<td>${formatMoney(i.amount, i.currency)}</td>
|
|
@@ -59,7 +59,7 @@ export default page({
|
|
|
59
59
|
</td>
|
|
60
60
|
<td>${formatDate(i.issuedAt)}</td>
|
|
61
61
|
<td>
|
|
62
|
-
<a href=${`/billing/invoices/${i.id}`}>Open</a>
|
|
62
|
+
<a data-link href=${routeUrl(`/billing/invoices/${i.id}`)}>Open</a>
|
|
63
63
|
</td>
|
|
64
64
|
</tr>
|
|
65
65
|
`,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// A page should be read top-to-bottom and understood without jumping files.
|
|
8
8
|
|
|
9
|
-
import { html, page } from "@madojs/mado";
|
|
9
|
+
import { html, page, routeUrl } from "@madojs/mado";
|
|
10
10
|
|
|
11
11
|
// 1. LOCAL STATE — none
|
|
12
12
|
// 2. DATA — none
|
|
@@ -14,20 +14,18 @@ import { html, page } from "@madojs/mado";
|
|
|
14
14
|
|
|
15
15
|
// 4. VIEW
|
|
16
16
|
export default page({
|
|
17
|
+
static: true,
|
|
17
18
|
title: "Home",
|
|
18
19
|
head: () => ({
|
|
19
20
|
description: "A modular Mado application.",
|
|
20
21
|
}),
|
|
21
|
-
bake: {
|
|
22
|
-
paths: () => [{}],
|
|
23
|
-
data: () => ({}),
|
|
24
|
-
},
|
|
25
22
|
view: () => html`
|
|
26
23
|
<section>
|
|
27
24
|
<h1>Mado App</h1>
|
|
28
25
|
<p>
|
|
29
|
-
Welcome. Try
|
|
30
|
-
<a href
|
|
26
|
+
Welcome. Try
|
|
27
|
+
<a data-link href=${routeUrl("/billing/invoices")}>billing</a>
|
|
28
|
+
or <a data-link href=${routeUrl("/login")}>sign in</a>.
|
|
31
29
|
</p>
|
|
32
30
|
</section>
|
|
33
31
|
`,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { html, page, routeUrl } from "@madojs/mado";
|
|
2
|
+
|
|
3
|
+
export default page({
|
|
4
|
+
title: "Not Found",
|
|
5
|
+
view: () => html`
|
|
6
|
+
<section>
|
|
7
|
+
<h1>404</h1>
|
|
8
|
+
<p>
|
|
9
|
+
This route does not exist.
|
|
10
|
+
<a data-link href=${routeUrl("/")}>Go home</a>.
|
|
11
|
+
</p>
|
|
12
|
+
</section>
|
|
13
|
+
`,
|
|
14
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module "*.css";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noImplicitOverride": true,
|
|
9
|
+
"noUncheckedIndexedAccess": true,
|
|
10
|
+
"exactOptionalPropertyTypes": true,
|
|
11
|
+
"noFallthroughCasesInSwitch": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"skipLibCheck": true,
|
|
16
|
+
"useDefineForClassFields": true,
|
|
17
|
+
"experimentalDecorators": false,
|
|
18
|
+
"isolatedModules": true,
|
|
19
|
+
"verbatimModuleSyntax": true,
|
|
20
|
+
"noEmit": true
|
|
21
|
+
},
|
|
22
|
+
"include": ["src/**/*.ts", "src/**/*.d.ts"],
|
|
23
|
+
"exclude": ["node_modules", "out"]
|
|
24
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { defineConfig, type Plugin } from "vite";
|
|
2
|
+
import { mado } from "@madojs/mado/vite";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tiny in-memory mock for the auth + billing API used by this starter.
|
|
6
|
+
*
|
|
7
|
+
* Mounted only in `dev` so `mado release` ships a clean bundle that
|
|
8
|
+
* talks to your real backend. The endpoint shape mirrors what
|
|
9
|
+
* `auth.connector.ts` and `stripe.connector.ts` expect: any change in
|
|
10
|
+
* one must be mirrored in the other.
|
|
11
|
+
*
|
|
12
|
+
* Disable by removing this plugin or setting `MADO_MOCK_API=0`.
|
|
13
|
+
*/
|
|
14
|
+
function devApiMock(): Plugin {
|
|
15
|
+
const users = new Map<string, { password: string; user: unknown }>([
|
|
16
|
+
[
|
|
17
|
+
"demo@mado.dev",
|
|
18
|
+
{
|
|
19
|
+
password: "demo",
|
|
20
|
+
user: {
|
|
21
|
+
id: "u_demo",
|
|
22
|
+
email: "demo@mado.dev",
|
|
23
|
+
roles: ["user"],
|
|
24
|
+
permissions: ["billing.read"],
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
]);
|
|
29
|
+
const invoices = Array.from({ length: 6 }, (_, i) => ({
|
|
30
|
+
id: `in_${1000 + i}`,
|
|
31
|
+
number: `INV-${1000 + i}`,
|
|
32
|
+
customer_email: "demo@mado.dev",
|
|
33
|
+
amount_due: 1000 * (i + 1),
|
|
34
|
+
currency: "usd",
|
|
35
|
+
status: i % 3 === 0 ? "paid" : i % 3 === 1 ? "pending" : "draft",
|
|
36
|
+
created: Math.floor(Date.now() / 1000) - i * 86_400,
|
|
37
|
+
}));
|
|
38
|
+
let token: string | null = null;
|
|
39
|
+
const json = (res: import("http").ServerResponse, status: number, body: unknown) => {
|
|
40
|
+
res.statusCode = status;
|
|
41
|
+
res.setHeader("content-type", "application/json");
|
|
42
|
+
res.end(JSON.stringify(body));
|
|
43
|
+
};
|
|
44
|
+
const readJson = (req: import("http").IncomingMessage) =>
|
|
45
|
+
new Promise<Record<string, unknown>>((resolve) => {
|
|
46
|
+
let buf = "";
|
|
47
|
+
req.on("data", (chunk) => (buf += chunk));
|
|
48
|
+
req.on("end", () => {
|
|
49
|
+
try {
|
|
50
|
+
resolve(buf ? JSON.parse(buf) : {});
|
|
51
|
+
} catch {
|
|
52
|
+
resolve({});
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
name: "starter:dev-api-mock",
|
|
59
|
+
apply: "serve",
|
|
60
|
+
configureServer(server) {
|
|
61
|
+
if (process.env.MADO_MOCK_API === "0") return;
|
|
62
|
+
server.middlewares.use(async (req, res, next) => {
|
|
63
|
+
const url = req.url ?? "";
|
|
64
|
+
if (!url.startsWith("/api/")) return next();
|
|
65
|
+
|
|
66
|
+
// ---- auth -----------------------------------------------------
|
|
67
|
+
if (url === "/api/auth/login" && req.method === "POST") {
|
|
68
|
+
const body = await readJson(req);
|
|
69
|
+
const entry = users.get(String(body.email ?? ""));
|
|
70
|
+
if (!entry || entry.password !== body.password) {
|
|
71
|
+
return json(res, 401, { error: "invalid_credentials" });
|
|
72
|
+
}
|
|
73
|
+
token = `tok_${Math.random().toString(36).slice(2)}`;
|
|
74
|
+
return json(res, 200, { token, user: entry.user });
|
|
75
|
+
}
|
|
76
|
+
if (url === "/api/auth/me" && req.method === "GET") {
|
|
77
|
+
if (!token) return json(res, 401, { error: "unauthenticated" });
|
|
78
|
+
const entry = users.values().next().value;
|
|
79
|
+
return json(res, 200, entry.user);
|
|
80
|
+
}
|
|
81
|
+
if (url === "/api/auth/logout" && req.method === "POST") {
|
|
82
|
+
token = null;
|
|
83
|
+
return json(res, 204, null);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ---- billing --------------------------------------------------
|
|
87
|
+
if (url.startsWith("/api/billing/stripe/invoices")) {
|
|
88
|
+
const id = url.match(/\/invoices\/([^/?]+)/)?.[1];
|
|
89
|
+
if (req.method === "POST" && url.endsWith("/pay") && id) {
|
|
90
|
+
const inv = invoices.find((x) => x.id === id);
|
|
91
|
+
if (!inv) return json(res, 404, { error: "not_found" });
|
|
92
|
+
inv.status = "paid";
|
|
93
|
+
return json(res, 200, inv);
|
|
94
|
+
}
|
|
95
|
+
if (id && req.method === "GET") {
|
|
96
|
+
const inv = invoices.find((x) => x.id === id);
|
|
97
|
+
return inv
|
|
98
|
+
? json(res, 200, inv)
|
|
99
|
+
: json(res, 404, { error: "not_found" });
|
|
100
|
+
}
|
|
101
|
+
if (req.method === "GET") {
|
|
102
|
+
return json(res, 200, { data: invoices, has_more: false });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return next();
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export default defineConfig({
|
|
113
|
+
plugins: [
|
|
114
|
+
devApiMock(),
|
|
115
|
+
mado({
|
|
116
|
+
// Public origin used to build absolute URLs for static snapshots
|
|
117
|
+
// (sitemap, canonical, OpenGraph). Combined with Vite's `base`,
|
|
118
|
+
// the canonical for a route is `site + base + pathname`.
|
|
119
|
+
//
|
|
120
|
+
// REQUIRED when any page declares `static`. Set to your deployment
|
|
121
|
+
// origin (e.g. https://your-app.example) before running
|
|
122
|
+
// `mado release`. Override per environment via:
|
|
123
|
+
// mado release --base-url https://staging.example
|
|
124
|
+
// MADO_SITE=https://staging.example mado release
|
|
125
|
+
// site: "https://your-app.example",
|
|
126
|
+
}),
|
|
127
|
+
],
|
|
128
|
+
css: {
|
|
129
|
+
transformer: "lightningcss",
|
|
130
|
+
},
|
|
131
|
+
});
|
package/TODO.md
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# TODO
|
|
2
|
-
|
|
3
|
-
Backlog, not a roadmap. Keep this file small and honest: ideas stay here only
|
|
4
|
-
while they are plausible next work. Promote concrete work to issues or release
|
|
5
|
-
notes when it becomes real.
|
|
6
|
-
|
|
7
|
-
## Before Public Demo / Release
|
|
8
|
-
|
|
9
|
-
- [ ] Run the default starter as a real demo app and feed every friction point
|
|
10
|
-
back into docs, CLI generators, or starter structure.
|
|
11
|
-
- [ ] Add focused tests for every `mado new` generator template.
|
|
12
|
-
- [ ] Audit EN/RU/FR docs after each tooling/runtime change; no old `dist/`,
|
|
13
|
-
`tsc-only`, custom-bundler, or legacy example wording should remain.
|
|
14
|
-
- [ ] Keep `mado preview` boring and reliable inside generated apps.
|
|
15
|
-
- [ ] Add a short maintainer-only release checklist if manual npm publish stays
|
|
16
|
-
in use.
|
|
17
|
-
|
|
18
|
-
## CLI / DX
|
|
19
|
-
|
|
20
|
-
- [ ] Decide whether `npx mado create my-app` is worth adding after package
|
|
21
|
-
naming and install friction are clear.
|
|
22
|
-
- [ ] Consider short aliases over `mado new` only after real usage shows they
|
|
23
|
-
save enough typing (`mado page`, `mado component`, etc.).
|
|
24
|
-
- [ ] Split preview/static-server internals only if `scripts/preview.mjs` grows.
|
|
25
|
-
- [ ] Typed navigation inferred from route patterns.
|
|
26
|
-
- [ ] Editor highlighting docs should prefer existing tooling first; build a
|
|
27
|
-
Mado-specific VS Code extension only if current HTML/CSS template support is
|
|
28
|
-
not enough.
|
|
29
|
-
|
|
30
|
-
## Runtime / App API
|
|
31
|
-
|
|
32
|
-
- [ ] Mutation rollback helper on top of `resource().mutate()` and `mutation()`.
|
|
33
|
-
- [ ] Document loading/error shell patterns before adding an `<x-async>` API.
|
|
34
|
-
- [ ] `liveResource()` over SSE / WebSocket after a real app needs live data.
|
|
35
|
-
- [ ] i18n helper once browser-native message formatting is realistic.
|
|
36
|
-
- [ ] Accessibility helpers: focus trap, live region, click outside.
|
|
37
|
-
|
|
38
|
-
## Styling
|
|
39
|
-
|
|
40
|
-
- [ ] Docs for `::part`, `::slotted()` and `:host-context()`.
|
|
41
|
-
- [ ] Document Vite CSS options, including `css.transformer: "lightningcss"`,
|
|
42
|
-
for apps that want Lightning CSS.
|
|
43
|
-
- [ ] Optional tiny utilities stylesheet only if the starter/demo repeats the
|
|
44
|
-
same small classes often enough to justify it.
|
|
45
|
-
- [ ] CSP-friendly style mode.
|
|
46
|
-
|
|
47
|
-
## Bake / Smart Static
|
|
48
|
-
|
|
49
|
-
- [ ] Add bake regression coverage for layout groups, dynamic params, sitemap,
|
|
50
|
-
and hard-refresh deploy paths.
|
|
51
|
-
- [ ] Incremental bake cache after a real content-heavy app needs it.
|
|
52
|
-
- [ ] Webhook re-bake endpoint only after incremental bake has a concrete use.
|
|
53
|
-
- [ ] RSS / Atom feed generator for docs/blog/demo content.
|
|
54
|
-
|
|
55
|
-
## Operations
|
|
56
|
-
|
|
57
|
-
- [ ] Benchmark against Lit / Solid / Preact.
|
|
58
|
-
- [ ] Source-map notes and a minimal dev error overlay.
|
|
59
|
-
- [ ] `eslint-plugin-mado` rules only if docs/starter conventions are not enough
|
|
60
|
-
to prevent recurring mistakes.
|
|
61
|
-
- [ ] Timeout / retry options for resource fetchers after real API friction.
|
|
62
|
-
- [ ] Schema validation hook for resources after choosing a dependency-free
|
|
63
|
-
story or a clearly optional integration.
|
|
64
|
-
- [ ] PWA scaffold only if the public demo or a real app needs offline behavior.
|
|
65
|
-
|
|
66
|
-
## Community / GitHub
|
|
67
|
-
|
|
68
|
-
- [ ] Minimal landing site built with Mado.
|
|
69
|
-
- [ ] Public live demo: CRUD starter and showcase hosted from Mado itself.
|
|
70
|
-
- [ ] Case study: real React page -> Mado + bake.
|
|
71
|
-
- [ ] Convert contribution-ready TODO items into GitHub issues with labels
|
|
72
|
-
(`good first issue`, `help wanted`, `docs`, `cli`, `ci`) after the first
|
|
73
|
-
public feedback round.
|
|
74
|
-
- [ ] Configure npm Trusted Publishing for GitHub Actions after verifying the
|
|
75
|
-
first manual publishes.
|
|
76
|
-
- [ ] Decide the release flow: continue manual npm publish for patch releases or
|
|
77
|
-
publish from signed `v*` tags through `.github/workflows/release.yml`.
|
|
78
|
-
- [ ] Improve generated release notes with PR links/authors when releases start
|
|
79
|
-
going through pull requests.
|
package/docs/en/01-routing.md
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
# Routing
|
|
2
|
-
|
|
3
|
-
> One app map. No folder scanners. No special path syntax.
|
|
4
|
-
|
|
5
|
-
Mado does not infer routes from files. The browser sees files as files; route
|
|
6
|
-
composition should be readable in one place.
|
|
7
|
-
|
|
8
|
-
## App Manifest
|
|
9
|
-
|
|
10
|
-
Use `src/app.routes.ts` as the application map:
|
|
11
|
-
|
|
12
|
-
```ts
|
|
13
|
-
import { layout, routes } from "@madojs/mado";
|
|
14
|
-
import { requireAuth } from "./modules/auth/auth.public";
|
|
15
|
-
import { authRoutes } from "./modules/auth/auth.routes";
|
|
16
|
-
import { billingRoutes } from "./modules/billing/billing.routes";
|
|
17
|
-
|
|
18
|
-
export const manifest = {
|
|
19
|
-
"/": () => import("./modules/home/home.page.js"),
|
|
20
|
-
"/login": layout({
|
|
21
|
-
layout: () => import("./layouts/auth-shell.layout.js"),
|
|
22
|
-
routes: authRoutes,
|
|
23
|
-
}),
|
|
24
|
-
"/billing": layout({
|
|
25
|
-
layout: () => import("./layouts/app-shell.layout.js"),
|
|
26
|
-
guard: requireAuth,
|
|
27
|
-
routes: billingRoutes,
|
|
28
|
-
}),
|
|
29
|
-
"*": () => import("./modules/home/not-found.page.js"),
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export default routes(manifest);
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Open `app.routes.ts` and you can see the whole app: public pages, auth zone,
|
|
36
|
-
protected app zones, guards and shells.
|
|
37
|
-
|
|
38
|
-
Exporting `manifest` is important because `mado bake` reads it.
|
|
39
|
-
|
|
40
|
-
## Module Routes
|
|
41
|
-
|
|
42
|
-
Modules export plain route maps. They do not call `layout()` and they do not
|
|
43
|
-
decide which shell wraps them.
|
|
44
|
-
|
|
45
|
-
```ts
|
|
46
|
-
// src/modules/billing/billing.routes.ts
|
|
47
|
-
export const billingRoutes = {
|
|
48
|
-
"/invoices": () => import("./pages/invoices-list.page.js"),
|
|
49
|
-
"/invoices/:id": () => import("./pages/invoice-detail.page.js"),
|
|
50
|
-
};
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
The prefix is applied by `src/app.routes.ts` when the module is mounted under
|
|
54
|
-
`"/billing"`.
|
|
55
|
-
|
|
56
|
-
## What Goes On The Right Side
|
|
57
|
-
|
|
58
|
-
### Lazy page import
|
|
59
|
-
|
|
60
|
-
```ts
|
|
61
|
-
"/users/:id": () => import("./modules/users/pages/user-profile.page.js"),
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Vite creates a separate chunk for dynamic imports in production.
|
|
65
|
-
|
|
66
|
-
### Eager page
|
|
67
|
-
|
|
68
|
-
```ts
|
|
69
|
-
import home from "./modules/home/home.page.js";
|
|
70
|
-
|
|
71
|
-
export const manifest = {
|
|
72
|
-
"/": home,
|
|
73
|
-
};
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Use this only for tiny critical pages.
|
|
77
|
-
|
|
78
|
-
### Layout group
|
|
79
|
-
|
|
80
|
-
```ts
|
|
81
|
-
"/admin": layout({
|
|
82
|
-
layout: () => import("./layouts/app-shell.layout.js"),
|
|
83
|
-
guard: requireAuth,
|
|
84
|
-
routes: adminRoutes,
|
|
85
|
-
}),
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
A layout is a normal `page({...})` file:
|
|
89
|
-
|
|
90
|
-
```ts
|
|
91
|
-
import { html, page } from "@madojs/mado";
|
|
92
|
-
|
|
93
|
-
export default page({
|
|
94
|
-
view: ({ child }) => html`
|
|
95
|
-
<div class="layout layout--app">
|
|
96
|
-
<main class="app-main">${child}</main>
|
|
97
|
-
</div>
|
|
98
|
-
`,
|
|
99
|
-
});
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Keep layout views stateless. Put page-specific signals, resources and forms in
|
|
103
|
-
pages/components/resources, not in layout locals.
|
|
104
|
-
|
|
105
|
-
## Page Contract
|
|
106
|
-
|
|
107
|
-
```ts
|
|
108
|
-
import { html, page } from "@madojs/mado";
|
|
109
|
-
|
|
110
|
-
export default page<{ id: string }>({
|
|
111
|
-
title: ({ id }) => `User ${id}`,
|
|
112
|
-
view: ({ params }) => html`<h1>${params.id}</h1>`,
|
|
113
|
-
});
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
If a lazy route does not default-export `page({...})`, `routes()` throws a clear
|
|
117
|
-
error.
|
|
118
|
-
|
|
119
|
-
## Navigation
|
|
120
|
-
|
|
121
|
-
```ts
|
|
122
|
-
import appRoutes from "./app.routes.js";
|
|
123
|
-
|
|
124
|
-
appRoutes.navigate("/billing/invoices");
|
|
125
|
-
appRoutes.navigate("/billing/invoices?page=2");
|
|
126
|
-
appRoutes.navigate("/login", { replace: true });
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
Clicks on `<a href="/foo">` are intercepted for same-origin SPA navigation.
|
|
130
|
-
External links still use normal browser navigation.
|
|
131
|
-
|
|
132
|
-
## Query Parameters
|
|
133
|
-
|
|
134
|
-
```ts
|
|
135
|
-
import { queryParam } from "@madojs/mado";
|
|
136
|
-
|
|
137
|
-
const page = queryParam("page", "1");
|
|
138
|
-
page();
|
|
139
|
-
page.set("2");
|
|
140
|
-
page.set(null);
|
|
141
|
-
page.set("3", { push: true });
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
`queryParam()` is a signal. Use it in pages or components when URL state is part
|
|
145
|
-
of the UI.
|
|
146
|
-
|
|
147
|
-
## What Is Intentionally Absent
|
|
148
|
-
|
|
149
|
-
- No auto-scan of page folders.
|
|
150
|
-
- No special filesystem route syntax like `[id]`, `(group)`, `_layout`.
|
|
151
|
-
- No server routes in the client manifest.
|
|
152
|
-
- No hidden layout discovery. App zones are explicit in `app.routes.ts`.
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# Project layout
|
|
2
|
-
|
|
3
|
-
Every Mado app uses the same shape. This is a **mandatory** convention — it
|
|
4
|
-
exists so that you, your teammates, and any LLM assistant always know where
|
|
5
|
-
things live.
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
my-app/
|
|
9
|
-
├── package.json # exactly one runtime dep: @madojs/mado
|
|
10
|
-
├── tsconfig.json # strict TS, ES2022, Bundler resolution
|
|
11
|
-
├── vite.config.ts # optional; use mado() from @madojs/mado/vite
|
|
12
|
-
├── index.html # Vite entry + SPA shell
|
|
13
|
-
├── public/ # static assets (favicons, images, robots.txt)
|
|
14
|
-
└── src/
|
|
15
|
-
├── main.ts # entry: mount router into #app
|
|
16
|
-
├── app.routes.ts # one app map (default + named `manifest`)
|
|
17
|
-
├── layouts/ # app-zone layouts, not domain modules
|
|
18
|
-
├── shared/ # ui, http, lib, styles
|
|
19
|
-
└── modules/ # bounded contexts
|
|
20
|
-
└── billing/
|
|
21
|
-
├── billing.routes.ts
|
|
22
|
-
├── billing.public.ts
|
|
23
|
-
├── billing.types.ts
|
|
24
|
-
├── pages/
|
|
25
|
-
├── data/
|
|
26
|
-
├── api/
|
|
27
|
-
└── _contracts/
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## The three artifact states (read this once, never wonder again)
|
|
31
|
-
|
|
32
|
-
| Folder | What it is | Who writes | Who reads | Deploy? |
|
|
33
|
-
|-------------|----------------------------------------------------------------|-------------------|----------------------------|-------------------|
|
|
34
|
-
| `src/` | your source (TypeScript) | you | Vite, `tsc --noEmit` | ❌ no |
|
|
35
|
-
| `public/` | static assets copied as-is (favicon, images, robots.txt) | you | Vite build | ✅ via `out/` |
|
|
36
|
-
| `out/` | **the deploy artifact**: SPA shell + assets + baked HTML | `mado release` | nginx / CDN / Cloudflare | ✅ **yes** |
|
|
37
|
-
|
|
38
|
-
One-liner to remember:
|
|
39
|
-
> Develop with `mado dev`. To ship: run `mado release`, then upload `out/`.
|
|
40
|
-
|
|
41
|
-
`mado release` = `typecheck` + Vite build (`out/index.html`, `out/assets/`,
|
|
42
|
-
`public/*`) + `bake` directly into deployable route paths + `sitemap.xml` +
|
|
43
|
-
precompressed assets and CDN helper files.
|
|
44
|
-
|
|
45
|
-
`index.html` belongs at the project root because Vite treats it as an entry
|
|
46
|
-
template, not as a static public file. Put only copy-as-is files in `public/`.
|
|
47
|
-
|
|
48
|
-
### Quick deployment matrix
|
|
49
|
-
|
|
50
|
-
| Target | Command | Where it goes |
|
|
51
|
-
|-----------------------|--------------------------------------|----------------------------|
|
|
52
|
-
| VPS + nginx | `mado release && rsync -avz out/ …` | `/var/www/<app>/` |
|
|
53
|
-
| Cloudflare Pages | `mado release && wrangler pages deploy out` | CF Pages |
|
|
54
|
-
| Netlify / S3 / GH Pages | `mado release && upload out/*` | any static host |
|
|
55
|
-
|
|
56
|
-
See `docs/en/13-deployment.md` for full recipes.
|
|
57
|
-
|
|
58
|
-
## Where to put a new file?
|
|
59
|
-
|
|
60
|
-
| What | Where |
|
|
61
|
-
|-------------------------------------|------------------------------------------------------|
|
|
62
|
-
| Page for a new URL | `src/modules/<module>/pages/<name>.page.ts` + module routes |
|
|
63
|
-
| Module route map | `src/modules/<module>/<module>.routes.ts` |
|
|
64
|
-
| App shell/layout | `src/layouts/<zone>.layout.ts` |
|
|
65
|
-
| Reusable shared UI widget | `src/shared/ui/<x-name>.component.ts` |
|
|
66
|
-
| Module-only UI widget | `src/modules/<module>/components/<name>.component.ts` |
|
|
67
|
-
| API connector | `src/modules/<module>/api/<provider>.connector.ts` |
|
|
68
|
-
| Data resource/mutation | `src/modules/<module>/data/<name>.resource.ts` |
|
|
69
|
-
| Auth/session | `src/modules/auth/` |
|
|
70
|
-
| Public module surface | `src/modules/<module>/<module>.public.ts` |
|
|
71
|
-
| Pure function with no UI | `src/shared/lib/<name>.ts` |
|
|
72
|
-
| Static image / favicon | `public/<file>` |
|
|
73
|
-
| App-zone shell CSS | `src/shared/styles/shell.css` |
|
|
74
|
-
| Page-level table/form/prose CSS | `src/shared/styles/content.css` |
|
|
75
|
-
|
|
76
|
-
If you don't know where — that is a signal that **the architecture is
|
|
77
|
-
suffering**. Ask the team and **record** the answer in `docs/`. Don't invent a
|
|
78
|
-
new top-level folder.
|
|
79
|
-
|
|
80
|
-
## Naming rules
|
|
81
|
-
|
|
82
|
-
| What | Style | Example |
|
|
83
|
-
|-------------------------------------|----------------------|------------------------|
|
|
84
|
-
| File | kebab-case | `user-profile.ts` |
|
|
85
|
-
| Component tag | `x-` + kebab | `<x-user-profile>` |
|
|
86
|
-
| Context | PascalCase + `Ctx` | `ThemeCtx`, `AuthCtx` |
|
|
87
|
-
| Signal | camelCase | `userId`, `isLoggedIn` |
|
|
88
|
-
| Page-internal element | `x-<route>-page` | `<x-posts-page>` |
|
|
89
|
-
|
|
90
|
-
## Vite config
|
|
91
|
-
|
|
92
|
-
App/dev/build settings live in `vite.config.ts`.
|
|
93
|
-
|
|
94
|
-
```ts
|
|
95
|
-
import { defineConfig } from "vite";
|
|
96
|
-
import { mado } from "@madojs/mado/vite";
|
|
97
|
-
|
|
98
|
-
export default defineConfig({
|
|
99
|
-
plugins: [mado()],
|
|
100
|
-
css: {
|
|
101
|
-
transformer: "lightningcss",
|
|
102
|
-
},
|
|
103
|
-
server: {
|
|
104
|
-
proxy: { "/api": "http://localhost:3000" },
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
The default starter opts into Vite's Lightning CSS transformer. CSS
|
|
110
|
-
minification is already handled by Vite; the explicit transformer keeps
|
|
111
|
-
prefixing and modern CSS lowering in Vite instead of in Mado.
|
|
112
|
-
|
|
113
|
-
`mado bake` uses conventions by default: `src/app.routes.ts` first,
|
|
114
|
-
then `src/routes.ts`, `index.html` as the template, and `out/` as output.
|
|
115
|
-
Use CLI flags (`--entry`, `--template`, `--out`, `--base-url`) for the few
|
|
116
|
-
values that are specific to prerendering.
|
|
117
|
-
|
|
118
|
-
## What does NOT go in `src/`
|
|
119
|
-
|
|
120
|
-
- ❌ Extra build tool configs — use `vite.config.ts` with `mado()` when needed.
|
|
121
|
-
- ❌ `.env` files — read env in `src/shared/lib/config.ts` from `import.meta.env` /
|
|
122
|
-
`process.env` and import that one module everywhere.
|
|
123
|
-
- ❌ Tests mixed with code — put them in `test/`.
|
|
124
|
-
- ❌ `examples/` folder — keep large demos outside the app repo.
|