@madojs/mado 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/AGENTS.md +291 -0
  2. package/CHANGELOG.md +23 -0
  3. package/LICENSE +21 -0
  4. package/README.md +371 -0
  5. package/ROADMAP.md +52 -0
  6. package/dist/src/component.d.ts +48 -0
  7. package/dist/src/component.js +140 -0
  8. package/dist/src/component.js.map +1 -0
  9. package/dist/src/context.d.ts +40 -0
  10. package/dist/src/context.js +67 -0
  11. package/dist/src/context.js.map +1 -0
  12. package/dist/src/css.d.ts +54 -0
  13. package/dist/src/css.js +137 -0
  14. package/dist/src/css.js.map +1 -0
  15. package/dist/src/devtools.d.ts +22 -0
  16. package/dist/src/devtools.js +63 -0
  17. package/dist/src/devtools.js.map +1 -0
  18. package/dist/src/diagnostics.d.ts +11 -0
  19. package/dist/src/diagnostics.js +28 -0
  20. package/dist/src/diagnostics.js.map +1 -0
  21. package/dist/src/each.d.ts +39 -0
  22. package/dist/src/each.js +35 -0
  23. package/dist/src/each.js.map +1 -0
  24. package/dist/src/forms.d.ts +71 -0
  25. package/dist/src/forms.js +161 -0
  26. package/dist/src/forms.js.map +1 -0
  27. package/dist/src/head.d.ts +19 -0
  28. package/dist/src/head.js +97 -0
  29. package/dist/src/head.js.map +1 -0
  30. package/dist/src/html/bindings.d.ts +78 -0
  31. package/dist/src/html/bindings.js +304 -0
  32. package/dist/src/html/bindings.js.map +1 -0
  33. package/dist/src/html/parser.d.ts +64 -0
  34. package/dist/src/html/parser.js +521 -0
  35. package/dist/src/html/parser.js.map +1 -0
  36. package/dist/src/html/template-types.d.ts +27 -0
  37. package/dist/src/html/template-types.js +8 -0
  38. package/dist/src/html/template-types.js.map +1 -0
  39. package/dist/src/html/template.d.ts +45 -0
  40. package/dist/src/html/template.js +119 -0
  41. package/dist/src/html/template.js.map +1 -0
  42. package/dist/src/html.d.ts +16 -0
  43. package/dist/src/html.js +16 -0
  44. package/dist/src/html.js.map +1 -0
  45. package/dist/src/index.d.ts +35 -0
  46. package/dist/src/index.js +39 -0
  47. package/dist/src/index.js.map +1 -0
  48. package/dist/src/lazy.d.ts +38 -0
  49. package/dist/src/lazy.js +73 -0
  50. package/dist/src/lazy.js.map +1 -0
  51. package/dist/src/lifecycle.d.ts +45 -0
  52. package/dist/src/lifecycle.js +66 -0
  53. package/dist/src/lifecycle.js.map +1 -0
  54. package/dist/src/page.d.ts +161 -0
  55. package/dist/src/page.js +38 -0
  56. package/dist/src/page.js.map +1 -0
  57. package/dist/src/persisted.d.ts +47 -0
  58. package/dist/src/persisted.js +119 -0
  59. package/dist/src/persisted.js.map +1 -0
  60. package/dist/src/resource.d.ts +120 -0
  61. package/dist/src/resource.js +275 -0
  62. package/dist/src/resource.js.map +1 -0
  63. package/dist/src/router/manifest.d.ts +56 -0
  64. package/dist/src/router/manifest.js +302 -0
  65. package/dist/src/router/manifest.js.map +1 -0
  66. package/dist/src/router/match.d.ts +62 -0
  67. package/dist/src/router/match.js +117 -0
  68. package/dist/src/router/match.js.map +1 -0
  69. package/dist/src/router/navigation.d.ts +89 -0
  70. package/dist/src/router/navigation.js +263 -0
  71. package/dist/src/router/navigation.js.map +1 -0
  72. package/dist/src/router.d.ts +13 -0
  73. package/dist/src/router.js +13 -0
  74. package/dist/src/router.js.map +1 -0
  75. package/dist/src/signal.d.ts +67 -0
  76. package/dist/src/signal.js +238 -0
  77. package/dist/src/signal.js.map +1 -0
  78. package/docs/README.md +12 -0
  79. package/docs/en/00-the-mado-way.md +106 -0
  80. package/docs/en/01-routing.md +204 -0
  81. package/docs/en/02-project-layout.md +58 -0
  82. package/docs/en/03-static-bake.md +251 -0
  83. package/docs/en/04-ide-setup.md +162 -0
  84. package/docs/en/05-why-mado.md +193 -0
  85. package/docs/en/06-for-backenders.md +422 -0
  86. package/docs/en/07-llm-pitfalls.md +486 -0
  87. package/docs/en/08-llm-zero-history-test.md +56 -0
  88. package/docs/en/09-shadow-vs-light-dom.md +122 -0
  89. package/docs/en/README.md +16 -0
  90. package/docs/fr/00-the-mado-way.md +108 -0
  91. package/docs/fr/01-routing.md +202 -0
  92. package/docs/fr/02-project-layout.md +58 -0
  93. package/docs/fr/03-static-bake.md +290 -0
  94. package/docs/fr/04-ide-setup.md +162 -0
  95. package/docs/fr/05-why-mado.md +193 -0
  96. package/docs/fr/06-for-backenders.md +432 -0
  97. package/docs/fr/07-llm-pitfalls.md +487 -0
  98. package/docs/fr/08-llm-zero-history-test.md +60 -0
  99. package/docs/fr/09-shadow-vs-light-dom.md +121 -0
  100. package/docs/fr/README.md +16 -0
  101. package/docs/ru/00-the-mado-way.md +93 -0
  102. package/docs/ru/01-routing.md +194 -0
  103. package/docs/ru/02-project-layout.md +57 -0
  104. package/docs/ru/03-static-bake.md +251 -0
  105. package/docs/ru/04-ide-setup.md +144 -0
  106. package/docs/ru/05-why-mado.md +193 -0
  107. package/docs/ru/06-for-backenders.md +422 -0
  108. package/docs/ru/07-llm-pitfalls.md +485 -0
  109. package/docs/ru/08-llm-zero-history-test.md +56 -0
  110. package/docs/ru/09-shadow-vs-light-dom.md +122 -0
  111. package/docs/ru/README.md +14 -0
  112. package/docs/uk/00-the-mado-way.md +54 -0
  113. package/docs/uk/01-routing.md +82 -0
  114. package/docs/uk/02-project-layout.md +46 -0
  115. package/docs/uk/03-static-bake.md +49 -0
  116. package/docs/uk/04-ide-setup.md +26 -0
  117. package/docs/uk/05-why-mado.md +34 -0
  118. package/docs/uk/06-for-backenders.md +50 -0
  119. package/docs/uk/07-llm-pitfalls.md +82 -0
  120. package/docs/uk/08-llm-zero-history-test.md +31 -0
  121. package/docs/uk/09-shadow-vs-light-dom.md +40 -0
  122. package/docs/uk/README.md +16 -0
  123. package/llms.txt +155 -0
  124. package/package.json +81 -0
  125. package/scripts/bake.mjs +406 -0
  126. package/scripts/bundle.mjs +146 -0
  127. package/scripts/cli.mjs +382 -0
  128. package/scripts/new.mjs +80 -0
  129. package/scripts/preview.mjs +176 -0
  130. package/scripts/release-notes.mjs +66 -0
  131. package/scripts/showcase-regression.mjs +392 -0
  132. package/server/serve.mjs +292 -0
  133. package/starters/crud/README.md +21 -0
  134. package/starters/crud/index.html +20 -0
  135. package/starters/crud/package.json +17 -0
  136. package/starters/crud/src/components/app-shell.ts +51 -0
  137. package/starters/crud/src/components/ticket-detail.ts +33 -0
  138. package/starters/crud/src/components/ticket-form.ts +69 -0
  139. package/starters/crud/src/components/ticket-list.ts +66 -0
  140. package/starters/crud/src/lib/api.ts +76 -0
  141. package/starters/crud/src/main.ts +12 -0
  142. package/starters/crud/src/pages/home.ts +18 -0
  143. package/starters/crud/src/pages/not-found.ts +12 -0
  144. package/starters/crud/src/pages/ticket-detail.ts +6 -0
  145. package/starters/crud/src/pages/ticket-new.ts +6 -0
  146. package/starters/crud/src/pages/tickets.ts +6 -0
  147. package/starters/crud/src/routes.ts +9 -0
  148. package/starters/crud/src/styles/global.ts +155 -0
  149. package/starters/crud/tsconfig.json +15 -0
  150. package/starters/minimal/README.md +19 -0
  151. package/starters/minimal/index.html +20 -0
  152. package/starters/minimal/package.json +17 -0
  153. package/starters/minimal/src/components/app-counter.ts +31 -0
  154. package/starters/minimal/src/main.ts +9 -0
  155. package/starters/minimal/src/pages/home.ts +18 -0
  156. package/starters/minimal/src/pages/not-found.ts +14 -0
  157. package/starters/minimal/src/routes.ts +6 -0
  158. package/starters/minimal/src/styles/global.ts +60 -0
  159. package/starters/minimal/tsconfig.json +15 -0
  160. package/templates/page-detail.ts +63 -0
  161. package/templates/page-form.ts +94 -0
  162. package/templates/page-list.ts +79 -0
