@jasonshimmy/custom-elements-runtime 3.5.0 → 3.7.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.
- package/README.md +10 -9
- package/dist/custom-elements-runtime.cjs.js +2 -2
- package/dist/custom-elements-runtime.cjs.js.map +1 -1
- package/dist/custom-elements-runtime.es.js +25 -4
- package/dist/custom-elements-runtime.es.js.map +1 -1
- package/dist/custom-elements-runtime.jit-css.cjs.js +1 -1
- package/dist/custom-elements-runtime.jit-css.es.js +2 -2
- package/dist/custom-elements-runtime.router.cjs.js +1 -1
- package/dist/custom-elements-runtime.router.es.js +3 -3
- package/dist/custom-elements-runtime.ssr-middleware.cjs.js +1 -1
- package/dist/custom-elements-runtime.ssr-middleware.es.js +1 -1
- package/dist/custom-elements-runtime.ssr.cjs.js +1 -1
- package/dist/custom-elements-runtime.ssr.es.js +2 -2
- package/dist/custom-elements-runtime.vite-plugin.cjs.js +1 -1
- package/dist/custom-elements-runtime.vite-plugin.es.js +1 -1
- package/dist/{helpers-DcEpRwq5.cjs → helpers-7zLtbh_q.cjs} +2 -2
- package/dist/helpers-7zLtbh_q.cjs.map +1 -0
- package/dist/{helpers-tJgb4Qve.js → helpers-kOWgceUQ.js} +4 -1
- package/dist/helpers-kOWgceUQ.js.map +1 -0
- package/dist/{hooks-CNfugc95.cjs → hooks-BY_35J9Y.cjs} +2 -2
- package/dist/{hooks-CNfugc95.cjs.map → hooks-BY_35J9Y.cjs.map} +1 -1
- package/dist/{hooks-CEUnvtsA.js → hooks-Dj1xwqpK.js} +2 -2
- package/dist/{hooks-CEUnvtsA.js.map → hooks-Dj1xwqpK.js.map} +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/runtime/component/async-component.d.ts +48 -0
- package/dist/runtime/render.d.ts +7 -0
- package/dist/{ssr-BpYy9XlW.js → ssr-Bg9jYXYv.js} +14 -13
- package/dist/{ssr-BpYy9XlW.js.map → ssr-Bg9jYXYv.js.map} +1 -1
- package/dist/{ssr-CFabTOyi.cjs → ssr-CucZ-Iwz.cjs} +3 -3
- package/dist/{ssr-CFabTOyi.cjs.map → ssr-CucZ-Iwz.cjs.map} +1 -1
- package/dist/ssr.d.ts +2 -0
- package/dist/{tag-utils-CoSXTr1F.js → tag-utils-Dg0vRKq9.js} +2 -2
- package/dist/{tag-utils-CoSXTr1F.js.map → tag-utils-Dg0vRKq9.js.map} +1 -1
- package/dist/{tag-utils-XJ3dkcPQ.cjs → tag-utils-ZOoyzCm9.cjs} +2 -2
- package/dist/{tag-utils-XJ3dkcPQ.cjs.map → tag-utils-ZOoyzCm9.cjs.map} +1 -1
- package/dist/{template-compiler-B4B_jAPN.cjs → template-compiler-BngILG2f.cjs} +6 -6
- package/dist/{template-compiler-B4B_jAPN.cjs.map → template-compiler-BngILG2f.cjs.map} +1 -1
- package/dist/{template-compiler-C3h8_vbE.js → template-compiler-q_rTI8PA.js} +63 -52
- package/dist/{template-compiler-C3h8_vbE.js.map → template-compiler-q_rTI8PA.js.map} +1 -1
- package/package.json +1 -1
- package/dist/helpers-DcEpRwq5.cjs.map +0 -1
- package/dist/helpers-tJgb4Qve.js.map +0 -1
package/README.md
CHANGED
|
@@ -430,15 +430,15 @@ export default defineConfig({
|
|
|
430
430
|
});
|
|
431
431
|
```
|
|
432
432
|
|
|
433
|
-
| Export
|
|
434
|
-
|
|
|
435
|
-
| `cerPlugin`
|
|
436
|
-
| `cerJITCSS`
|
|
437
|
-
| `cerComponentImports`
|
|
438
|
-
| `resolveTagName`
|
|
439
|
-
| `extractTemplateTagNames`
|
|
440
|
-
| `extractComponentRegistrations` | Extract resolved tag names from all `component('name', …)` calls in a source string.
|
|
441
|
-
| **Types**
|
|
433
|
+
| Export | Description |
|
|
434
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
435
|
+
| `cerPlugin` | Combined plugin: JIT CSS + SSR config (`virtual:cer-ssr-config`). Returns a `Plugin[]` array. |
|
|
436
|
+
| `cerJITCSS` | JIT CSS-only Vite plugin that scans source files at build time and emits pre-generated CSS. |
|
|
437
|
+
| `cerComponentImports` | Transform plugin that injects per-file static imports for custom elements, enabling per-page code splitting. |
|
|
438
|
+
| `resolveTagName` | Normalize a component name to its kebab-case custom-element tag name (prefixes with `cer-` if no hyphen). |
|
|
439
|
+
| `extractTemplateTagNames` | Extract hyphenated custom-element tag names from a source string's `html\`` templates. |
|
|
440
|
+
| `extractComponentRegistrations` | Extract resolved tag names from all `component('name', …)` calls in a source string. |
|
|
441
|
+
| **Types** | `CerPluginOptions`, `CerSSROptions`, `CerJITCSSPluginOptions`, `CerComponentImportsOptions` |
|
|
442
442
|
|
|
443
443
|
---
|
|
444
444
|
|
|
@@ -540,6 +540,7 @@ For examples and implementation details, explore the source code in `src/lib/`.
|
|
|
540
540
|
|
|
541
541
|
### Community Gallery
|
|
542
542
|
|
|
543
|
+
- [CER App Framework](https://github.com/jshimkoski/cer-app-framework) allows you to SPA, SSR, and SSG apps with this runtime.
|
|
543
544
|
- [Material Design 3 Components](https://github.com/jshimkoski/cer-material) built with this runtime.
|
|
544
545
|
- [Solatro](https://solatro.netlify.app) is a RTS card-based game built with this runtime.
|
|
545
546
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./logger-Dkht1dCX.cjs`),t=require(`./helpers-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./logger-Dkht1dCX.cjs`),t=require(`./helpers-7zLtbh_q.cjs`),n=require(`./template-compiler-BngILG2f.cjs`),r=require(`./css-utils-CFeP8SK1.cjs`),i=require(`./hooks-BY_35J9Y.cjs`);function a(e,t,r){let a=`idle`,o=null,s={fn:()=>{}},c=!1,l=!1,u=null,d=(e,t)=>{l||(l=!0,u!==null&&clearTimeout(u),t&&(o=t),a=e,s.fn())},f=()=>{c||(c=!0,a=`loading`,r?.timeout!=null&&(u=setTimeout(()=>d(`timeout`),r.timeout)),t().then(e=>d(`resolved`,e)).catch(()=>d(`error`)))};n.n(e,()=>{let e=i.i();switch(typeof e?.requestRender==`function`&&(s.fn=()=>{e.requestRender()}),a){case`idle`:return f(),r?.loading?.()??[];case`loading`:return r?.loading?.()??[];case`resolved`:return o();case`error`:case`timeout`:return r?.error?.()??[]}})}function o(e=document){typeof CustomEvent>`u`||(e instanceof Document?e.documentElement:e).dispatchEvent(new CustomEvent(`cer:hydrate`,{bubbles:!0,composed:!0}))}var s=100,c=3e4;function l(e,t,n){return n===`jitCacheHitRate`?e<t*.5?`critical`:e<t?`warning`:`healthy`:e>t*2?`critical`:e>t?`warning`:`healthy`}function u(e){let t=[];return e.memoryUsage?.status!==`healthy`&&t.push(`Consider reducing component complexity or implementing better memory cleanup`),e.averageRenderTime?.status!==`healthy`&&t.push(`Optimize component render functions - consider lazy loading or virtualization`),e.jitCacheHitRate?.status!==`healthy`&&t.push(`JIT CSS cache performance is poor - review CSS patterns for optimization`),e.componentErrorRate?.status!==`healthy`&&t.push(`High component error rate detected - review error handling and component logic`),e.activeReactiveStates?.status!==`healthy`&&t.push(`High number of reactive states - consider state consolidation or cleanup`),e.memoryLeakIndicator?.status!==`healthy`&&t.push(`Potential memory leak detected - review component cleanup and event listener management`),t}function d(){let t=new Map,n=new Set,r=null;function i(e,n,r){t.set(e,{name:e,value:n,threshold:r,status:`healthy`,lastUpdated:Date.now(),history:[]})}function a(){i(`activeComponents`,0,1e3),i(`componentCreateRate`,0,50),i(`componentErrorRate`,0,.1),i(`memoryUsage`,0,50*1024*1024),i(`memoryGrowthRate`,0,1024*1024),i(`averageRenderTime`,0,16),i(`slowRenderCount`,0,10),i(`jitCacheHitRate`,100,80),i(`activeReactiveStates`,0,5e3),i(`dependencyUpdates`,0,100),i(`memoryLeakIndicator`,0,.1)}function o(e,n){let r=t.get(e);r&&(r.value=n,r.lastUpdated=Date.now(),r.history.push(n),r.history.length>s&&r.history.shift(),r.status=l(n,r.threshold,e))}function d(){let e={},n=`healthy`;for(let[r,i]of t)e[r]={...i},i.status===`critical`?n=`critical`:i.status===`warning`&&n===`healthy`&&(n=`warning`);return{overall:n,metrics:e,timestamp:Date.now(),recommendations:u(e)}}function f(){if(`memory`in performance&&performance.memory){let e=performance.memory;o(`memoryUsage`,e.usedJSHeapSize);let n=t.get(`memoryUsage`);if(n&&n.history.length>1){let e=n.history[n.history.length-2],t=n.history[n.history.length-1];o(`memoryGrowthRate`,Math.max(0,t-e))}}}function p(t){for(let r of n)try{r(t)}catch(t){e.t(`Error in health monitor listener:`,t)}}function m(){f();let t=d();p(t),t.overall===`critical`?e.t(`🚨 Runtime Health: Critical issues detected`,t.recommendations):t.overall===`warning`&&e.r(`⚠️ Runtime Health: Performance warnings`,t.recommendations)}function h(){typeof window>`u`||(r=setInterval(m,c))}function g(){r!==null&&(clearInterval(r),r=null)}function _(e){n.add(e)}function v(e){n.delete(e)}function y(e){let n=t.get(e);return n?[...n.history]:[]}function b(){for(let e of t.values())e.history=[]}return a(),h(),{updateMetric:o,getHealthReport:d,addListener:_,removeListener:v,stop:g,getMetricHistory:y,clearHistory:b}}var f=null;function p(){return f||=d(),f}function m(e,t){p().updateMetric(e,t)}function h(){return p().getHealthReport()}function g(){typeof window>`u`||typeof customElements>`u`||customElements.get(`cer-keep-alive`)||customElements.define(`cer-keep-alive`,_())}function _(){return class extends HTMLElement{_cache=new Map;_slot=null;_slotListener=null;connectedCallback(){this.shadowRoot||this.attachShadow({mode:`open`}),this.shadowRoot.querySelector(`slot`)||(this.shadowRoot.innerHTML=`<slot></slot>`),this._slot=this.shadowRoot.querySelector(`slot`),this._slot&&(this._slotListener=()=>this._handleSlotChange(),this._slot.addEventListener(`slotchange`,this._slotListener),this._handleSlotChange())}disconnectedCallback(){this._slot&&this._slotListener&&this._slot.removeEventListener(`slotchange`,this._slotListener),this._slotListener=null}clearCache(e){e?this._cache.delete(e):this._cache.clear()}_handleSlotChange(){if(!this._slot)return;let e=this._slot.assignedElements({flatten:!0});for(let t of e){let e=this._buildCacheKey(t);if(!this._cache.has(e))this._cache.set(e,t);else{let n=this._cache.get(e);if(n!==t)try{t.parentNode?.replaceChild(n,t)}catch{this._cache.set(e,t)}}}}_buildCacheKey(e){let t=e.tagName.toLowerCase(),n=e.getAttribute(`id`);return n?`${t}:${n}`:t}}}function v(){typeof customElements<`u`&&customElements.get(`cer-suspense`)||n.n(`cer-suspense`,()=>{let{pending:e}=i._({pending:!1});return e?n.t`<slot name="fallback"><span>Loading…</span></slot>`:n.t`<slot></slot>`})}function y(){typeof customElements<`u`&&customElements.get(`cer-error-boundary`)||n.n(`cer-error-boundary`,()=>{let e=t.y(!1),r=t.y(``);return i.g(t=>{e.value=!0,r.value=t.message}),i.d({_cerHandleChildError:t=>{e.peek()||(e.value=!0,r.value=t instanceof Error?t.message:String(t))},reset:()=>{e.value=!1,r.value=``}}),e.value?n.t`<slot name="fallback"
|
|
2
2
|
><div role="alert">
|
|
3
3
|
<strong>Something went wrong.</strong>
|
|
4
4
|
${r.value?n.t`<p>${r.value}</p>`:n.t``}
|
|
5
5
|
</div></slot
|
|
6
|
-
>`:n.t`<slot></slot>`})}function
|
|
6
|
+
>`:n.t`<slot></slot>`})}function b(){v(),y(),g()}function x(e){if(typeof document>`u`||i.o())return{portal:()=>{},destroy:()=>{}};if(i.i()){let n=t.v.getOrCreateState(null),r=n.peek();if(r!==null)return r;let i=S(e,()=>n.initSilent(null));return n.initSilent(i),i}return S(e)}function S(e,t){let r=typeof e==`string`?document.querySelector(e):e;if(!r)return console.warn(`[useTeleport] Target "${String(e)}" not found in the document. Teleported content will not be rendered.`),{portal:()=>{},destroy:()=>{}};let i=document.createElement(`cer-teleport`);i.dataset.cerTeleport=``,r.appendChild(i);let a={};return{portal(e){n.r(i,e==null?[]:Array.isArray(e)?e:[e],void 0,a)},destroy(){try{n.r(i,[],void 0,a)}catch{}i.parentNode&&i.parentNode.removeChild(i),t?.()}}}exports.ReactiveState=t.h,exports.component=n.n,exports.computed=t.g,exports.createComposable=i.n,exports.createHealthMonitor=d,exports.css=r.o,exports.decodeEntities=t.n,exports.defineAsyncComponent=a,exports.defineModel=i.r,exports.devLog=e.n,exports.flushDOMUpdates=t.E,exports.getCurrentComponentContext=i.i,exports.getHealthMonitor=p,exports.getHealthStatus=h,exports.html=n.t,exports.hydrateApp=o,exports.inject=i.a,exports.isReactiveState=t._,exports.nextTick=t.D,exports.provide=i.s,exports.ref=t.y,exports.registerBuiltinComponents=b,exports.registerErrorBoundary=y,exports.registerKeepAlive=g,exports.registerSuspense=v,exports.scheduleWithPriority=t.k,exports.setDevMode=e.i,exports.unsafeHTML=t.m,exports.updateHealthMetric=m,exports.useDesignTokens=i.l,exports.useEmit=i.u,exports.useExpose=i.d,exports.useGlobalStyle=i.f,exports.useOnAttributeChanged=i.p,exports.useOnConnected=i.m,exports.useOnDisconnected=i.h,exports.useOnError=i.g,exports.useProps=i._,exports.useSlots=i.v,exports.useStyle=i.y,exports.useTeleport=x,exports.watch=t.b,exports.watchEffect=t.x;
|
|
7
7
|
//# sourceMappingURL=custom-elements-runtime.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-elements-runtime.cjs.js","names":[],"sources":["../src/lib/runtime/hydration.ts","../src/lib/runtime/monitoring/health-monitor.ts","../src/lib/keep-alive.ts","../src/lib/runtime/builtin-components.ts","../src/lib/teleport.ts"],"sourcesContent":["/**\n * Client-side hydration helpers.\n *\n * When the server renders with `dsd: true`, each custom element's shadow DOM\n * is already present in the HTML via a Declarative Shadow DOM template. The\n * browser parses that template and attaches the shadow root before any\n * JavaScript runs. When the JS bundle loads, the custom element constructors\n * detect the existing shadow root and skip the `attachShadow()` call, then\n * hydrate (attach reactivity to) the existing DOM on `connectedCallback`.\n *\n * `hydrateApp` is a signal/entry point for this process. In most cases custom\n * elements self-hydrate automatically on connection — this function is useful\n * when you need to explicitly trigger or defer the hydration of a subtree.\n */\n\n/**\n * Trigger hydration for all registered custom elements within `root`.\n *\n * In practice, custom elements self-hydrate as soon as their class definition\n * is registered and the element is connected to the DOM. Call `hydrateApp`\n * after importing and calling `component()` for all your components to signal\n * that the page is ready for reactive activation.\n *\n * @param root - The root element to hydrate. Defaults to `document`.\n *\n * @example\n * ```ts\n * import { component, hydrateApp } from '@jasonshimmy/custom-elements-runtime';\n * import './components'; // registers all components via component()\n *\n * hydrateApp(); // activate all DSD-rendered components on the page\n * ```\n */\nexport function hydrateApp(root: Element | Document = document): void {\n if (typeof CustomEvent === 'undefined') return;\n const target =\n root instanceof Document ? root.documentElement : (root as Element);\n target.dispatchEvent(\n new CustomEvent('cer:hydrate', { bubbles: true, composed: true }),\n );\n}\n","/**\n * Runtime Health Monitoring System\n * Tracks framework health metrics and provides early warning for potential issues\n */\n\nimport { devWarn, devError } from '../logger';\n\ninterface HealthMetric {\n name: string;\n value: number;\n threshold: number;\n status: 'healthy' | 'warning' | 'critical';\n lastUpdated: number;\n history: number[];\n}\n\ninterface HealthReport {\n overall: 'healthy' | 'warning' | 'critical';\n metrics: Record<string, HealthMetric>;\n timestamp: number;\n recommendations: string[];\n}\nexport type { HealthReport };\n\n/**\n * Public interface for a health monitor instance.\n * All state is managed internally via closures — no class syntax.\n */\nexport interface HealthMonitorInstance {\n /** Update a specific health metric value */\n updateMetric(name: string, value: number): void;\n /** Get the current health report */\n getHealthReport(): HealthReport;\n /** Add a listener to be notified when a health check runs */\n addListener(listener: (report: HealthReport) => void): void;\n /** Remove a previously registered listener */\n removeListener(listener: (report: HealthReport) => void): void;\n /** Stop the periodic health monitoring timer */\n stop(): void;\n /** Get historical values for a specific metric */\n getMetricHistory(name: string): number[];\n /** Clear all metric history */\n clearHistory(): void;\n}\n\nconst MAX_HISTORY_SIZE = 100;\nconst CHECK_INTERVAL = 30_000; // 30 seconds\n\nfunction calcStatus(\n value: number,\n threshold: number,\n metricName: string,\n): 'healthy' | 'warning' | 'critical' {\n if (metricName === 'jitCacheHitRate') {\n if (value < threshold * 0.5) return 'critical';\n if (value < threshold) return 'warning';\n return 'healthy';\n }\n if (value > threshold * 2) return 'critical';\n if (value > threshold) return 'warning';\n return 'healthy';\n}\n\nfunction buildRecommendations(metrics: Record<string, HealthMetric>): string[] {\n const out: string[] = [];\n if (metrics.memoryUsage?.status !== 'healthy')\n out.push(\n 'Consider reducing component complexity or implementing better memory cleanup',\n );\n if (metrics.averageRenderTime?.status !== 'healthy')\n out.push(\n 'Optimize component render functions - consider lazy loading or virtualization',\n );\n if (metrics.jitCacheHitRate?.status !== 'healthy')\n out.push(\n 'JIT CSS cache performance is poor - review CSS patterns for optimization',\n );\n if (metrics.componentErrorRate?.status !== 'healthy')\n out.push(\n 'High component error rate detected - review error handling and component logic',\n );\n if (metrics.activeReactiveStates?.status !== 'healthy')\n out.push(\n 'High number of reactive states - consider state consolidation or cleanup',\n );\n if (metrics.memoryLeakIndicator?.status !== 'healthy')\n out.push(\n 'Potential memory leak detected - review component cleanup and event listener management',\n );\n return out;\n}\n\n/**\n * Create a new health monitor instance.\n * All mutable state lives in closures — no `class` or `this`.\n */\nexport function createHealthMonitor(): HealthMonitorInstance {\n const metricsMap = new Map<string, HealthMetric>();\n const listeners = new Set<(report: HealthReport) => void>();\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n function addMetric(name: string, value: number, threshold: number): void {\n metricsMap.set(name, {\n name,\n value,\n threshold,\n status: 'healthy',\n lastUpdated: Date.now(),\n history: [],\n });\n }\n\n function initializeMetrics(): void {\n addMetric('activeComponents', 0, 1000);\n addMetric('componentCreateRate', 0, 50);\n addMetric('componentErrorRate', 0, 0.1);\n addMetric('memoryUsage', 0, 50 * 1024 * 1024);\n addMetric('memoryGrowthRate', 0, 1024 * 1024);\n addMetric('averageRenderTime', 0, 16);\n addMetric('slowRenderCount', 0, 10);\n addMetric('jitCacheHitRate', 100, 80);\n addMetric('activeReactiveStates', 0, 5000);\n addMetric('dependencyUpdates', 0, 100);\n addMetric('memoryLeakIndicator', 0, 0.1);\n }\n\n function updateMetric(name: string, value: number): void {\n const metric = metricsMap.get(name);\n if (!metric) return;\n metric.value = value;\n metric.lastUpdated = Date.now();\n metric.history.push(value);\n if (metric.history.length > MAX_HISTORY_SIZE) metric.history.shift();\n metric.status = calcStatus(value, metric.threshold, name);\n }\n\n function getHealthReport(): HealthReport {\n const snapshot: Record<string, HealthMetric> = {};\n let overall: 'healthy' | 'warning' | 'critical' = 'healthy';\n for (const [name, metric] of metricsMap) {\n snapshot[name] = { ...metric };\n if (metric.status === 'critical') overall = 'critical';\n else if (metric.status === 'warning' && overall === 'healthy')\n overall = 'warning';\n }\n return {\n overall,\n metrics: snapshot,\n timestamp: Date.now(),\n recommendations: buildRecommendations(snapshot),\n };\n }\n\n function updateMemoryMetrics(): void {\n if (\n 'memory' in performance &&\n (performance as Record<string, unknown>).memory\n ) {\n const mem = (performance as Record<string, unknown>).memory as {\n usedJSHeapSize: number;\n };\n updateMetric('memoryUsage', mem.usedJSHeapSize);\n const m = metricsMap.get('memoryUsage');\n if (m && m.history.length > 1) {\n const prev = m.history[m.history.length - 2];\n const curr = m.history[m.history.length - 1];\n updateMetric('memoryGrowthRate', Math.max(0, curr - prev));\n }\n }\n }\n\n function notifyListeners(report: HealthReport): void {\n for (const listener of listeners) {\n try {\n listener(report);\n } catch (e) {\n devError('Error in health monitor listener:', e);\n }\n }\n }\n\n function performHealthCheck(): void {\n updateMemoryMetrics();\n const report = getHealthReport();\n notifyListeners(report);\n if (report.overall === 'critical')\n devError(\n '🚨 Runtime Health: Critical issues detected',\n report.recommendations,\n );\n else if (report.overall === 'warning')\n devWarn(\n '⚠️ Runtime Health: Performance warnings',\n report.recommendations,\n );\n }\n\n function startMonitoring(): void {\n if (typeof window === 'undefined') return;\n intervalId = setInterval(performHealthCheck, CHECK_INTERVAL);\n }\n\n function stop(): void {\n if (intervalId !== null) {\n clearInterval(intervalId);\n intervalId = null;\n }\n }\n\n function addListener(listener: (report: HealthReport) => void): void {\n listeners.add(listener);\n }\n function removeListener(listener: (report: HealthReport) => void): void {\n listeners.delete(listener);\n }\n function getMetricHistory(name: string): number[] {\n const m = metricsMap.get(name);\n return m ? [...m.history] : [];\n }\n function clearHistory(): void {\n for (const m of metricsMap.values()) m.history = [];\n }\n\n initializeMetrics();\n startMonitoring();\n\n return {\n updateMetric,\n getHealthReport,\n addListener,\n removeListener,\n stop,\n getMetricHistory,\n clearHistory,\n };\n}\n\n// Global singleton\nlet _monitor: HealthMonitorInstance | null = null;\n\n/**\n * Get the global health monitor singleton instance.\n */\nexport function getHealthMonitor(): HealthMonitorInstance {\n if (!_monitor) _monitor = createHealthMonitor();\n return _monitor;\n}\n\n/**\n * Update a health metric from anywhere in the framework.\n */\nexport function updateHealthMetric(name: string, value: number): void {\n getHealthMonitor().updateMetric(name, value);\n}\n\n/**\n * Get the current health status report.\n */\nexport function getHealthStatus(): HealthReport {\n return getHealthMonitor().getHealthReport();\n}\n","/**\n * keep-alive.ts\n *\n * Preserves component state when a component is removed from and later\n * re-inserted into the DOM. By default, custom elements lose all JavaScript\n * state when `disconnectedCallback` fires. `cer-keep-alive` intercepts\n * that lifecycle and keeps the child element alive in memory, re-attaching\n * it when a matching component is re-inserted.\n *\n * ## Usage\n *\n * Wrap any custom element with `<cer-keep-alive>`:\n * ```html\n * <cer-keep-alive>\n * <my-counter></my-counter>\n * </cer-keep-alive>\n * ```\n *\n * Or register it programmatically:\n * ```ts\n * import { registerKeepAlive } from '@jasonshimmy/custom-elements-runtime';\n * registerKeepAlive(); // registers <cer-keep-alive> globally\n * ```\n *\n * ## How it works\n *\n * `cer-keep-alive` uses a slotted layout. When a slotted child component is\n * about to leave the DOM (via a re-render of a parent), KeepAlive intercepts\n * `slotchange` events and preserves the detached child element in an internal\n * cache keyed by tag name. When the same tag re-appears in the slot, the\n * cached element is re-inserted, restoring all JavaScript state.\n *\n * ## Limitations\n *\n * - The first slot child per tag name is cached. Multiple children with the\n * same tag use separate cache entries keyed by their `id` attribute.\n * - Only components registered with the same tag name are matched.\n * - Cache entries can be manually evicted with `clearCache()`.\n */\n\n/** Cache key = tagName[:id] */\ntype CacheKey = string;\n\n/**\n * Register the `<cer-keep-alive>` custom element.\n * Safe to call multiple times — subsequent calls are no-ops.\n *\n * @example\n * ```ts\n * import { registerKeepAlive } from '@jasonshimmy/custom-elements-runtime';\n * registerKeepAlive();\n * ```\n */\nexport function registerKeepAlive(): void {\n if (\n typeof window === 'undefined' ||\n typeof customElements === 'undefined' ||\n customElements.get('cer-keep-alive')\n ) {\n return;\n }\n\n customElements.define('cer-keep-alive', createKeepAliveClass());\n}\n\nfunction createKeepAliveClass(): CustomElementConstructor {\n return class CerKeepAlive extends HTMLElement {\n /** Preserved component instances keyed by tag[:id]. */\n private _cache = new Map<CacheKey, Element>();\n private _slot: HTMLSlotElement | null = null;\n private _slotListener: (() => void) | null = null;\n\n connectedCallback(): void {\n if (!this.shadowRoot) {\n this.attachShadow({ mode: 'open' });\n }\n\n if (!this.shadowRoot!.querySelector('slot')) {\n this.shadowRoot!.innerHTML = '<slot></slot>';\n }\n\n this._slot = this.shadowRoot!.querySelector('slot');\n if (this._slot) {\n this._slotListener = () => this._handleSlotChange();\n this._slot.addEventListener('slotchange', this._slotListener);\n // Process current slotted content\n this._handleSlotChange();\n }\n }\n\n disconnectedCallback(): void {\n if (this._slot && this._slotListener) {\n this._slot.removeEventListener('slotchange', this._slotListener);\n }\n this._slotListener = null;\n }\n\n /**\n * Evict a cached element by its cache key (`tagName` or `tagName:id`).\n * The evicted element is disconnected and removed from the cache.\n */\n clearCache(key?: CacheKey): void {\n if (key) {\n this._cache.delete(key);\n } else {\n this._cache.clear();\n }\n }\n\n private _handleSlotChange(): void {\n if (!this._slot) return;\n\n const slottedElements = this._slot.assignedElements({ flatten: true });\n\n for (const child of slottedElements) {\n const cacheKey = this._buildCacheKey(child);\n\n if (!this._cache.has(cacheKey)) {\n // New element — cache it so we can restore it later\n this._cache.set(cacheKey, child);\n } else {\n const cached = this._cache.get(cacheKey)!;\n if (cached !== child) {\n // A different instance appeared for the same slot.\n // Replace it with the cached instance to restore state.\n try {\n child.parentNode?.replaceChild(cached, child);\n } catch {\n // If replacement fails, update the cache with the new element\n this._cache.set(cacheKey, child);\n }\n }\n }\n }\n }\n\n private _buildCacheKey(el: Element): CacheKey {\n const tag = el.tagName.toLowerCase();\n const id = el.getAttribute('id');\n return id ? `${tag}:${id}` : tag;\n }\n };\n}\n","/**\n * Built-in utility components provided by the custom-elements runtime.\n *\n * These components are registered automatically when this module is imported.\n * They are designed to be minimal, tree-shakeable, and zero-dependency.\n *\n * Included components:\n * - `<cer-suspense>` — Shows a fallback while async work is pending\n * - `<cer-error-boundary>` — Catches render errors and shows a fallback UI\n * - `<cer-keep-alive>` — Preserves component state across DOM removal/re-insertion\n */\n\nimport { component } from './component';\nimport { html } from './template-compiler';\nimport { ref } from './reactive';\nimport { useProps, useOnError, useExpose } from './hooks';\nimport { registerKeepAlive } from '../keep-alive';\n\n// ── cer-suspense ──────────────────────────────────────────────────────────────\n\n/**\n * A built-in component that conditionally renders either the default slot\n * content or the `fallback` slot content, controlled by the `pending` prop.\n *\n * Use the `pending` attribute/property to signal that async work is in\n * progress; the component will swap to the `fallback` slot until `pending`\n * becomes falsy.\n *\n * @example\n * ```html\n * <cer-suspense pending>\n * <!-- shown when pending=false -->\n * <my-async-content></my-async-content>\n *\n * <!-- shown while pending=true -->\n * <div slot=\"fallback\">Loading…</div>\n * </cer-suspense>\n * ```\n *\n * @example Programmatic usage\n * ```ts\n * component('my-data-loader', () => {\n * const pending = ref(true);\n * useOnConnected(async () => {\n * await fetchData();\n * pending.value = false;\n * });\n * return html`\n * <cer-suspense pending=\"${pending.value}\">\n * <my-data-view></my-data-view>\n * <div slot=\"fallback\">Loading data…</div>\n * </cer-suspense>\n * `;\n * });\n * ```\n */\nexport function registerSuspense(): void {\n if (typeof customElements !== 'undefined' && customElements.get('cer-suspense')) return;\n\n component('cer-suspense', () => {\n const { pending } = useProps({ pending: false });\n\n return pending\n ? html`<slot name=\"fallback\"><span>Loading…</span></slot>`\n : html`<slot></slot>`;\n });\n}\n\n// ── cer-error-boundary ────────────────────────────────────────────────────────\n\n/**\n * A built-in component that catches errors thrown during child component\n * rendering and displays a fallback UI instead of crashing the page.\n *\n * Errors are caught via the `useOnError` lifecycle hook. Once an error is\n * caught the component switches to showing the `fallback` named slot (or a\n * default \"Something went wrong\" message if no fallback slot is provided).\n *\n * Call the custom `reset()` method on the element to clear the error and\n * attempt re-rendering the default slot.\n *\n * @example\n * ```html\n * <cer-error-boundary>\n * <my-risky-component></my-risky-component>\n *\n * <div slot=\"fallback\">\n * <p>Something went wrong. <button onclick=\"this.closest('cer-error-boundary').reset()\">Retry</button></p>\n * </div>\n * </cer-error-boundary>\n * ```\n */\nexport function registerErrorBoundary(): void {\n if (typeof customElements !== 'undefined' && customElements.get('cer-error-boundary')) return;\n\n component('cer-error-boundary', () => {\n const hasError = ref(false);\n const errorMessage = ref('');\n\n useOnError((err: Error) => {\n hasError.value = true;\n errorMessage.value = err.message;\n });\n\n // Expose a reset() method so parent templates can call\n // `errorBoundaryRef.value.reset()` to clear the error and retry.\n // Also expose an internal `_cerHandleChildError` receiver so that the\n // component runtime can propagate uncaught errors from slotted child\n // components up to the nearest ancestor <cer-error-boundary>.\n useExpose({\n _cerHandleChildError: (err: unknown) => {\n // Use peek() to read the current value without registering a reactive\n // dependency — the child component's render context may be active when\n // this handler runs, and we must not accidentally subscribe the child\n // to this boundary's internal state.\n if (!hasError.peek()) {\n hasError.value = true;\n errorMessage.value = err instanceof Error ? err.message : String(err);\n }\n },\n reset: () => {\n hasError.value = false;\n errorMessage.value = '';\n },\n });\n\n return hasError.value\n ? html`<slot name=\"fallback\"\n ><div role=\"alert\">\n <strong>Something went wrong.</strong>\n ${errorMessage.value ? html`<p>${errorMessage.value}</p>` : html``}\n </div></slot\n >`\n : html`<slot></slot>`;\n });\n}\n\n// ── Auto-register all components ─────────────────────────────────────────────\n\n/**\n * Register all built-in components (`cer-suspense`, `cer-error-boundary`,\n * `cer-keep-alive`).\n * Safe to call multiple times — each registration is guarded by a\n * `customElements.get()` check.\n */\nexport function registerBuiltinComponents(): void {\n registerSuspense();\n registerErrorBoundary();\n registerKeepAlive();\n}\n","/**\n * teleport.ts\n *\n * Renders virtual DOM content into an arbitrary DOM target located outside\n * the current component's Shadow Root. Useful for modals, tooltips, popovers,\n * and any UI that must visually escape the component boundary.\n *\n * @example\n * ```ts\n * import { component, html, ref, useOnDisconnected, useTeleport } from '@jasonshimmy/custom-elements-runtime';\n *\n * component('modal-trigger', () => {\n * const isOpen = ref(false);\n *\n * // Render modal content into <body> outside the shadow root\n * const { portal, destroy } = useTeleport('#modal-root');\n * useOnDisconnected(destroy);\n *\n * // Call portal() to update the teleported content on each render\n * if (isOpen.value) {\n * portal(html`<div class=\"modal\">\n * <h2>Hello</h2>\n * <button @click=\"${() => (isOpen.value = false)}\">Close</button>\n * </div>`);\n * } else {\n * portal(null);\n * }\n *\n * return html`\n * <button @click=\"${() => (isOpen.value = true)}\">Open modal</button>\n * `;\n * });\n * ```\n */\n\nimport type { VNode, VDomRefs } from './runtime/types';\nimport { vdomRenderer } from './runtime/vdom';\nimport { reactiveSystem } from './runtime/reactive';\nimport { getCurrentComponentContext, isDiscoveryRender } from './runtime/hooks';\n\n/** Handle returned by {@link useTeleport} for managing a portal. */\nexport interface TeleportHandle {\n /**\n * Render (or clear) content at the teleport target.\n * Pass `null` or `undefined` to remove previously rendered content.\n */\n portal(content: VNode | VNode[] | null | undefined): void;\n\n /**\n * Destroy the teleport container and clean up all rendered content.\n * Call this in `useOnDisconnected` to prevent memory leaks.\n */\n destroy(): void;\n}\n\n/**\n * Create a teleport portal that renders content outside the current Shadow Root.\n *\n * @param target - A CSS selector string or an `Element` reference to render into.\n * @returns A {@link TeleportHandle} with `portal()` (update content) and `destroy()` (cleanup).\n *\n * @example\n * ```ts\n * import { component, html, useOnDisconnected, useTeleport } from '@jasonshimmy/custom-elements-runtime';\n *\n * component('my-tooltip', () => {\n * const { portal, destroy } = useTeleport('body');\n * useOnDisconnected(destroy);\n *\n * portal(html`<div class=\"tooltip\">Tooltip content</div>`);\n * return html`<span>Hover me</span>`;\n * });\n * ```\n */\nexport function useTeleport(target: string | Element): TeleportHandle {\n // SSR guard\n if (typeof document === 'undefined') {\n return { portal: () => {}, destroy: () => {} };\n }\n\n // During discovery render the component is not yet mounted — return a no-op\n // handle so the library can detect props/hooks without side-effects.\n if (isDiscoveryRender()) {\n return { portal: () => {}, destroy: () => {} };\n }\n\n // If called inside a component render, use the reactive state-index slot\n // mechanism to ensure the same handle is returned on every re-render of the\n // same component instance. Without this, each re-render would create a new\n // <cer-teleport> container in the target, leaking all but the last one.\n const ctx = getCurrentComponentContext();\n if (ctx) {\n // getOrCreateState uses an incrementing stateIndex that is reset to 0 at\n // the start of each render, so the same call site always gets the same slot.\n const slot = reactiveSystem.getOrCreateState<TeleportHandle | null>(null);\n const existing = slot.peek();\n if (existing !== null) {\n return existing;\n }\n // First render: create the handle and store it without triggering a\n // reactive update (initSilent bypasses triggerUpdate).\n // Pass a slot-invalidation callback so that destroy() clears the slot,\n // allowing a reconnected component to create a fresh container.\n const handle = _createTeleportHandle(target, () => slot.initSilent(null));\n slot.initSilent(handle);\n return handle;\n }\n\n // Outside a component context (e.g. called directly in tests or scripts):\n // fall through to a non-cached, non-stable handle.\n return _createTeleportHandle(target);\n}\n\n/** Internal: create a fresh teleport handle pointing at `target`.\n * @param onDestroy - Optional callback invoked after cleanup in destroy(), used\n * to invalidate a cached slot so the next render creates a fresh handle.\n */\nfunction _createTeleportHandle(\n target: string | Element,\n onDestroy?: () => void,\n): TeleportHandle {\n const targetEl =\n typeof target === 'string'\n ? (document.querySelector(target) as Element | null)\n : target;\n\n if (!targetEl) {\n console.warn(\n `[useTeleport] Target \"${String(target)}\" not found in the document. ` +\n 'Teleported content will not be rendered.',\n );\n return { portal: () => {}, destroy: () => {} };\n }\n\n // Create a dedicated container so we never clobber sibling content.\n const container = document.createElement('cer-teleport');\n container.dataset.cerTeleport = '';\n targetEl.appendChild(container);\n\n // Shared refs bag — passed consistently so ref directives work across updates.\n const refs: VDomRefs = {};\n\n const handle: TeleportHandle = {\n portal(content: VNode | VNode[] | null | undefined): void {\n const nodes: VNode[] =\n content == null ? [] : Array.isArray(content) ? content : [content];\n // vdomRenderer stores _prevVNode/_prevDom on the root object for diffing.\n // Casting to ShadowRoot is safe: we only access properties that exist on\n // HTMLElement (firstChild, appendChild, replaceChild, childNodes).\n vdomRenderer(container as unknown as ShadowRoot, nodes, undefined, refs);\n },\n\n destroy(): void {\n // Render empty nodes to clean up event listeners and refs.\n try {\n vdomRenderer(container as unknown as ShadowRoot, [], undefined, refs);\n } catch {\n /* best-effort cleanup */\n }\n if (container.parentNode) {\n container.parentNode.removeChild(container);\n }\n // Invalidate the cached slot so that if the component reconnects and\n // re-renders, useTeleport() creates a fresh container rather than\n // reusing this destroyed one.\n onDestroy?.();\n },\n };\n\n return handle;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"custom-elements-runtime.cjs.js","names":[],"sources":["../src/lib/runtime/component/async-component.ts","../src/lib/runtime/hydration.ts","../src/lib/runtime/monitoring/health-monitor.ts","../src/lib/keep-alive.ts","../src/lib/runtime/builtin-components.ts","../src/lib/teleport.ts"],"sourcesContent":["import type { VNode } from '../types';\nimport { component } from '../component';\nimport { getCurrentComponentContext } from '../hooks';\n\n/**\n * Options for `defineAsyncComponent`.\n */\nexport interface AsyncComponentOptions {\n /**\n * Render function shown while the async component is loading.\n * Defaults to rendering nothing (empty fragment).\n */\n loading?: () => VNode | VNode[];\n /**\n * Render function shown when the loader Promise rejects or times out.\n * Defaults to rendering nothing.\n */\n error?: () => VNode | VNode[];\n /**\n * Maximum milliseconds to wait before treating the loader as timed out.\n * When exceeded, the component transitions to the `error` state.\n * Defaults to no timeout.\n */\n timeout?: number;\n}\n\ntype AsyncComponentState = 'idle' | 'loading' | 'resolved' | 'error' | 'timeout';\n\n/**\n * Defines a component whose render function is loaded asynchronously.\n *\n * On first render a loading placeholder is shown (if provided). When the\n * loader resolves the real component is registered under the same tag name\n * and the element re-renders with the full implementation.\n *\n * This is the Web Components equivalent of Vue's `defineAsyncComponent` /\n * React's `React.lazy`.\n *\n * @example\n * ```ts\n * // app/components/heavy-editor.ts\n * defineAsyncComponent(\n * 'heavy-editor',\n * () => import('./heavy-editor-impl.ts').then(m => m.renderFn),\n * { loading: () => html`<p>Loading editor…</p>` },\n * )\n * ```\n *\n * @param tag - Custom element tag name (must contain a hyphen).\n * @param loader - A function that returns a Promise resolving to a render\n * function `() => VNode | VNode[]`.\n * @param options - Optional loading/error placeholders and timeout.\n */\nexport function defineAsyncComponent(\n tag: string,\n loader: () => Promise<() => VNode | VNode[]>,\n options?: AsyncComponentOptions,\n): void {\n let state: AsyncComponentState = 'idle';\n let resolvedRenderFn: (() => VNode | VNode[]) | null = null;\n\n // Mutable reference updated on every real render so `settle` always calls\n // the most-recent live `requestRender`. This avoids the pitfall where the\n // first invocation of the render function is the framework's discovery render\n // (which supplies a no-op `requestRender`) and `load()` would otherwise\n // capture that no-op permanently.\n const rerenderRef: { fn: () => void } = { fn: (): void => {} };\n\n let loaderStarted = false;\n let settled = false;\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const settle = (nextState: AsyncComponentState, fn?: () => VNode | VNode[]): void => {\n if (settled) return;\n settled = true;\n if (timeoutId !== null) clearTimeout(timeoutId);\n if (fn) resolvedRenderFn = fn;\n state = nextState;\n rerenderRef.fn();\n };\n\n const startLoader = (): void => {\n if (loaderStarted) return;\n loaderStarted = true;\n state = 'loading';\n\n if (options?.timeout != null) {\n timeoutId = setTimeout(() => settle('timeout'), options.timeout);\n }\n\n loader()\n .then((renderFn) => settle('resolved', renderFn))\n .catch(() => settle('error'));\n };\n\n component(tag, () => {\n // Always refresh the rerender reference from the current render context.\n // The discovery render (prop introspection) supplies a no-op requestRender;\n // real renders supply the live element requestRender. By updating on every\n // call, settle() will use the most-recent live context when it fires.\n const ctx = getCurrentComponentContext() as { requestRender?: () => void } | null;\n if (typeof ctx?.requestRender === 'function') {\n rerenderRef.fn = (): void => { ctx.requestRender!(); };\n }\n\n switch (state) {\n case 'idle':\n startLoader();\n return (options?.loading?.() ?? []) as VNode | VNode[];\n case 'loading':\n return (options?.loading?.() ?? []) as VNode | VNode[];\n case 'resolved':\n return resolvedRenderFn!();\n case 'error':\n case 'timeout':\n return (options?.error?.() ?? []) as VNode | VNode[];\n }\n });\n}\n","/**\n * Client-side hydration helpers.\n *\n * When the server renders with `dsd: true`, each custom element's shadow DOM\n * is already present in the HTML via a Declarative Shadow DOM template. The\n * browser parses that template and attaches the shadow root before any\n * JavaScript runs. When the JS bundle loads, the custom element constructors\n * detect the existing shadow root and skip the `attachShadow()` call, then\n * hydrate (attach reactivity to) the existing DOM on `connectedCallback`.\n *\n * `hydrateApp` is a signal/entry point for this process. In most cases custom\n * elements self-hydrate automatically on connection — this function is useful\n * when you need to explicitly trigger or defer the hydration of a subtree.\n */\n\n/**\n * Trigger hydration for all registered custom elements within `root`.\n *\n * In practice, custom elements self-hydrate as soon as their class definition\n * is registered and the element is connected to the DOM. Call `hydrateApp`\n * after importing and calling `component()` for all your components to signal\n * that the page is ready for reactive activation.\n *\n * @param root - The root element to hydrate. Defaults to `document`.\n *\n * @example\n * ```ts\n * import { component, hydrateApp } from '@jasonshimmy/custom-elements-runtime';\n * import './components'; // registers all components via component()\n *\n * hydrateApp(); // activate all DSD-rendered components on the page\n * ```\n */\nexport function hydrateApp(root: Element | Document = document): void {\n if (typeof CustomEvent === 'undefined') return;\n const target =\n root instanceof Document ? root.documentElement : (root as Element);\n target.dispatchEvent(\n new CustomEvent('cer:hydrate', { bubbles: true, composed: true }),\n );\n}\n","/**\n * Runtime Health Monitoring System\n * Tracks framework health metrics and provides early warning for potential issues\n */\n\nimport { devWarn, devError } from '../logger';\n\ninterface HealthMetric {\n name: string;\n value: number;\n threshold: number;\n status: 'healthy' | 'warning' | 'critical';\n lastUpdated: number;\n history: number[];\n}\n\ninterface HealthReport {\n overall: 'healthy' | 'warning' | 'critical';\n metrics: Record<string, HealthMetric>;\n timestamp: number;\n recommendations: string[];\n}\nexport type { HealthReport };\n\n/**\n * Public interface for a health monitor instance.\n * All state is managed internally via closures — no class syntax.\n */\nexport interface HealthMonitorInstance {\n /** Update a specific health metric value */\n updateMetric(name: string, value: number): void;\n /** Get the current health report */\n getHealthReport(): HealthReport;\n /** Add a listener to be notified when a health check runs */\n addListener(listener: (report: HealthReport) => void): void;\n /** Remove a previously registered listener */\n removeListener(listener: (report: HealthReport) => void): void;\n /** Stop the periodic health monitoring timer */\n stop(): void;\n /** Get historical values for a specific metric */\n getMetricHistory(name: string): number[];\n /** Clear all metric history */\n clearHistory(): void;\n}\n\nconst MAX_HISTORY_SIZE = 100;\nconst CHECK_INTERVAL = 30_000; // 30 seconds\n\nfunction calcStatus(\n value: number,\n threshold: number,\n metricName: string,\n): 'healthy' | 'warning' | 'critical' {\n if (metricName === 'jitCacheHitRate') {\n if (value < threshold * 0.5) return 'critical';\n if (value < threshold) return 'warning';\n return 'healthy';\n }\n if (value > threshold * 2) return 'critical';\n if (value > threshold) return 'warning';\n return 'healthy';\n}\n\nfunction buildRecommendations(metrics: Record<string, HealthMetric>): string[] {\n const out: string[] = [];\n if (metrics.memoryUsage?.status !== 'healthy')\n out.push(\n 'Consider reducing component complexity or implementing better memory cleanup',\n );\n if (metrics.averageRenderTime?.status !== 'healthy')\n out.push(\n 'Optimize component render functions - consider lazy loading or virtualization',\n );\n if (metrics.jitCacheHitRate?.status !== 'healthy')\n out.push(\n 'JIT CSS cache performance is poor - review CSS patterns for optimization',\n );\n if (metrics.componentErrorRate?.status !== 'healthy')\n out.push(\n 'High component error rate detected - review error handling and component logic',\n );\n if (metrics.activeReactiveStates?.status !== 'healthy')\n out.push(\n 'High number of reactive states - consider state consolidation or cleanup',\n );\n if (metrics.memoryLeakIndicator?.status !== 'healthy')\n out.push(\n 'Potential memory leak detected - review component cleanup and event listener management',\n );\n return out;\n}\n\n/**\n * Create a new health monitor instance.\n * All mutable state lives in closures — no `class` or `this`.\n */\nexport function createHealthMonitor(): HealthMonitorInstance {\n const metricsMap = new Map<string, HealthMetric>();\n const listeners = new Set<(report: HealthReport) => void>();\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n function addMetric(name: string, value: number, threshold: number): void {\n metricsMap.set(name, {\n name,\n value,\n threshold,\n status: 'healthy',\n lastUpdated: Date.now(),\n history: [],\n });\n }\n\n function initializeMetrics(): void {\n addMetric('activeComponents', 0, 1000);\n addMetric('componentCreateRate', 0, 50);\n addMetric('componentErrorRate', 0, 0.1);\n addMetric('memoryUsage', 0, 50 * 1024 * 1024);\n addMetric('memoryGrowthRate', 0, 1024 * 1024);\n addMetric('averageRenderTime', 0, 16);\n addMetric('slowRenderCount', 0, 10);\n addMetric('jitCacheHitRate', 100, 80);\n addMetric('activeReactiveStates', 0, 5000);\n addMetric('dependencyUpdates', 0, 100);\n addMetric('memoryLeakIndicator', 0, 0.1);\n }\n\n function updateMetric(name: string, value: number): void {\n const metric = metricsMap.get(name);\n if (!metric) return;\n metric.value = value;\n metric.lastUpdated = Date.now();\n metric.history.push(value);\n if (metric.history.length > MAX_HISTORY_SIZE) metric.history.shift();\n metric.status = calcStatus(value, metric.threshold, name);\n }\n\n function getHealthReport(): HealthReport {\n const snapshot: Record<string, HealthMetric> = {};\n let overall: 'healthy' | 'warning' | 'critical' = 'healthy';\n for (const [name, metric] of metricsMap) {\n snapshot[name] = { ...metric };\n if (metric.status === 'critical') overall = 'critical';\n else if (metric.status === 'warning' && overall === 'healthy')\n overall = 'warning';\n }\n return {\n overall,\n metrics: snapshot,\n timestamp: Date.now(),\n recommendations: buildRecommendations(snapshot),\n };\n }\n\n function updateMemoryMetrics(): void {\n if (\n 'memory' in performance &&\n (performance as Record<string, unknown>).memory\n ) {\n const mem = (performance as Record<string, unknown>).memory as {\n usedJSHeapSize: number;\n };\n updateMetric('memoryUsage', mem.usedJSHeapSize);\n const m = metricsMap.get('memoryUsage');\n if (m && m.history.length > 1) {\n const prev = m.history[m.history.length - 2];\n const curr = m.history[m.history.length - 1];\n updateMetric('memoryGrowthRate', Math.max(0, curr - prev));\n }\n }\n }\n\n function notifyListeners(report: HealthReport): void {\n for (const listener of listeners) {\n try {\n listener(report);\n } catch (e) {\n devError('Error in health monitor listener:', e);\n }\n }\n }\n\n function performHealthCheck(): void {\n updateMemoryMetrics();\n const report = getHealthReport();\n notifyListeners(report);\n if (report.overall === 'critical')\n devError(\n '🚨 Runtime Health: Critical issues detected',\n report.recommendations,\n );\n else if (report.overall === 'warning')\n devWarn(\n '⚠️ Runtime Health: Performance warnings',\n report.recommendations,\n );\n }\n\n function startMonitoring(): void {\n if (typeof window === 'undefined') return;\n intervalId = setInterval(performHealthCheck, CHECK_INTERVAL);\n }\n\n function stop(): void {\n if (intervalId !== null) {\n clearInterval(intervalId);\n intervalId = null;\n }\n }\n\n function addListener(listener: (report: HealthReport) => void): void {\n listeners.add(listener);\n }\n function removeListener(listener: (report: HealthReport) => void): void {\n listeners.delete(listener);\n }\n function getMetricHistory(name: string): number[] {\n const m = metricsMap.get(name);\n return m ? [...m.history] : [];\n }\n function clearHistory(): void {\n for (const m of metricsMap.values()) m.history = [];\n }\n\n initializeMetrics();\n startMonitoring();\n\n return {\n updateMetric,\n getHealthReport,\n addListener,\n removeListener,\n stop,\n getMetricHistory,\n clearHistory,\n };\n}\n\n// Global singleton\nlet _monitor: HealthMonitorInstance | null = null;\n\n/**\n * Get the global health monitor singleton instance.\n */\nexport function getHealthMonitor(): HealthMonitorInstance {\n if (!_monitor) _monitor = createHealthMonitor();\n return _monitor;\n}\n\n/**\n * Update a health metric from anywhere in the framework.\n */\nexport function updateHealthMetric(name: string, value: number): void {\n getHealthMonitor().updateMetric(name, value);\n}\n\n/**\n * Get the current health status report.\n */\nexport function getHealthStatus(): HealthReport {\n return getHealthMonitor().getHealthReport();\n}\n","/**\n * keep-alive.ts\n *\n * Preserves component state when a component is removed from and later\n * re-inserted into the DOM. By default, custom elements lose all JavaScript\n * state when `disconnectedCallback` fires. `cer-keep-alive` intercepts\n * that lifecycle and keeps the child element alive in memory, re-attaching\n * it when a matching component is re-inserted.\n *\n * ## Usage\n *\n * Wrap any custom element with `<cer-keep-alive>`:\n * ```html\n * <cer-keep-alive>\n * <my-counter></my-counter>\n * </cer-keep-alive>\n * ```\n *\n * Or register it programmatically:\n * ```ts\n * import { registerKeepAlive } from '@jasonshimmy/custom-elements-runtime';\n * registerKeepAlive(); // registers <cer-keep-alive> globally\n * ```\n *\n * ## How it works\n *\n * `cer-keep-alive` uses a slotted layout. When a slotted child component is\n * about to leave the DOM (via a re-render of a parent), KeepAlive intercepts\n * `slotchange` events and preserves the detached child element in an internal\n * cache keyed by tag name. When the same tag re-appears in the slot, the\n * cached element is re-inserted, restoring all JavaScript state.\n *\n * ## Limitations\n *\n * - The first slot child per tag name is cached. Multiple children with the\n * same tag use separate cache entries keyed by their `id` attribute.\n * - Only components registered with the same tag name are matched.\n * - Cache entries can be manually evicted with `clearCache()`.\n */\n\n/** Cache key = tagName[:id] */\ntype CacheKey = string;\n\n/**\n * Register the `<cer-keep-alive>` custom element.\n * Safe to call multiple times — subsequent calls are no-ops.\n *\n * @example\n * ```ts\n * import { registerKeepAlive } from '@jasonshimmy/custom-elements-runtime';\n * registerKeepAlive();\n * ```\n */\nexport function registerKeepAlive(): void {\n if (\n typeof window === 'undefined' ||\n typeof customElements === 'undefined' ||\n customElements.get('cer-keep-alive')\n ) {\n return;\n }\n\n customElements.define('cer-keep-alive', createKeepAliveClass());\n}\n\nfunction createKeepAliveClass(): CustomElementConstructor {\n return class CerKeepAlive extends HTMLElement {\n /** Preserved component instances keyed by tag[:id]. */\n private _cache = new Map<CacheKey, Element>();\n private _slot: HTMLSlotElement | null = null;\n private _slotListener: (() => void) | null = null;\n\n connectedCallback(): void {\n if (!this.shadowRoot) {\n this.attachShadow({ mode: 'open' });\n }\n\n if (!this.shadowRoot!.querySelector('slot')) {\n this.shadowRoot!.innerHTML = '<slot></slot>';\n }\n\n this._slot = this.shadowRoot!.querySelector('slot');\n if (this._slot) {\n this._slotListener = () => this._handleSlotChange();\n this._slot.addEventListener('slotchange', this._slotListener);\n // Process current slotted content\n this._handleSlotChange();\n }\n }\n\n disconnectedCallback(): void {\n if (this._slot && this._slotListener) {\n this._slot.removeEventListener('slotchange', this._slotListener);\n }\n this._slotListener = null;\n }\n\n /**\n * Evict a cached element by its cache key (`tagName` or `tagName:id`).\n * The evicted element is disconnected and removed from the cache.\n */\n clearCache(key?: CacheKey): void {\n if (key) {\n this._cache.delete(key);\n } else {\n this._cache.clear();\n }\n }\n\n private _handleSlotChange(): void {\n if (!this._slot) return;\n\n const slottedElements = this._slot.assignedElements({ flatten: true });\n\n for (const child of slottedElements) {\n const cacheKey = this._buildCacheKey(child);\n\n if (!this._cache.has(cacheKey)) {\n // New element — cache it so we can restore it later\n this._cache.set(cacheKey, child);\n } else {\n const cached = this._cache.get(cacheKey)!;\n if (cached !== child) {\n // A different instance appeared for the same slot.\n // Replace it with the cached instance to restore state.\n try {\n child.parentNode?.replaceChild(cached, child);\n } catch {\n // If replacement fails, update the cache with the new element\n this._cache.set(cacheKey, child);\n }\n }\n }\n }\n }\n\n private _buildCacheKey(el: Element): CacheKey {\n const tag = el.tagName.toLowerCase();\n const id = el.getAttribute('id');\n return id ? `${tag}:${id}` : tag;\n }\n };\n}\n","/**\n * Built-in utility components provided by the custom-elements runtime.\n *\n * These components are registered automatically when this module is imported.\n * They are designed to be minimal, tree-shakeable, and zero-dependency.\n *\n * Included components:\n * - `<cer-suspense>` — Shows a fallback while async work is pending\n * - `<cer-error-boundary>` — Catches render errors and shows a fallback UI\n * - `<cer-keep-alive>` — Preserves component state across DOM removal/re-insertion\n */\n\nimport { component } from './component';\nimport { html } from './template-compiler';\nimport { ref } from './reactive';\nimport { useProps, useOnError, useExpose } from './hooks';\nimport { registerKeepAlive } from '../keep-alive';\n\n// ── cer-suspense ──────────────────────────────────────────────────────────────\n\n/**\n * A built-in component that conditionally renders either the default slot\n * content or the `fallback` slot content, controlled by the `pending` prop.\n *\n * Use the `pending` attribute/property to signal that async work is in\n * progress; the component will swap to the `fallback` slot until `pending`\n * becomes falsy.\n *\n * @example\n * ```html\n * <cer-suspense pending>\n * <!-- shown when pending=false -->\n * <my-async-content></my-async-content>\n *\n * <!-- shown while pending=true -->\n * <div slot=\"fallback\">Loading…</div>\n * </cer-suspense>\n * ```\n *\n * @example Programmatic usage\n * ```ts\n * component('my-data-loader', () => {\n * const pending = ref(true);\n * useOnConnected(async () => {\n * await fetchData();\n * pending.value = false;\n * });\n * return html`\n * <cer-suspense pending=\"${pending.value}\">\n * <my-data-view></my-data-view>\n * <div slot=\"fallback\">Loading data…</div>\n * </cer-suspense>\n * `;\n * });\n * ```\n */\nexport function registerSuspense(): void {\n if (typeof customElements !== 'undefined' && customElements.get('cer-suspense')) return;\n\n component('cer-suspense', () => {\n const { pending } = useProps({ pending: false });\n\n return pending\n ? html`<slot name=\"fallback\"><span>Loading…</span></slot>`\n : html`<slot></slot>`;\n });\n}\n\n// ── cer-error-boundary ────────────────────────────────────────────────────────\n\n/**\n * A built-in component that catches errors thrown during child component\n * rendering and displays a fallback UI instead of crashing the page.\n *\n * Errors are caught via the `useOnError` lifecycle hook. Once an error is\n * caught the component switches to showing the `fallback` named slot (or a\n * default \"Something went wrong\" message if no fallback slot is provided).\n *\n * Call the custom `reset()` method on the element to clear the error and\n * attempt re-rendering the default slot.\n *\n * @example\n * ```html\n * <cer-error-boundary>\n * <my-risky-component></my-risky-component>\n *\n * <div slot=\"fallback\">\n * <p>Something went wrong. <button onclick=\"this.closest('cer-error-boundary').reset()\">Retry</button></p>\n * </div>\n * </cer-error-boundary>\n * ```\n */\nexport function registerErrorBoundary(): void {\n if (typeof customElements !== 'undefined' && customElements.get('cer-error-boundary')) return;\n\n component('cer-error-boundary', () => {\n const hasError = ref(false);\n const errorMessage = ref('');\n\n useOnError((err: Error) => {\n hasError.value = true;\n errorMessage.value = err.message;\n });\n\n // Expose a reset() method so parent templates can call\n // `errorBoundaryRef.value.reset()` to clear the error and retry.\n // Also expose an internal `_cerHandleChildError` receiver so that the\n // component runtime can propagate uncaught errors from slotted child\n // components up to the nearest ancestor <cer-error-boundary>.\n useExpose({\n _cerHandleChildError: (err: unknown) => {\n // Use peek() to read the current value without registering a reactive\n // dependency — the child component's render context may be active when\n // this handler runs, and we must not accidentally subscribe the child\n // to this boundary's internal state.\n if (!hasError.peek()) {\n hasError.value = true;\n errorMessage.value = err instanceof Error ? err.message : String(err);\n }\n },\n reset: () => {\n hasError.value = false;\n errorMessage.value = '';\n },\n });\n\n return hasError.value\n ? html`<slot name=\"fallback\"\n ><div role=\"alert\">\n <strong>Something went wrong.</strong>\n ${errorMessage.value ? html`<p>${errorMessage.value}</p>` : html``}\n </div></slot\n >`\n : html`<slot></slot>`;\n });\n}\n\n// ── Auto-register all components ─────────────────────────────────────────────\n\n/**\n * Register all built-in components (`cer-suspense`, `cer-error-boundary`,\n * `cer-keep-alive`).\n * Safe to call multiple times — each registration is guarded by a\n * `customElements.get()` check.\n */\nexport function registerBuiltinComponents(): void {\n registerSuspense();\n registerErrorBoundary();\n registerKeepAlive();\n}\n","/**\n * teleport.ts\n *\n * Renders virtual DOM content into an arbitrary DOM target located outside\n * the current component's Shadow Root. Useful for modals, tooltips, popovers,\n * and any UI that must visually escape the component boundary.\n *\n * @example\n * ```ts\n * import { component, html, ref, useOnDisconnected, useTeleport } from '@jasonshimmy/custom-elements-runtime';\n *\n * component('modal-trigger', () => {\n * const isOpen = ref(false);\n *\n * // Render modal content into <body> outside the shadow root\n * const { portal, destroy } = useTeleport('#modal-root');\n * useOnDisconnected(destroy);\n *\n * // Call portal() to update the teleported content on each render\n * if (isOpen.value) {\n * portal(html`<div class=\"modal\">\n * <h2>Hello</h2>\n * <button @click=\"${() => (isOpen.value = false)}\">Close</button>\n * </div>`);\n * } else {\n * portal(null);\n * }\n *\n * return html`\n * <button @click=\"${() => (isOpen.value = true)}\">Open modal</button>\n * `;\n * });\n * ```\n */\n\nimport type { VNode, VDomRefs } from './runtime/types';\nimport { vdomRenderer } from './runtime/vdom';\nimport { reactiveSystem } from './runtime/reactive';\nimport { getCurrentComponentContext, isDiscoveryRender } from './runtime/hooks';\n\n/** Handle returned by {@link useTeleport} for managing a portal. */\nexport interface TeleportHandle {\n /**\n * Render (or clear) content at the teleport target.\n * Pass `null` or `undefined` to remove previously rendered content.\n */\n portal(content: VNode | VNode[] | null | undefined): void;\n\n /**\n * Destroy the teleport container and clean up all rendered content.\n * Call this in `useOnDisconnected` to prevent memory leaks.\n */\n destroy(): void;\n}\n\n/**\n * Create a teleport portal that renders content outside the current Shadow Root.\n *\n * @param target - A CSS selector string or an `Element` reference to render into.\n * @returns A {@link TeleportHandle} with `portal()` (update content) and `destroy()` (cleanup).\n *\n * @example\n * ```ts\n * import { component, html, useOnDisconnected, useTeleport } from '@jasonshimmy/custom-elements-runtime';\n *\n * component('my-tooltip', () => {\n * const { portal, destroy } = useTeleport('body');\n * useOnDisconnected(destroy);\n *\n * portal(html`<div class=\"tooltip\">Tooltip content</div>`);\n * return html`<span>Hover me</span>`;\n * });\n * ```\n */\nexport function useTeleport(target: string | Element): TeleportHandle {\n // SSR guard\n if (typeof document === 'undefined') {\n return { portal: () => {}, destroy: () => {} };\n }\n\n // During discovery render the component is not yet mounted — return a no-op\n // handle so the library can detect props/hooks without side-effects.\n if (isDiscoveryRender()) {\n return { portal: () => {}, destroy: () => {} };\n }\n\n // If called inside a component render, use the reactive state-index slot\n // mechanism to ensure the same handle is returned on every re-render of the\n // same component instance. Without this, each re-render would create a new\n // <cer-teleport> container in the target, leaking all but the last one.\n const ctx = getCurrentComponentContext();\n if (ctx) {\n // getOrCreateState uses an incrementing stateIndex that is reset to 0 at\n // the start of each render, so the same call site always gets the same slot.\n const slot = reactiveSystem.getOrCreateState<TeleportHandle | null>(null);\n const existing = slot.peek();\n if (existing !== null) {\n return existing;\n }\n // First render: create the handle and store it without triggering a\n // reactive update (initSilent bypasses triggerUpdate).\n // Pass a slot-invalidation callback so that destroy() clears the slot,\n // allowing a reconnected component to create a fresh container.\n const handle = _createTeleportHandle(target, () => slot.initSilent(null));\n slot.initSilent(handle);\n return handle;\n }\n\n // Outside a component context (e.g. called directly in tests or scripts):\n // fall through to a non-cached, non-stable handle.\n return _createTeleportHandle(target);\n}\n\n/** Internal: create a fresh teleport handle pointing at `target`.\n * @param onDestroy - Optional callback invoked after cleanup in destroy(), used\n * to invalidate a cached slot so the next render creates a fresh handle.\n */\nfunction _createTeleportHandle(\n target: string | Element,\n onDestroy?: () => void,\n): TeleportHandle {\n const targetEl =\n typeof target === 'string'\n ? (document.querySelector(target) as Element | null)\n : target;\n\n if (!targetEl) {\n console.warn(\n `[useTeleport] Target \"${String(target)}\" not found in the document. ` +\n 'Teleported content will not be rendered.',\n );\n return { portal: () => {}, destroy: () => {} };\n }\n\n // Create a dedicated container so we never clobber sibling content.\n const container = document.createElement('cer-teleport');\n container.dataset.cerTeleport = '';\n targetEl.appendChild(container);\n\n // Shared refs bag — passed consistently so ref directives work across updates.\n const refs: VDomRefs = {};\n\n const handle: TeleportHandle = {\n portal(content: VNode | VNode[] | null | undefined): void {\n const nodes: VNode[] =\n content == null ? [] : Array.isArray(content) ? content : [content];\n // vdomRenderer stores _prevVNode/_prevDom on the root object for diffing.\n // Casting to ShadowRoot is safe: we only access properties that exist on\n // HTMLElement (firstChild, appendChild, replaceChild, childNodes).\n vdomRenderer(container as unknown as ShadowRoot, nodes, undefined, refs);\n },\n\n destroy(): void {\n // Render empty nodes to clean up event listeners and refs.\n try {\n vdomRenderer(container as unknown as ShadowRoot, [], undefined, refs);\n } catch {\n /* best-effort cleanup */\n }\n if (container.parentNode) {\n container.parentNode.removeChild(container);\n }\n // Invalidate the cached slot so that if the component reconnects and\n // re-renders, useTeleport() creates a fresh container rather than\n // reusing this destroyed one.\n onDestroy?.();\n },\n };\n\n return handle;\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
import { i as e, n as t, r as n, t as r } from "./logger-DIJ0UH3R.js";
|
|
2
|
-
import { D as i, E as a, _ as o, b as s, g as c, h as l, k as u, m as d, n as f, v as p, x as m, y as h } from "./helpers-
|
|
3
|
-
import { n as g, r as _, t as v } from "./template-compiler-
|
|
2
|
+
import { D as i, E as a, _ as o, b as s, g as c, h as l, k as u, m as d, n as f, v as p, x as m, y as h } from "./helpers-kOWgceUQ.js";
|
|
3
|
+
import { n as g, r as _, t as v } from "./template-compiler-q_rTI8PA.js";
|
|
4
4
|
import { o as y } from "./css-utils-Bn-dO44e.js";
|
|
5
|
-
import { _ as b, a as x, d as S, f as C, g as w, h as T, i as E, l as D, m as O, n as k, o as A, p as
|
|
5
|
+
import { _ as b, a as x, d as S, f as C, g as w, h as T, i as E, l as D, m as O, n as k, o as A, p as ee, r as j, s as M, u as N, v as P, y as F } from "./hooks-Dj1xwqpK.js";
|
|
6
|
+
//#region src/lib/runtime/component/async-component.ts
|
|
7
|
+
function I(e, t, n) {
|
|
8
|
+
let r = "idle", i = null, a = { fn: () => {} }, o = !1, s = !1, c = null, l = (e, t) => {
|
|
9
|
+
s || (s = !0, c !== null && clearTimeout(c), t && (i = t), r = e, a.fn());
|
|
10
|
+
}, u = () => {
|
|
11
|
+
o || (o = !0, r = "loading", n?.timeout != null && (c = setTimeout(() => l("timeout"), n.timeout)), t().then((e) => l("resolved", e)).catch(() => l("error")));
|
|
12
|
+
};
|
|
13
|
+
g(e, () => {
|
|
14
|
+
let e = E();
|
|
15
|
+
switch (typeof e?.requestRender == "function" && (a.fn = () => {
|
|
16
|
+
e.requestRender();
|
|
17
|
+
}), r) {
|
|
18
|
+
case "idle": return u(), n?.loading?.() ?? [];
|
|
19
|
+
case "loading": return n?.loading?.() ?? [];
|
|
20
|
+
case "resolved": return i();
|
|
21
|
+
case "error":
|
|
22
|
+
case "timeout": return n?.error?.() ?? [];
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
6
27
|
//#region src/lib/runtime/hydration.ts
|
|
7
28
|
function L(e = document) {
|
|
8
29
|
typeof CustomEvent > "u" || (e instanceof Document ? e.documentElement : e).dispatchEvent(new CustomEvent("cer:hydrate", {
|
|
@@ -220,6 +241,6 @@ function $(e, t) {
|
|
|
220
241
|
};
|
|
221
242
|
}
|
|
222
243
|
//#endregion
|
|
223
|
-
export { l as ReactiveState, g as component, c as computed, k as createComposable, H as createHealthMonitor, y as css, f as decodeEntities,
|
|
244
|
+
export { l as ReactiveState, g as component, c as computed, k as createComposable, H as createHealthMonitor, y as css, f as decodeEntities, I as defineAsyncComponent, j as defineModel, t as devLog, a as flushDOMUpdates, E as getCurrentComponentContext, W as getHealthMonitor, K as getHealthStatus, v as html, L as hydrateApp, x as inject, o as isReactiveState, i as nextTick, M as provide, h as ref, Z as registerBuiltinComponents, X as registerErrorBoundary, q as registerKeepAlive, Y as registerSuspense, u as scheduleWithPriority, e as setDevMode, d as unsafeHTML, G as updateHealthMetric, D as useDesignTokens, N as useEmit, S as useExpose, C as useGlobalStyle, ee as useOnAttributeChanged, O as useOnConnected, T as useOnDisconnected, w as useOnError, b as useProps, P as useSlots, F as useStyle, Q as useTeleport, s as watch, m as watchEffect };
|
|
224
245
|
|
|
225
246
|
//# sourceMappingURL=custom-elements-runtime.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-elements-runtime.es.js","names":[],"sources":["../src/lib/runtime/hydration.ts","../src/lib/runtime/monitoring/health-monitor.ts","../src/lib/keep-alive.ts","../src/lib/runtime/builtin-components.ts","../src/lib/teleport.ts"],"sourcesContent":["/**\n * Client-side hydration helpers.\n *\n * When the server renders with `dsd: true`, each custom element's shadow DOM\n * is already present in the HTML via a Declarative Shadow DOM template. The\n * browser parses that template and attaches the shadow root before any\n * JavaScript runs. When the JS bundle loads, the custom element constructors\n * detect the existing shadow root and skip the `attachShadow()` call, then\n * hydrate (attach reactivity to) the existing DOM on `connectedCallback`.\n *\n * `hydrateApp` is a signal/entry point for this process. In most cases custom\n * elements self-hydrate automatically on connection — this function is useful\n * when you need to explicitly trigger or defer the hydration of a subtree.\n */\n\n/**\n * Trigger hydration for all registered custom elements within `root`.\n *\n * In practice, custom elements self-hydrate as soon as their class definition\n * is registered and the element is connected to the DOM. Call `hydrateApp`\n * after importing and calling `component()` for all your components to signal\n * that the page is ready for reactive activation.\n *\n * @param root - The root element to hydrate. Defaults to `document`.\n *\n * @example\n * ```ts\n * import { component, hydrateApp } from '@jasonshimmy/custom-elements-runtime';\n * import './components'; // registers all components via component()\n *\n * hydrateApp(); // activate all DSD-rendered components on the page\n * ```\n */\nexport function hydrateApp(root: Element | Document = document): void {\n if (typeof CustomEvent === 'undefined') return;\n const target =\n root instanceof Document ? root.documentElement : (root as Element);\n target.dispatchEvent(\n new CustomEvent('cer:hydrate', { bubbles: true, composed: true }),\n );\n}\n","/**\n * Runtime Health Monitoring System\n * Tracks framework health metrics and provides early warning for potential issues\n */\n\nimport { devWarn, devError } from '../logger';\n\ninterface HealthMetric {\n name: string;\n value: number;\n threshold: number;\n status: 'healthy' | 'warning' | 'critical';\n lastUpdated: number;\n history: number[];\n}\n\ninterface HealthReport {\n overall: 'healthy' | 'warning' | 'critical';\n metrics: Record<string, HealthMetric>;\n timestamp: number;\n recommendations: string[];\n}\nexport type { HealthReport };\n\n/**\n * Public interface for a health monitor instance.\n * All state is managed internally via closures — no class syntax.\n */\nexport interface HealthMonitorInstance {\n /** Update a specific health metric value */\n updateMetric(name: string, value: number): void;\n /** Get the current health report */\n getHealthReport(): HealthReport;\n /** Add a listener to be notified when a health check runs */\n addListener(listener: (report: HealthReport) => void): void;\n /** Remove a previously registered listener */\n removeListener(listener: (report: HealthReport) => void): void;\n /** Stop the periodic health monitoring timer */\n stop(): void;\n /** Get historical values for a specific metric */\n getMetricHistory(name: string): number[];\n /** Clear all metric history */\n clearHistory(): void;\n}\n\nconst MAX_HISTORY_SIZE = 100;\nconst CHECK_INTERVAL = 30_000; // 30 seconds\n\nfunction calcStatus(\n value: number,\n threshold: number,\n metricName: string,\n): 'healthy' | 'warning' | 'critical' {\n if (metricName === 'jitCacheHitRate') {\n if (value < threshold * 0.5) return 'critical';\n if (value < threshold) return 'warning';\n return 'healthy';\n }\n if (value > threshold * 2) return 'critical';\n if (value > threshold) return 'warning';\n return 'healthy';\n}\n\nfunction buildRecommendations(metrics: Record<string, HealthMetric>): string[] {\n const out: string[] = [];\n if (metrics.memoryUsage?.status !== 'healthy')\n out.push(\n 'Consider reducing component complexity or implementing better memory cleanup',\n );\n if (metrics.averageRenderTime?.status !== 'healthy')\n out.push(\n 'Optimize component render functions - consider lazy loading or virtualization',\n );\n if (metrics.jitCacheHitRate?.status !== 'healthy')\n out.push(\n 'JIT CSS cache performance is poor - review CSS patterns for optimization',\n );\n if (metrics.componentErrorRate?.status !== 'healthy')\n out.push(\n 'High component error rate detected - review error handling and component logic',\n );\n if (metrics.activeReactiveStates?.status !== 'healthy')\n out.push(\n 'High number of reactive states - consider state consolidation or cleanup',\n );\n if (metrics.memoryLeakIndicator?.status !== 'healthy')\n out.push(\n 'Potential memory leak detected - review component cleanup and event listener management',\n );\n return out;\n}\n\n/**\n * Create a new health monitor instance.\n * All mutable state lives in closures — no `class` or `this`.\n */\nexport function createHealthMonitor(): HealthMonitorInstance {\n const metricsMap = new Map<string, HealthMetric>();\n const listeners = new Set<(report: HealthReport) => void>();\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n function addMetric(name: string, value: number, threshold: number): void {\n metricsMap.set(name, {\n name,\n value,\n threshold,\n status: 'healthy',\n lastUpdated: Date.now(),\n history: [],\n });\n }\n\n function initializeMetrics(): void {\n addMetric('activeComponents', 0, 1000);\n addMetric('componentCreateRate', 0, 50);\n addMetric('componentErrorRate', 0, 0.1);\n addMetric('memoryUsage', 0, 50 * 1024 * 1024);\n addMetric('memoryGrowthRate', 0, 1024 * 1024);\n addMetric('averageRenderTime', 0, 16);\n addMetric('slowRenderCount', 0, 10);\n addMetric('jitCacheHitRate', 100, 80);\n addMetric('activeReactiveStates', 0, 5000);\n addMetric('dependencyUpdates', 0, 100);\n addMetric('memoryLeakIndicator', 0, 0.1);\n }\n\n function updateMetric(name: string, value: number): void {\n const metric = metricsMap.get(name);\n if (!metric) return;\n metric.value = value;\n metric.lastUpdated = Date.now();\n metric.history.push(value);\n if (metric.history.length > MAX_HISTORY_SIZE) metric.history.shift();\n metric.status = calcStatus(value, metric.threshold, name);\n }\n\n function getHealthReport(): HealthReport {\n const snapshot: Record<string, HealthMetric> = {};\n let overall: 'healthy' | 'warning' | 'critical' = 'healthy';\n for (const [name, metric] of metricsMap) {\n snapshot[name] = { ...metric };\n if (metric.status === 'critical') overall = 'critical';\n else if (metric.status === 'warning' && overall === 'healthy')\n overall = 'warning';\n }\n return {\n overall,\n metrics: snapshot,\n timestamp: Date.now(),\n recommendations: buildRecommendations(snapshot),\n };\n }\n\n function updateMemoryMetrics(): void {\n if (\n 'memory' in performance &&\n (performance as Record<string, unknown>).memory\n ) {\n const mem = (performance as Record<string, unknown>).memory as {\n usedJSHeapSize: number;\n };\n updateMetric('memoryUsage', mem.usedJSHeapSize);\n const m = metricsMap.get('memoryUsage');\n if (m && m.history.length > 1) {\n const prev = m.history[m.history.length - 2];\n const curr = m.history[m.history.length - 1];\n updateMetric('memoryGrowthRate', Math.max(0, curr - prev));\n }\n }\n }\n\n function notifyListeners(report: HealthReport): void {\n for (const listener of listeners) {\n try {\n listener(report);\n } catch (e) {\n devError('Error in health monitor listener:', e);\n }\n }\n }\n\n function performHealthCheck(): void {\n updateMemoryMetrics();\n const report = getHealthReport();\n notifyListeners(report);\n if (report.overall === 'critical')\n devError(\n '🚨 Runtime Health: Critical issues detected',\n report.recommendations,\n );\n else if (report.overall === 'warning')\n devWarn(\n '⚠️ Runtime Health: Performance warnings',\n report.recommendations,\n );\n }\n\n function startMonitoring(): void {\n if (typeof window === 'undefined') return;\n intervalId = setInterval(performHealthCheck, CHECK_INTERVAL);\n }\n\n function stop(): void {\n if (intervalId !== null) {\n clearInterval(intervalId);\n intervalId = null;\n }\n }\n\n function addListener(listener: (report: HealthReport) => void): void {\n listeners.add(listener);\n }\n function removeListener(listener: (report: HealthReport) => void): void {\n listeners.delete(listener);\n }\n function getMetricHistory(name: string): number[] {\n const m = metricsMap.get(name);\n return m ? [...m.history] : [];\n }\n function clearHistory(): void {\n for (const m of metricsMap.values()) m.history = [];\n }\n\n initializeMetrics();\n startMonitoring();\n\n return {\n updateMetric,\n getHealthReport,\n addListener,\n removeListener,\n stop,\n getMetricHistory,\n clearHistory,\n };\n}\n\n// Global singleton\nlet _monitor: HealthMonitorInstance | null = null;\n\n/**\n * Get the global health monitor singleton instance.\n */\nexport function getHealthMonitor(): HealthMonitorInstance {\n if (!_monitor) _monitor = createHealthMonitor();\n return _monitor;\n}\n\n/**\n * Update a health metric from anywhere in the framework.\n */\nexport function updateHealthMetric(name: string, value: number): void {\n getHealthMonitor().updateMetric(name, value);\n}\n\n/**\n * Get the current health status report.\n */\nexport function getHealthStatus(): HealthReport {\n return getHealthMonitor().getHealthReport();\n}\n","/**\n * keep-alive.ts\n *\n * Preserves component state when a component is removed from and later\n * re-inserted into the DOM. By default, custom elements lose all JavaScript\n * state when `disconnectedCallback` fires. `cer-keep-alive` intercepts\n * that lifecycle and keeps the child element alive in memory, re-attaching\n * it when a matching component is re-inserted.\n *\n * ## Usage\n *\n * Wrap any custom element with `<cer-keep-alive>`:\n * ```html\n * <cer-keep-alive>\n * <my-counter></my-counter>\n * </cer-keep-alive>\n * ```\n *\n * Or register it programmatically:\n * ```ts\n * import { registerKeepAlive } from '@jasonshimmy/custom-elements-runtime';\n * registerKeepAlive(); // registers <cer-keep-alive> globally\n * ```\n *\n * ## How it works\n *\n * `cer-keep-alive` uses a slotted layout. When a slotted child component is\n * about to leave the DOM (via a re-render of a parent), KeepAlive intercepts\n * `slotchange` events and preserves the detached child element in an internal\n * cache keyed by tag name. When the same tag re-appears in the slot, the\n * cached element is re-inserted, restoring all JavaScript state.\n *\n * ## Limitations\n *\n * - The first slot child per tag name is cached. Multiple children with the\n * same tag use separate cache entries keyed by their `id` attribute.\n * - Only components registered with the same tag name are matched.\n * - Cache entries can be manually evicted with `clearCache()`.\n */\n\n/** Cache key = tagName[:id] */\ntype CacheKey = string;\n\n/**\n * Register the `<cer-keep-alive>` custom element.\n * Safe to call multiple times — subsequent calls are no-ops.\n *\n * @example\n * ```ts\n * import { registerKeepAlive } from '@jasonshimmy/custom-elements-runtime';\n * registerKeepAlive();\n * ```\n */\nexport function registerKeepAlive(): void {\n if (\n typeof window === 'undefined' ||\n typeof customElements === 'undefined' ||\n customElements.get('cer-keep-alive')\n ) {\n return;\n }\n\n customElements.define('cer-keep-alive', createKeepAliveClass());\n}\n\nfunction createKeepAliveClass(): CustomElementConstructor {\n return class CerKeepAlive extends HTMLElement {\n /** Preserved component instances keyed by tag[:id]. */\n private _cache = new Map<CacheKey, Element>();\n private _slot: HTMLSlotElement | null = null;\n private _slotListener: (() => void) | null = null;\n\n connectedCallback(): void {\n if (!this.shadowRoot) {\n this.attachShadow({ mode: 'open' });\n }\n\n if (!this.shadowRoot!.querySelector('slot')) {\n this.shadowRoot!.innerHTML = '<slot></slot>';\n }\n\n this._slot = this.shadowRoot!.querySelector('slot');\n if (this._slot) {\n this._slotListener = () => this._handleSlotChange();\n this._slot.addEventListener('slotchange', this._slotListener);\n // Process current slotted content\n this._handleSlotChange();\n }\n }\n\n disconnectedCallback(): void {\n if (this._slot && this._slotListener) {\n this._slot.removeEventListener('slotchange', this._slotListener);\n }\n this._slotListener = null;\n }\n\n /**\n * Evict a cached element by its cache key (`tagName` or `tagName:id`).\n * The evicted element is disconnected and removed from the cache.\n */\n clearCache(key?: CacheKey): void {\n if (key) {\n this._cache.delete(key);\n } else {\n this._cache.clear();\n }\n }\n\n private _handleSlotChange(): void {\n if (!this._slot) return;\n\n const slottedElements = this._slot.assignedElements({ flatten: true });\n\n for (const child of slottedElements) {\n const cacheKey = this._buildCacheKey(child);\n\n if (!this._cache.has(cacheKey)) {\n // New element — cache it so we can restore it later\n this._cache.set(cacheKey, child);\n } else {\n const cached = this._cache.get(cacheKey)!;\n if (cached !== child) {\n // A different instance appeared for the same slot.\n // Replace it with the cached instance to restore state.\n try {\n child.parentNode?.replaceChild(cached, child);\n } catch {\n // If replacement fails, update the cache with the new element\n this._cache.set(cacheKey, child);\n }\n }\n }\n }\n }\n\n private _buildCacheKey(el: Element): CacheKey {\n const tag = el.tagName.toLowerCase();\n const id = el.getAttribute('id');\n return id ? `${tag}:${id}` : tag;\n }\n };\n}\n","/**\n * Built-in utility components provided by the custom-elements runtime.\n *\n * These components are registered automatically when this module is imported.\n * They are designed to be minimal, tree-shakeable, and zero-dependency.\n *\n * Included components:\n * - `<cer-suspense>` — Shows a fallback while async work is pending\n * - `<cer-error-boundary>` — Catches render errors and shows a fallback UI\n * - `<cer-keep-alive>` — Preserves component state across DOM removal/re-insertion\n */\n\nimport { component } from './component';\nimport { html } from './template-compiler';\nimport { ref } from './reactive';\nimport { useProps, useOnError, useExpose } from './hooks';\nimport { registerKeepAlive } from '../keep-alive';\n\n// ── cer-suspense ──────────────────────────────────────────────────────────────\n\n/**\n * A built-in component that conditionally renders either the default slot\n * content or the `fallback` slot content, controlled by the `pending` prop.\n *\n * Use the `pending` attribute/property to signal that async work is in\n * progress; the component will swap to the `fallback` slot until `pending`\n * becomes falsy.\n *\n * @example\n * ```html\n * <cer-suspense pending>\n * <!-- shown when pending=false -->\n * <my-async-content></my-async-content>\n *\n * <!-- shown while pending=true -->\n * <div slot=\"fallback\">Loading…</div>\n * </cer-suspense>\n * ```\n *\n * @example Programmatic usage\n * ```ts\n * component('my-data-loader', () => {\n * const pending = ref(true);\n * useOnConnected(async () => {\n * await fetchData();\n * pending.value = false;\n * });\n * return html`\n * <cer-suspense pending=\"${pending.value}\">\n * <my-data-view></my-data-view>\n * <div slot=\"fallback\">Loading data…</div>\n * </cer-suspense>\n * `;\n * });\n * ```\n */\nexport function registerSuspense(): void {\n if (typeof customElements !== 'undefined' && customElements.get('cer-suspense')) return;\n\n component('cer-suspense', () => {\n const { pending } = useProps({ pending: false });\n\n return pending\n ? html`<slot name=\"fallback\"><span>Loading…</span></slot>`\n : html`<slot></slot>`;\n });\n}\n\n// ── cer-error-boundary ────────────────────────────────────────────────────────\n\n/**\n * A built-in component that catches errors thrown during child component\n * rendering and displays a fallback UI instead of crashing the page.\n *\n * Errors are caught via the `useOnError` lifecycle hook. Once an error is\n * caught the component switches to showing the `fallback` named slot (or a\n * default \"Something went wrong\" message if no fallback slot is provided).\n *\n * Call the custom `reset()` method on the element to clear the error and\n * attempt re-rendering the default slot.\n *\n * @example\n * ```html\n * <cer-error-boundary>\n * <my-risky-component></my-risky-component>\n *\n * <div slot=\"fallback\">\n * <p>Something went wrong. <button onclick=\"this.closest('cer-error-boundary').reset()\">Retry</button></p>\n * </div>\n * </cer-error-boundary>\n * ```\n */\nexport function registerErrorBoundary(): void {\n if (typeof customElements !== 'undefined' && customElements.get('cer-error-boundary')) return;\n\n component('cer-error-boundary', () => {\n const hasError = ref(false);\n const errorMessage = ref('');\n\n useOnError((err: Error) => {\n hasError.value = true;\n errorMessage.value = err.message;\n });\n\n // Expose a reset() method so parent templates can call\n // `errorBoundaryRef.value.reset()` to clear the error and retry.\n // Also expose an internal `_cerHandleChildError` receiver so that the\n // component runtime can propagate uncaught errors from slotted child\n // components up to the nearest ancestor <cer-error-boundary>.\n useExpose({\n _cerHandleChildError: (err: unknown) => {\n // Use peek() to read the current value without registering a reactive\n // dependency — the child component's render context may be active when\n // this handler runs, and we must not accidentally subscribe the child\n // to this boundary's internal state.\n if (!hasError.peek()) {\n hasError.value = true;\n errorMessage.value = err instanceof Error ? err.message : String(err);\n }\n },\n reset: () => {\n hasError.value = false;\n errorMessage.value = '';\n },\n });\n\n return hasError.value\n ? html`<slot name=\"fallback\"\n ><div role=\"alert\">\n <strong>Something went wrong.</strong>\n ${errorMessage.value ? html`<p>${errorMessage.value}</p>` : html``}\n </div></slot\n >`\n : html`<slot></slot>`;\n });\n}\n\n// ── Auto-register all components ─────────────────────────────────────────────\n\n/**\n * Register all built-in components (`cer-suspense`, `cer-error-boundary`,\n * `cer-keep-alive`).\n * Safe to call multiple times — each registration is guarded by a\n * `customElements.get()` check.\n */\nexport function registerBuiltinComponents(): void {\n registerSuspense();\n registerErrorBoundary();\n registerKeepAlive();\n}\n","/**\n * teleport.ts\n *\n * Renders virtual DOM content into an arbitrary DOM target located outside\n * the current component's Shadow Root. Useful for modals, tooltips, popovers,\n * and any UI that must visually escape the component boundary.\n *\n * @example\n * ```ts\n * import { component, html, ref, useOnDisconnected, useTeleport } from '@jasonshimmy/custom-elements-runtime';\n *\n * component('modal-trigger', () => {\n * const isOpen = ref(false);\n *\n * // Render modal content into <body> outside the shadow root\n * const { portal, destroy } = useTeleport('#modal-root');\n * useOnDisconnected(destroy);\n *\n * // Call portal() to update the teleported content on each render\n * if (isOpen.value) {\n * portal(html`<div class=\"modal\">\n * <h2>Hello</h2>\n * <button @click=\"${() => (isOpen.value = false)}\">Close</button>\n * </div>`);\n * } else {\n * portal(null);\n * }\n *\n * return html`\n * <button @click=\"${() => (isOpen.value = true)}\">Open modal</button>\n * `;\n * });\n * ```\n */\n\nimport type { VNode, VDomRefs } from './runtime/types';\nimport { vdomRenderer } from './runtime/vdom';\nimport { reactiveSystem } from './runtime/reactive';\nimport { getCurrentComponentContext, isDiscoveryRender } from './runtime/hooks';\n\n/** Handle returned by {@link useTeleport} for managing a portal. */\nexport interface TeleportHandle {\n /**\n * Render (or clear) content at the teleport target.\n * Pass `null` or `undefined` to remove previously rendered content.\n */\n portal(content: VNode | VNode[] | null | undefined): void;\n\n /**\n * Destroy the teleport container and clean up all rendered content.\n * Call this in `useOnDisconnected` to prevent memory leaks.\n */\n destroy(): void;\n}\n\n/**\n * Create a teleport portal that renders content outside the current Shadow Root.\n *\n * @param target - A CSS selector string or an `Element` reference to render into.\n * @returns A {@link TeleportHandle} with `portal()` (update content) and `destroy()` (cleanup).\n *\n * @example\n * ```ts\n * import { component, html, useOnDisconnected, useTeleport } from '@jasonshimmy/custom-elements-runtime';\n *\n * component('my-tooltip', () => {\n * const { portal, destroy } = useTeleport('body');\n * useOnDisconnected(destroy);\n *\n * portal(html`<div class=\"tooltip\">Tooltip content</div>`);\n * return html`<span>Hover me</span>`;\n * });\n * ```\n */\nexport function useTeleport(target: string | Element): TeleportHandle {\n // SSR guard\n if (typeof document === 'undefined') {\n return { portal: () => {}, destroy: () => {} };\n }\n\n // During discovery render the component is not yet mounted — return a no-op\n // handle so the library can detect props/hooks without side-effects.\n if (isDiscoveryRender()) {\n return { portal: () => {}, destroy: () => {} };\n }\n\n // If called inside a component render, use the reactive state-index slot\n // mechanism to ensure the same handle is returned on every re-render of the\n // same component instance. Without this, each re-render would create a new\n // <cer-teleport> container in the target, leaking all but the last one.\n const ctx = getCurrentComponentContext();\n if (ctx) {\n // getOrCreateState uses an incrementing stateIndex that is reset to 0 at\n // the start of each render, so the same call site always gets the same slot.\n const slot = reactiveSystem.getOrCreateState<TeleportHandle | null>(null);\n const existing = slot.peek();\n if (existing !== null) {\n return existing;\n }\n // First render: create the handle and store it without triggering a\n // reactive update (initSilent bypasses triggerUpdate).\n // Pass a slot-invalidation callback so that destroy() clears the slot,\n // allowing a reconnected component to create a fresh container.\n const handle = _createTeleportHandle(target, () => slot.initSilent(null));\n slot.initSilent(handle);\n return handle;\n }\n\n // Outside a component context (e.g. called directly in tests or scripts):\n // fall through to a non-cached, non-stable handle.\n return _createTeleportHandle(target);\n}\n\n/** Internal: create a fresh teleport handle pointing at `target`.\n * @param onDestroy - Optional callback invoked after cleanup in destroy(), used\n * to invalidate a cached slot so the next render creates a fresh handle.\n */\nfunction _createTeleportHandle(\n target: string | Element,\n onDestroy?: () => void,\n): TeleportHandle {\n const targetEl =\n typeof target === 'string'\n ? (document.querySelector(target) as Element | null)\n : target;\n\n if (!targetEl) {\n console.warn(\n `[useTeleport] Target \"${String(target)}\" not found in the document. ` +\n 'Teleported content will not be rendered.',\n );\n return { portal: () => {}, destroy: () => {} };\n }\n\n // Create a dedicated container so we never clobber sibling content.\n const container = document.createElement('cer-teleport');\n container.dataset.cerTeleport = '';\n targetEl.appendChild(container);\n\n // Shared refs bag — passed consistently so ref directives work across updates.\n const refs: VDomRefs = {};\n\n const handle: TeleportHandle = {\n portal(content: VNode | VNode[] | null | undefined): void {\n const nodes: VNode[] =\n content == null ? [] : Array.isArray(content) ? content : [content];\n // vdomRenderer stores _prevVNode/_prevDom on the root object for diffing.\n // Casting to ShadowRoot is safe: we only access properties that exist on\n // HTMLElement (firstChild, appendChild, replaceChild, childNodes).\n vdomRenderer(container as unknown as ShadowRoot, nodes, undefined, refs);\n },\n\n destroy(): void {\n // Render empty nodes to clean up event listeners and refs.\n try {\n vdomRenderer(container as unknown as ShadowRoot, [], undefined, refs);\n } catch {\n /* best-effort cleanup */\n }\n if (container.parentNode) {\n container.parentNode.removeChild(container);\n }\n // Invalidate the cached slot so that if the component reconnects and\n // re-renders, useTeleport() creates a fresh container rather than\n // reusing this destroyed one.\n onDestroy?.();\n },\n };\n\n return handle;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"custom-elements-runtime.es.js","names":[],"sources":["../src/lib/runtime/component/async-component.ts","../src/lib/runtime/hydration.ts","../src/lib/runtime/monitoring/health-monitor.ts","../src/lib/keep-alive.ts","../src/lib/runtime/builtin-components.ts","../src/lib/teleport.ts"],"sourcesContent":["import type { VNode } from '../types';\nimport { component } from '../component';\nimport { getCurrentComponentContext } from '../hooks';\n\n/**\n * Options for `defineAsyncComponent`.\n */\nexport interface AsyncComponentOptions {\n /**\n * Render function shown while the async component is loading.\n * Defaults to rendering nothing (empty fragment).\n */\n loading?: () => VNode | VNode[];\n /**\n * Render function shown when the loader Promise rejects or times out.\n * Defaults to rendering nothing.\n */\n error?: () => VNode | VNode[];\n /**\n * Maximum milliseconds to wait before treating the loader as timed out.\n * When exceeded, the component transitions to the `error` state.\n * Defaults to no timeout.\n */\n timeout?: number;\n}\n\ntype AsyncComponentState = 'idle' | 'loading' | 'resolved' | 'error' | 'timeout';\n\n/**\n * Defines a component whose render function is loaded asynchronously.\n *\n * On first render a loading placeholder is shown (if provided). When the\n * loader resolves the real component is registered under the same tag name\n * and the element re-renders with the full implementation.\n *\n * This is the Web Components equivalent of Vue's `defineAsyncComponent` /\n * React's `React.lazy`.\n *\n * @example\n * ```ts\n * // app/components/heavy-editor.ts\n * defineAsyncComponent(\n * 'heavy-editor',\n * () => import('./heavy-editor-impl.ts').then(m => m.renderFn),\n * { loading: () => html`<p>Loading editor…</p>` },\n * )\n * ```\n *\n * @param tag - Custom element tag name (must contain a hyphen).\n * @param loader - A function that returns a Promise resolving to a render\n * function `() => VNode | VNode[]`.\n * @param options - Optional loading/error placeholders and timeout.\n */\nexport function defineAsyncComponent(\n tag: string,\n loader: () => Promise<() => VNode | VNode[]>,\n options?: AsyncComponentOptions,\n): void {\n let state: AsyncComponentState = 'idle';\n let resolvedRenderFn: (() => VNode | VNode[]) | null = null;\n\n // Mutable reference updated on every real render so `settle` always calls\n // the most-recent live `requestRender`. This avoids the pitfall where the\n // first invocation of the render function is the framework's discovery render\n // (which supplies a no-op `requestRender`) and `load()` would otherwise\n // capture that no-op permanently.\n const rerenderRef: { fn: () => void } = { fn: (): void => {} };\n\n let loaderStarted = false;\n let settled = false;\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const settle = (nextState: AsyncComponentState, fn?: () => VNode | VNode[]): void => {\n if (settled) return;\n settled = true;\n if (timeoutId !== null) clearTimeout(timeoutId);\n if (fn) resolvedRenderFn = fn;\n state = nextState;\n rerenderRef.fn();\n };\n\n const startLoader = (): void => {\n if (loaderStarted) return;\n loaderStarted = true;\n state = 'loading';\n\n if (options?.timeout != null) {\n timeoutId = setTimeout(() => settle('timeout'), options.timeout);\n }\n\n loader()\n .then((renderFn) => settle('resolved', renderFn))\n .catch(() => settle('error'));\n };\n\n component(tag, () => {\n // Always refresh the rerender reference from the current render context.\n // The discovery render (prop introspection) supplies a no-op requestRender;\n // real renders supply the live element requestRender. By updating on every\n // call, settle() will use the most-recent live context when it fires.\n const ctx = getCurrentComponentContext() as { requestRender?: () => void } | null;\n if (typeof ctx?.requestRender === 'function') {\n rerenderRef.fn = (): void => { ctx.requestRender!(); };\n }\n\n switch (state) {\n case 'idle':\n startLoader();\n return (options?.loading?.() ?? []) as VNode | VNode[];\n case 'loading':\n return (options?.loading?.() ?? []) as VNode | VNode[];\n case 'resolved':\n return resolvedRenderFn!();\n case 'error':\n case 'timeout':\n return (options?.error?.() ?? []) as VNode | VNode[];\n }\n });\n}\n","/**\n * Client-side hydration helpers.\n *\n * When the server renders with `dsd: true`, each custom element's shadow DOM\n * is already present in the HTML via a Declarative Shadow DOM template. The\n * browser parses that template and attaches the shadow root before any\n * JavaScript runs. When the JS bundle loads, the custom element constructors\n * detect the existing shadow root and skip the `attachShadow()` call, then\n * hydrate (attach reactivity to) the existing DOM on `connectedCallback`.\n *\n * `hydrateApp` is a signal/entry point for this process. In most cases custom\n * elements self-hydrate automatically on connection — this function is useful\n * when you need to explicitly trigger or defer the hydration of a subtree.\n */\n\n/**\n * Trigger hydration for all registered custom elements within `root`.\n *\n * In practice, custom elements self-hydrate as soon as their class definition\n * is registered and the element is connected to the DOM. Call `hydrateApp`\n * after importing and calling `component()` for all your components to signal\n * that the page is ready for reactive activation.\n *\n * @param root - The root element to hydrate. Defaults to `document`.\n *\n * @example\n * ```ts\n * import { component, hydrateApp } from '@jasonshimmy/custom-elements-runtime';\n * import './components'; // registers all components via component()\n *\n * hydrateApp(); // activate all DSD-rendered components on the page\n * ```\n */\nexport function hydrateApp(root: Element | Document = document): void {\n if (typeof CustomEvent === 'undefined') return;\n const target =\n root instanceof Document ? root.documentElement : (root as Element);\n target.dispatchEvent(\n new CustomEvent('cer:hydrate', { bubbles: true, composed: true }),\n );\n}\n","/**\n * Runtime Health Monitoring System\n * Tracks framework health metrics and provides early warning for potential issues\n */\n\nimport { devWarn, devError } from '../logger';\n\ninterface HealthMetric {\n name: string;\n value: number;\n threshold: number;\n status: 'healthy' | 'warning' | 'critical';\n lastUpdated: number;\n history: number[];\n}\n\ninterface HealthReport {\n overall: 'healthy' | 'warning' | 'critical';\n metrics: Record<string, HealthMetric>;\n timestamp: number;\n recommendations: string[];\n}\nexport type { HealthReport };\n\n/**\n * Public interface for a health monitor instance.\n * All state is managed internally via closures — no class syntax.\n */\nexport interface HealthMonitorInstance {\n /** Update a specific health metric value */\n updateMetric(name: string, value: number): void;\n /** Get the current health report */\n getHealthReport(): HealthReport;\n /** Add a listener to be notified when a health check runs */\n addListener(listener: (report: HealthReport) => void): void;\n /** Remove a previously registered listener */\n removeListener(listener: (report: HealthReport) => void): void;\n /** Stop the periodic health monitoring timer */\n stop(): void;\n /** Get historical values for a specific metric */\n getMetricHistory(name: string): number[];\n /** Clear all metric history */\n clearHistory(): void;\n}\n\nconst MAX_HISTORY_SIZE = 100;\nconst CHECK_INTERVAL = 30_000; // 30 seconds\n\nfunction calcStatus(\n value: number,\n threshold: number,\n metricName: string,\n): 'healthy' | 'warning' | 'critical' {\n if (metricName === 'jitCacheHitRate') {\n if (value < threshold * 0.5) return 'critical';\n if (value < threshold) return 'warning';\n return 'healthy';\n }\n if (value > threshold * 2) return 'critical';\n if (value > threshold) return 'warning';\n return 'healthy';\n}\n\nfunction buildRecommendations(metrics: Record<string, HealthMetric>): string[] {\n const out: string[] = [];\n if (metrics.memoryUsage?.status !== 'healthy')\n out.push(\n 'Consider reducing component complexity or implementing better memory cleanup',\n );\n if (metrics.averageRenderTime?.status !== 'healthy')\n out.push(\n 'Optimize component render functions - consider lazy loading or virtualization',\n );\n if (metrics.jitCacheHitRate?.status !== 'healthy')\n out.push(\n 'JIT CSS cache performance is poor - review CSS patterns for optimization',\n );\n if (metrics.componentErrorRate?.status !== 'healthy')\n out.push(\n 'High component error rate detected - review error handling and component logic',\n );\n if (metrics.activeReactiveStates?.status !== 'healthy')\n out.push(\n 'High number of reactive states - consider state consolidation or cleanup',\n );\n if (metrics.memoryLeakIndicator?.status !== 'healthy')\n out.push(\n 'Potential memory leak detected - review component cleanup and event listener management',\n );\n return out;\n}\n\n/**\n * Create a new health monitor instance.\n * All mutable state lives in closures — no `class` or `this`.\n */\nexport function createHealthMonitor(): HealthMonitorInstance {\n const metricsMap = new Map<string, HealthMetric>();\n const listeners = new Set<(report: HealthReport) => void>();\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n function addMetric(name: string, value: number, threshold: number): void {\n metricsMap.set(name, {\n name,\n value,\n threshold,\n status: 'healthy',\n lastUpdated: Date.now(),\n history: [],\n });\n }\n\n function initializeMetrics(): void {\n addMetric('activeComponents', 0, 1000);\n addMetric('componentCreateRate', 0, 50);\n addMetric('componentErrorRate', 0, 0.1);\n addMetric('memoryUsage', 0, 50 * 1024 * 1024);\n addMetric('memoryGrowthRate', 0, 1024 * 1024);\n addMetric('averageRenderTime', 0, 16);\n addMetric('slowRenderCount', 0, 10);\n addMetric('jitCacheHitRate', 100, 80);\n addMetric('activeReactiveStates', 0, 5000);\n addMetric('dependencyUpdates', 0, 100);\n addMetric('memoryLeakIndicator', 0, 0.1);\n }\n\n function updateMetric(name: string, value: number): void {\n const metric = metricsMap.get(name);\n if (!metric) return;\n metric.value = value;\n metric.lastUpdated = Date.now();\n metric.history.push(value);\n if (metric.history.length > MAX_HISTORY_SIZE) metric.history.shift();\n metric.status = calcStatus(value, metric.threshold, name);\n }\n\n function getHealthReport(): HealthReport {\n const snapshot: Record<string, HealthMetric> = {};\n let overall: 'healthy' | 'warning' | 'critical' = 'healthy';\n for (const [name, metric] of metricsMap) {\n snapshot[name] = { ...metric };\n if (metric.status === 'critical') overall = 'critical';\n else if (metric.status === 'warning' && overall === 'healthy')\n overall = 'warning';\n }\n return {\n overall,\n metrics: snapshot,\n timestamp: Date.now(),\n recommendations: buildRecommendations(snapshot),\n };\n }\n\n function updateMemoryMetrics(): void {\n if (\n 'memory' in performance &&\n (performance as Record<string, unknown>).memory\n ) {\n const mem = (performance as Record<string, unknown>).memory as {\n usedJSHeapSize: number;\n };\n updateMetric('memoryUsage', mem.usedJSHeapSize);\n const m = metricsMap.get('memoryUsage');\n if (m && m.history.length > 1) {\n const prev = m.history[m.history.length - 2];\n const curr = m.history[m.history.length - 1];\n updateMetric('memoryGrowthRate', Math.max(0, curr - prev));\n }\n }\n }\n\n function notifyListeners(report: HealthReport): void {\n for (const listener of listeners) {\n try {\n listener(report);\n } catch (e) {\n devError('Error in health monitor listener:', e);\n }\n }\n }\n\n function performHealthCheck(): void {\n updateMemoryMetrics();\n const report = getHealthReport();\n notifyListeners(report);\n if (report.overall === 'critical')\n devError(\n '🚨 Runtime Health: Critical issues detected',\n report.recommendations,\n );\n else if (report.overall === 'warning')\n devWarn(\n '⚠️ Runtime Health: Performance warnings',\n report.recommendations,\n );\n }\n\n function startMonitoring(): void {\n if (typeof window === 'undefined') return;\n intervalId = setInterval(performHealthCheck, CHECK_INTERVAL);\n }\n\n function stop(): void {\n if (intervalId !== null) {\n clearInterval(intervalId);\n intervalId = null;\n }\n }\n\n function addListener(listener: (report: HealthReport) => void): void {\n listeners.add(listener);\n }\n function removeListener(listener: (report: HealthReport) => void): void {\n listeners.delete(listener);\n }\n function getMetricHistory(name: string): number[] {\n const m = metricsMap.get(name);\n return m ? [...m.history] : [];\n }\n function clearHistory(): void {\n for (const m of metricsMap.values()) m.history = [];\n }\n\n initializeMetrics();\n startMonitoring();\n\n return {\n updateMetric,\n getHealthReport,\n addListener,\n removeListener,\n stop,\n getMetricHistory,\n clearHistory,\n };\n}\n\n// Global singleton\nlet _monitor: HealthMonitorInstance | null = null;\n\n/**\n * Get the global health monitor singleton instance.\n */\nexport function getHealthMonitor(): HealthMonitorInstance {\n if (!_monitor) _monitor = createHealthMonitor();\n return _monitor;\n}\n\n/**\n * Update a health metric from anywhere in the framework.\n */\nexport function updateHealthMetric(name: string, value: number): void {\n getHealthMonitor().updateMetric(name, value);\n}\n\n/**\n * Get the current health status report.\n */\nexport function getHealthStatus(): HealthReport {\n return getHealthMonitor().getHealthReport();\n}\n","/**\n * keep-alive.ts\n *\n * Preserves component state when a component is removed from and later\n * re-inserted into the DOM. By default, custom elements lose all JavaScript\n * state when `disconnectedCallback` fires. `cer-keep-alive` intercepts\n * that lifecycle and keeps the child element alive in memory, re-attaching\n * it when a matching component is re-inserted.\n *\n * ## Usage\n *\n * Wrap any custom element with `<cer-keep-alive>`:\n * ```html\n * <cer-keep-alive>\n * <my-counter></my-counter>\n * </cer-keep-alive>\n * ```\n *\n * Or register it programmatically:\n * ```ts\n * import { registerKeepAlive } from '@jasonshimmy/custom-elements-runtime';\n * registerKeepAlive(); // registers <cer-keep-alive> globally\n * ```\n *\n * ## How it works\n *\n * `cer-keep-alive` uses a slotted layout. When a slotted child component is\n * about to leave the DOM (via a re-render of a parent), KeepAlive intercepts\n * `slotchange` events and preserves the detached child element in an internal\n * cache keyed by tag name. When the same tag re-appears in the slot, the\n * cached element is re-inserted, restoring all JavaScript state.\n *\n * ## Limitations\n *\n * - The first slot child per tag name is cached. Multiple children with the\n * same tag use separate cache entries keyed by their `id` attribute.\n * - Only components registered with the same tag name are matched.\n * - Cache entries can be manually evicted with `clearCache()`.\n */\n\n/** Cache key = tagName[:id] */\ntype CacheKey = string;\n\n/**\n * Register the `<cer-keep-alive>` custom element.\n * Safe to call multiple times — subsequent calls are no-ops.\n *\n * @example\n * ```ts\n * import { registerKeepAlive } from '@jasonshimmy/custom-elements-runtime';\n * registerKeepAlive();\n * ```\n */\nexport function registerKeepAlive(): void {\n if (\n typeof window === 'undefined' ||\n typeof customElements === 'undefined' ||\n customElements.get('cer-keep-alive')\n ) {\n return;\n }\n\n customElements.define('cer-keep-alive', createKeepAliveClass());\n}\n\nfunction createKeepAliveClass(): CustomElementConstructor {\n return class CerKeepAlive extends HTMLElement {\n /** Preserved component instances keyed by tag[:id]. */\n private _cache = new Map<CacheKey, Element>();\n private _slot: HTMLSlotElement | null = null;\n private _slotListener: (() => void) | null = null;\n\n connectedCallback(): void {\n if (!this.shadowRoot) {\n this.attachShadow({ mode: 'open' });\n }\n\n if (!this.shadowRoot!.querySelector('slot')) {\n this.shadowRoot!.innerHTML = '<slot></slot>';\n }\n\n this._slot = this.shadowRoot!.querySelector('slot');\n if (this._slot) {\n this._slotListener = () => this._handleSlotChange();\n this._slot.addEventListener('slotchange', this._slotListener);\n // Process current slotted content\n this._handleSlotChange();\n }\n }\n\n disconnectedCallback(): void {\n if (this._slot && this._slotListener) {\n this._slot.removeEventListener('slotchange', this._slotListener);\n }\n this._slotListener = null;\n }\n\n /**\n * Evict a cached element by its cache key (`tagName` or `tagName:id`).\n * The evicted element is disconnected and removed from the cache.\n */\n clearCache(key?: CacheKey): void {\n if (key) {\n this._cache.delete(key);\n } else {\n this._cache.clear();\n }\n }\n\n private _handleSlotChange(): void {\n if (!this._slot) return;\n\n const slottedElements = this._slot.assignedElements({ flatten: true });\n\n for (const child of slottedElements) {\n const cacheKey = this._buildCacheKey(child);\n\n if (!this._cache.has(cacheKey)) {\n // New element — cache it so we can restore it later\n this._cache.set(cacheKey, child);\n } else {\n const cached = this._cache.get(cacheKey)!;\n if (cached !== child) {\n // A different instance appeared for the same slot.\n // Replace it with the cached instance to restore state.\n try {\n child.parentNode?.replaceChild(cached, child);\n } catch {\n // If replacement fails, update the cache with the new element\n this._cache.set(cacheKey, child);\n }\n }\n }\n }\n }\n\n private _buildCacheKey(el: Element): CacheKey {\n const tag = el.tagName.toLowerCase();\n const id = el.getAttribute('id');\n return id ? `${tag}:${id}` : tag;\n }\n };\n}\n","/**\n * Built-in utility components provided by the custom-elements runtime.\n *\n * These components are registered automatically when this module is imported.\n * They are designed to be minimal, tree-shakeable, and zero-dependency.\n *\n * Included components:\n * - `<cer-suspense>` — Shows a fallback while async work is pending\n * - `<cer-error-boundary>` — Catches render errors and shows a fallback UI\n * - `<cer-keep-alive>` — Preserves component state across DOM removal/re-insertion\n */\n\nimport { component } from './component';\nimport { html } from './template-compiler';\nimport { ref } from './reactive';\nimport { useProps, useOnError, useExpose } from './hooks';\nimport { registerKeepAlive } from '../keep-alive';\n\n// ── cer-suspense ──────────────────────────────────────────────────────────────\n\n/**\n * A built-in component that conditionally renders either the default slot\n * content or the `fallback` slot content, controlled by the `pending` prop.\n *\n * Use the `pending` attribute/property to signal that async work is in\n * progress; the component will swap to the `fallback` slot until `pending`\n * becomes falsy.\n *\n * @example\n * ```html\n * <cer-suspense pending>\n * <!-- shown when pending=false -->\n * <my-async-content></my-async-content>\n *\n * <!-- shown while pending=true -->\n * <div slot=\"fallback\">Loading…</div>\n * </cer-suspense>\n * ```\n *\n * @example Programmatic usage\n * ```ts\n * component('my-data-loader', () => {\n * const pending = ref(true);\n * useOnConnected(async () => {\n * await fetchData();\n * pending.value = false;\n * });\n * return html`\n * <cer-suspense pending=\"${pending.value}\">\n * <my-data-view></my-data-view>\n * <div slot=\"fallback\">Loading data…</div>\n * </cer-suspense>\n * `;\n * });\n * ```\n */\nexport function registerSuspense(): void {\n if (typeof customElements !== 'undefined' && customElements.get('cer-suspense')) return;\n\n component('cer-suspense', () => {\n const { pending } = useProps({ pending: false });\n\n return pending\n ? html`<slot name=\"fallback\"><span>Loading…</span></slot>`\n : html`<slot></slot>`;\n });\n}\n\n// ── cer-error-boundary ────────────────────────────────────────────────────────\n\n/**\n * A built-in component that catches errors thrown during child component\n * rendering and displays a fallback UI instead of crashing the page.\n *\n * Errors are caught via the `useOnError` lifecycle hook. Once an error is\n * caught the component switches to showing the `fallback` named slot (or a\n * default \"Something went wrong\" message if no fallback slot is provided).\n *\n * Call the custom `reset()` method on the element to clear the error and\n * attempt re-rendering the default slot.\n *\n * @example\n * ```html\n * <cer-error-boundary>\n * <my-risky-component></my-risky-component>\n *\n * <div slot=\"fallback\">\n * <p>Something went wrong. <button onclick=\"this.closest('cer-error-boundary').reset()\">Retry</button></p>\n * </div>\n * </cer-error-boundary>\n * ```\n */\nexport function registerErrorBoundary(): void {\n if (typeof customElements !== 'undefined' && customElements.get('cer-error-boundary')) return;\n\n component('cer-error-boundary', () => {\n const hasError = ref(false);\n const errorMessage = ref('');\n\n useOnError((err: Error) => {\n hasError.value = true;\n errorMessage.value = err.message;\n });\n\n // Expose a reset() method so parent templates can call\n // `errorBoundaryRef.value.reset()` to clear the error and retry.\n // Also expose an internal `_cerHandleChildError` receiver so that the\n // component runtime can propagate uncaught errors from slotted child\n // components up to the nearest ancestor <cer-error-boundary>.\n useExpose({\n _cerHandleChildError: (err: unknown) => {\n // Use peek() to read the current value without registering a reactive\n // dependency — the child component's render context may be active when\n // this handler runs, and we must not accidentally subscribe the child\n // to this boundary's internal state.\n if (!hasError.peek()) {\n hasError.value = true;\n errorMessage.value = err instanceof Error ? err.message : String(err);\n }\n },\n reset: () => {\n hasError.value = false;\n errorMessage.value = '';\n },\n });\n\n return hasError.value\n ? html`<slot name=\"fallback\"\n ><div role=\"alert\">\n <strong>Something went wrong.</strong>\n ${errorMessage.value ? html`<p>${errorMessage.value}</p>` : html``}\n </div></slot\n >`\n : html`<slot></slot>`;\n });\n}\n\n// ── Auto-register all components ─────────────────────────────────────────────\n\n/**\n * Register all built-in components (`cer-suspense`, `cer-error-boundary`,\n * `cer-keep-alive`).\n * Safe to call multiple times — each registration is guarded by a\n * `customElements.get()` check.\n */\nexport function registerBuiltinComponents(): void {\n registerSuspense();\n registerErrorBoundary();\n registerKeepAlive();\n}\n","/**\n * teleport.ts\n *\n * Renders virtual DOM content into an arbitrary DOM target located outside\n * the current component's Shadow Root. Useful for modals, tooltips, popovers,\n * and any UI that must visually escape the component boundary.\n *\n * @example\n * ```ts\n * import { component, html, ref, useOnDisconnected, useTeleport } from '@jasonshimmy/custom-elements-runtime';\n *\n * component('modal-trigger', () => {\n * const isOpen = ref(false);\n *\n * // Render modal content into <body> outside the shadow root\n * const { portal, destroy } = useTeleport('#modal-root');\n * useOnDisconnected(destroy);\n *\n * // Call portal() to update the teleported content on each render\n * if (isOpen.value) {\n * portal(html`<div class=\"modal\">\n * <h2>Hello</h2>\n * <button @click=\"${() => (isOpen.value = false)}\">Close</button>\n * </div>`);\n * } else {\n * portal(null);\n * }\n *\n * return html`\n * <button @click=\"${() => (isOpen.value = true)}\">Open modal</button>\n * `;\n * });\n * ```\n */\n\nimport type { VNode, VDomRefs } from './runtime/types';\nimport { vdomRenderer } from './runtime/vdom';\nimport { reactiveSystem } from './runtime/reactive';\nimport { getCurrentComponentContext, isDiscoveryRender } from './runtime/hooks';\n\n/** Handle returned by {@link useTeleport} for managing a portal. */\nexport interface TeleportHandle {\n /**\n * Render (or clear) content at the teleport target.\n * Pass `null` or `undefined` to remove previously rendered content.\n */\n portal(content: VNode | VNode[] | null | undefined): void;\n\n /**\n * Destroy the teleport container and clean up all rendered content.\n * Call this in `useOnDisconnected` to prevent memory leaks.\n */\n destroy(): void;\n}\n\n/**\n * Create a teleport portal that renders content outside the current Shadow Root.\n *\n * @param target - A CSS selector string or an `Element` reference to render into.\n * @returns A {@link TeleportHandle} with `portal()` (update content) and `destroy()` (cleanup).\n *\n * @example\n * ```ts\n * import { component, html, useOnDisconnected, useTeleport } from '@jasonshimmy/custom-elements-runtime';\n *\n * component('my-tooltip', () => {\n * const { portal, destroy } = useTeleport('body');\n * useOnDisconnected(destroy);\n *\n * portal(html`<div class=\"tooltip\">Tooltip content</div>`);\n * return html`<span>Hover me</span>`;\n * });\n * ```\n */\nexport function useTeleport(target: string | Element): TeleportHandle {\n // SSR guard\n if (typeof document === 'undefined') {\n return { portal: () => {}, destroy: () => {} };\n }\n\n // During discovery render the component is not yet mounted — return a no-op\n // handle so the library can detect props/hooks without side-effects.\n if (isDiscoveryRender()) {\n return { portal: () => {}, destroy: () => {} };\n }\n\n // If called inside a component render, use the reactive state-index slot\n // mechanism to ensure the same handle is returned on every re-render of the\n // same component instance. Without this, each re-render would create a new\n // <cer-teleport> container in the target, leaking all but the last one.\n const ctx = getCurrentComponentContext();\n if (ctx) {\n // getOrCreateState uses an incrementing stateIndex that is reset to 0 at\n // the start of each render, so the same call site always gets the same slot.\n const slot = reactiveSystem.getOrCreateState<TeleportHandle | null>(null);\n const existing = slot.peek();\n if (existing !== null) {\n return existing;\n }\n // First render: create the handle and store it without triggering a\n // reactive update (initSilent bypasses triggerUpdate).\n // Pass a slot-invalidation callback so that destroy() clears the slot,\n // allowing a reconnected component to create a fresh container.\n const handle = _createTeleportHandle(target, () => slot.initSilent(null));\n slot.initSilent(handle);\n return handle;\n }\n\n // Outside a component context (e.g. called directly in tests or scripts):\n // fall through to a non-cached, non-stable handle.\n return _createTeleportHandle(target);\n}\n\n/** Internal: create a fresh teleport handle pointing at `target`.\n * @param onDestroy - Optional callback invoked after cleanup in destroy(), used\n * to invalidate a cached slot so the next render creates a fresh handle.\n */\nfunction _createTeleportHandle(\n target: string | Element,\n onDestroy?: () => void,\n): TeleportHandle {\n const targetEl =\n typeof target === 'string'\n ? (document.querySelector(target) as Element | null)\n : target;\n\n if (!targetEl) {\n console.warn(\n `[useTeleport] Target \"${String(target)}\" not found in the document. ` +\n 'Teleported content will not be rendered.',\n );\n return { portal: () => {}, destroy: () => {} };\n }\n\n // Create a dedicated container so we never clobber sibling content.\n const container = document.createElement('cer-teleport');\n container.dataset.cerTeleport = '';\n targetEl.appendChild(container);\n\n // Shared refs bag — passed consistently so ref directives work across updates.\n const refs: VDomRefs = {};\n\n const handle: TeleportHandle = {\n portal(content: VNode | VNode[] | null | undefined): void {\n const nodes: VNode[] =\n content == null ? [] : Array.isArray(content) ? content : [content];\n // vdomRenderer stores _prevVNode/_prevDom on the root object for diffing.\n // Casting to ShadowRoot is safe: we only access properties that exist on\n // HTMLElement (firstChild, appendChild, replaceChild, childNodes).\n vdomRenderer(container as unknown as ShadowRoot, nodes, undefined, refs);\n },\n\n destroy(): void {\n // Render empty nodes to clean up event listeners and refs.\n try {\n vdomRenderer(container as unknown as ShadowRoot, [], undefined, refs);\n } catch {\n /* best-effort cleanup */\n }\n if (container.parentNode) {\n container.parentNode.removeChild(container);\n }\n // Invalidate the cached slot so that if the component reconnects and\n // re-renders, useTeleport() creates a fresh container rather than\n // reusing this destroyed one.\n onDestroy?.();\n },\n };\n\n return handle;\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./helpers-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./helpers-7zLtbh_q.cjs`),t=require(`./hooks-BY_35J9Y.cjs`),n=require(`./style-A8l3aQ52.cjs`);function r(r){if(e.w())return;let i=t.i()?._host?.shadowRoot??null;i?n.g(i,r):n.i(r)}function i(e){return e}exports.cls=i,exports.colors=n.t,exports.containerVariants=n.n,exports.disableJITCSS=n.r,exports.enableJITCSS=n.i,exports.extractClassesFromHTML=n.a,exports.getJITCSSOptions=n.o,exports.isJITCSSEnabled=n.s,exports.isJITCSSEnabledFor=n.c,exports.jitCSS=n.l,exports.mediaVariants=n.u,exports.parseArbitrary=n.d,exports.parseColorClass=n.f,exports.parseColorWithOpacity=n.p,exports.parseGradientColorStop=n.m,exports.parseSpacing=n.h,exports.registerJITCSSComponent=n.g,exports.selectorVariants=n._,exports.useDesignTokens=t.l,exports.useGlobalStyle=t.f,exports.useJITCSS=r,exports.utilityMap=n.v;
|
|
2
2
|
//# sourceMappingURL=custom-elements-runtime.jit-css.cjs.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { w as e } from "./helpers-
|
|
2
|
-
import { f as t, i as n, l as r } from "./hooks-
|
|
1
|
+
import { w as e } from "./helpers-kOWgceUQ.js";
|
|
2
|
+
import { f as t, i as n, l as r } from "./hooks-Dj1xwqpK.js";
|
|
3
3
|
import { _ as i, a, c as o, d as s, f as c, g as l, h as u, i as d, l as f, m as p, n as m, o as h, p as g, r as _, s as v, t as y, u as b, v as x } from "./style-DSSoCbC9.js";
|
|
4
4
|
//#region src/lib/runtime/jit-hooks.ts
|
|
5
5
|
function S(t) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./logger-Dkht1dCX.cjs`),t=require(`./helpers-DcEpRwq5.cjs`),n=require(`./template-compiler-B4B_jAPN.cjs`),r=require(`./custom-elements-runtime.directives.cjs.js`),i=require(`./hooks-CNfugc95.cjs`),a=require(`./custom-elements-runtime.store.cjs.js`);var o={enabled:!0,offset:0,timeoutMs:2e3},s=e=>!e||typeof URLSearchParams>`u`?{}:Object.fromEntries(new URLSearchParams(e)),c=e=>{if(!e||Object.keys(e).length===0)return``;try{return`?`+new URLSearchParams(e).toString()}catch{return``}},l=e=>e?/^\s*javascript\s*:/i.test(e):!1,u=e=>/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(e)||e.startsWith(`//`),d=e=>{try{return decodeURIComponent(e)}catch{return e}};function f(e){if(!e)return`/`;let t=e.replace(/\/+/g,`/`);return t.startsWith(`/`)||(t=`/`+t),t.length>1&&t.endsWith(`/`)&&(t=t.slice(0,-1)),t}var p=e=>{if(!e)return``;let t=f(e);return t===`/`?``:t},m=new WeakMap;function h(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function g(t){let n=f(t.path||`/`),r=n===`/`?[]:n.split(`/`).filter(Boolean),i=[],a=[];for(let n=0;n<r.length;n++){let o=r[n];if(o===`*`){if(n!==r.length-1)return e.r(`Route '${t.path}' contains a '*' splat in a non-terminal position; splats must be the last segment. This route will be ignored.`),{invalid:!0};let o=`splat${i.length}`;i.push(o),a.push(`__SPLAT__`);continue}let s=o.match(/^:([A-Za-z0-9_-]+)(\*)?$/);if(s){let o=s[1],c=!!s[2];if(c&&n!==r.length-1)return e.r(`Route '${t.path}' contains a splat param ':${o}*' in a non-terminal position; splats must be the last segment. This route will be ignored.`),{invalid:!0};i.push(o),a.push(c?`__SPLAT__`:`([^/]+)`);continue}a.push(h(o))}let o;if(a.length===0)o=`^/$`;else if(a[a.length-1]===`__SPLAT__`){let e=a.slice(0,-1).join(`/`);o=e?`^/${e}(?:/(.*))?(?:/)?$`:`^(?:/(.*))?(?:/)?$`}else o=`^/${a.join(`/`)}(?:/)?$`;try{return{regex:new RegExp(o),paramNames:i}}catch(n){return e.r(`Failed to compile route regex for '${t.path}': ${String(n)}`),{invalid:!0}}}var _=(e,t)=>{let n=f(t);for(let t of e){let e=m.get(t);if(e||(e=g(t),m.set(t,e)),e.invalid)continue;let{regex:r,paramNames:i}=e,a=r.exec(n);if(a){let e={};for(let t=0;t<i.length;t++){let n=a[t+1]||``;e[i[t]]=n?d(n):``}return{route:t,params:e}}}return{route:null,params:{}}};function v(e,t){for(let n of e)if(_([n],t).route!==null)return n;return null}function y(e,t){let n=t.split(`?`)[0].split(`#`)[0];return _(e,n)}var b=50,x={},S={};function C(){x={},S={}}function w(){let e=Object.entries(x);if(e.length<=b)return;let t=e.sort(([,e],[,t])=>e.lastAccessed-t.lastAccessed).slice(0,e.length-b);for(let[e]of t)delete x[e]}async function T(t){if(t.component)return t.component;if(t.load){let n=x[t.path];if(n)return n.lastAccessed=Date.now(),n.component;if(S[t.path]!==void 0)return S[t.path];let r=typeof window>`u`;try{let n=t.load().then(e=>{w();let n=e.default;return x[t.path]={component:n,lastAccessed:Date.now()},delete S[t.path],n}).catch(n=>{delete S[t.path];let i=n instanceof Error?n.message:String(n);throw r&&e.t(`SSR component load failed for route: ${t.path}. ${i}`),Error(`Failed to load component for route: ${t.path}. ${i}`)});return S[t.path]=n,await n}catch(e){let n=e instanceof Error?e.message:String(e);throw Error(`Failed to load component for route: ${t.path}. ${n}`,{cause:e})}}throw Error(`No component or loader defined for route: ${t.path}`)}var E=null;function D(e){E=e}function O(){return E}var k=new Set,A=null;function j(){if(A){try{A()}catch{}A=null}if(E)try{let e=!1;A=E.subscribe(t=>{e=!0;for(let e of k)try{e(t)}catch{}});try{N.base=E.base}catch{}if(e){let e=E.getCurrent();for(let t of k)try{t(e)}catch{}}else{let e=E.getCurrent();for(let t of k)try{t(e)}catch{}}try{typeof window<`u`&&t.E()}catch{}}catch{A=null}}function M(){j()}var N={store:{subscribe(e){if(E)return E.store.subscribe(e);try{e({path:`/`,params:{},query:{}})}catch{}return()=>{}},getState(){return E?E.getCurrent():{path:`/`,params:{},query:{}}},setState(e){if(E)try{E.store.setState(e)}catch{}}},subscribe(t){if(typeof t!=`function`)return e.r(`activeRouterProxy.subscribe: listener must be a function`),()=>{};if(k.add(t),E)if(!A)j();else try{let e=E.getCurrent();e&&t(e)}catch(t){e.r(`activeRouterProxy subscription failed`,t)}else try{t({path:`/`,params:{},query:{}})}catch(t){e.r(`activeRouterProxy fallback state delivery failed`,t)}return()=>{try{if(k.delete(t),k.size===0&&A){try{A()}catch(t){e.r(`activeRouterProxy inner unsubscribe failed`,t)}A=null}}catch(t){e.r(`activeRouterProxy unsubscribe failed`,t)}}},getCurrent(){return E?E.getCurrent():{path:`/`,params:{},query:{}}},async push(e){return E?E.push(e):Promise.resolve()},async replace(e){return E?E.replace(e):Promise.resolve()},back(){if(E)return E.back()},matchRoute(e){return E?E.matchRoute(e):{route:null,params:{}}},resolveRouteComponent(e){return E?E.resolveRouteComponent(e):Promise.reject(Error(`No active router`))},base:``,scrollToFragment(e){return E?E.scrollToFragment(e):Promise.resolve(!1)},destroy(){E&&E.destroy()}};function P(t){let{routes:n,base:r=``,initialUrl:i,scrollToFragment:l=!0}=t,u=p(r),d=typeof l==`boolean`?{...o,enabled:l}:{...o,...l},m,h,g,y,b,x,S,C=()=>{},w=new Set,E=0,D=async(t,r)=>{let i=v(n,t.path);if(!i||!i.beforeEnter)return!0;try{let n=await i.beforeEnter(t,r);if(typeof n==`string`){let r=`${t.path}->${n}`;return w.has(r)||E>=10?(e.t(`Redirect loop detected: ${r}`),!1):n}return n!==!1}catch(t){e.t(`beforeEnter error`,t);try{g.setState(r)}catch{}throw t}},O=async(t,r)=>{let i=v(n,t.path);if(!i||!i.onEnter)return!0;try{let n=await i.onEnter(t,r);if(typeof n==`string`){let r=`${t.path}->${n}`;return w.has(r)||E>=10?(e.t(`Redirect loop detected: ${r}`),!1):n}return n!==!1}catch(t){e.t(`onEnter error`,t);try{g.setState(r)}catch{}throw t}},k=(t,r)=>{let i=v(n,t.path);if(!(!i||!i.afterEnter))try{let n=i.afterEnter(t,r);n instanceof Promise&&n.catch(t=>{e.t(`afterEnter async error`,t)})}catch(t){e.t(`afterEnter error`,t)}},A=new Map,j=e=>{if(A.has(e))return A.get(e);let t=_(n,e);if(A.size>=100){let e=Array.from(A.keys());for(let t=0;t<25&&t<e.length;t++)A.delete(e[t])}return A.set(e,t),t},M=()=>{};async function N(e,t=0){try{let n=document.getElementById(e);if(!n)return!1;if(t>0)try{let e=n.getBoundingClientRect(),r=Math.max(0,window.scrollY+e.top-t);typeof window.scrollTo==`function`&&window.scrollTo({top:r,behavior:`auto`})}catch{try{n.scrollIntoView()}catch{return!1}}else if(typeof n.scrollIntoView==`function`)try{n.scrollIntoView({behavior:`auto`,block:`start`,inline:`nearest`})}catch{try{n.scrollIntoView()}catch{return!1}}return!0}catch{return!1}}function P(t,n=0,r=2e3){return new Promise(i=>{let a=!1,o=null,s=null,c=Date.now(),l=e=>{a||(a=!0,o&&clearTimeout(o),s!==null&&cancelAnimationFrame(s),i(e))};o=setTimeout(()=>{l(!1)},r),(async()=>{if(!a)try{if(await N(t,n))return l(!0);let i=async()=>{if(!a){if(Date.now()-c>=r)return l(!1);try{if(await N(t,n))return l(!0);s=requestAnimationFrame(i)}catch(t){e.r(`Scroll retry attempt failed:`,t),s=requestAnimationFrame(i)}}};s=requestAnimationFrame(i)}catch(t){e.r(`Initial scroll attempt failed:`,t),l(!1)}})().catch(t=>{e.r(`Scroll attempt failed:`,t),l(!1)})})}let F=!1,I=async(t,n=!1)=>{if(F){e.r(`Navigation to ${t} blocked - navigation already in progress`);return}F=!0,E=0,w.clear();try{await R(t,n)}finally{F=!1,E=0,w.clear()}},L=e=>{let t=e.indexOf(`#`),n=t>=0?e.slice(t+1):``,r=t>=0?e.slice(0,t):e,i=r.indexOf(`?`),a=i>=0?r.slice(0,i):r,o=i>=0?s(r.slice(i)):{};return{path:f((a.startsWith(u)?a.slice(u.length):a)||`/`),query:o,fragment:n}},R=async(t,r=!1)=>{try{let e=L(t),n=j(e.path);if(!n.route)throw Error(`No route found for ${e.path}`);let i=g.getState(),a={path:e.path,params:n.params,query:e.query,fragment:e.fragment},o=await D(a,i);if(o===!1)return;if(typeof o==`string`){E++;let e=`${a.path}->${o}`;w.add(e),await R(o,!0);return}let s=await O(a,i);if(s===!1)return;if(typeof s==`string`){E++;let e=`${a.path}->${s}`;w.add(e),await R(s,!0);return}if(typeof window<`u`&&typeof document<`u`){let t=c(e.query),n=u+e.path+(t||``)+(e.fragment?`#`+e.fragment:``);r?window.history.replaceState({},``,n):window.history.pushState({},``,n)}if(g.setState(a),k(a,i),typeof window<`u`&&typeof document<`u`)try{let e=a.fragment;d.enabled&&e&&P(String(e),d.offset,d.timeoutMs).catch(()=>{})}catch{}}catch(t){if(e.t(`Navigation error:`,t),t instanceof Error&&(t.stack?.includes(`runBeforeEnter`)||t.stack?.includes(`runOnEnter`)))throw t;try{if(!_(n,g.getState().path).route){let t=n.find(e=>e.path===`/`);if(t||=n.find(e=>!e.path.includes(`:`)&&!e.path.includes(`*`)),!t&&n.length>0&&(t=n[0]),t){let e=_(n,t.path);g.setState({path:t.path,params:e.params,query:{}})}else e.t(`No fallback route available for error recovery`)}}catch(t){e.r(`State recovery failed during navigation error:`,t)}}};if((t=>{if(!t||t.length===0)return e.t(`Router configuration error: No routes provided`),!1;let n=new Set;for(let r of t){if(!r.path)return e.t(`Router configuration error: Route missing path`,r),!1;n.has(r.path)&&e.r(`Duplicate route path detected: ${r.path}`),n.add(r.path),!r.component&&!r.load&&e.r(`Route '${r.path}' has no component or load function`)}return!0})(n),typeof window>`u`||i!==void 0){for(let e of n)j(e.path);e.r(`Pre-compiled ${n.length} routes for SSR`)}if(typeof window<`u`&&typeof document<`u`&&i===void 0){m=()=>{try{let e=new URL(window.location.href),t=e.pathname;return{path:f((t.startsWith(u)?t.slice(u.length):t)||`/`),query:s(e.search),fragment:e.hash&&e.hash.length?e.hash.slice(1):``}}catch(t){return e.r(`Invalid URL detected, falling back to safe defaults`,t),{path:`/`,query:{},fragment:``}}},h=m();let t=j(h.path);g=a.createStore({path:h.path,params:t.params,query:h.query,fragment:h.fragment}),y=async(e=!1)=>{await I(m().path,e)};let n=()=>y(!0);window.addEventListener(`popstate`,n),C=()=>window.removeEventListener(`popstate`,n),b=e=>I(e,!1),x=e=>I(e,!0),S=()=>window.history.back(),queueMicrotask(()=>{I(h.path,!0).catch(t=>{e.t(`Initial navigation error:`,t)})})}else{m=()=>{try{let e=new URL(i||`/`,`http://localhost`),t=e.pathname;return{path:f((t.startsWith(u)?t.slice(u.length):t)||`/`),query:s(e.search),fragment:e.hash&&e.hash.length?e.hash.slice(1):``}}catch(t){return e.r(`Invalid SSR URL detected, falling back to safe defaults`,t),{path:`/`,query:{},fragment:``}}},h=m();let t=j(h.path);g=a.createStore({path:h.path,params:t.params,query:h.query,fragment:h.fragment}),y=async()=>{await r(m().path)};let r=async t=>{if(E++,E>10){e.t(`SSR redirect depth exceeded for path: ${t}`);return}try{let e=L(t),i=j(e.path);if(!i.route)throw Error(`No route found for ${e.path}`);let a=g.getState(),o={path:e.path,params:i.params,query:e.query,fragment:e.fragment},s=v(n,o.path);if(s?.beforeEnter){let e=await s.beforeEnter(o,a);if(typeof e==`string`){let t=`${o.path}->${e}`;w.add(t),await r(e);return}if(e===!1)return}if(s?.onEnter){let e=await s.onEnter(o,a);if(typeof e==`string`){let t=`${o.path}->${e}`;w.add(t),await r(e);return}if(e===!1)return}g.setState(o),s?.afterEnter&&s.afterEnter(o,a)}catch(t){throw e.t(`SSR navigation error:`,t),t}};b=async e=>(E=0,w.clear(),r(e)),x=async e=>(E=0,w.clear(),r(e)),S=()=>{}}return{_cleanupScrollState:M,destroy:C,store:g,push:b,replace:x,back:S,subscribe:g.subscribe,matchRoute:e=>j(e),getCurrent:()=>g.getState(),resolveRouteComponent:T,base:u,scrollToFragment:e=>{let t=e||g.getState().fragment;return!t||typeof window>`u`||typeof document>`u`?Promise.resolve(!1):P(t,d.offset,d.timeoutMs)}}}function F(a){C();let o=P(a),s=O();if(s){try{s.destroy()}catch{}try{s._cleanupScrollState?.()}catch{}}D(o);try{M();try{typeof window<`u`&&t.E()}catch{}try{typeof window<`u`&&queueMicrotask(()=>{try{t.E()}catch{}})}catch{}}catch{}return n.n(`router-view`,async()=>{let r=i.i()?._router,a=r??N;if(!O()&&!r)return n.t`<div>Router not initialized.</div>`;let o=t.y(a.getCurrent()),s=typeof window>`u`,c;s||(i.m(()=>{try{typeof a.subscribe==`function`&&(c=a.subscribe(t=>{try{t&&typeof t==`object`&&typeof t.path==`string`?o.value=t:(e.r(`router-view received invalid state`,t),o.value={path:`/`,params:{},query:{}})}catch(t){e.r(`router-view subscription update failed`,t);try{o.value={path:`/`,params:{},query:{}}}catch{}}}))}catch(t){e.r(`router-view subscribe failed`,t)}}),i.h(()=>{if(typeof c==`function`){try{c()}catch(t){e.r(`router-view unsubscribe failed`,t)}c=void 0}}));let l=a.matchRoute(o.value.path);if(!l||!l.route)return n.t`<div>Not found</div>`;try{let e=await a.resolveRouteComponent(l.route);if(typeof e==`string`)return{tag:e,props:{attrs:{...o.value.params}},children:[]};if(typeof e==`function`){let t=e();return(t instanceof Promise?t:Promise.resolve(t)).then(e=>typeof e==`string`?{tag:e,props:{},children:[]}:e)}return n.t`<div>Invalid route component</div>`}catch{return n.t`<div>Invalid route component</div>`}}),n.n(`router-link`,()=>{let a=i._({to:``,tag:`a`,replace:!1,exact:!1,activeClass:`active`,exactActiveClass:`exact-active`,ariaCurrentValue:`page`,disabled:!1,external:!1,class:``,style:``}),o=typeof window>`u`,s=t.y(N.getCurrent()),c=s.value?.path||`/`,d=String(a.to||``),p=o?{isExactActive:I(c,d,N.base),isActive:L(c,d,N.base),isExternal:u(d)||!!a.external}:null,m;i.y(()=>`a,button{display:inline-block;}`);let h=t.y(a.class||``),g=t.y(a.style||``);if(!o){let n=i.i(),r=n?._host??null,o=null,c=null;i.m(()=>{r instanceof HTMLElement&&(c=()=>{let e=String(a.to||``);if(e&&!u(e))try{let{route:t}=N.matchRoute(e);t?.load&&T(t).catch(()=>{})}catch{}},r.addEventListener(`mouseenter`,c,{once:!0}));try{if(typeof N.subscribe==`function`){m=N.subscribe(t=>{try{t&&typeof t==`object`&&typeof t.path==`string`?s.value=t:(e.r(`router-link received invalid state`,t),s.value={path:`/`,params:{},query:{}})}catch(t){e.r(`router-link subscription update failed`,t);try{s.value={path:`/`,params:{},query:{}}}catch{}}});try{let e=N.getCurrent();e&&typeof e.path==`string`&&(s.value=e)}catch(t){e.r(`router-link initial state sync failed`,t)}o=setInterval(()=>{try{let e=N.getCurrent();e&&typeof e.path==`string`&&JSON.stringify(s.value)!==JSON.stringify(e)&&(s.value=e)}catch{}},100)}}catch(t){e.r(`router-link subscribe failed`,t)}try{if(r instanceof HTMLElement){let e=r.getAttribute(`class`),i=r.getAttribute(`style`);e&&(h.value=e),i&&(g.value=i),e!==null&&r.removeAttribute(`class`),i!==null&&r.removeAttribute(`style`);try{n?._requestRender?.();try{t.E()}catch{}}catch{}}}catch(t){e.r(`router-link host migration failed`,t)}}),i.h(()=>{if(c&&r instanceof HTMLElement)try{r.removeEventListener(`mouseenter`,c)}catch{}finally{c=null}if(typeof m==`function`)try{m()}catch(t){e.r(`router-link unsubscribe failed`,t)}finally{m=void 0}if(o)try{clearInterval(o)}catch(t){e.r(`router-link sync interval cleanup failed`,t)}finally{o=null}})}let _=t.g(()=>{if(o&&p)return p.isExactActive;try{let e=N.base??``,t=a.to||``;return!s.value||typeof s.value.path!=`string`?!1:I(s.value.path,t,e)}catch(t){return e.r(`isExactActive computation error`,t),!1}}),v=t.g(()=>{if(o&&p)return p.isActive;try{let e=N.base??``,t=a.to||``;return!s.value||typeof s.value.path!=`string`?!1:a.exact?_.value:L(s.value.path,t,e)}catch(t){return e.r(`isActive computation error`,t),!1}}),y=t.g(()=>{let e=String(a.to||``);if(l(e))return null;if(u(e))return e;let[t,n]=e.split(`#`),[r,i]=(t||``).split(`?`),o=N.base??``,s=r||`/`;if(o&&o!==`/`){let e=f(o),t=f(s);s=t.startsWith(e)?t.slice(e.length)||`/`:t}return o+f(s||`/`)+(i?`?`+i:``)+(n?`#`+n:``)}),b=t.g(()=>{let e=(h&&h.value||a.class||``).split(/\s+/).filter(Boolean),t={};for(let n of e)t[n]=!0;return t}),x=t.g(()=>({...b.value,[a.activeClass||`active`]:v.value,[a.exactActiveClass||`exact-active`]:_.value})),S=t.g(()=>Object.keys(x.value).filter(e=>x.value[e]).join(` `)),C=t.g(()=>a.tag||`a`),w=t.g(()=>C.value===`button`),E=t.g(()=>_.value?a.ariaCurrentValue:null),D=t.g(()=>!!a.disabled),O=t.g(()=>(u(String(a.to||``))||!!a.external)&&C.value===`a`),k=t.g(()=>g&&g.value||a.style||``),A=t=>{if(!(t.defaultPrevented||t.button!==0||t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)){if(D.value){t.preventDefault();return}if(l(String(a.to||``))){try{t.preventDefault()}catch{}e.r(`Blocked unsafe javascript: URI in router-link.to`);return}O.value||(t.preventDefault(),a.replace?N.replace(a.to):N.push(a.to))}};return n.t`
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./logger-Dkht1dCX.cjs`),t=require(`./helpers-7zLtbh_q.cjs`),n=require(`./template-compiler-BngILG2f.cjs`),r=require(`./custom-elements-runtime.directives.cjs.js`),i=require(`./hooks-BY_35J9Y.cjs`),a=require(`./custom-elements-runtime.store.cjs.js`);var o={enabled:!0,offset:0,timeoutMs:2e3},s=e=>!e||typeof URLSearchParams>`u`?{}:Object.fromEntries(new URLSearchParams(e)),c=e=>{if(!e||Object.keys(e).length===0)return``;try{return`?`+new URLSearchParams(e).toString()}catch{return``}},l=e=>e?/^\s*javascript\s*:/i.test(e):!1,u=e=>/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(e)||e.startsWith(`//`),d=e=>{try{return decodeURIComponent(e)}catch{return e}};function f(e){if(!e)return`/`;let t=e.replace(/\/+/g,`/`);return t.startsWith(`/`)||(t=`/`+t),t.length>1&&t.endsWith(`/`)&&(t=t.slice(0,-1)),t}var p=e=>{if(!e)return``;let t=f(e);return t===`/`?``:t},m=new WeakMap;function h(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function g(t){let n=f(t.path||`/`),r=n===`/`?[]:n.split(`/`).filter(Boolean),i=[],a=[];for(let n=0;n<r.length;n++){let o=r[n];if(o===`*`){if(n!==r.length-1)return e.r(`Route '${t.path}' contains a '*' splat in a non-terminal position; splats must be the last segment. This route will be ignored.`),{invalid:!0};let o=`splat${i.length}`;i.push(o),a.push(`__SPLAT__`);continue}let s=o.match(/^:([A-Za-z0-9_-]+)(\*)?$/);if(s){let o=s[1],c=!!s[2];if(c&&n!==r.length-1)return e.r(`Route '${t.path}' contains a splat param ':${o}*' in a non-terminal position; splats must be the last segment. This route will be ignored.`),{invalid:!0};i.push(o),a.push(c?`__SPLAT__`:`([^/]+)`);continue}a.push(h(o))}let o;if(a.length===0)o=`^/$`;else if(a[a.length-1]===`__SPLAT__`){let e=a.slice(0,-1).join(`/`);o=e?`^/${e}(?:/(.*))?(?:/)?$`:`^(?:/(.*))?(?:/)?$`}else o=`^/${a.join(`/`)}(?:/)?$`;try{return{regex:new RegExp(o),paramNames:i}}catch(n){return e.r(`Failed to compile route regex for '${t.path}': ${String(n)}`),{invalid:!0}}}var _=(e,t)=>{let n=f(t);for(let t of e){let e=m.get(t);if(e||(e=g(t),m.set(t,e)),e.invalid)continue;let{regex:r,paramNames:i}=e,a=r.exec(n);if(a){let e={};for(let t=0;t<i.length;t++){let n=a[t+1]||``;e[i[t]]=n?d(n):``}return{route:t,params:e}}}return{route:null,params:{}}};function v(e,t){for(let n of e)if(_([n],t).route!==null)return n;return null}function y(e,t){let n=t.split(`?`)[0].split(`#`)[0];return _(e,n)}var b=50,x={},S={};function C(){x={},S={}}function w(){let e=Object.entries(x);if(e.length<=b)return;let t=e.sort(([,e],[,t])=>e.lastAccessed-t.lastAccessed).slice(0,e.length-b);for(let[e]of t)delete x[e]}async function T(t){if(t.component)return t.component;if(t.load){let n=x[t.path];if(n)return n.lastAccessed=Date.now(),n.component;if(S[t.path]!==void 0)return S[t.path];let r=typeof window>`u`;try{let n=t.load().then(e=>{w();let n=e.default;return x[t.path]={component:n,lastAccessed:Date.now()},delete S[t.path],n}).catch(n=>{delete S[t.path];let i=n instanceof Error?n.message:String(n);throw r&&e.t(`SSR component load failed for route: ${t.path}. ${i}`),Error(`Failed to load component for route: ${t.path}. ${i}`)});return S[t.path]=n,await n}catch(e){let n=e instanceof Error?e.message:String(e);throw Error(`Failed to load component for route: ${t.path}. ${n}`,{cause:e})}}throw Error(`No component or loader defined for route: ${t.path}`)}var E=null;function D(e){E=e}function O(){return E}var k=new Set,A=null;function j(){if(A){try{A()}catch{}A=null}if(E)try{let e=!1;A=E.subscribe(t=>{e=!0;for(let e of k)try{e(t)}catch{}});try{N.base=E.base}catch{}if(e){let e=E.getCurrent();for(let t of k)try{t(e)}catch{}}else{let e=E.getCurrent();for(let t of k)try{t(e)}catch{}}try{typeof window<`u`&&t.E()}catch{}}catch{A=null}}function M(){j()}var N={store:{subscribe(e){if(E)return E.store.subscribe(e);try{e({path:`/`,params:{},query:{}})}catch{}return()=>{}},getState(){return E?E.getCurrent():{path:`/`,params:{},query:{}}},setState(e){if(E)try{E.store.setState(e)}catch{}}},subscribe(t){if(typeof t!=`function`)return e.r(`activeRouterProxy.subscribe: listener must be a function`),()=>{};if(k.add(t),E)if(!A)j();else try{let e=E.getCurrent();e&&t(e)}catch(t){e.r(`activeRouterProxy subscription failed`,t)}else try{t({path:`/`,params:{},query:{}})}catch(t){e.r(`activeRouterProxy fallback state delivery failed`,t)}return()=>{try{if(k.delete(t),k.size===0&&A){try{A()}catch(t){e.r(`activeRouterProxy inner unsubscribe failed`,t)}A=null}}catch(t){e.r(`activeRouterProxy unsubscribe failed`,t)}}},getCurrent(){return E?E.getCurrent():{path:`/`,params:{},query:{}}},async push(e){return E?E.push(e):Promise.resolve()},async replace(e){return E?E.replace(e):Promise.resolve()},back(){if(E)return E.back()},matchRoute(e){return E?E.matchRoute(e):{route:null,params:{}}},resolveRouteComponent(e){return E?E.resolveRouteComponent(e):Promise.reject(Error(`No active router`))},base:``,scrollToFragment(e){return E?E.scrollToFragment(e):Promise.resolve(!1)},destroy(){E&&E.destroy()}};function P(t){let{routes:n,base:r=``,initialUrl:i,scrollToFragment:l=!0}=t,u=p(r),d=typeof l==`boolean`?{...o,enabled:l}:{...o,...l},m,h,g,y,b,x,S,C=()=>{},w=new Set,E=0,D=async(t,r)=>{let i=v(n,t.path);if(!i||!i.beforeEnter)return!0;try{let n=await i.beforeEnter(t,r);if(typeof n==`string`){let r=`${t.path}->${n}`;return w.has(r)||E>=10?(e.t(`Redirect loop detected: ${r}`),!1):n}return n!==!1}catch(t){e.t(`beforeEnter error`,t);try{g.setState(r)}catch{}throw t}},O=async(t,r)=>{let i=v(n,t.path);if(!i||!i.onEnter)return!0;try{let n=await i.onEnter(t,r);if(typeof n==`string`){let r=`${t.path}->${n}`;return w.has(r)||E>=10?(e.t(`Redirect loop detected: ${r}`),!1):n}return n!==!1}catch(t){e.t(`onEnter error`,t);try{g.setState(r)}catch{}throw t}},k=(t,r)=>{let i=v(n,t.path);if(!(!i||!i.afterEnter))try{let n=i.afterEnter(t,r);n instanceof Promise&&n.catch(t=>{e.t(`afterEnter async error`,t)})}catch(t){e.t(`afterEnter error`,t)}},A=new Map,j=e=>{if(A.has(e))return A.get(e);let t=_(n,e);if(A.size>=100){let e=Array.from(A.keys());for(let t=0;t<25&&t<e.length;t++)A.delete(e[t])}return A.set(e,t),t},M=()=>{};async function N(e,t=0){try{let n=document.getElementById(e);if(!n)return!1;if(t>0)try{let e=n.getBoundingClientRect(),r=Math.max(0,window.scrollY+e.top-t);typeof window.scrollTo==`function`&&window.scrollTo({top:r,behavior:`auto`})}catch{try{n.scrollIntoView()}catch{return!1}}else if(typeof n.scrollIntoView==`function`)try{n.scrollIntoView({behavior:`auto`,block:`start`,inline:`nearest`})}catch{try{n.scrollIntoView()}catch{return!1}}return!0}catch{return!1}}function P(t,n=0,r=2e3){return new Promise(i=>{let a=!1,o=null,s=null,c=Date.now(),l=e=>{a||(a=!0,o&&clearTimeout(o),s!==null&&cancelAnimationFrame(s),i(e))};o=setTimeout(()=>{l(!1)},r),(async()=>{if(!a)try{if(await N(t,n))return l(!0);let i=async()=>{if(!a){if(Date.now()-c>=r)return l(!1);try{if(await N(t,n))return l(!0);s=requestAnimationFrame(i)}catch(t){e.r(`Scroll retry attempt failed:`,t),s=requestAnimationFrame(i)}}};s=requestAnimationFrame(i)}catch(t){e.r(`Initial scroll attempt failed:`,t),l(!1)}})().catch(t=>{e.r(`Scroll attempt failed:`,t),l(!1)})})}let F=!1,I=async(t,n=!1)=>{if(F){e.r(`Navigation to ${t} blocked - navigation already in progress`);return}F=!0,E=0,w.clear();try{await R(t,n)}finally{F=!1,E=0,w.clear()}},L=e=>{let t=e.indexOf(`#`),n=t>=0?e.slice(t+1):``,r=t>=0?e.slice(0,t):e,i=r.indexOf(`?`),a=i>=0?r.slice(0,i):r,o=i>=0?s(r.slice(i)):{};return{path:f((a.startsWith(u)?a.slice(u.length):a)||`/`),query:o,fragment:n}},R=async(t,r=!1)=>{try{let e=L(t),n=j(e.path);if(!n.route)throw Error(`No route found for ${e.path}`);let i=g.getState(),a={path:e.path,params:n.params,query:e.query,fragment:e.fragment},o=await D(a,i);if(o===!1)return;if(typeof o==`string`){E++;let e=`${a.path}->${o}`;w.add(e),await R(o,!0);return}let s=await O(a,i);if(s===!1)return;if(typeof s==`string`){E++;let e=`${a.path}->${s}`;w.add(e),await R(s,!0);return}if(typeof window<`u`&&typeof document<`u`){let t=c(e.query),n=u+e.path+(t||``)+(e.fragment?`#`+e.fragment:``);r?window.history.replaceState({},``,n):window.history.pushState({},``,n)}if(g.setState(a),k(a,i),typeof window<`u`&&typeof document<`u`)try{let e=a.fragment;d.enabled&&e&&P(String(e),d.offset,d.timeoutMs).catch(()=>{})}catch{}}catch(t){if(e.t(`Navigation error:`,t),t instanceof Error&&(t.stack?.includes(`runBeforeEnter`)||t.stack?.includes(`runOnEnter`)))throw t;try{if(!_(n,g.getState().path).route){let t=n.find(e=>e.path===`/`);if(t||=n.find(e=>!e.path.includes(`:`)&&!e.path.includes(`*`)),!t&&n.length>0&&(t=n[0]),t){let e=_(n,t.path);g.setState({path:t.path,params:e.params,query:{}})}else e.t(`No fallback route available for error recovery`)}}catch(t){e.r(`State recovery failed during navigation error:`,t)}}};if((t=>{if(!t||t.length===0)return e.t(`Router configuration error: No routes provided`),!1;let n=new Set;for(let r of t){if(!r.path)return e.t(`Router configuration error: Route missing path`,r),!1;n.has(r.path)&&e.r(`Duplicate route path detected: ${r.path}`),n.add(r.path),!r.component&&!r.load&&e.r(`Route '${r.path}' has no component or load function`)}return!0})(n),typeof window>`u`||i!==void 0){for(let e of n)j(e.path);e.r(`Pre-compiled ${n.length} routes for SSR`)}if(typeof window<`u`&&typeof document<`u`&&i===void 0){m=()=>{try{let e=new URL(window.location.href),t=e.pathname;return{path:f((t.startsWith(u)?t.slice(u.length):t)||`/`),query:s(e.search),fragment:e.hash&&e.hash.length?e.hash.slice(1):``}}catch(t){return e.r(`Invalid URL detected, falling back to safe defaults`,t),{path:`/`,query:{},fragment:``}}},h=m();let t=j(h.path);g=a.createStore({path:h.path,params:t.params,query:h.query,fragment:h.fragment}),y=async(e=!1)=>{await I(m().path,e)};let n=()=>y(!0);window.addEventListener(`popstate`,n),C=()=>window.removeEventListener(`popstate`,n),b=e=>I(e,!1),x=e=>I(e,!0),S=()=>window.history.back(),queueMicrotask(()=>{I(h.path,!0).catch(t=>{e.t(`Initial navigation error:`,t)})})}else{m=()=>{try{let e=new URL(i||`/`,`http://localhost`),t=e.pathname;return{path:f((t.startsWith(u)?t.slice(u.length):t)||`/`),query:s(e.search),fragment:e.hash&&e.hash.length?e.hash.slice(1):``}}catch(t){return e.r(`Invalid SSR URL detected, falling back to safe defaults`,t),{path:`/`,query:{},fragment:``}}},h=m();let t=j(h.path);g=a.createStore({path:h.path,params:t.params,query:h.query,fragment:h.fragment}),y=async()=>{await r(m().path)};let r=async t=>{if(E++,E>10){e.t(`SSR redirect depth exceeded for path: ${t}`);return}try{let e=L(t),i=j(e.path);if(!i.route)throw Error(`No route found for ${e.path}`);let a=g.getState(),o={path:e.path,params:i.params,query:e.query,fragment:e.fragment},s=v(n,o.path);if(s?.beforeEnter){let e=await s.beforeEnter(o,a);if(typeof e==`string`){let t=`${o.path}->${e}`;w.add(t),await r(e);return}if(e===!1)return}if(s?.onEnter){let e=await s.onEnter(o,a);if(typeof e==`string`){let t=`${o.path}->${e}`;w.add(t),await r(e);return}if(e===!1)return}g.setState(o),s?.afterEnter&&s.afterEnter(o,a)}catch(t){throw e.t(`SSR navigation error:`,t),t}};b=async e=>(E=0,w.clear(),r(e)),x=async e=>(E=0,w.clear(),r(e)),S=()=>{}}return{_cleanupScrollState:M,destroy:C,store:g,push:b,replace:x,back:S,subscribe:g.subscribe,matchRoute:e=>j(e),getCurrent:()=>g.getState(),resolveRouteComponent:T,base:u,scrollToFragment:e=>{let t=e||g.getState().fragment;return!t||typeof window>`u`||typeof document>`u`?Promise.resolve(!1):P(t,d.offset,d.timeoutMs)}}}function F(a){C();let o=P(a),s=O();if(s){try{s.destroy()}catch{}try{s._cleanupScrollState?.()}catch{}}D(o);try{M();try{typeof window<`u`&&t.E()}catch{}try{typeof window<`u`&&queueMicrotask(()=>{try{t.E()}catch{}})}catch{}}catch{}return n.n(`router-view`,async()=>{let r=i.i()?._router,a=r??N;if(!O()&&!r)return n.t`<div>Router not initialized.</div>`;let o=t.y(a.getCurrent()),s=typeof window>`u`,c;s||(i.m(()=>{try{typeof a.subscribe==`function`&&(c=a.subscribe(t=>{try{t&&typeof t==`object`&&typeof t.path==`string`?o.value=t:(e.r(`router-view received invalid state`,t),o.value={path:`/`,params:{},query:{}})}catch(t){e.r(`router-view subscription update failed`,t);try{o.value={path:`/`,params:{},query:{}}}catch{}}}))}catch(t){e.r(`router-view subscribe failed`,t)}}),i.h(()=>{if(typeof c==`function`){try{c()}catch(t){e.r(`router-view unsubscribe failed`,t)}c=void 0}}));let l=a.matchRoute(o.value.path);if(!l||!l.route)return n.t`<div>Not found</div>`;try{let e=await a.resolveRouteComponent(l.route);if(typeof e==`string`)return{tag:e,props:{attrs:{...o.value.params}},children:[]};if(typeof e==`function`){let t=e();return(t instanceof Promise?t:Promise.resolve(t)).then(e=>typeof e==`string`?{tag:e,props:{},children:[]}:e)}return n.t`<div>Invalid route component</div>`}catch{return n.t`<div>Invalid route component</div>`}}),n.n(`router-link`,()=>{let a=i._({to:``,tag:`a`,replace:!1,exact:!1,activeClass:`active`,exactActiveClass:`exact-active`,ariaCurrentValue:`page`,disabled:!1,external:!1,class:``,style:``}),o=typeof window>`u`,s=t.y(N.getCurrent()),c=s.value?.path||`/`,d=String(a.to||``),p=o?{isExactActive:I(c,d,N.base),isActive:L(c,d,N.base),isExternal:u(d)||!!a.external}:null,m;i.y(()=>`a,button{display:inline-block;}`);let h=t.y(a.class||``),g=t.y(a.style||``);if(!o){let n=i.i(),r=n?._host??null,o=null,c=null;i.m(()=>{r instanceof HTMLElement&&(c=()=>{let e=String(a.to||``);if(e&&!u(e))try{let{route:t}=N.matchRoute(e);t?.load&&T(t).catch(()=>{})}catch{}},r.addEventListener(`mouseenter`,c,{once:!0}));try{if(typeof N.subscribe==`function`){m=N.subscribe(t=>{try{t&&typeof t==`object`&&typeof t.path==`string`?s.value=t:(e.r(`router-link received invalid state`,t),s.value={path:`/`,params:{},query:{}})}catch(t){e.r(`router-link subscription update failed`,t);try{s.value={path:`/`,params:{},query:{}}}catch{}}});try{let e=N.getCurrent();e&&typeof e.path==`string`&&(s.value=e)}catch(t){e.r(`router-link initial state sync failed`,t)}o=setInterval(()=>{try{let e=N.getCurrent();e&&typeof e.path==`string`&&JSON.stringify(s.value)!==JSON.stringify(e)&&(s.value=e)}catch{}},100)}}catch(t){e.r(`router-link subscribe failed`,t)}try{if(r instanceof HTMLElement){let e=r.getAttribute(`class`),i=r.getAttribute(`style`);e&&(h.value=e),i&&(g.value=i),e!==null&&r.removeAttribute(`class`),i!==null&&r.removeAttribute(`style`);try{n?._requestRender?.();try{t.E()}catch{}}catch{}}}catch(t){e.r(`router-link host migration failed`,t)}}),i.h(()=>{if(c&&r instanceof HTMLElement)try{r.removeEventListener(`mouseenter`,c)}catch{}finally{c=null}if(typeof m==`function`)try{m()}catch(t){e.r(`router-link unsubscribe failed`,t)}finally{m=void 0}if(o)try{clearInterval(o)}catch(t){e.r(`router-link sync interval cleanup failed`,t)}finally{o=null}})}let _=t.g(()=>{if(o&&p)return p.isExactActive;try{let e=N.base??``,t=a.to||``;return!s.value||typeof s.value.path!=`string`?!1:I(s.value.path,t,e)}catch(t){return e.r(`isExactActive computation error`,t),!1}}),v=t.g(()=>{if(o&&p)return p.isActive;try{let e=N.base??``,t=a.to||``;return!s.value||typeof s.value.path!=`string`?!1:a.exact?_.value:L(s.value.path,t,e)}catch(t){return e.r(`isActive computation error`,t),!1}}),y=t.g(()=>{let e=String(a.to||``);if(l(e))return null;if(u(e))return e;let[t,n]=e.split(`#`),[r,i]=(t||``).split(`?`),o=N.base??``,s=r||`/`;if(o&&o!==`/`){let e=f(o),t=f(s);s=t.startsWith(e)?t.slice(e.length)||`/`:t}return o+f(s||`/`)+(i?`?`+i:``)+(n?`#`+n:``)}),b=t.g(()=>{let e=(h&&h.value||a.class||``).split(/\s+/).filter(Boolean),t={};for(let n of e)t[n]=!0;return t}),x=t.g(()=>({...b.value,[a.activeClass||`active`]:v.value,[a.exactActiveClass||`exact-active`]:_.value})),S=t.g(()=>Object.keys(x.value).filter(e=>x.value[e]).join(` `)),C=t.g(()=>a.tag||`a`),w=t.g(()=>C.value===`button`),E=t.g(()=>_.value?a.ariaCurrentValue:null),D=t.g(()=>!!a.disabled),O=t.g(()=>(u(String(a.to||``))||!!a.external)&&C.value===`a`),k=t.g(()=>g&&g.value||a.style||``),A=t=>{if(!(t.defaultPrevented||t.button!==0||t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)){if(D.value){t.preventDefault();return}if(l(String(a.to||``))){try{t.preventDefault()}catch{}e.r(`Blocked unsafe javascript: URI in router-link.to`);return}O.value||(t.preventDefault(),a.replace?N.replace(a.to):N.push(a.to))}};return n.t`
|
|
2
2
|
${r.match().when(w.value,n.t`
|
|
3
3
|
<button
|
|
4
4
|
part="button"
|