@madojs/mado 0.12.0 → 0.13.1

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 (154) hide show
  1. package/AGENTS.md +15 -8
  2. package/CHANGELOG.md +66 -0
  3. package/README.md +25 -3
  4. package/dist/src/component.d.ts +3 -1
  5. package/dist/src/component.d.ts.map +1 -0
  6. package/dist/src/component.js +65 -30
  7. package/dist/src/component.js.map +1 -1
  8. package/dist/src/context.d.ts +13 -32
  9. package/dist/src/context.d.ts.map +1 -0
  10. package/dist/src/context.js +73 -48
  11. package/dist/src/context.js.map +1 -1
  12. package/dist/src/css.d.ts +1 -0
  13. package/dist/src/css.d.ts.map +1 -0
  14. package/dist/src/css.js.map +1 -1
  15. package/dist/src/devtools-hook.d.ts +14 -0
  16. package/dist/src/devtools-hook.d.ts.map +1 -0
  17. package/dist/src/devtools-hook.js +27 -0
  18. package/dist/src/devtools-hook.js.map +1 -0
  19. package/dist/src/devtools.d.ts +24 -22
  20. package/dist/src/devtools.d.ts.map +1 -0
  21. package/dist/src/devtools.js +230 -58
  22. package/dist/src/devtools.js.map +1 -1
  23. package/dist/src/diagnostics.d.ts +12 -0
  24. package/dist/src/diagnostics.d.ts.map +1 -0
  25. package/dist/src/diagnostics.js +68 -8
  26. package/dist/src/diagnostics.js.map +1 -1
  27. package/dist/src/each.d.ts +1 -5
  28. package/dist/src/each.d.ts.map +1 -0
  29. package/dist/src/each.js +0 -8
  30. package/dist/src/each.js.map +1 -1
  31. package/dist/src/forms.d.ts +27 -96
  32. package/dist/src/forms.d.ts.map +1 -0
  33. package/dist/src/forms.js +184 -394
  34. package/dist/src/forms.js.map +1 -1
  35. package/dist/src/head.d.ts +1 -0
  36. package/dist/src/head.d.ts.map +1 -0
  37. package/dist/src/head.js +2 -1
  38. package/dist/src/head.js.map +1 -1
  39. package/dist/src/html/bindings.d.ts +1 -0
  40. package/dist/src/html/bindings.d.ts.map +1 -0
  41. package/dist/src/html/bindings.js.map +1 -1
  42. package/dist/src/html/parser.d.ts +3 -4
  43. package/dist/src/html/parser.d.ts.map +1 -0
  44. package/dist/src/html/parser.js +63 -14
  45. package/dist/src/html/parser.js.map +1 -1
  46. package/dist/src/html/template-types.d.ts +1 -0
  47. package/dist/src/html/template-types.d.ts.map +1 -0
  48. package/dist/src/html/template-types.js.map +1 -1
  49. package/dist/src/html/template.d.ts +5 -1
  50. package/dist/src/html/template.d.ts.map +1 -0
  51. package/dist/src/html/template.js +78 -3
  52. package/dist/src/html/template.js.map +1 -1
  53. package/dist/src/index.d.ts +7 -8
  54. package/dist/src/index.d.ts.map +1 -0
  55. package/dist/src/index.js +5 -7
  56. package/dist/src/index.js.map +1 -1
  57. package/dist/src/json.d.ts +3 -0
  58. package/dist/src/json.d.ts.map +1 -0
  59. package/dist/src/json.js +14 -0
  60. package/dist/src/json.js.map +1 -0
  61. package/dist/src/lifecycle.d.ts +1 -0
  62. package/dist/src/lifecycle.d.ts.map +1 -0
  63. package/dist/src/lifecycle.js +3 -2
  64. package/dist/src/lifecycle.js.map +1 -1
  65. package/dist/src/page.d.ts +1 -0
  66. package/dist/src/page.d.ts.map +1 -0
  67. package/dist/src/page.js.map +1 -1
  68. package/dist/src/persisted.d.ts +6 -1
  69. package/dist/src/persisted.d.ts.map +1 -0
  70. package/dist/src/persisted.js +17 -11
  71. package/dist/src/persisted.js.map +1 -1
  72. package/dist/src/resource.d.ts +2 -1
  73. package/dist/src/resource.d.ts.map +1 -0
  74. package/dist/src/resource.js +126 -49
  75. package/dist/src/resource.js.map +1 -1
  76. package/dist/src/router/base.d.ts +1 -0
  77. package/dist/src/router/base.d.ts.map +1 -0
  78. package/dist/src/router/base.js.map +1 -1
  79. package/dist/src/router/manifest.d.ts +1 -0
  80. package/dist/src/router/manifest.d.ts.map +1 -0
  81. package/dist/src/router/manifest.js +89 -148
  82. package/dist/src/router/manifest.js.map +1 -1
  83. package/dist/src/router/match.d.ts +1 -0
  84. package/dist/src/router/match.d.ts.map +1 -0
  85. package/dist/src/router/match.js +19 -5
  86. package/dist/src/router/match.js.map +1 -1
  87. package/dist/src/router/navigation.d.ts +1 -0
  88. package/dist/src/router/navigation.d.ts.map +1 -0
  89. package/dist/src/router/navigation.js.map +1 -1
  90. package/dist/src/signal.d.ts +1 -0
  91. package/dist/src/signal.d.ts.map +1 -0
  92. package/dist/src/signal.js +46 -17
  93. package/dist/src/signal.js.map +1 -1
  94. package/dist/src/static-runtime.d.ts +1 -0
  95. package/dist/src/static-runtime.d.ts.map +1 -0
  96. package/dist/src/static-runtime.js.map +1 -1
  97. package/dist/src/vite/index.d.ts +1 -0
  98. package/dist/src/vite/index.d.ts.map +1 -0
  99. package/dist/src/vite/index.js +4 -3
  100. package/dist/src/vite/index.js.map +1 -1
  101. package/docs/architecture/v1-roadmap.md +19 -0
  102. package/docs/en/10-pages-and-components.md +9 -1
  103. package/docs/en/13-data.md +8 -6
  104. package/docs/en/14-forms.md +66 -219
  105. package/docs/en/15-static-snapshots.md +4 -4
  106. package/docs/en/16-app-architecture.md +3 -2
  107. package/docs/en/20-deployment.md +21 -8
  108. package/docs/en/21-error-handling.md +9 -6
  109. package/docs/en/24-devtools-and-diagnostics.md +45 -0
  110. package/docs/en/30-api-freeze-map.md +8 -9
  111. package/docs/en/32-v1-stability.md +2 -2
  112. package/docs/en/33-migration-0.12-0.13.md +56 -0
  113. package/docs/en/40-llm-guide.md +7 -9
  114. package/docs/en/41-for-backenders.md +13 -9
  115. package/docs/en/42-why-mado.md +2 -2
  116. package/docs/en/README.md +2 -0
  117. package/docs/recipes/nginx/nginx.conf +2 -2
  118. package/llms.txt +25 -7
  119. package/package.json +37 -16
  120. package/scripts/clean.mjs +8 -0
  121. package/scripts/cli/generate.mjs +11 -14
  122. package/scripts/cli/help.mjs +2 -0
  123. package/scripts/cli/index.mjs +14 -6
  124. package/scripts/cli/init.mjs +13 -18
  125. package/scripts/cli/release.mjs +22 -19
  126. package/scripts/cli/run.mjs +5 -3
  127. package/scripts/logger.mjs +117 -0
  128. package/scripts/output-guard.mjs +128 -0
  129. package/scripts/preview.mjs +11 -19
  130. package/scripts/static/browser.mjs +58 -8
  131. package/scripts/static/discover.mjs +4 -1
  132. package/scripts/static.mjs +21 -7
  133. package/starters/default/package.json +4 -4
  134. package/starters/default/tsconfig.node.json +13 -0
  135. package/starters/default/vite.config.ts +0 -3
  136. package/starters/modular/README.md +4 -4
  137. package/starters/modular/package.json +4 -4
  138. package/starters/modular/src/layouts/app-shell.layout.ts +1 -2
  139. package/starters/modular/src/modules/auth/login.page.ts +5 -12
  140. package/starters/modular/src/modules/billing/pages/invoice-detail.page.ts +3 -4
  141. package/starters/modular/src/modules/billing/pages/invoices-list.page.ts +1 -1
  142. package/starters/modular/src/shared/styles/reset.css +21 -1
  143. package/starters/modular/tsconfig.json +10 -1
  144. package/starters/modular/tsconfig.node.json +13 -0
  145. package/starters/modular/vite.config.ts +6 -5
  146. package/dist/src/lazy.d.ts +0 -38
  147. package/dist/src/lazy.js +0 -73
  148. package/dist/src/lazy.js.map +0 -1
  149. package/scripts/bake.mjs +0 -5
  150. package/scripts/docs-lint.mjs +0 -170
  151. package/scripts/package-smoke.mjs +0 -135
  152. package/scripts/release-notes.mjs +0 -66
  153. package/scripts/size-budget.mjs +0 -95
  154. package/starters/modular/src/shared/ui/x-button.component.ts +0 -49
