import { PropertyValues } from './property-values'; import { type ContextKey } from './context.js'; import { type MiuraIsland } from './miura-island.js'; import { PropertyDeclarations } from './properties'; import type { RouteSignalLike, RouterBridgeLike } from './router-bridge.js'; import { Signal, ReadonlySignal } from './signals.js'; import { type FieldRef } from './field-ref.js'; import { type SharedKey } from './shared.js'; import { Form, FormOptions } from './form.js'; import { Resource, ResourceKey, ResourceOptions } from './resource.js'; import { type Beacon, type Pulse } from './channels.js'; import { TemplateResult, CSSResult } from '@miurajs/miura-render'; /** * Base class for miura custom elements. * Provides reactive properties, template rendering, and lifecycle management. * * @template {PropertyKey} [K=PropertyKey] */ export declare class MiuraElement extends HTMLElement { /** * The tag name for this element. * Used for automatic component registration. * Subclasses should override this to define their custom element tag name. * @type {string} */ static tagName?: string; /** * Selects the rendering strategy for this component class. * * - `'JIT'` (default) — uses the full `TemplateProcessor` pipeline with * `BindingManager`-managed `Binding` objects. Supports all binding types * including directives, signals, and async bindings. * * - `'AOT'` — uses the `TemplateCompiler` pipeline that generates optimised * `render()` / `update()` functions compiled from the parsed template. * Element refs are cached after the first render so subsequent updates * perform **zero DOM queries**. Best suited for high-frequency, simple * bindings (counters, lists, data tables). * * @example * class PerfCounter extends MiuraElement { * static compiler = 'AOT' as const; * ... * } */ static compiler?: 'JIT' | 'AOT'; /** * Property declarations for the element. * Subclasses should override this to define their properties. * @type {PropertyDeclarations} */ static properties: PropertyDeclarations; static signals?: PropertyDeclarations; static globals?: Record; static beacons?: Record>; static pulses?: Record; /** * Optional component-specific debugger configuration. * Used by the dev overlay and component layer visualizations. */ static debug?: { disabled?: boolean; report?: boolean; overlay?: boolean; layers?: boolean; performance?: boolean; label?: string; color?: string; showName?: boolean; showRenderTime?: boolean; }; /** * Defines computed properties for the element. * These are reactive properties derived from other properties. * * @example * static computed() { * return { * fullName: { * dependencies: ['firstName', 'lastName'], * get() { * return `${this.firstName} ${this.lastName}`; * } * } * } * } */ static computed?: () => { [key: string]: { dependencies: string[]; get: () => any; set?: (v: any) => void; }; }; /** * Define the styles for the component. * Subclasses can override this getter to provide styles. * @returns {CSSResult | CSSResult[]} */ static styles: CSSResult | CSSResult[]; /** * Promise that resolves when the element has completed an update. * @type {Promise} */ updateComplete: Promise; /** @private */ private updateResolver?; /** * Shadow root where the template is rendered. * @type {ShadowRoot} */ readonly shadowRoot: ShadowRoot; /** * Template processor instance for this element. * @type {TemplateProcessor} * @private */ private processor; /** * Lazy initialization of template processor * @private */ private get _processor(); /** * The current template instance, or null if not yet rendered. * @type {*} * @private */ private templateInstance; /** Cached element refs for the AOT render path @private */ private _aotRefs; /** Compiled template (shared, not per-instance) stored for update calls @private */ private _aotCompiled; /** NodeBinding instances for Node-type refs in the AOT path @private */ private _aotNodeBindings; /** DirectiveBinding instances for Directive-type refs in the AOT path @private */ private _aotDirectiveBindings; /** @private */ private _initialized; /** Unique ID for DevTools tracking @private */ __miura_id: string; /** @private */ private _pendingUpdate; /** @private */ private _hasRendered; /** @private */ private _hasError; /** @private */ private _slotListeners; /** @private */ private _shadowRoot; /** Root render region markers so we can replace the component template safely. */ private _renderStart; /** @private */ private _renderEnd; /** @private */ private _updatePromise; /** @private */ private _changedProperties; /** * Cache for computed property values * @private */ private _computedCache; /** * Update batching for better performance * @private */ private _updateScheduled; /** * Unsubscribe functions for property signal → requestUpdate wiring * @private */ private _propSignalUnsubs; /** Reconnect-safe subscription factories for bridge helpers and other external signals */ private _connectionSetups; /** Debug registrations for framework-aware inspector panels. */ private _debugResources; /** @private */ private _debugForms; /** @private */ private _debugRouteSignals; /** Latest resolved island props for this component when hydrated inside a . */ private _islandPropsValue; /** Lazy proxy so field initializers can keep a stable object reference before connection. */ private _islandPropsProxy; /** Lazy proxy for signal-backed field refs (`this.$.fieldName`). */ private _signalRefsProxy; /** Stable cache of per-field ref wrappers. */ private _fieldRefCache; /** * Memory optimization: WeakMap for property storage * @private */ private static __propertyStorage; /** * Map of attribute names to property names for sync. * Built once per class. */ private static __attributeToPropertyMap; private static __propertyToAttributeMap; /** * Internal state field names (for future use in lifecycle hooks, etc.) */ private static __stateFieldNames; /** * Computed property definitions cache * @private */ private static __computedDefinitions; /** * Performance tracking * @private */ private _performanceMetrics; /** Track the current root template identity so we can recreate when shape changes. */ private _templateStrings; /** Tracks whether firstUpdated has been invoked. */ private _hasCalledFirstUpdated; /** Properties whose template reads were promoted to direct binding signals. */ private _fineGrainedTemplateProps; /** Signal reads collected during the current template() call. */ private _templateReadRecords; /** Latest AOT values after signal unwrapping. */ private _aotValues; /** Per-binding signal subscriptions for AOT templates. */ private _aotSignalSubscriptions; /** Dedupe template promotion diagnostics for this component instance. */ private _templateDiagnosticKeys; /** * Gets the computed value for a property, using cache if available. * @private * @param {string} propertyName - The name of the computed property * @returns {any} The computed value */ private _getComputedValue; /** * Invalidates the cache for a computed property and its dependents. * @private * @param {string} propertyName - The name of the property that changed */ private _invalidateComputedCache; /** * Build property <-> attribute maps for this class. */ private static __ensurePropertyAttributeMaps; /** * List of attributes to observe for changes. */ static get observedAttributes(): string[]; /** * Called when an observed attribute changes. * Syncs attribute changes to the corresponding property. */ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void; /** * Constructs a new MiuraElement, initializes properties and shadow root. */ constructor(); /** * Called when the element is inserted into the DOM. * Initializes observers and performs initial render. * @returns {void} */ connectedCallback(): void; /** * Called when the element is moved to a new document. * Delegates to the overridable onAdopt() hook. */ adoptedCallback(): void; /** * Called when the element is removed from the DOM. * Cleans up observers and resources. * @returns {void} */ disconnectedCallback(): void; /** * Requests an asynchronous update to the element. * @param {PropertyKey} [propertyName] - Name of the property that changed * @param {unknown} [oldValue] - Previous value of the property * @protected * @returns {void} */ protected requestUpdate(propertyName?: PropertyKey, oldValue?: unknown): void; /** * Performs the element update, including template rendering. * Handles update coalescing to ensure all updates are processed. * @protected * @returns {Promise} */ protected performUpdate(resolveUpdate?: (value: boolean) => void): Promise; private resetUpdateCompletePromise; private _evaluateTemplateWithDependencyCollection; private _promoteDirectTemplateReads; private _reportTemplateDiagnosticOnce; /** * Ensures the template instance is created and updated with the latest values. * Only creates a new instance on first render; updates values on subsequent renders. * @private * @param {TemplateResult} template - The template to render or update * @returns {Promise} */ private renderTemplateInstance; private prepareAotValues; private ensureAotSignalSubscription; private applyAotSignalValue; private updateAotRuntimeBindings; private clearRenderedRegion; private disconnectAotBindings; /** * Renders the element's template. * Subclasses should override this to return a TemplateResult. * @protected * @returns {TemplateResult | void} */ protected template(): TemplateResult | void; /** * Emits a custom event from this element. * Convenience wrapper around CustomEvent and dispatchEvent. * * @param {string} eventName - The name of the event to emit * @param {any} detail - The detail payload for the event * @param {CustomEventInit} options - Additional event options (bubbles, composed, cancelable) * @returns {boolean} false if event is cancelable and was cancelled, true otherwise * * @example * this.emit('close'); * this.emit('selection-change', { selectedIds: [...this._selectedIds] }); * this.emit('item-select', { id: this.id }, { bubbles: true, composed: true }); */ protected emit(eventName: string, detail?: any, options?: CustomEventInit): boolean; /** * Checks if a named slot has any assigned content. * Useful for conditionally rendering UI based on slot content. * * @param {string} name - The name of the slot to check * @returns {boolean} true if the slot has assigned nodes, false otherwise * * @example * if (this.hasSlot('actions')) { * // Render actions container * } */ protected hasSlot(name: string): boolean; /** * Called after an update is completed. * Subclasses can override to react to updates. * @protected * @param {PropertyValues} [changedProperties] - Map of changed properties with their previous values * @returns {void} */ protected updated(changedProperties?: PropertyValues): void; /** * Called once after the first successful render completes. * Use for one-time DOM setup that should happen as soon as the initial * template is present in the shadow root. * * @param {PropertyValues} [changedProperties] - Map of changed properties with their previous values * @protected */ protected firstUpdated(changedProperties?: PropertyValues): void; /** * Called once after the component's first render completes. * Use for one-time setup that requires DOM access: * fetching data, setting up third-party libraries, etc. * * @example * onMount() { * this.chart = new Chart(this.shadowRoot.querySelector('canvas')); * this.fetchData(); * } * @protected */ protected onMount(): void; /** * Called when the element is removed from the DOM. * Use for cleanup: cancel fetches, remove global listeners, * dispose of resources. * * @example * onUnmount() { * this.abortController?.abort(); * this.chart?.destroy(); * } * @protected */ protected onUnmount(): void; /** * Called before each render. Use for pre-render computation * (e.g., deriving values from changed properties). * * @param {PropertyValues} changedProperties - Map of changed property names to old values * @protected */ protected willUpdate(changedProperties: PropertyValues): void; /** * Return false to skip an update entirely. * Useful for performance optimization when you know a render is unnecessary. * * @param {PropertyValues} changedProperties - Map of changed property names to old values * @returns {boolean} true to proceed with update (default), false to skip * @protected */ protected shouldUpdate(changedProperties: PropertyValues): boolean; /** * Called when the element is moved to a new document (adoptedCallback). * Rarely needed — useful for elements that depend on document-level resources. * @protected */ protected onAdopt(): void; /** * Error boundary handler. Called when an error occurs during rendering. * Override to display fallback UI or report errors. * * Return `true` to indicate the error was handled (suppresses console.error). * Return `false` or don't override to let the error propagate. * * @example * onError(error: Error) { * this.errorMessage = error.message; * this.shadowRoot.innerHTML = `
${error.message}
`; * return true; * } * * @param {Error} error - The error that occurred * @returns {boolean} true if the error was handled * @protected */ protected onError(error: Error): boolean; /** * Whether this component is currently in an error state. */ get hasError(): boolean; /** * Create a two-way binder for a reactive property. * Returns a `{ value, set }` object that can be passed to an `&` binding. * * @param {string} propertyName - The reactive property to bind * @returns {{ value: unknown, set: (v: unknown) => void }} * * @example * template() { * return html``; * } */ protected bind(propertyName: string): { value: unknown; set: (v: unknown) => void; }; /** * Create a standalone writable signal tied to this component. * Pass it directly to template expressions for fine-grained updates. * * Use this for reactive state that is NOT part of the public `static * properties` interface — e.g. internal counters, derived values, etc. * * @example * readonly #open = this.$signal(false); * template() { return html`...`; } */ protected $signal(initial: T): Signal; /** * Create a derived read-only signal tied to this component. * * @example * readonly #doubled = this.$computed(() => this.count() * 2); */ protected $computed(fn: () => T): ReadonlySignal; /** * Stable proxy for signal-backed fields. * * Use `this.$.count` in templates to bind directly to the backing Signal of * a decorated `@signal` / `@global` field while preserving plain property * syntax in component logic. */ protected get $(): Record>; /** * Access the backing Signal for an `@signal` field by name. * * This enables direct fine-grained bindings while the decorated field keeps * plain property syntax for component logic. */ protected $signalRef(propertyName: string): FieldRef; /** * Access the backing Signal for an `@global` field by name. */ protected $globalRef(propertyName: string): FieldRef; /** * Access the backing Signal for a decorated `@signal` or `@global` field. */ protected $ref(propertyName: string): FieldRef; /** * Create or retrieve shared reactive state by key. * Multiple components using the same key receive the same signal instance. */ protected $shared(key: SharedKey, initial: T): Signal; /** * Create or retrieve a named global event channel with payload. */ protected $beacon(key: string): Beacon; /** * Create or retrieve a named global void event channel. */ protected $pulse(key: string): Pulse; /** * Framework-native helper for emitting on beacons and pulses. */ protected $emit(channel: Pulse): void; protected $emit(channel: Beacon, value: T): void; /** * Subscribe to a beacon or pulse for the lifetime of this component * connection. The unsubscribe function is cleaned up automatically on * disconnect. */ protected $on(channel: Pulse, listener: () => void): () => void; protected $on(channel: Beacon, listener: (value: T) => void): () => void; /** * Subscribe once to a beacon or pulse for the lifetime of this component * connection. */ protected $once(channel: Pulse, listener: () => void): () => void; protected $once(channel: Beacon, listener: (value: T) => void): () => void; /** * Access the nearest enclosing wrapper, if this component was hydrated as an island. */ protected $island(): MiuraIsland | null; /** * Access the props that were serialized into the nearest enclosing . * Returns a stable proxy so it can be assigned during field initialization. */ protected $islandProps = Record>(): T; /** * Create a resource that starts from island-provided data and can optionally revalidate on the client. */ protected $islandResource(hydrate: () => T | undefined, loader: () => Promise | T, options?: Omit & { key?: ResourceKey | (() => ResourceKey); revalidate?: boolean; }): Resource; /** * Provide a tree-scoped value to descendant components. * Descendants can retrieve it with `$inject()`. */ protected $provide(key: ContextKey, value: T): T; /** * Resolve the nearest tree-scoped value for this component. * When the provided value is a signal, descendants can bind it directly for reactive updates. */ protected $inject(key: ContextKey, fallback?: T): T | undefined; /** * Access the current route context as a signal-like value. */ protected $route(router: RouterBridgeLike): RouteSignalLike; /** * Derive a reactive value from the current route context. */ protected $routeSelect(router: RouterBridgeLike, selector: (context: unknown) => T): RouteSignalLike; /** * Access route loader data by key as a signal-like value. */ protected $routeData(router: RouterBridgeLike): RouteSignalLike; protected $routeData(router: RouterBridgeLike, key: string, fallback?: T): RouteSignalLike; /** * Create a resource that refreshes whenever a selected route-derived value changes. */ protected $routeResource(router: RouterBridgeLike, selector: (context: unknown) => TKey, loader: (key: TKey) => Promise | T, options?: ResourceOptions & { skip?: (key: TKey) => boolean; equals?: (previous: TKey, next: TKey) => boolean; key?: ResourceKey | ((key: TKey) => ResourceKey); hydrateFromRouteData?: true | string | ((context: unknown) => T | undefined); }): Resource; private _resolveRouteResourceKey; private _hydrateRouteResource; /** * Create a resource tied to this component. * A resource tracks async loading state and requests an update whenever its * state changes. */ protected $resource(loader: () => Promise | T, options?: ResourceOptions): Resource; /** * Create a form tied to this component. * A form tracks values, dirty/touched state, validation, and submit state. */ protected $form>(initialValues: T, options?: FormOptions): Form; /** * Query slotted elements assigned to a named slot (or default slot). * * @param {string} [slotName=''] - Name of the slot. Empty string for default slot. * @returns {Element[]} Array of elements assigned to the slot. * * @example * const items = this.querySlotted('items'); * const defaultContent = this.querySlotted(); */ protected querySlotted(slotName?: string): Element[]; /** * Register a callback for slot change events. * Automatically cleaned up on disconnect. * * @param {string} slotName - Name of the slot (empty string for default) * @param {(elements: Element[]) => void} callback - Called with the new assigned elements * * @example * onMount() { * this.onSlotChange('', (elements) => { * console.log('Default slot changed:', elements); * }); * this.onSlotChange('header', (elements) => { * this.hasHeader = elements.length > 0; * }); * } */ protected onSlotChange(slotName: string, callback: (elements?: Element[]) => void): void; /** * Apply the component's styles to its shadow root. * @private * @returns {void} */ private applyStyles; /** * Returns the component's styles, supporting both static property and static getter. */ protected getComponentStyles(): CSSResult | CSSResult[] | undefined; /** * Subscribes each signal-backed property (and state) to call requestUpdate() * when its value changes. Called in connectedCallback, torn down in * disconnectedCallback via _propSignalUnsubs. * @private */ private _subscribePropertySignals; private _setupConnectionSubscriptions; private _resolveIslandProps; private _initializeChannelFields; private _resolveFieldRefSignal; private _getOrCreateFieldRef; private _registerDebugResource; private _registerDebugForm; private _registerDebugRouteSignal; private _getComponentDebugOptions; private _createDebugValuesSnapshot; private _createDebugResourceSnapshot; private _createDebugFormSnapshot; private _createDebugRouteSnapshot; private _reportDebugLayer; private _reportElementDiagnostic; private _annotateErrorSource; private _resolveDiagnosticSource; /** * Initializes property observers and event listeners. * Subclasses can override to set up observers. * @private * @returns {void} */ protected initializeObservers(): void; /** * Cleans up observers and event listeners. * Subclasses can override to clean up observers. * @private * @returns {void} */ protected cleanupObservers(): void; /** * Subclasses can override to define internal, non-reflected state fields. * @returns {PropertyDeclarations} */ static state?(): PropertyDeclarations; /** * Get performance metrics for this element * @returns {PerformanceMetrics} */ get performanceMetrics(): { renderTime: number; updateCount: number; lastRenderTime: number; }; } //# sourceMappingURL=miura-element.d.ts.map