package/ROADMAP.md ADDED
@@ -0,0 +1,52 @@
1
+ # Roadmap
2
+
3
+ Mado is pre-v1. The goal is not to grow sideways, but to keep the runtime small,
4
+ readable and reliable under real application pressure.
5
+
6
+ ## Current Focus
7
+
8
+ - Keep the public runtime API small: `html`, `component`, `routes`, `resource`,
9
+ `mutation`, `useForm`, `each`, `createContext`, `persisted`, `lazy`.
10
+ - Use real apps and examples as pressure tests before adding primitives.
11
+ - Keep runtime dependencies at zero.
12
+ - Keep English as the public/default project language, with localized docs in
13
+ `docs/ru`, `docs/fr` and `docs/uk`.
14
+
15
+ ## Before v1
16
+
17
+ - Browser compatibility pass across current Chrome, Edge, Firefox and Safari.
18
+ - Accessibility pass for examples and common component patterns.
19
+ - Public API audit: names, warnings, lifecycle rules, docs coverage.
20
+ - Release hygiene: npm provenance, GitHub repo metadata, tags and changelog.
21
+ - Size reporting command or CI summary with ESM and bundled/minified budgets.
22
+ - Real commercial app test: validate auth, forms, tables, resources, route
23
+ transitions and long-lived sessions outside toy examples.
24
+
25
+ ## Explicitly Out Of Scope For Now
26
+
27
+ - Runtime dependencies.
28
+ - UI kit / component library.
29
+ - SSR with hydration.
30
+ - Large props API.
31
+ - Framework-specific build tool.
32
+ - Plugin ecosystem before the core is stable.
33
+
34
+ ## Release History
35
+
36
+ | Date | Milestone | Notes |
37
+ |---|---|---|
38
+ | 2026-06-03 | Core stabilization | Parser rewritten as a state machine; keyed `each()` reconciliation; lifecycle-aware `resource()`; router isolation; lazy `computed()`; regression tests added. |
39
+ | 2026-06-03 | Documentation foundation | Honest README, routing docs, static bake docs, IDE setup, “Why Mado”, backend mental model and LLM pitfalls. |
40
+ | 2026-06-03 | Publish readiness | Package metadata, exports, MIT license, contributing guide, GitHub templates and CI scaffolding. |
41
+ | 2026-06-03 | Cloudflare prerender PoC | Edge prerender example for SEO without SSR hydration. |
42
+ | 2026-06-03 | AI-ready files | `AGENTS.md`, `llms.txt`, Copilot instructions and LLM-specific pitfalls. |
43
+ | 2026-06-03 | v0.3 hardening | Nested template cleanup, stale async route guard, Shadow DOM link/prefetch tests, scroll behavior, `warnOnce`, component reconnect/style tests. |
44
+ | 2026-06-05 | v0.4 showcase max | `examples/showcase` became a SaaS CRM pressure app with accounts, deals, activity, nested routes, context services and browser regression. |
45
+ | 2026-06-06 | v0.5 project shape | Unified `mado` CLI, dev server logs, docs language skeleton, examples cleanup (`basic`, `tickets`, `showcase`, `cloudflare`). |
46
+ | 2026-06-06 | Mado rebrand | Public package/import name `madojs`, CLI `mado`, brand/docs/examples updated, internal legacy markers cleaned. |
47
+ | 2026-06-06 | Public polish | English public surface, localized docs, translated code comments/examples/templates/GitHub files. |
48
+
49
+ ## Future Ideas
50
+
51
+ Ideas live in `TODO.md`. They are not commitments until a real app or issue
52
+ proves the need.
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Wrapper around Custom Elements.
3
+ *
4
+ * component('x-counter', () => {
5
+ * const count = signal(0);
6
+ * return () => html`<button @click=${() => count.update(n=>n+1)}>${count}</button>`;
7
+ * }, {
8
+ * styles: css`button { padding: .5rem }`,
9
+ * });
10
+ *
11
+ * The setup function is called once on the first connectedCallback.
12
+ * The returned render function is called via an effect, so any signals
13
+ * read inside it automatically re-render the template.
14
+ *
15
+ * Shadow DOM (open) is used by default. It can be disabled, and
16
+ * styles will be scoped via @scope (or a tag-prefix fallback).
17
+ */
18
+ import { type Disposer } from "./signal.js";
19
+ import { html, type TemplateResult } from "./html.js";
20
+ import { type CSSResult } from "./css.js";
21
+ export interface ComponentContext {
22
+ host: HTMLElement;
23
+ /** Run cleanup when the component is removed. */
24
+ onDispose(fn: Disposer): void;
25
+ }
26
+ export type SetupFn = (ctx: ComponentContext) => () => TemplateResult;
27
+ export type StyleInput = string | CSSResult | Array<string | CSSResult>;
28
+ export interface ComponentOptions {
29
+ /** Enable Shadow DOM (default: true). */
30
+ shadow?: boolean;
31
+ /**
32
+ * Component styles. Accepts:
33
+ * - a CSS string (quick start)
34
+ * - a CSSStyleSheet via `css\`...\`` (recommended — one copy in memory)
35
+ * - an array of the above
36
+ */
37
+ styles?: StyleInput;
38
+ /**
39
+ * List of observed attributes.
40
+ *
41
+ * v0.3: this is plain reflection into host[attr], without a reactive props API.
42
+ * If you need to re-render the component when an attribute changes, create a signal()
43
+ * inside setup() and update it manually from your own wrapper/event.
44
+ */
45
+ observedAttributes?: readonly string[];
46
+ }
47
+ export declare function component(tagName: string, setup: SetupFn, options?: ComponentOptions): void;
48
+ export { html };
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Wrapper around Custom Elements.
3
+ *
4
+ * component('x-counter', () => {
5
+ * const count = signal(0);
6
+ * return () => html`<button @click=${() => count.update(n=>n+1)}>${count}</button>`;
7
+ * }, {
8
+ * styles: css`button { padding: .5rem }`,
9
+ * });
10
+ *
11
+ * The setup function is called once on the first connectedCallback.
12
+ * The returned render function is called via an effect, so any signals
13
+ * read inside it automatically re-render the template.
14
+ *
15
+ * Shadow DOM (open) is used by default. It can be disabled, and
16
+ * styles will be scoped via @scope (or a tag-prefix fallback).
17
+ */
18
+ import { effect } from "./signal.js";
19
+ import { html, render } from "./html.js";
20
+ import { adopt, scopeStyles } from "./css.js";
21
+ import { createLifecycle, runInLifecycle } from "./lifecycle.js";
22
+ import { warnOnce } from "./diagnostics.js";
23
+ export function component(tagName, setup, options = {}) {
24
+ if (!tagName.includes("-")) {
25
+ warnOnce(`component-invalid-tag-${tagName}`, `component("${tagName}") skipped: Custom Element names must contain a hyphen.`);
26
+ return;
27
+ }
28
+ const existingMeta = registered.get(tagName);
29
+ if (customElements.get(tagName)) {
30
+ if (!existingMeta ||
31
+ existingMeta.setup !== setup ||
32
+ !sameComponentOptions(existingMeta.options, options)) {
33
+ warnOnce(`component-duplicate-${tagName}`, `component("${tagName}") is already registered. Re-registration with different setup/options is ignored.`);
34
+ }
35
+ return; // idempotent hot-reload
36
+ }
37
+ const useShadow = options.shadow !== false;
38
+ const observed = options.observedAttributes ?? [];
39
+ // Normalize styles to an array of CSSStyleSheet once.
40
+ // Sheets are shared across all instances — memory is not duplicated.
41
+ const stylesheets = normalizeStyles(options.styles, tagName, useShadow);
42
+ class MadoElement extends HTMLElement {
43
+ static get observedAttributes() {
44
+ return [...observed];
45
+ }
46
+ #root;
47
+ #renderer = null;
48
+ #effectDispose = null;
49
+ #lifecycle = null;
50
+ #connected = false;
51
+ constructor() {
52
+ super();
53
+ this.#root = useShadow ? this.attachShadow({ mode: "open" }) : this;
54
+ }
55
+ connectedCallback() {
56
+ if (this.#connected)
57
+ return;
58
+ this.#connected = true;
59
+ if (stylesheets.length > 0) {
60
+ if (useShadow) {
61
+ adopt(this.#root, ...stylesheets);
62
+ }
63
+ else {
64
+ installGlobalSheets(stylesheets);
65
+ }
66
+ }
67
+ // create a lifecycle for this component. Any function
68
+ // called from setup() (resource, ...) will see it via
69
+ // getCurrentLifecycle() and register its own cleanup.
70
+ const lifecycle = createLifecycle();
71
+ this.#lifecycle = lifecycle;
72
+ const ctx = {
73
+ host: this,
74
+ // ctx.onDispose proxies to lifecycle — the single source of truth
75
+ // for component cleanups (including auto-cleanup from
76
+ // resource(), navigator listeners, etc.).
77
+ onDispose: (fn) => lifecycle.onDispose(fn),
78
+ };
79
+ this.#renderer = runInLifecycle(lifecycle, () => setup(ctx));
80
+ this.#effectDispose = effect(() => {
81
+ render(this.#renderer(), this.#root);
82
+ });
83
+ }
84
+ disconnectedCallback() {
85
+ this.#effectDispose?.();
86
+ this.#effectDispose = null;
87
+ this.#lifecycle?.dispose();
88
+ this.#lifecycle = null;
89
+ this.#connected = false;
90
+ }
91
+ attributeChangedCallback(name, _old, value) {
92
+ // reflect attribute to property — user can bind a signal to it
93
+ this[name] = value;
94
+ }
95
+ }
96
+ customElements.define(tagName, MadoElement);
97
+ registered.set(tagName, { setup, options });
98
+ }
99
+ // ---------- helpers ----------
100
+ function normalizeStyles(input, tagName, useShadow) {
101
+ if (!input)
102
+ return [];
103
+ const arr = Array.isArray(input) ? input : [input];
104
+ return arr.map((s) => {
105
+ let sheet;
106
+ if (typeof s === "string") {
107
+ sheet = new CSSStyleSheet();
108
+ sheet.replaceSync(s);
109
+ }
110
+ else {
111
+ sheet = s;
112
+ }
113
+ // light DOM: scope by tag name
114
+ return useShadow ? sheet : scopeStyles(tagName, sheet);
115
+ });
116
+ }
117
+ const installedGlobal = new WeakSet();
118
+ const registered = new Map();
119
+ function sameComponentOptions(a, b) {
120
+ if (a.shadow !== b.shadow)
121
+ return false;
122
+ if (a.styles !== b.styles)
123
+ return false;
124
+ const aa = a.observedAttributes ?? [];
125
+ const bb = b.observedAttributes ?? [];
126
+ if (aa.length !== bb.length)
127
+ return false;
128
+ return aa.every((name, i) => name === bb[i]);
129
+ }
130
+ function installGlobalSheets(sheets) {
131
+ const toAdd = sheets.filter((s) => !installedGlobal.has(s));
132
+ if (toAdd.length === 0)
133
+ return;
134
+ for (const s of toAdd)
135
+ installedGlobal.add(s);
136
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, ...toAdd];
137
+ }
138
+ // Convenience re-export.
139
+ export { html };
140
+ //# sourceMappingURL=component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component.js","sourceRoot":"","sources":["../../src/component.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Context (DI) without props drilling. A native pattern from Lit / Open Web Components.
3
+ *
4
+ * const ThemeCtx = createContext<'light'|'dark'>('light');
5
+ *
6
+ * component('x-app', ({ host }) => {
7
+ * provide(host, ThemeCtx, signal('dark'));
8
+ * return () => html`<x-child/>`;
9
+ * });
10
+ *
11
+ * component('x-child', ({ host }) => {
12
+ * const theme = inject(host, ThemeCtx); // signal: () => 'dark' | 'light'
13
+ * return () => html`<div data-theme=${theme}>...</div>`;
14
+ * });
15
+ *
16
+ * How it works:
17
+ * provide() listens for the 'mado:context' event on the host — when a child
18
+ * component dispatches it (bubbles), the parent writes the current signal
19
+ * into the event detail and calls preventDefault.
20
+ * inject() dispatches the event and reads the result.
21
+ * This fully conforms to the Web Components Community Context Protocol.
22
+ */
23
+ import { type Signal } from "./signal.js";
24
+ export interface Context<T> {
25
+ readonly _ctx: true;
26
+ readonly key: symbol;
27
+ readonly defaultValue: T;
28
+ }
29
+ export declare function createContext<T>(defaultValue: T): Context<T>;
30
+ /**
31
+ * Declare that this host provides a value for the given context.
32
+ * Returns the signal itself so the provider can update it.
33
+ */
34
+ export declare function provide<T>(host: HTMLElement, ctx: Context<T>, initial: T | Signal<T>): Signal<T>;
35
+ /**
36
+ * Request a context value. Walks up the DOM tree (including
37
+ * Shadow DOM via composed) to the first provider. If not found —
38
+ * returns the defaultValue wrapped in a signal.
39
+ */
40
+ export declare function inject<T>(host: HTMLElement, ctx: Context<T>): Signal<T>;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Context (DI) without props drilling. A native pattern from Lit / Open Web Components.
3
+ *
4
+ * const ThemeCtx = createContext<'light'|'dark'>('light');
5
+ *
6
+ * component('x-app', ({ host }) => {
7
+ * provide(host, ThemeCtx, signal('dark'));
8
+ * return () => html`<x-child/>`;
9
+ * });
10
+ *
11
+ * component('x-child', ({ host }) => {
12
+ * const theme = inject(host, ThemeCtx); // signal: () => 'dark' | 'light'
13
+ * return () => html`<div data-theme=${theme}>...</div>`;
14
+ * });
15
+ *
16
+ * How it works:
17
+ * provide() listens for the 'mado:context' event on the host — when a child
18
+ * component dispatches it (bubbles), the parent writes the current signal
19
+ * into the event detail and calls preventDefault.
20
+ * inject() dispatches the event and reads the result.
21
+ * This fully conforms to the Web Components Community Context Protocol.
22
+ */
23
+ import { signal } from "./signal.js";
24
+ const CONTEXT_EVENT = "mado:context";
25
+ export function createContext(defaultValue) {
26
+ return {
27
+ _ctx: true,
28
+ key: Symbol("madoctx"),
29
+ defaultValue,
30
+ };
31
+ }
32
+ /**
33
+ * Declare that this host provides a value for the given context.
34
+ * Returns the signal itself so the provider can update it.
35
+ */
36
+ export function provide(host, ctx, initial) {
37
+ const sig = typeof initial === "function"
38
+ ? initial
39
+ : signal(initial);
40
+ const handler = (e) => {
41
+ const ce = e;
42
+ if (ce.detail.key !== ctx.key)
43
+ return;
44
+ ce.detail.value = sig;
45
+ e.stopPropagation();
46
+ };
47
+ host.addEventListener(CONTEXT_EVENT, handler);
48
+ return sig;
49
+ }
50
+ /**
51
+ * Request a context value. Walks up the DOM tree (including
52
+ * Shadow DOM via composed) to the first provider. If not found —
53
+ * returns the defaultValue wrapped in a signal.
54
+ */
55
+ export function inject(host, ctx) {
56
+ const detail = { key: ctx.key };
57
+ host.dispatchEvent(new CustomEvent(CONTEXT_EVENT, {
58
+ detail,
59
+ bubbles: true,
60
+ composed: true,
61
+ cancelable: true,
62
+ }));
63
+ if (detail.value)
64
+ return detail.value;
65
+ return signal(ctx.defaultValue);
66
+ }
67
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,MAAM,EAAe,MAAM,aAAa,CAAC;AAElD,MAAM,aAAa,GAAG,cAAc,CAAC;AAQrC,MAAM,UAAU,aAAa,CAAI,YAAe;IAC9C,OAAO;QACL,IAAI,EAAE,IAAI;QACV,GAAG,EAAE,MAAM,CAAC,SAAS,CAAC;QACtB,YAAY;KACb,CAAC;AACJ,CAAC;AAOD;;;GAGG;AACH,MAAM,UAAU,OAAO,CACrB,IAAiB,EACjB,GAAe,EACf,OAAsB;IAEtB,MAAM,GAAG,GACP,OAAO,OAAO,KAAK,UAAU;QAC3B,CAAC,CAAE,OAAqB;QACxB,CAAC,CAAC,MAAM,CAAC,OAAY,CAAC,CAAC;IAE3B,MAAM,OAAO,GAAG,CAAC,CAAQ,EAAE,EAAE;QAC3B,MAAM,EAAE,GAAG,CAAoC,CAAC;QAChD,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG;YAAE,OAAO;QACtC,EAAE,CAAC,MAAM,CAAC,KAAK,GAAG,GAAsB,CAAC;QACzC,CAAC,CAAC,eAAe,EAAE,CAAC;IACtB,CAAC,CAAC;IACF,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAE9C,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAI,IAAiB,EAAE,GAAe;IAC1D,MAAM,MAAM,GAAuB,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;IACpD,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,aAAa,EAAE;QAC7B,MAAM;QACN,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,IAAI;QACd,UAAU,EAAE,IAAI;KACjB,CAAC,CACH,CAAC;IACF,IAAI,MAAM,CAAC,KAAK;QAAE,OAAO,MAAM,CAAC,KAAkB,CAAC;IACnD,OAAO,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Styles without CSS-in-JS, but ergonomic.
3
+ *
4
+ * Idea:
5
+ * 1. `css\`...\`` — tagged literal, returns a CSSStyleSheet (Constructable Stylesheet).
6
+ * 2. Sheet is shared across all component instances (one copy in memory).
7
+ * 3. Component applies the sheet via `shadowRoot.adoptedStyleSheets`.
8
+ * 4. No runtime CSS parsers, no className hashes —
9
+ * the browser does all the work.
10
+ *
11
+ * Theming:
12
+ * - change CSS variables on :host or :root — no re-renders needed.
13
+ * - `cssVars({ '--accent': color })` → ready string for style="...".
14
+ *
15
+ * Optional scope without Shadow DOM:
16
+ * - if the browser has @scope (Chrome 118+, Safari 17.4+), we wrap styles.
17
+ * - fallback: prefix selectors with the tag — a simple regex at string level.
18
+ */
19
+ export type CSSResult = CSSStyleSheet;
20
+ /**
21
+ * Tagged literal for CSS. Returns a CSSStyleSheet ready for
22
+ * adoptedStyleSheets. Value interpolation — only primitives or
23
+ * other CSSResult (for composition).
24
+ *
25
+ * Injection guard: strings with `<` or `>` are forbidden so that
26
+ * script tags cannot accidentally be injected through styles.
27
+ */
28
+ export declare function css(strings: TemplateStringsArray, ...values: unknown[]): CSSResult;
29
+ /**
30
+ * Build an inline-style string from a CSS variables object.
31
+ *
32
+ * cssVars({ '--accent': '#f00', '--pad': '1rem' })
33
+ * → '--accent: #f00; --pad: 1rem;'
34
+ *
35
+ * Usage:
36
+ * html`<div style=${cssVars({ '--accent': color })}>...</div>`
37
+ * html`<x-app style=${cssVars(theme())}>...</x-app>` // signal — auto-update
38
+ */
39
+ export declare function cssVars(vars: Record<string, string | number>): string;
40
+ /**
41
+ * Apply sheets to a ShadowRoot. Idempotent: the same sheet
42
+ * can be adopted into dozens of components without duplicating styles.
43
+ */
44
+ export declare function adopt(root: ShadowRoot, ...sheets: CSSResult[]): void;
45
+ /**
46
+ * Build scoped style text limited to a selector (for light DOM).
47
+ * Uses native @scope if the browser supports it.
48
+ * Otherwise — naive selector prefixing.
49
+ *
50
+ * scopeStyles('x-button', 'button { color: red }')
51
+ * → '@scope (x-button) { button { color: red } }' // or
52
+ * → 'x-button button { color: red }'
53
+ */
54
+ export declare function scopeStyles(tagName: string, sheet: CSSResult): CSSResult;
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Styles without CSS-in-JS, but ergonomic.
3
+ *
4
+ * Idea:
5
+ * 1. `css\`...\`` — tagged literal, returns a CSSStyleSheet (Constructable Stylesheet).
6
+ * 2. Sheet is shared across all component instances (one copy in memory).
7
+ * 3. Component applies the sheet via `shadowRoot.adoptedStyleSheets`.
8
+ * 4. No runtime CSS parsers, no className hashes —
9
+ * the browser does all the work.
10
+ *
11
+ * Theming:
12
+ * - change CSS variables on :host or :root — no re-renders needed.
13
+ * - `cssVars({ '--accent': color })` → ready string for style="...".
14
+ *
15
+ * Optional scope without Shadow DOM:
16
+ * - if the browser has @scope (Chrome 118+, Safari 17.4+), we wrap styles.
17
+ * - fallback: prefix selectors with the tag — a simple regex at string level.
18
+ */
19
+ /**
20
+ * Tagged literal for CSS. Returns a CSSStyleSheet ready for
21
+ * adoptedStyleSheets. Value interpolation — only primitives or
22
+ * other CSSResult (for composition).
23
+ *
24
+ * Injection guard: strings with `<` or `>` are forbidden so that
25
+ * script tags cannot accidentally be injected through styles.
26
+ */
27
+ export function css(strings, ...values) {
28
+ let text = "";
29
+ for (let i = 0; i < strings.length; i++) {
30
+ text += strings[i];
31
+ if (i < strings.length - 1) {
32
+ const v = values[i];
33
+ if (v == null)
34
+ continue;
35
+ if (v instanceof CSSStyleSheet) {
36
+ // composition — insert all rules
37
+ for (const rule of v.cssRules)
38
+ text += rule.cssText;
39
+ continue;
40
+ }
41
+ const s = String(v);
42
+ if (/[<>]/.test(s)) {
43
+ throw new Error("css``: `<` and `>` are forbidden in interpolations");
44
+ }
45
+ text += s;
46
+ }
47
+ }
48
+ const sheet = new CSSStyleSheet();
49
+ sheet.replaceSync(text);
50
+ return sheet;
51
+ }
52
+ /**
53
+ * Build an inline-style string from a CSS variables object.
54
+ *
55
+ * cssVars({ '--accent': '#f00', '--pad': '1rem' })
56
+ * → '--accent: #f00; --pad: 1rem;'
57
+ *
58
+ * Usage:
59
+ * html`<div style=${cssVars({ '--accent': color })}>...</div>`
60
+ * html`<x-app style=${cssVars(theme())}>...</x-app>` // signal — auto-update
61
+ */
62
+ export function cssVars(vars) {
63
+ let out = "";
64
+ for (const k in vars) {
65
+ const v = vars[k];
66
+ if (v == null)
67
+ continue;
68
+ const name = k.startsWith("--") ? k : `--${k}`;
69
+ out += `${name}:${v};`;
70
+ }
71
+ return out;
72
+ }
73
+ /**
74
+ * Apply sheets to a ShadowRoot. Idempotent: the same sheet
75
+ * can be adopted into dozens of components without duplicating styles.
76
+ */
77
+ export function adopt(root, ...sheets) {
78
+ // append rather than overwrite — user may have already adopted something
79
+ const existing = root.adoptedStyleSheets;
80
+ const toAdd = sheets.filter((s) => !existing.includes(s));
81
+ if (toAdd.length === 0)
82
+ return;
83
+ root.adoptedStyleSheets = [...existing, ...toAdd];
84
+ }
85
+ // ---------- Scope for light DOM (without Shadow DOM) ----------
86
+ const hasScope = (() => {
87
+ try {
88
+ return CSS.supports("selector(:scope)") &&
89
+ // @scope at-rule support check
90
+ typeof CSSRule.SCOPE_RULE !==
91
+ "undefined";
92
+ }
93
+ catch {
94
+ return false;
95
+ }
96
+ })();
97
+ /**
98
+ * Build scoped style text limited to a selector (for light DOM).
99
+ * Uses native @scope if the browser supports it.
100
+ * Otherwise — naive selector prefixing.
101
+ *
102
+ * scopeStyles('x-button', 'button { color: red }')
103
+ * → '@scope (x-button) { button { color: red } }' // or
104
+ * → 'x-button button { color: red }'
105
+ */
106
+ export function scopeStyles(tagName, sheet) {
107
+ let text = "";
108
+ for (const rule of sheet.cssRules)
109
+ text += rule.cssText;
110
+ let scoped;
111
+ if (hasScope) {
112
+ scoped = `@scope (${tagName}) { ${text} }`;
113
+ }
114
+ else {
115
+ // naive: prefix every top-level selector with `tagName `.
116
+ // Works for simple cases "button {...}", "p, span {...}".
117
+ // Does not touch @-rules.
118
+ scoped = text.replace(/(^|\})\s*([^{}@][^{}]*)\{/g, (_m, brace, sel) => {
119
+ const prefixed = sel
120
+ .split(",")
121
+ .map((s) => {
122
+ const trimmed = s.trim();
123
+ if (!trimmed)
124
+ return trimmed;
125
+ if (trimmed.startsWith(tagName))
126
+ return trimmed;
127
+ return `${tagName} ${trimmed}`;
128
+ })
129
+ .join(", ");
130
+ return `${brace} ${prefixed} {`;
131
+ });
132
+ }
133
+ const out = new CSSStyleSheet();
134
+ out.replaceSync(scoped);
135
+ return out;
136
+ }
137
+ //# sourceMappingURL=css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css.js","sourceRoot":"","sources":["../../src/css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH;;;;;;;GAOG;AACH,MAAM,UAAU,GAAG,CACjB,OAA6B,EAC7B,GAAG,MAAiB;IAEpB,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACpB,IAAI,CAAC,IAAI,IAAI;gBAAE,SAAS;YACxB,IAAI,CAAC,YAAY,aAAa,EAAE,CAAC;gBAC/B,iCAAiC;gBACjC,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,QAAQ;oBAAE,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC;gBACpD,SAAS;YACX,CAAC;YACD,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACpB,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;YACxE,CAAC;YACD,IAAI,IAAI,CAAC,CAAC;QACZ,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,aAAa,EAAE,CAAC;IAClC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,OAAO,CAAC,IAAqC;IAC3D,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,IAAI,IAAI;YAAE,SAAS;QACxB,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,GAAG,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACzB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,IAAgB,EAAE,GAAG,MAAmB;IAC5D,yEAAyE;IACzE,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAC/B,IAAI,CAAC,kBAAkB,GAAG,CAAC,GAAG,QAAQ,EAAE,GAAG,KAAK,CAAC,CAAC;AACpD,CAAC;AAED,iEAAiE;AAGjE,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YACrC,+BAA+B;YAC/B,OAAQ,OAA8C,CAAC,UAAU;gBAC/D,WAAW,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC,EAAE,CAAC;AAEL;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,KAAgB;IAC3D,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,QAAQ;QAAE,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC;IAExD,IAAI,MAAc,CAAC;IACnB,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,GAAG,WAAW,OAAO,OAAO,IAAI,IAAI,CAAC;IAC7C,CAAC;SAAM,CAAC;QACN,0DAA0D;QAC1D,0DAA0D;QAC1D,0BAA0B;QAC1B,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,4BAA4B,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACrE,MAAM,QAAQ,GAAG,GAAG;iBACjB,KAAK,CAAC,GAAG,CAAC;iBACV,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE;gBACjB,MAAM,OAAO,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;gBACzB,IAAI,CAAC,OAAO;oBAAE,OAAO,OAAO,CAAC;gBAC7B,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC;oBAAE,OAAO,OAAO,CAAC;gBAChD,OAAO,GAAG,OAAO,IAAI,OAAO,EAAE,CAAC;YACjC,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,OAAO,GAAG,KAAK,IAAI,QAAQ,IAAI,CAAC;QAClC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,aAAa,EAAE,CAAC;IAChC,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACxB,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Dev logger for signals and effects.
3
+ *
4
+ * Enabled by a flag in localStorage:
5
+ * localStorage.madoDebug = '1'
6
+ *
7
+ * What it does:
8
+ * - wraps signal/effect/computed for logging
9
+ * - shows [signal] name: old → new
10
+ * - groups effects via console.group / console.groupEnd
11
+ *
12
+ * Setup:
13
+ * import '@madojs/mado/devtools.js'; // at the very top of app.ts
14
+ *
15
+ * In production — just don't import this file, and all dev code will be
16
+ * tree-shaken (or simply absent from the module graph).
17
+ *
18
+ * Implementation: imports the already-initialised signal module and
19
+ * patches the factory. A simple proof-of-concept approach; for production-
20
+ * grade DevTools a full hook is needed, but that's a different story.
21
+ */
22
+ export {};
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Dev logger for signals and effects.
3
+ *
4
+ * Enabled by a flag in localStorage:
5
+ * localStorage.madoDebug = '1'
6
+ *
7
+ * What it does:
8
+ * - wraps signal/effect/computed for logging
9
+ * - shows [signal] name: old → new
10
+ * - groups effects via console.group / console.groupEnd
11
+ *
12
+ * Setup:
13
+ * import '@madojs/mado/devtools.js'; // at the very top of app.ts
14
+ *
15
+ * In production — just don't import this file, and all dev code will be
16
+ * tree-shaken (or simply absent from the module graph).
17
+ *
18
+ * Implementation: imports the already-initialised signal module and
19
+ * patches the factory. A simple proof-of-concept approach; for production-
20
+ * grade DevTools a full hook is needed, but that's a different story.
21
+ */
22
+ import * as signalModule from "./signal.js";
23
+ const ENABLED = typeof localStorage !== "undefined" &&
24
+ localStorage.getItem("madoDebug") === "1";
25
+ if (ENABLED) {
26
+ // eslint-disable-next-line no-console
27
+ console.info("%c[mado] devtools enabled", "color: #888");
28
+ const origSignal = signalModule.signal;
29
+ let counter = 0;
30
+ // @ts-expect-error patching module live
31
+ signalModule.signal = function patchedSignal(initial) {
32
+ const name = `s${++counter}`;
33
+ const sig = origSignal(initial);
34
+ const origSet = sig.set;
35
+ sig.set = (next) => {
36
+ const prev = sig.peek();
37
+ if (!Object.is(prev, next)) {
38
+ // eslint-disable-next-line no-console
39
+ console.log(`%c[signal ${name}]`, "color: #888", prev, "→", next);
40
+ }
41
+ origSet(next);
42
+ };
43
+ return sig;
44
+ };
45
+ const origEffect = signalModule.effect;
46
+ // @ts-expect-error patching module live
47
+ signalModule.effect = function patchedEffect(fn) {
48
+ let id = 0;
49
+ return origEffect(() => {
50
+ const tag = `[effect #${++id}]`;
51
+ // eslint-disable-next-line no-console
52
+ console.groupCollapsed(tag);
53
+ try {
54
+ return fn();
55
+ }
56
+ finally {
57
+ // eslint-disable-next-line no-console
58
+ console.groupEnd();
59
+ }
60
+ });
61
+ };
62
+ }
63
+ //# sourceMappingURL=devtools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devtools.js","sourceRoot":"","sources":["../../src/devtools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,YAAY,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GACX,OAAO,YAAY,KAAK,WAAW;IACnC,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,GAAG,CAAC;AAE5C,IAAI,OAAO,EAAE,CAAC;IACZ,sCAAsC;IACtC,OAAO,CAAC,IAAI,CAAC,2BAA2B,EAAE,aAAa,CAAC,CAAC;IAEzD,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,CAAC;IACvC,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,wCAAwC;IACxC,YAAY,CAAC,MAAM,GAAG,SAAS,aAAa,CAAI,OAAU;QACxD,MAAM,IAAI,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC;QACxB,GAAG,CAAC,GAAG,GAAG,CAAC,IAAO,EAAE,EAAE;YACpB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;gBAC3B,sCAAsC;gBACtC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YACpE,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC,CAAC;QACF,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,CAAC;IACvC,wCAAwC;IACxC,YAAY,CAAC,MAAM,GAAG,SAAS,aAAa,CAAC,EAAiB;QAC5D,IAAI,EAAE,GAAG,CAAC,CAAC;QACX,OAAO,UAAU,CAAC,GAAG,EAAE;YACrB,MAAM,GAAG,GAAG,YAAY,EAAE,EAAE,GAAG,CAAC;YAChC,sCAAsC;YACtC,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,CAAC;gBACH,OAAO,EAAE,EAAU,CAAC;YACtB,CAAC;oBAAS,CAAC;gBACT,sCAAsC;gBACtC,OAAO,CAAC,QAAQ,EAAE,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Internal dev diagnostics.
3
+ *
4
+ * Warnings are intentionally best-effort: they never throw and they are printed
5
+ * once per code so noisy app renders do not flood the console.
6
+ */
7
+ export declare function warnOnce(code: string, message: string): void;
8
+ export declare const _testHooks: {
9
+ resetWarnings(): void;
10
+ seenWarnings(): string[];
11
+ };