@@ -1 +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"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"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","sourcesContent":["/** Context DI implemented through the Web Components Context Protocol. */\n\nimport { effect, signal, type Disposer, type Signal } from \"./signal.js\";\nimport { getCurrentLifecycle } from \"./lifecycle.js\";\nimport { warnOnce } from \"./diagnostics.js\";\n\nconst CONTEXT_EVENT = \"context-request\";\n\nexport interface Context<T> {\n readonly _ctx: true;\n readonly key: symbol;\n readonly defaultValue: T;\n}\n\nexport interface ContextRequestEventDetail<T = unknown> {\n context: unknown;\n callback(value: T, unsubscribe?: Disposer): void;\n subscribe?: boolean;\n}\n\nexport function createContext<T>(defaultValue: T): Context<T> {\n return {\n _ctx: true,\n key: Symbol(\"mado-context\"),\n defaultValue,\n };\n}\n\n/**\n * Provide a context value from a host. The returned signal updates protocol\n * subscribers. Listener/effect cleanup follows the current Mado lifecycle.\n */\nexport function provide<T>(\n host: HTMLElement,\n context: Context<T>,\n initial: T | Signal<T>,\n): Signal<T> {\n const source = isSignal<T>(initial) ? initial : signal(initial as T);\n const subscribers = new Set<(value: T, unsubscribe?: Disposer) => void>();\n let disposed = false;\n\n const handler = (event: Event): void => {\n const request = event as CustomEvent<ContextRequestEventDetail<T>>;\n if (request.detail?.context !== context.key) return;\n event.stopPropagation();\n\n if (!request.detail.subscribe) {\n request.detail.callback(source.peek());\n return;\n }\n\n const callback = request.detail.callback;\n let active = true;\n const unsubscribe = (): void => {\n if (!active) return;\n active = false;\n subscribers.delete(callback);\n };\n subscribers.add(callback);\n callback(source.peek(), unsubscribe);\n };\n\n host.addEventListener(CONTEXT_EVENT, handler);\n const stopPublish = effect(() => {\n const value = source();\n for (const callback of [...subscribers]) callback(value);\n });\n\n const dispose = (): void => {\n if (disposed) return;\n disposed = true;\n host.removeEventListener(CONTEXT_EVENT, handler);\n subscribers.clear();\n stopPublish();\n };\n const lifecycle = getCurrentLifecycle();\n if (lifecycle) lifecycle.onDispose(dispose);\n else {\n warnOnce(\n \"context-provider-outside-lifecycle\",\n \"provide() called outside a component/page lifecycle; its event listener cannot be cleaned up automatically.\",\n );\n }\n\n return source;\n}\n\n/** Request the nearest provider and expose its current value as a Signal. */\nexport function inject<T>(host: HTMLElement, context: Context<T>): Signal<T> {\n const value = signal(context.defaultValue);\n let unsubscribe: Disposer | undefined;\n const detail: ContextRequestEventDetail<T> = {\n context: context.key,\n subscribe: true,\n callback(next, dispose) {\n value.set(next);\n if (dispose) unsubscribe = dispose;\n },\n };\n\n host.dispatchEvent(\n new CustomEvent<ContextRequestEventDetail<T>>(CONTEXT_EVENT, {\n detail,\n bubbles: true,\n composed: true,\n }),\n );\n\n getCurrentLifecycle()?.onDispose(() => {\n unsubscribe?.();\n unsubscribe = undefined;\n });\n return value;\n}\n\nfunction isSignal<T>(value: T | Signal<T>): value is Signal<T> {\n return typeof value === \"function\" &&\n typeof (value as Partial<Signal<T>>).set === \"function\" &&\n typeof (value as Partial<Signal<T>>).peek === \"function\";\n}\n"]}
package/dist/src/css.d.ts CHANGED
@@ -52,3 +52,4 @@ export declare function adopt(root: ShadowRoot, ...sheets: CSSResult[]): void;
52
52
  * → 'x-button button { color: red }'
53
53
  */
54
54
  export declare function scopeStyles(tagName: string, sheet: CSSResult): CSSResult;
