import 'reflect-metadata'; import type { HttpContext } from '@adonisjs/core/http'; import type { WireStream, WireEffect, AdowireConfig } from './types.js'; /** * Extract the **template data** type for a given `WireComponent` subclass. * * This is the set of variables available inside the component's `.edge` * template: every public instance property (excluding framework-internal * `$`-prefixed fields and methods) plus the injected `$errors` and * `$component` helpers. * * @example * ```ts * import type { ViewData } from 'adowire' * import type ModelSubmit from '#adowire/examples/model_submit' * * // Hover over this in your editor to see every available template variable: * type View = ViewData * ``` */ export type ViewData = { [K in keyof T as K extends `$${string}` ? never : K extends `_${string}` ? never : T[K] extends (...args: any[]) => any ? never : K]: T[K]; } & { /** Validation errors keyed by field name */ $errors: Record; /** The component instance (access `$component.$ctx`, etc.) */ $component: T; }; /** * Base class for all adowire components. * * Extend this class to create a reactive server-driven component: * * ```ts * import { WireComponent } from 'adowire' * * export default class Counter extends WireComponent { * count = 0 * increment() { this.count++ } * } * ``` */ /** * Method names that are reserved by the framework and cannot be called * from the client via `adowire:click` or similar directives. * * If you need an action called "reset", name it something else * (e.g. `clearAll`, `resetForm`, `resetCount`). */ export type ReservedMethodNames = 'mount' | 'boot' | 'hydrate' | 'dehydrate' | 'updating' | 'updated' | 'rendering' | 'rendered' | 'exception' | 'render' | 'validate' | 'resetValidation' | 'addError' | 'skipRender' | 'fill' | 'reset' | 'pull' | 'only' | 'all'; export declare abstract class WireComponent { /** Unique component instance ID (ulid) */ $id: string; /** Registered component name, e.g. "counter" or "posts.index" */ $name: string; /** The AdonisJS HTTP context for the current request */ $ctx: HttpContext; /** adowire config resolved from the provider */ $config: AdowireConfig; /** The Edge.js template engine instance, injected by the request handler */ $edge: any; /** Validation errors keyed by property name */ $errors: Record; /** Queued effects to be sent back to the client */ $effects: WireEffect; /** Whether to skip re-rendering after this action */ $skipRender: boolean; /** * Real-time stream writer callback, injected by the request handler when * the response is in SSE streaming mode. * * When set, `$stream()` calls this function **immediately** so the chunk * is flushed to the client over the open SSE connection. When `null`, * `$stream()` falls back to buffering in `$effects.streams[]` (sent in * the final JSON response — no real-time push). * * @internal — set by `WireRequestHandler`, not by user code. */ $streamWriter: ((stream: WireStream) => void) | null; /** * Dynamic page title. Set this in mount() or any action to override * the static @Title decorator. Equivalent to Livewire's ->title() method. * * @example * async mount() { * this.$title = `Edit: ${this.post.title}` * } */ $title: string | null; /** Cache for computed properties within a single request */ private $computedCache; /** * Called once when the component is first initialised (initial GET render). * Use this to set up state from props or the database. * NOT called on subsequent AJAX updates. */ mount(_props: Record): Promise; /** * Called on every single request — both the initial render and all * subsequent AJAX updates. Runs before `hydrate()` and before actions. * Use this for setup that must happen on every request (e.g. auth guards). */ boot(): Promise; /** * Called every time the component is re-hydrated from a snapshot * (i.e. on every subsequent AJAX request after the initial render). * NOT called on the first render. */ hydrate(): Promise; /** * Called right before the component state is serialised to a snapshot * and sent back to the client. Called on every request. */ dehydrate(): Promise; /** * Called before a public property is updated from the client. * @param _name Property name * @param _value Incoming value from the client */ updating(_name: string, _value: any): Promise; /** * Called after a public property has been updated from the client. * @param _name Property name * @param _value The new value that was set */ updated(_name: string, _value: any): Promise; /** * Called before the component template is rendered. * Return a (possibly mutated) `data` object to inject extra variables into * the view, or override variables produced by `$getPublicState()`. * * @param _view The Edge.js view name that will be rendered * @param data The data object that will be passed to the view */ rendering(_view: string, data: Record): Promise>; /** * Called after the component template has been rendered to HTML. * You may inspect or mutate the HTML string before it is sent to the client. * * @param _view The Edge.js view name that was rendered * @param html The rendered HTML string */ rendered(_view: string, html: string): Promise; /** * Called when an unhandled exception is thrown during the request lifecycle * (boot, hydrate, property update, action call, or dehydrate). * * Call `stopPropagation()` inside this hook to swallow the error and prevent * it from bubbling further. If you do not call it, the exception is re-thrown. * * @param _error The caught exception * @param stopPropagation Call this to prevent further propagation */ exception(_error: unknown, stopPropagation: () => void): Promise; /** * Renders the component's Edge.js template and returns the resulting HTML. * * Calls `rendering()` and `rendered()` hooks around the Edge render call. * Override this method in your app component to customise the view path, * pass extra data, or use a completely different rendering strategy. * * Template data includes: * - All public component state (from `$getPublicState()`) * - `$errors` — validation errors keyed by field name * - `$component` — the component instance itself (access `$component.$ctx.auth`, etc.) * * When `this.$ctx.view` is available (i.e. during a commit request handled * by the AdonisJS middleware stack) we use `ctx.view.render()` so that * templates automatically receive the full HttpContext: `auth`, `session`, * `csrfToken`, `route()`, `signedRoute()`, `vite()`, and any other * shared state from AdonisJS middleware. Falls back to direct * `edge.render()` when `ctx.view` is not available (e.g. initial SSR * from the `@wire` tag). */ render(): Promise; /** * Process all `adowire:show="expr"` attributes in the rendered HTML. * * For each match the expression is evaluated against the component's public * state (the same data object passed to the Edge template). When the * expression is falsy the element receives `style="display:none"` (or the * rule is appended to an existing `style` attribute). When truthy the * element is left as-is (visible by default). * * This runs on the server so the initial HTML already contains the correct * visibility — no client-side JavaScript is required for the first paint. * The client-side `adowire:show` directive then maintains the state for * subsequent interactions. */ protected $processShowDirectives(html: string, state: Record): string; /** * Safely evaluate a simple JS expression against component state. * * State properties are injected as named local variables so expressions * like `starred`, `!starred`, `count > 0` work naturally. * * @returns The boolean result, or `false` on any evaluation error. */ private $evaluateShowExpression; /** * Bulk-assign public properties from a plain object. * Only properties that already exist on the component are set — * unknown keys are silently ignored for safety. * * @param data Key/value pairs to assign */ fill(data: Record): void; /** * Reset one or more properties to their initial (class-field default) values. * If no properties are given, ALL public properties are reset. * * Initial values are captured lazily and cached per class so that all * instances share the same defaults snapshot. * * @param props Property names to reset (omit to reset all) */ reset(...props: string[]): void; /** * Reset the given properties to their initial values and return the values * they held *before* the reset. * * @param props Properties to pull (reset and retrieve) * @returns Plain object of old values keyed by property name */ pull(...props: string[]): Record; /** * Return a subset of the current public state. * * @param props The property names to include */ only(...props: string[]): Record; /** * Return all public component state as a plain object. * Developer-facing alias of `$getPublicState()`. */ all(): Record; /** * Returns all public properties of this component as a plain object. * Properties whose names start with `$` or `_` are excluded, as are * methods and framework-internal values. */ $getPublicState(): Record; /** * Capture and return the initial (default) property values for this component. * The snapshot is created once per class and cached on the prototype so all * instances share it. * * @internal */ $getInitialState(): Record; /** * Returns the names of all locked properties (decorated with @Locked). */ $getLockedProperties(): string[]; /** * Returns the validation rules defined via @Validate decorators. */ $getValidationRules(): Record; /** * Returns the event listeners defined via @On decorators. * Shape: { eventName: methodName } */ $getEventListeners(): Record; /** * Returns the layout config defined via @Layout decorator (for page components). */ $getLayout(): { name: string; slot: string; } | null; /** * Returns the page title for this component. * * Resolution order: * 1. `this.$title` — set dynamically in mount() / actions / boot() * 2. `@Title('...')` decorator on the class * 3. `null` — no title */ $getTitle(): string | null; /** * Returns true if the named method has the @Renderless decorator. */ $isRenderless(method: string): boolean; /** * Returns true if the named method has the @Async decorator. */ $isAsync(method: string): boolean; /** * Returns true if the named method has the @Json decorator. */ $isJson(method: string): boolean; /** * Invoke a computed method, caching the result for the lifetime of this request. */ $resolveComputed(key: string): Promise; /** * Clears the computed cache (called between lifecycle hooks if needed). */ $clearComputedCache(): void; /** * Discover and call trait-prefixed lifecycle hooks on this component. * * Convention: `()` where `TraitName` starts with an * uppercase letter. For example, if a mixin defines `mountWithPagination()` * or `bootWithFileUploads()`, this method finds and calls them automatically * when the framework triggers the corresponding root lifecycle hook. * * @param hook The lifecycle hook name (e.g. "mount", "boot", "hydrate") * @param args Arguments forwarded verbatim to the trait hook */ $callTraitHooks(hook: string, ...args: any[]): Promise; /** * Triggers a re-render of the component without calling any action. * (No-op server-side; the framework re-renders automatically after every action.) */ $refresh(): void; /** * Set a public property value. * Equivalent to `adowire:click="$set('prop', value)"` on the client. */ $set(prop: string, value: any): void; /** * Toggle a boolean public property. * Equivalent to `adowire:click="$toggle('prop')"` on the client. */ $toggle(prop: string): void; /** * Dispatch a component event. * Other components listening via @On('event-name') will receive it. * * @param name Event name * @param params Optional parameters to pass to listeners */ $dispatch(name: string, params?: any[]): void; /** * Dispatch an event only to the component itself (self). */ $dispatchSelf(name: string, params?: any[]): void; /** * Dispatch an event to a specific named component. */ $dispatchTo(targetName: string, name: string, params?: any[]): void; /** * Redirect the browser to the given URL after the response is sent. * Automatically skips re-rendering. */ $redirect(url: string, _options?: { navigate?: boolean; }): void; /** * Redirect using adowire:navigate (SPA-style, no full page reload). */ $redirectRoute(url: string): void; /** * Stream a text chunk to a `adowire:stream="name"` element on the client. * Use inside an async action with a for-await loop for AI/LLM streaming. * * @param name The adowire:stream target name * @param content The text content chunk to stream * @param replace If true, replace the element content instead of appending */ $stream(name: string, content: string, replace?: boolean): void; /** * Trigger a file download on the client. * * @param name File name as it will appear in the download dialog * @param url URL to the file (can be a signed storage URL, etc.) */ $download(name: string, url: string): void; /** * Invoke a named JavaScript action defined in the component's * `