55
+ //# sourceMappingURL=css.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../src/css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,SAAS,GAAG,aAAa,CAAC;AAItC;;;;;;;GAOG;AACH,wBAAgB,GAAG,CACjB,OAAO,EAAE,oBAAoB,EAC7B,GAAG,MAAM,EAAE,OAAO,EAAE,GACnB,SAAS,CAwBX;AAoBD;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CASrE;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAMpE;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS,CA4BxE"}
@@ -1 +1 @@
1
- {"version":3,"file":"css.js","sourceRoot":"","sources":["../../src/css.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"css.js","sourceRoot":"","sources":["../../src/css.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Styles without CSS-in-JS, but ergonomic.\n *\n * Idea:\n * 1. `css\\`...\\`` — tagged literal, returns a CSSStyleSheet (Constructable Stylesheet).\n * 2. Sheet is shared across all component instances (one copy in memory).\n * 3. Component applies the sheet via `shadowRoot.adoptedStyleSheets`.\n * 4. No runtime CSS parsers, no className hashes —\n * the browser does all the work.\n *\n * Theming:\n * - change CSS variables on :host or :root — no re-renders needed.\n * - `cssVars({ '--accent': color })` → ready string for style=\"...\".\n *\n * Optional scope without Shadow DOM:\n * - if the browser has @scope (Chrome 118+, Safari 17.4+), we wrap styles.\n * - fallback: prefix selectors with the tag — a simple regex at string level.\n */\n\nexport type CSSResult = CSSStyleSheet;\n\nconst stylesheetText = new WeakMap<CSSStyleSheet, string>();\n\n/**\n * Tagged literal for CSS. Returns a CSSStyleSheet ready for\n * adoptedStyleSheets. Value interpolation — only primitives or\n * other CSSResult (for composition).\n *\n * Injection guard: strings with `<` or `>` are forbidden so that\n * script tags cannot accidentally be injected through styles.\n */\nexport function css(\n strings: TemplateStringsArray,\n ...values: unknown[]\n): CSSResult {\n let text = \"\";\n for (let i = 0; i < strings.length; i++) {\n text += strings[i];\n if (i < strings.length - 1) {\n const v = values[i];\n if (v == null) continue;\n if (v instanceof CSSStyleSheet) {\n // composition — insert all rules\n text += getStylesheetText(v) ?? rulesText(v);\n continue;\n }\n const s = String(v);\n if (/[<>]/.test(s)) {\n throw new Error(\"css``: `<` and `>` are forbidden in interpolations\");\n }\n text += s;\n }\n }\n\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(text);\n stylesheetText.set(sheet, text);\n return sheet;\n}\n\n/** @internal */\nexport function createStylesheet(text: string): CSSResult {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(text);\n stylesheetText.set(sheet, text);\n return sheet;\n}\n\n/** @internal */\nexport function getStylesheetText(sheet: CSSStyleSheet): string | undefined {\n return stylesheetText.get(sheet);\n}\n\n/** @internal */\nexport function getStylesheetHash(sheet: CSSStyleSheet): string {\n return stableHash(getStylesheetText(sheet) ?? rulesText(sheet));\n}\n\n/**\n * Build an inline-style string from a CSS variables object.\n *\n * cssVars({ '--accent': '#f00', '--pad': '1rem' })\n * → '--accent: #f00; --pad: 1rem;'\n *\n * Usage:\n * html`<div style=${cssVars({ '--accent': color })}>...</div>`\n * html`<x-app style=${cssVars(theme())}>...</x-app>` // signal — auto-update\n */\nexport function cssVars(vars: Record<string, string | number>): string {\n let out = \"\";\n for (const k in vars) {\n const v = vars[k];\n if (v == null) continue;\n const name = k.startsWith(\"--\") ? k : `--${k}`;\n out += `${name}:${v};`;\n }\n return out;\n}\n\n/**\n * Apply sheets to a ShadowRoot. Idempotent: the same sheet\n * can be adopted into dozens of components without duplicating styles.\n */\nexport function adopt(root: ShadowRoot, ...sheets: CSSResult[]): void {\n // append rather than overwrite — user may have already adopted something\n const existing = root.adoptedStyleSheets;\n const toAdd = sheets.filter((s) => !existing.includes(s));\n if (toAdd.length === 0) return;\n root.adoptedStyleSheets = [...existing, ...toAdd];\n}\n\n// ---------- Scope for light DOM (without Shadow DOM) ----------\n\n\nconst hasScope = (() => {\n try {\n return CSS.supports(\"selector(:scope)\") &&\n // @scope at-rule support check\n typeof (CSSRule as unknown as { SCOPE_RULE?: number }).SCOPE_RULE !==\n \"undefined\";\n } catch {\n return false;\n }\n})();\n\n/**\n * Build scoped style text limited to a selector (for light DOM).\n * Uses native @scope if the browser supports it.\n * Otherwise — naive selector prefixing.\n *\n * scopeStyles('x-button', 'button { color: red }')\n * → '@scope (x-button) { button { color: red } }' // or\n * → 'x-button button { color: red }'\n */\nexport function scopeStyles(tagName: string, sheet: CSSResult): CSSResult {\n const text = getStylesheetText(sheet) ?? rulesText(sheet);\n\n let scoped: string;\n if (hasScope) {\n scoped = `@scope (${tagName}) { ${text} }`;\n } else {\n // naive: prefix every top-level selector with `tagName `.\n // Works for simple cases \"button {...}\", \"p, span {...}\".\n // Does not touch @-rules.\n scoped = text.replace(/(^|\\})\\s*([^{}@][^{}]*)\\{/g, (_m, brace, sel) => {\n const prefixed = sel\n .split(\",\")\n .map((s: string) => {\n const trimmed = s.trim();\n if (!trimmed) return trimmed;\n if (trimmed.startsWith(tagName)) return trimmed;\n return `${tagName} ${trimmed}`;\n })\n .join(\", \");\n return `${brace} ${prefixed} {`;\n });\n }\n\n const out = new CSSStyleSheet();\n out.replaceSync(scoped);\n stylesheetText.set(out, scoped);\n return out;\n}\n\nfunction rulesText(sheet: CSSStyleSheet): string {\n let text = \"\";\n for (const rule of sheet.cssRules) text += rule.cssText;\n return text;\n}\n\nfunction stableHash(text: string): string {\n let hash = 0x811c9dc5;\n for (let i = 0; i < text.length; i++) {\n hash ^= text.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0).toString(36);\n}\n"]}
@@ -0,0 +1,14 @@
1
+ export declare const DEVTOOLS_HOOK_KEY: unique symbol;
2
+ export interface MadoDevtoolsEvent {
3
+ time: number;
4
+ kind: string;
5
+ target?: object;
6
+ data?: unknown;
7
+ }
8
+ export interface MadoDevtoolsHook {
9
+ readonly version: 1;
10
+ emit(event: MadoDevtoolsEvent): void;
11
+ }
12
+ export declare function emitDevtools(kind: string, target?: object, data?: unknown): void;
13
+ export declare function installDevtoolsHook(hook: MadoDevtoolsHook): () => void;
14
+ //# sourceMappingURL=devtools-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devtools-hook.d.ts","sourceRoot":"","sources":["../../src/devtools-hook.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,eAAsC,CAAC;AAErE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACtC;AAGD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,OAAO,GACb,IAAI,CAWN;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAStE"}
@@ -0,0 +1,27 @@
1
+ export const DEVTOOLS_HOOK_KEY = Symbol.for("@madojs/mado:devtools");
2
+ /* @__NO_SIDE_EFFECTS__ */
3
+ export function emitDevtools(kind, target, data) {
4
+ const hook = globalThis[DEVTOOLS_HOOK_KEY];
5
+ if (hook?.version !== 1)
6
+ return;
7
+ hook.emit({
8
+ time: typeof performance !== "undefined" ? performance.now() : Date.now(),
9
+ kind,
10
+ ...(target ? { target } : {}),
11
+ ...(data === undefined ? {} : { data }),
12
+ });
13
+ }
14
+ export function installDevtoolsHook(hook) {
15
+ const root = globalThis;
16
+ const previous = root[DEVTOOLS_HOOK_KEY];
17
+ root[DEVTOOLS_HOOK_KEY] = hook;
18
+ return () => {
19
+ if (root[DEVTOOLS_HOOK_KEY] !== hook)
20
+ return;
21
+ if (previous === undefined)
22
+ delete root[DEVTOOLS_HOOK_KEY];
23
+ else
24
+ root[DEVTOOLS_HOOK_KEY] = previous;
25
+ };
26
+ }
27
+ //# sourceMappingURL=devtools-hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devtools-hook.js","sourceRoot":"","sources":["../../src/devtools-hook.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;AAcrE,0BAA0B;AAC1B,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,MAAe,EACf,IAAc;IAEd,MAAM,IAAI,GAAI,UAA2C,CAAC,iBAAiB,CAE9D,CAAC;IACd,IAAI,IAAI,EAAE,OAAO,KAAK,CAAC;QAAE,OAAO;IAChC,IAAI,CAAC,IAAI,CAAC;QACR,IAAI,EAAE,OAAO,WAAW,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;QACzE,IAAI;QACJ,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;KACxC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAsB;IACxD,MAAM,IAAI,GAAG,UAA0C,CAAC;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACzC,IAAI,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC;IAC/B,OAAO,GAAG,EAAE;QACV,IAAI,IAAI,CAAC,iBAAiB,CAAC,KAAK,IAAI;YAAE,OAAO;QAC7C,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,iBAAiB,CAAC,CAAC;;YACtD,IAAI,CAAC,iBAAiB,CAAC,GAAG,QAAQ,CAAC;IAC1C,CAAC,CAAC;AACJ,CAAC","sourcesContent":["export const DEVTOOLS_HOOK_KEY = Symbol.for(\"@madojs/mado:devtools\");\n\nexport interface MadoDevtoolsEvent {\n time: number;\n kind: string;\n target?: object;\n data?: unknown;\n}\n\nexport interface MadoDevtoolsHook {\n readonly version: 1;\n emit(event: MadoDevtoolsEvent): void;\n}\n\n/* @__NO_SIDE_EFFECTS__ */\nexport function emitDevtools(\n kind: string,\n target?: object,\n data?: unknown,\n): void {\n const hook = (globalThis as Record<PropertyKey, unknown>)[DEVTOOLS_HOOK_KEY] as\n | MadoDevtoolsHook\n | undefined;\n if (hook?.version !== 1) return;\n hook.emit({\n time: typeof performance !== \"undefined\" ? performance.now() : Date.now(),\n kind,\n ...(target ? { target } : {}),\n ...(data === undefined ? {} : { data }),\n });\n}\n\nexport function installDevtoolsHook(hook: MadoDevtoolsHook): () => void {\n const root = globalThis as Record<PropertyKey, unknown>;\n const previous = root[DEVTOOLS_HOOK_KEY];\n root[DEVTOOLS_HOOK_KEY] = hook;\n return () => {\n if (root[DEVTOOLS_HOOK_KEY] !== hook) return;\n if (previous === undefined) delete root[DEVTOOLS_HOOK_KEY];\n else root[DEVTOOLS_HOOK_KEY] = previous;\n };\n}\n"]}
@@ -1,22 +1,24 @@
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 {};
1
+ /** Dev-only instrumentation overlay. Import this subpath before app startup. */
2
+ export type DevtoolsLogLevel = "debug" | "info" | "warn" | "error" | "silent";
3
+ export interface DevtoolsRecord {
4
+ id: number;
5
+ time: number;
6
+ kind: string;
7
+ targetId?: number;
8
+ data?: unknown;
9
+ }
10
+ export interface DevtoolsSnapshot {
11
+ version: 1;
12
+ paused: boolean;
13
+ records: DevtoolsRecord[];
14
+ }
15
+ export interface DevtoolsController {
16
+ open(): void;
17
+ close(): void;
18
+ toggle(): void;
19
+ clear(): void;
20
+ setLogLevel(level: DevtoolsLogLevel): void;
21
+ snapshot(): DevtoolsSnapshot;
22
+ }
23
+ export declare const devtools: DevtoolsController;
24
+ //# sourceMappingURL=devtools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../../src/devtools.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAShF,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE9E,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,IAAI,IAAI,CAAC;IACb,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,IAAI,IAAI,CAAC;IACf,KAAK,IAAI,IAAI,CAAC;IACd,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3C,QAAQ,IAAI,gBAAgB,CAAC;CAC9B;AAuBD,eAAO,MAAM,QAAQ,EAAE,kBAkCtB,CAAC"}
@@ -1,63 +1,235 @@
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);
1
+ /** Dev-only instrumentation overlay. Import this subpath before app startup. */
2
+ import { installDevtoolsHook, } from "./devtools-hook.js";
3
+ import { reportDiagnostic } from "./diagnostics.js";
4
+ const MAX_RECORDS = 1_000;
5
+ const records = [];
6
+ const targetIds = new WeakMap();
7
+ let nextRecordId = 0;
8
+ let nextTargetId = 0;
9
+ let paused = false;
10
+ let overlay = null;
11
+ let renderQueued = false;
12
+ const hook = {
13
+ version: 1,
14
+ emit(event) {
15
+ if (paused)
16
+ return;
17
+ records.push(toRecord(event));
18
+ if (records.length > MAX_RECORDS)
19
+ records.splice(0, records.length - MAX_RECORDS);
20
+ queueOverlayRender();
21
+ },
22
+ };
23
+ installDevtoolsHook(hook);
24
+ export const devtools = {
25
+ open() {
26
+ if (typeof document === "undefined")
27
+ return;
28
+ overlay ??= createOverlay();
29
+ if (!overlay.isConnected)
30
+ document.documentElement.appendChild(overlay);
31
+ overlay.hidden = false;
32
+ renderOverlay();
33
+ },
34
+ close() {
35
+ if (overlay)
36
+ overlay.hidden = true;
37
+ },
38
+ toggle() {
39
+ if (!overlay || overlay.hidden || !overlay.isConnected)
40
+ this.open();
41
+ else
42
+ this.close();
43
+ },
44
+ clear() {
45
+ records.length = 0;
46
+ renderOverlay();
47
+ },
48
+ setLogLevel(level) {
49
+ if (!isLogLevel(level))
50
+ throw new TypeError(`Unknown Mado log level: ${level}`);
51
+ try {
52
+ localStorage.setItem("mado:log-level", level);
53
+ }
54
+ catch {
55
+ /* storage may be disabled */
56
+ }
57
+ },
58
+ snapshot() {
59
+ return {
60
+ version: 1,
61
+ paused,
62
+ records: records.map((record) => ({ ...record })),
42
63
  };
43
- return sig;
64
+ },
65
+ };
66
+ if (typeof window !== "undefined") {
67
+ window.addEventListener("keydown", (event) => {
68
+ if (event.altKey && event.shiftKey && event.key.toLowerCase() === "m") {
69
+ event.preventDefault();
70
+ devtools.toggle();
71
+ }
72
+ });
73
+ try {
74
+ if (localStorage.getItem("madoDebug") === "1") {
75
+ reportDiagnostic("warn", "devtools", "legacy-debug-flag", "localStorage.madoDebug is deprecated; use Alt+Shift+M or devtools.open().");
76
+ devtools.setLogLevel("debug");
77
+ queueMicrotask(() => devtools.open());
78
+ }
79
+ }
80
+ catch {
81
+ /* storage may be disabled */
82
+ }
83
+ }
84
+ function toRecord(event) {
85
+ let targetId;
86
+ if (event.target) {
87
+ targetId = targetIds.get(event.target);
88
+ if (!targetId) {
89
+ targetId = ++nextTargetId;
90
+ targetIds.set(event.target, targetId);
91
+ }
92
+ }
93
+ return {
94
+ id: ++nextRecordId,
95
+ time: event.time,
96
+ kind: event.kind,
97
+ ...(targetId ? { targetId } : {}),
98
+ ...(event.data === undefined ? {} : { data: safePreview(event.data) }),
44
99
  };
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
- }
100
+ }
101
+ function createOverlay() {
102
+ const host = document.createElement("aside");
103
+ host.id = "mado-devtools";
104
+ host.setAttribute("aria-label", "Mado developer tools");
105
+ const root = host.attachShadow({ mode: "open" });
106
+ root.innerHTML = `
107
+ <style>${OVERLAY_CSS}</style>
108
+ <header><strong>Mado Devtools</strong><span data-summary></span><button data-close title="Close">×</button></header>
109
+ <nav>
110
+ <button data-tab="overview">Overview</button>
111
+ <button data-tab="reactivity">Reactivity</button>
112
+ <button data-tab="components">Components</button>
113
+ <button data-tab="router-data">Router/Data</button>
114
+ <button data-tab="timeline">Timeline/Errors</button>
115
+ </nav>
116
+ <section class="tools">
117
+ <input data-filter aria-label="Filter events" placeholder="Filter events">
118
+ <button data-pause>Pause</button><button data-clear>Clear</button><button data-copy>Copy snapshot</button>
119
+ </section>
120
+ <main data-content></main>`;
121
+ host.dataset.tab = "overview";
122
+ root.querySelector("[data-close]")?.addEventListener("click", () => devtools.close());
123
+ root.querySelector("[data-clear]")?.addEventListener("click", () => devtools.clear());
124
+ root.querySelector("[data-pause]")?.addEventListener("click", () => {
125
+ paused = !paused;
126
+ renderOverlay();
127
+ });
128
+ root.querySelector("[data-copy]")?.addEventListener("click", () => {
129
+ void navigator.clipboard?.writeText(JSON.stringify(devtools.snapshot(), null, 2));
130
+ });
131
+ root.querySelector("[data-filter]")?.addEventListener("input", () => renderOverlay());
132
+ for (const button of root.querySelectorAll("[data-tab]")) {
133
+ button.addEventListener("click", () => {
134
+ host.dataset.tab = button.dataset.tab ?? "overview";
135
+ renderOverlay();
60
136
  });
61
- };
137
+ }
138
+ return host;
139
+ }
140
+ function queueOverlayRender() {
141
+ if (!overlay?.isConnected || overlay.hidden || renderQueued)
142
+ return;
143
+ renderQueued = true;
144
+ queueMicrotask(() => {
145
+ renderQueued = false;
146
+ renderOverlay();
147
+ });
148
+ }
149
+ function renderOverlay() {
150
+ const root = overlay?.shadowRoot;
151
+ if (!root)
152
+ return;
153
+ const tab = overlay?.dataset.tab ?? "overview";
154
+ const filter = (root.querySelector("[data-filter]")?.value ?? "").toLowerCase();
155
+ const visible = records.filter((record) => {
156
+ if (!matchesTab(record.kind, tab))
157
+ return false;
158
+ return !filter || `${record.kind} ${JSON.stringify(record.data ?? "")}`.toLowerCase().includes(filter);
159
+ });
160
+ const summary = root.querySelector("[data-summary]");
161
+ if (summary)
162
+ summary.textContent = `${records.length} events · ${paused ? "paused" : "live"}`;
163
+ const pause = root.querySelector("[data-pause]");
164
+ if (pause)
165
+ pause.textContent = paused ? "Resume" : "Pause";
166
+ const content = root.querySelector("[data-content]");
167
+ if (!content)
168
+ return;
169
+ if (tab === "overview") {
170
+ const counts = countGroups(records);
171
+ content.innerHTML = `<div class="cards">${Object.entries(counts)
172
+ .map(([name, count]) => `<article><b>${escapeHtml(name)}</b><span>${count}</span></article>`)
173
+ .join("")}</div>`;
174
+ return;
175
+ }
176
+ content.innerHTML = visible.slice(-250).reverse().map((record) => `
177
+ <article class="event ${record.kind.includes("error") ? "error" : ""}">
178
+ <time>${record.time.toFixed(1)}</time><b>${escapeHtml(record.kind)}</b>
179
+ ${record.targetId ? `<i>#${record.targetId}</i>` : ""}
180
+ <code>${escapeHtml(JSON.stringify(record.data ?? ""))}</code>
181
+ </article>`).join("") || `<p class="empty">No matching events</p>`;
182
+ }
183
+ function matchesTab(kind, tab) {
184
+ if (tab === "overview")
185
+ return true;
186
+ if (tab === "reactivity")
187
+ return /^(signal|computed|effect):/.test(kind);
188
+ if (tab === "components")
189
+ return kind.startsWith("component:");
190
+ if (tab === "router-data")
191
+ return /^(router|resource|mutation):/.test(kind);
192
+ return kind.startsWith("diagnostic:") || kind.includes("error");
193
+ }
194
+ function countGroups(input) {
195
+ const counts = {};
196
+ for (const record of input) {
197
+ const group = record.kind.split(":", 1)[0] ?? "other";
198
+ counts[group] = (counts[group] ?? 0) + 1;
199
+ }
200
+ return counts;
201
+ }
202
+ function safePreview(value, depth = 0) {
203
+ if (value == null || typeof value === "string" || typeof value === "number" ||
204
+ typeof value === "boolean")
205
+ return value;
206
+ if (typeof value === "bigint")
207
+ return `${value}n`;
208
+ if (typeof value === "function")
209
+ return `[Function ${value.name || "anonymous"}]`;
210
+ if (value instanceof Error)
211
+ return { name: value.name, message: value.message, stack: value.stack };
212
+ if (depth >= 2)
213
+ return `[${Object.prototype.toString.call(value).slice(8, -1)}]`;
214
+ if (Array.isArray(value))
215
+ return value.slice(0, 20).map((item) => safePreview(item, depth + 1));
216
+ const result = {};
217
+ for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
218
+ if (Object.keys(result).length >= 20)
219
+ break;
220
+ result[key] = "value" in descriptor ? safePreview(descriptor.value, depth + 1) : "[Getter]";
221
+ }
222
+ return result;
223
+ }
224
+ function escapeHtml(value) {
225
+ return value.replace(/[&<>"']/g, (char) => ({
226
+ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;",
227
+ })[char]);
228
+ }
229
+ function isLogLevel(value) {
230
+ return ["debug", "info", "warn", "error", "silent"].includes(value);
62
231
  }
232
+ const OVERLAY_CSS = `
233
+ :host{all:initial;position:fixed;right:12px;bottom:12px;width:min(720px,calc(100vw - 24px));height:min(520px,calc(100vh - 24px));z-index:2147483647;color:#e7e9ee;background:#11141a;border:1px solid #384152;border-radius:12px;box-shadow:0 18px 70px #0009;font:12px/1.45 ui-monospace,SFMono-Regular,Consolas,monospace;display:grid;grid-template-rows:auto auto auto 1fr;overflow:hidden}
234
+ :host([hidden]){display:none}header{display:flex;gap:12px;align-items:center;padding:10px 12px;background:#1a1f29}header strong{font:600 14px system-ui}header span{color:#929bad;flex:1}button,input{font:inherit;color:inherit;background:#222936;border:1px solid #3a4558;border-radius:6px;padding:5px 8px}button{cursor:pointer}button:hover{background:#30394a}nav,.tools{display:flex;gap:6px;padding:7px 10px;border-top:1px solid #2a3240}.tools input{flex:1}main{overflow:auto;padding:10px}.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px}.cards article{display:flex;justify-content:space-between;padding:12px;background:#1b212b;border-radius:8px}.cards span{font-size:18px;color:#77d6a5}.event{display:grid;grid-template-columns:64px 150px 40px 1fr;gap:8px;padding:6px;border-bottom:1px solid #262e3a}.event time,.event i{color:#7f8a9d}.event code{white-space:pre-wrap;overflow-wrap:anywhere;color:#bac3d2}.event.error{background:#4b1f263d;color:#ff9aa8}.empty{color:#8993a4;text-align:center;margin-top:40px}`;
63
235
  //# sourceMappingURL=devtools.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"devtools.js","sourceRoot":"","sources":["../../src/devtools.ts"],"names":[],"mappings":"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","sourcesContent":["/** Dev-only instrumentation overlay. Import this subpath before app startup. */\n\nimport {\n installDevtoolsHook,\n type MadoDevtoolsEvent,\n type MadoDevtoolsHook,\n} from \"./devtools-hook.js\";\nimport { reportDiagnostic } from \"./diagnostics.js\";\n\nexport type DevtoolsLogLevel = \"debug\" | \"info\" | \"warn\" | \"error\" | \"silent\";\n\nexport interface DevtoolsRecord {\n id: number;\n time: number;\n kind: string;\n targetId?: number;\n data?: unknown;\n}\n\nexport interface DevtoolsSnapshot {\n version: 1;\n paused: boolean;\n records: DevtoolsRecord[];\n}\n\nexport interface DevtoolsController {\n open(): void;\n close(): void;\n toggle(): void;\n clear(): void;\n setLogLevel(level: DevtoolsLogLevel): void;\n snapshot(): DevtoolsSnapshot;\n}\n\nconst MAX_RECORDS = 1_000;\nconst records: DevtoolsRecord[] = [];\nconst targetIds = new WeakMap<object, number>();\nlet nextRecordId = 0;\nlet nextTargetId = 0;\nlet paused = false;\nlet overlay: HTMLElement | null = null;\nlet renderQueued = false;\n\nconst hook: MadoDevtoolsHook = {\n version: 1,\n emit(event) {\n if (paused) return;\n records.push(toRecord(event));\n if (records.length > MAX_RECORDS) records.splice(0, records.length - MAX_RECORDS);\n queueOverlayRender();\n },\n};\n\ninstallDevtoolsHook(hook);\n\nexport const devtools: DevtoolsController = {\n open() {\n if (typeof document === \"undefined\") return;\n overlay ??= createOverlay();\n if (!overlay.isConnected) document.documentElement.appendChild(overlay);\n overlay.hidden = false;\n renderOverlay();\n },\n close() {\n if (overlay) overlay.hidden = true;\n },\n toggle() {\n if (!overlay || overlay.hidden || !overlay.isConnected) this.open();\n else this.close();\n },\n clear() {\n records.length = 0;\n renderOverlay();\n },\n setLogLevel(level) {\n if (!isLogLevel(level)) throw new TypeError(`Unknown Mado log level: ${level}`);\n try {\n localStorage.setItem(\"mado:log-level\", level);\n } catch {\n /* storage may be disabled */\n }\n },\n snapshot() {\n return {\n version: 1,\n paused,\n records: records.map((record) => ({ ...record })),\n };\n },\n};\n\nif (typeof window !== \"undefined\") {\n window.addEventListener(\"keydown\", (event) => {\n if (event.altKey && event.shiftKey && event.key.toLowerCase() === \"m\") {\n event.preventDefault();\n devtools.toggle();\n }\n });\n try {\n if (localStorage.getItem(\"madoDebug\") === \"1\") {\n reportDiagnostic(\n \"warn\",\n \"devtools\",\n \"legacy-debug-flag\",\n \"localStorage.madoDebug is deprecated; use Alt+Shift+M or devtools.open().\",\n );\n devtools.setLogLevel(\"debug\");\n queueMicrotask(() => devtools.open());\n }\n } catch {\n /* storage may be disabled */\n }\n}\n\nfunction toRecord(event: MadoDevtoolsEvent): DevtoolsRecord {\n let targetId: number | undefined;\n if (event.target) {\n targetId = targetIds.get(event.target);\n if (!targetId) {\n targetId = ++nextTargetId;\n targetIds.set(event.target, targetId);\n }\n }\n return {\n id: ++nextRecordId,\n time: event.time,\n kind: event.kind,\n ...(targetId ? { targetId } : {}),\n ...(event.data === undefined ? {} : { data: safePreview(event.data) }),\n };\n}\n\nfunction createOverlay(): HTMLElement {\n const host = document.createElement(\"aside\");\n host.id = \"mado-devtools\";\n host.setAttribute(\"aria-label\", \"Mado developer tools\");\n const root = host.attachShadow({ mode: \"open\" });\n root.innerHTML = `\n <style>${OVERLAY_CSS}</style>\n <header><strong>Mado Devtools</strong><span data-summary></span><button data-close title=\"Close\">×</button></header>\n <nav>\n <button data-tab=\"overview\">Overview</button>\n <button data-tab=\"reactivity\">Reactivity</button>\n <button data-tab=\"components\">Components</button>\n <button data-tab=\"router-data\">Router/Data</button>\n <button data-tab=\"timeline\">Timeline/Errors</button>\n </nav>\n <section class=\"tools\">\n <input data-filter aria-label=\"Filter events\" placeholder=\"Filter events\">\n <button data-pause>Pause</button><button data-clear>Clear</button><button data-copy>Copy snapshot</button>\n </section>\n <main data-content></main>`;\n host.dataset.tab = \"overview\";\n root.querySelector(\"[data-close]\")?.addEventListener(\"click\", () => devtools.close());\n root.querySelector(\"[data-clear]\")?.addEventListener(\"click\", () => devtools.clear());\n root.querySelector(\"[data-pause]\")?.addEventListener(\"click\", () => {\n paused = !paused;\n renderOverlay();\n });\n root.querySelector(\"[data-copy]\")?.addEventListener(\"click\", () => {\n void navigator.clipboard?.writeText(JSON.stringify(devtools.snapshot(), null, 2));\n });\n root.querySelector(\"[data-filter]\")?.addEventListener(\"input\", () => renderOverlay());\n for (const button of root.querySelectorAll<HTMLElement>(\"[data-tab]\")) {\n button.addEventListener(\"click\", () => {\n host.dataset.tab = button.dataset.tab ?? \"overview\";\n renderOverlay();\n });\n }\n return host;\n}\n\nfunction queueOverlayRender(): void {\n if (!overlay?.isConnected || overlay.hidden || renderQueued) return;\n renderQueued = true;\n queueMicrotask(() => {\n renderQueued = false;\n renderOverlay();\n });\n}\n\nfunction renderOverlay(): void {\n const root = overlay?.shadowRoot;\n if (!root) return;\n const tab = overlay?.dataset.tab ?? \"overview\";\n const filter = (root.querySelector<HTMLInputElement>(\"[data-filter]\")?.value ?? \"\").toLowerCase();\n const visible = records.filter((record) => {\n if (!matchesTab(record.kind, tab)) return false;\n return !filter || `${record.kind} ${JSON.stringify(record.data ?? \"\")}`.toLowerCase().includes(filter);\n });\n const summary = root.querySelector(\"[data-summary]\");\n if (summary) summary.textContent = `${records.length} events · ${paused ? \"paused\" : \"live\"}`;\n const pause = root.querySelector(\"[data-pause]\");\n if (pause) pause.textContent = paused ? \"Resume\" : \"Pause\";\n const content = root.querySelector(\"[data-content]\");\n if (!content) return;\n if (tab === \"overview\") {\n const counts = countGroups(records);\n content.innerHTML = `<div class=\"cards\">${Object.entries(counts)\n .map(([name, count]) => `<article><b>${escapeHtml(name)}</b><span>${count}</span></article>`)\n .join(\"\")}</div>`;\n return;\n }\n content.innerHTML = visible.slice(-250).reverse().map((record) => `\n <article class=\"event ${record.kind.includes(\"error\") ? \"error\" : \"\"}\">\n <time>${record.time.toFixed(1)}</time><b>${escapeHtml(record.kind)}</b>\n ${record.targetId ? `<i>#${record.targetId}</i>` : \"\"}\n <code>${escapeHtml(JSON.stringify(record.data ?? \"\"))}</code>\n </article>`).join(\"\") || `<p class=\"empty\">No matching events</p>`;\n}\n\nfunction matchesTab(kind: string, tab: string): boolean {\n if (tab === \"overview\") return true;\n if (tab === \"reactivity\") return /^(signal|computed|effect):/.test(kind);\n if (tab === \"components\") return kind.startsWith(\"component:\");\n if (tab === \"router-data\") return /^(router|resource|mutation):/.test(kind);\n return kind.startsWith(\"diagnostic:\") || kind.includes(\"error\");\n}\n\nfunction countGroups(input: DevtoolsRecord[]): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const record of input) {\n const group = record.kind.split(\":\", 1)[0] ?? \"other\";\n counts[group] = (counts[group] ?? 0) + 1;\n }\n return counts;\n}\n\nfunction safePreview(value: unknown, depth = 0): unknown {\n if (value == null || typeof value === \"string\" || typeof value === \"number\" ||\n typeof value === \"boolean\") return value;\n if (typeof value === \"bigint\") return `${value}n`;\n if (typeof value === \"function\") return `[Function ${value.name || \"anonymous\"}]`;\n if (value instanceof Error) return { name: value.name, message: value.message, stack: value.stack };\n if (depth >= 2) return `[${Object.prototype.toString.call(value).slice(8, -1)}]`;\n if (Array.isArray(value)) return value.slice(0, 20).map((item) => safePreview(item, depth + 1));\n const result: Record<string, unknown> = {};\n for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {\n if (Object.keys(result).length >= 20) break;\n result[key] = \"value\" in descriptor ? safePreview(descriptor.value, depth + 1) : \"[Getter]\";\n }\n return result;\n}\n\nfunction escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (char) => ({\n \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\",\n })[char]!);\n}\n\nfunction isLogLevel(value: string): value is DevtoolsLogLevel {\n return [\"debug\", \"info\", \"warn\", \"error\", \"silent\"].includes(value);\n}\n\nconst OVERLAY_CSS = `\n :host{all:initial;position:fixed;right:12px;bottom:12px;width:min(720px,calc(100vw - 24px));height:min(520px,calc(100vh - 24px));z-index:2147483647;color:#e7e9ee;background:#11141a;border:1px solid #384152;border-radius:12px;box-shadow:0 18px 70px #0009;font:12px/1.45 ui-monospace,SFMono-Regular,Consolas,monospace;display:grid;grid-template-rows:auto auto auto 1fr;overflow:hidden}\n :host([hidden]){display:none}header{display:flex;gap:12px;align-items:center;padding:10px 12px;background:#1a1f29}header strong{font:600 14px system-ui}header span{color:#929bad;flex:1}button,input{font:inherit;color:inherit;background:#222936;border:1px solid #3a4558;border-radius:6px;padding:5px 8px}button{cursor:pointer}button:hover{background:#30394a}nav,.tools{display:flex;gap:6px;padding:7px 10px;border-top:1px solid #2a3240}.tools input{flex:1}main{overflow:auto;padding:10px}.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px}.cards article{display:flex;justify-content:space-between;padding:12px;background:#1b212b;border-radius:8px}.cards span{font-size:18px;color:#77d6a5}.event{display:grid;grid-template-columns:64px 150px 40px 1fr;gap:8px;padding:6px;border-bottom:1px solid #262e3a}.event time,.event i{color:#7f8a9d}.event code{white-space:pre-wrap;overflow-wrap:anywhere;color:#bac3d2}.event.error{background:#4b1f263d;color:#ff9aa8}.empty{color:#8993a4;text-align:center;margin-top:40px}`;\n"]}
@@ -4,4 +4,16 @@
4
4
  * Warnings are intentionally best-effort: they never throw and they are printed
5
5
  * once per code so noisy app renders do not flood the console.
6
6
  */
7
+ export type DiagnosticLevel = "debug" | "info" | "warn" | "error";
8
+ export interface DiagnosticRecord {
9
+ timestamp: number;
10
+ level: DiagnosticLevel;
11
+ scope: string;
12
+ code: string;
13
+ message: string;
14
+ data?: unknown;
15
+ }
16
+ export declare function reportDiagnostic(level: DiagnosticLevel, scope: string, code: string, message: string, data?: unknown): void;
17
+ export declare function reportError(scope: string, code: string, message: string, error: unknown): void;
7
18
  export declare function warnOnce(code: string, message: string): void;
19
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/diagnostics.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAElE,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAWD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,OAAO,GACb,IAAI,CAiCN;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,OAAO,GACb,IAAI,CAEN;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAQ5D"}
@@ -1,17 +1,58 @@
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
- */
1
+ import { emitDevtools } from "./devtools-hook.js";
7
2
  const seen = new Set();
3
+ const LEVELS = {
4
+ debug: 10,
5
+ info: 20,
6
+ warn: 30,
7
+ error: 40,
8
+ silent: 50,
9
+ };
10
+ export function reportDiagnostic(level, scope, code, message, data) {
11
+ if (LEVELS[level] < LEVELS[configuredLevel()])
12
+ return;
13
+ const record = {
14
+ timestamp: Date.now(),
15
+ level,
16
+ scope,
17
+ code,
18
+ message,
19
+ ...(data === undefined ? {} : { data }),
20
+ };
21
+ try {
22
+ const method = level === "debug" ? "debug" : level;
23
+ const colour = level === "error"
24
+ ? "color:#d33;font-weight:600"
25
+ : level === "warn"
26
+ ? "color:#b26a00;font-weight:600"
27
+ : level === "info"
28
+ ? "color:#087ea4"
29
+ : "color:#777";
30
+ const args = [
31
+ `%c[mado:${scope}:${code}]%c ${message}`,
32
+ colour,
33
+ "color:inherit;font-weight:normal",
34
+ ];
35
+ if (data !== undefined)
36
+ args.push(data);
37
+ console[method](...args);
38
+ }
39
+ catch {
40
+ /* diagnostics must never affect application behaviour */
41
+ }
42
+ if (typeof __MADO_DEVTOOLS__ === "undefined" || __MADO_DEVTOOLS__) {
43
+ emitDevtools(`diagnostic:${level}`, undefined, record);
44
+ }
45
+ dispatchDiagnostic(record);
46
+ }
47
+ export function reportError(scope, code, message, error) {
48
+ reportDiagnostic("error", scope, code, message, error);
49
+ }
8
50
  export function warnOnce(code, message) {
9
51
  if (seen.has(code))
10
52
  return;
11
53
  seen.add(code);
12
54
  try {
13
- // eslint-disable-next-line no-console
14
- console.warn(`[mado:${code}] ${message}`);
55
+ reportDiagnostic("warn", "runtime", code, message);
15
56
  }
16
57
  catch {
17
58
  /* noop */
@@ -26,4 +67,23 @@ export const _testHooks = {
26
67
  return [...seen];
27
68
  },
28
69
  };
70
+ function configuredLevel() {
71
+ try {
72
+ const value = localStorage.getItem("mado:log-level") ?? "warn";
73
+ if (Object.hasOwn(LEVELS, value))
74
+ return value;
75
+ }
76
+ catch {
77
+ /* localStorage is unavailable in Node, workers and locked-down pages */
78
+ }
79
+ return "warn";
80
+ }
81
+ function dispatchDiagnostic(record) {
82
+ try {
83
+ globalThis.dispatchEvent?.(new CustomEvent("mado:diagnostic", { detail: record }));
84
+ }
85
+ catch {
86
+ /* EventTarget/CustomEvent may not exist in non-browser runtimes. */
87
+ }
88
+ }
29
89
  //# sourceMappingURL=diagnostics.js.map