Relevant source files
The following files were used as context for generating this wiki page:
- src/jsx/dom/render.ts
- src/jsx/components.ts
- src/jsx/hooks/index.ts
- src/jsx/dom/client.ts
- src/jsx/streaming.ts
- src/jsx/base.ts
- src/jsx/dom/intrinsic-element/components.ts
- src/jsx/jsx-runtime.ts
- src/jsx/intrinsic-element/components.ts
- src/jsx/dom/index.ts
- src/middleware/jsx-renderer/index.ts
- src/jsx/index.ts
- src/jsx/dom/hooks/index.ts
- src/jsx/dom/jsx-dev-runtime.ts
- src/jsx/jsx-dev-runtime.ts
- src/jsx/dom/components.ts
- src/jsx/dom/css.ts
- src/jsx/context.ts
- src/jsx/dom/server.ts
- src/helper/css/index.ts
- src/jsx/dom/jsx-runtime.ts
- src/jsx/dom/context.ts
- src/utils/html.ts
- src/jsx/children.ts
- src/jsx/constants.ts
- src/jsx/dom/utils.ts
- src/jsx/intrinsic-elements.ts
- src/jsx/types.ts
- runtime-tests/deno-jsx/deno.react-jsx.json
Overview
Hono provides a lightweight, React-compatible client DOM rendering and reconciliation engine via hono/jsx/dom that manages browser mounting, state management, and updates. It exposes mounting APIs such as createRoot and hydrateRoot to attach JSX element trees to document containers and orchestrate tree reconciliation. Core state and lifecycle primitives like useState, useEffect, and useMemo handle component reactivity and effect execution, while advanced hooks including useTransition, useDeferredValue, and useSyncExternalStore coordinate concurrency and external data stores. Additionally, browser context propagation, error boundaries, client-specific intrinsic elements, and specialized hooks support interactive form handling and metadata management.
Sources: src/jsx/dom/client.ts:23-84, src/jsx/dom/render.ts:497-516, src/jsx/hooks/index.ts:119-176, src/jsx/dom/hooks/index.ts:41-90
Client Entry Points and Roots
Public Mounting APIs and Root Management
Hono exposes browser client mounting functions through src/jsx/dom/client.ts, providing compatibility with react-dom/client. The core API centers on createRoot, which accepts an HTMLElement or DocumentFragment render target and an optional RootOptions record. If any options are supplied, a development warning is emitted via console.warn('createRoot options are not supported yet').
export const createRoot = (
element: HTMLElement | DocumentFragment,
options: RootOptions = {}
): Root => {
let setJsxNode:
| undefined // initial state
| ((jsxNode: unknown) => void) // rendered
| null = // unmounted
undefined
if (Object.keys(options).length > 0) {
console.warn('createRoot options are not supported yet')
}
return {
render(jsxNode: unknown) {
if (setJsxNode === null) {
// unmounted
throw new Error('Cannot update an unmounted root')
}
if (setJsxNode) {
// rendered
setJsxNode(jsxNode)
} else {
renderNode(
buildNode({
tag: () => {
const [_jsxNode, _setJsxNode] = useState(jsxNode)
setJsxNode = _setJsxNode
return _jsxNode
},
props: {},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any) as NodeObject,
element
)
}
},
unmount() {
setJsxNode?.(null)
setJsxNode = null
},
}
}Sources: src/jsx/dom/client.ts:23-65
The returned Root object exposes two primary methods: render(children: Child) and unmount(). The internal lifecycle state tracked by setJsxNode transitions through three phases:
undefined: Initial unrendered state, triggering node construction and DOM insertion viarenderNodeandbuildNode.- Function: Active rendered state where subsequent calls to
root.render()invokesetJsxNode(jsxNode)to schedule updates. null: Unmounted state, where invokingrender()throws an error (Cannot update an unmounted root), and callingunmount()sets the node value tonull.
export const hydrateRoot = (
element: HTMLElement | DocumentFragment,
reactNode: Child,
options: RootOptions = {}
): Root => {
const root = createRoot(element, options)
root.render(reactNode)
return root
}Sources: src/jsx/dom/client.ts:76-84
Note
In hono/jsx/dom, hydrateRoot is functionally equivalent to createRoot followed immediately by root.render(reactNode).
Sources: src/jsx/dom/client.ts:68-70, src/jsx/dom/client.ts:76-84
JSX Runtime and Compilation Setup
The package provides two JSX runtimes under src/jsx/dom/jsx-runtime.ts and src/jsx/dom/jsx-dev-runtime.ts. Both modules export jsxDEV as the underlying factory function for jsx, jsxs, and development fragments.
export const jsxDEV = (tag: string | Function, props: Props, key?: string): JSXNode => {
if (typeof tag === 'string' && intrinsicElementTags[tag as keyof typeof intrinsicElementTags]) {
tag = intrinsicElementTags[tag as keyof typeof intrinsicElementTags]
}
return {
tag,
type: tag,
props,
key,
ref: props.ref,
} as JSXNode
}
export const Fragment = (props: Record<string, unknown>): JSXNode => jsxDEV('', props, undefined)Sources: src/jsx/dom/jsx-dev-runtime.ts:10-22
When jsxDEV processes an intrinsic element tag supplied as a string, it checks against intrinsicElementTags. If a matching component definition exists in src/jsx/dom/intrinsic-element/components, the string tag is resolved directly to that component function. The resulting JSXNode bundles the resolved tag, type, props, optional key, and ref extracted from props.ref.
Sources: src/jsx/dom/jsx-dev-runtime.ts:8-20, src/jsx/dom/jsx-runtime.ts:1-8
DOM Rendering and Node Reconciliation
Reconciliation Engine and Node Construction
The reconciliation engine manages DOM trees through applyNodeObject, which coordinates node creation, updates, and removals by evaluating virtual node objects against a container element. It processes next children arrays and cleanup lists, mapping virtual representations to live DOM nodes.
const applyNodeObject = (node: NodeObject, container: Container, isNew: boolean): void => {
const next: Node[] = []
const remove: Node[] = []
const callbacks: EffectData[] = []
getNextChildren(node, container, next, remove, callbacks)
remove.forEach(removeNode)
// ...
}Sources: src/jsx/dom/render.ts:387-392
When constructing or updating DOM elements, applyNodeObject verifies whether nodes can be reused or if text nodes and element structures need instantiation. String nodes update their textContent when flagged as dirty (child.d), or get created via document.createTextNode. Elements utilize document.createElement or document.createElementNS for namespace-aware tags such as SVG and MathML, retrieving namespaces via nameSpaceMap.
| Namespace Key | URI Value |
|---|---|
svg | 2000/svg |
math | 1998/Math/MathML |
Sources: src/jsx/dom/render.ts:24-27, src/jsx/dom/render.ts:420-435
Note
Event aliases are normalized during property application; Change maps to Input and DoubleClick maps to DblClick.
Sources: src/jsx/dom/render.ts:19-22
Child Indexing and DOM Mutation
Child node positioning relies on findChildNodeIndex, which scans container child node collections to locate matching reference nodes. During inserts and updates, offsets are resolved relative to existing child nodes or insertion bounds, placing nodes via container.appendChild or container.insertBefore.
const findChildNodeIndex = (
childNodes: NodeListOf<ChildNode>,
child: ChildNode | undefined
): number | undefined => {
if (!child) {
return
}
for (let i = 0, len = childNodes.length; i < len; i++) {
if (childNodes[i] === child) {
return i
}
}
return
}Sources: src/jsx/dom/render.ts:369-384
Portals use the internal marker _hp (HONO_PORTAL_ELEMENT) to adjust tracking offsets, decrementing the offset counter so portal contents bypass standard container index assumptions. Once element trees and insertions complete, accumulated lifecycle callbacks are executed. Layout effects (useLayoutEffect) and insertion effects (useInsertionEffect) fire synchronously, while standard effects (useEffect) are scheduled asynchronously via requestAnimationFrame.
if (child.tag === HONO_PORTAL_ELEMENT) {
offset--
} else if (isNew) {
if (!el.parentNode) {
container.appendChild(el)
}
} else if (childNodes[offset] !== el && childNodes[offset - 1] !== el) {
if (childNodes[offset + 1] === el) {
container.appendChild(childNodes[offset])
} else {
container.insertBefore(el, insertBeforeNode || childNodes[offset] || null)
}
}Sources: src/jsx/dom/render.ts:444-457
Core State and Lifecycle Hooks
Stash Indices and State Hooks
Client hook execution relies on internal stash arrays mapped to each component node via the DOM_STASH symbol constant. Within src/jsx/hooks/index.ts, stashes are categorized using specific numeric indices to segregate different hook types.
| Stash Index Constant | Numerical Value | Target Hook Type |
|---|---|---|
STASH_SATE | 0 | State hooks (useState) |
STASH_EFFECT | 1 | Effect hooks (useEffect, useLayoutEffect, useInsertionEffect) |
STASH_CALLBACK | 2 | Callback hooks (useCallback) |
STASH_MEMO | 3 | Memoization hooks (useMemo) |
STASH_REF | 4 | Reference hooks (useRef) |
Sources: src/jsx/constants.ts:3, src/jsx/hooks/index.ts:8-12
The useState implementation resolves its initial state by evaluating initializer functions if provided, checking whether buildDataStack contains an active node object context. If no active build data exists, it returns the initial state paired with a no-op updater. Otherwise, it retrieves or initializes the state array inside the node stash, increments the hook index counter, and stores a state-updater tuple.
export const useState: UseStateType = <T>(
initialState?: T | (() => T)
): [T, UpdateStateFunction<T>] => {
const resolveInitialState = () =>
typeof initialState === 'function' ? (initialState as () => T)() : (initialState as T)
const buildData = buildDataStack.at(-1) as [unknown, NodeObject]
if (!buildData) {
return [resolveInitialState(), () => {}]
}
const [, node] = buildData
const stateArray = (node[DOM_STASH][1][STASH_SATE] ||= [])
const hookIndex = node[DOM_STASH][0]++
return (stateArray[hookIndex] ||= [
resolveInitialState(),
(newState: T | ((currentState: T) => T)) => {
const localUpdateHook = updateHook
const stateData = stateArray[hookIndex]
if (typeof newState === 'function') {
newState = (newState as (currentState: T) => T)(stateData[0])
}
Sources: src/jsx/hooks/index.ts:182-205
Dependency Resolution and Effect Scheduling
Dependency arrays are validated via isDepsChanged, which evaluates whether previous dependencies are undefined, length mismatches occur, or any individual dependency reference fails strict equality checks (dep !== prevDeps[i]).
const isDepsChanged = (
prevDeps: readonly unknown[] | undefined,
deps: readonly unknown[] | undefined
): boolean =>
!prevDeps ||
!deps ||
prevDeps.length !== deps.length ||
deps.some((dep, i) => dep !== prevDeps[i])
Sources: src/jsx/hooks/index.ts:27-35
Effect metadata is encapsulated within EffectData tuples, holding dependency arrays alongside callbacks for layout effects, cleanups, standard effects, and insertion effects.
export type EffectData = [
readonly unknown[] | undefined, // deps
(() => void | (() => void)) | undefined, // layout effect
(() => void) | undefined, // cleanup
(() => void) | undefined, // effect
(() => void) | undefined, // insertion effect
]Sources: src/jsx/hooks/index.ts:14-20
Note
When applyNodeObject flushes accumulated callbacks, insertion effects and layout effects execute synchronously before paint, while standard useEffect callbacks are deferred using requestAnimationFrame.
Sources: src/jsx/dom/render.ts:462-480
Concurrent and Store Hooks
Transitions and View Transitions
Advanced concurrency in Hono's JSX Runtime coordinates asynchronous renders via startTransition, useTransition, and view transitions. The runCallback helper wraps asynchronous execution by pushing a [type, promise] tuple onto the module-level pendingStack. Specifically, startTransition invokes runCallback with pending type 1, whereas startTransitionHook registers pending type 2.
// PendingType is defined in "../dom/render", 3 is used for useDeferredValue
const pendingStack: [PendingType | 3, Promise<void>][] = []
const runCallback = (type: PendingType, callback: Function): void => {
let resolve: (() => void) | undefined
const promise = new Promise<void>((r) => (resolve = r))
pendingStack.push([type, promise])
try {
const res = callback()
if (res instanceof Promise) {
res.then(resolve, resolve)
} else {
resolve!()
}
} finally {
pendingStack.pop()
}
}
export const startTransition = (callback: () => void): void => {
runCallback(1, callback)
}
const startTransitionHook = (callback: () => void | Promise<void>): void => {
runCallback(2, callback)
}Sources: src/jsx/hooks/index.ts:101-124
The useTransition hook reads the current active build stack frame to determine execution context. It maintains local error and boolean state variables via useState. When a state update occurs within a transition callback, startTransitionLocalHook triggers an internal boolean toggle and catches any asynchronous errors to propagate them into the component's error boundary stack.
export const useTransition = (): [boolean, (callback: () => void | Promise<void>) => void] => {
const buildData = buildDataStack.at(-1) as [Context, NodeObject]
if (!buildData) {
return [false, () => {}]
}
const [error, setError] = useState<[Error]>()
const [state, updateState] = useState<boolean>()
if (error) {
throw error[0]
}
const startTransitionLocalHook = useCallback<typeof startTransitionHook>(
(callback) => {
startTransitionHook(() => {
updateState((state) => !state)
let res = callback()
if (res instanceof Promise) {
res = res.catch((e) => {
setError([e])
})
}
return res
})
},
[state]
)
const [context] = buildData
return [context[0] === 2, startTransitionLocalHook]
}Sources: src/jsx/hooks/index.ts:126-155
View transitions leverage browser-native document.startViewTransition APIs where supported, falling back to immediate synchronous execution and resolved promise stubs if the API is absent.
const documentStartViewTransition: (cb: () => void) => { finished: Promise<void> } = (cb) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((document as any)?.startViewTransition) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (document as any).startViewTransition(cb)
} else {
cb()
return { finished: Promise.resolve() }
}
}Sources: src/jsx/hooks/index.ts:43-52
The viewTransitionHook coordinates two-phase updates, checking node version stamps (node.vC) before and after DOM commitment to safely execute transition callbacks while mutating viewTransitionState.
Sources: src/jsx/hooks/index.ts:55-77
Note
The useViewTransition hook returns a boolean indicating transition activity along with the startViewTransition dispatcher, returning [false, () => {}] safely if called outside an active render tree build.
Sources: src/jsx/hooks/index.ts:89-99
Deferred Values
The useDeferredValue hook delays value updates during concurrent rendering passes. It accepts a target value and optional initial state, maintaining a tuple of [cachedValue, currentValue] in internal state. If the incoming value matches the cached secondary value via Object.is, it returns immediately. Otherwise, it pushes pending type 3 onto pendingStack, assigns a temporary updateHook to update the deferred reference once children finish rendering, and updates state.
type UseDeferredValue = <T>(value: T, initialValue?: T) => T
export const useDeferredValue: UseDeferredValue = <T>(value: T, ...rest: [T | undefined]): T => {
const [values, setValues] = useState<[T, T]>(
(rest.length ? [rest[0], rest[0]] : [value, value]) as [T, T]
)
if (Object.is(values[1], value)) {
return values[1]
}
pendingStack.push([3, Promise.resolve()])
updateHook = async (context: Context, _, cb: (context: Context) => void) => {
cb(context)
values[0] = value
}
setValues([values[0], value])
updateHook = undefined
pendingStack.pop()
return values[0]
}Sources: src/jsx/hooks/index.ts:157-176
Client Context and Error Handling
Scoped Context Propagation
Context propagation in Hono's DOM JSX implementation relies on factory-generated provider functions created via createContextProviderFunction. When a context provider renders, it pushes its current value onto the module-level values array and appends an internal marker node equipped with DOM_INTERNAL_TAG to execute values.push(value).
export const createContextProviderFunction =
<T>(values: T[]): Function =>
({ value, children }: { value: T; children: Child[] }) => {
if (!children) {
return undefined
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const props: { children: any } = {
children: [
{
tag: setInternalTagFlag(() => {
values.push(value)
}),
props: {},
},
],
}Sources: src/jsx/dom/context.ts:7-24
After processing the children array, the provider appends a closing internal node that pops the value off the stack (values.pop()). Furthermore, the returned fragment object attaches a DOM_ERROR_HANDLER symbol property to intercept failures, ensuring that if rendering throws an exception within the provider subtree, the context stack value is correctly popped before the error bubbles upward.
props.children.push({
tag: setInternalTagFlag(() => {
values.pop()
}),
props: {},
})
const res = { tag: '', props, type: '' }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(res as any)[DOM_ERROR_HANDLER] = (err: unknown) => {
values.pop()
throw err
}
return res
}Sources: src/jsx/dom/context.ts:30-43
The base createContext function initializes this values array with the provided defaultValue, wraps it in createContextProviderFunction, appends the resulting context object to the global globalContexts registry, and exposes .Provider pointing directly to the provider function.
export const createContext = <T>(defaultValue: T): Context<T> => {
const values = [defaultValue]
const context: Context<T> = createContextProviderFunction(values) as Context<T>
context.values = values
context.Provider = context
globalContexts.push(context as Context<unknown>)
return context
}Sources: src/jsx/dom/context.ts:45-52
Note
The DOM_INTERNAL_TAG flag is set via setInternalTagFlag to mark push and pop operations, distinguishing context boundary nodes from regular intrinsic elements during tree construction.
Sources: src/jsx/dom/context.ts:18-31, src/jsx/dom/utils.ts:3-6
Error Boundary and Suspense Coordination
Component error management and asynchronous chunk loading are coordinated by specialized wrapper components that attach error interception hooks through the DOM_ERROR_HANDLER symbol. The ErrorBoundary component wraps its children in a fragment and assigns an error handler that checks whether the caught error is a Promise. If it represents a promise, it rethrows it to propagate asynchronous suspension; otherwise, it invokes any custom onError callback and renders either the fallbackRender result or the static fallback node.
export const ErrorBoundary: FC<
PropsWithChildren<{
fallback?: Child
fallbackRender?: FallbackRender
onError?: ErrorHandler
}>
> = (({ children, fallback, fallbackRender, onError }: any) => {
const res = Fragment({ children })
;(res as any)[DOM_ERROR_HANDLER] = (err: any) => {
if (err instanceof Promise) {
throw err
}
onError?.(err)
return fallbackRender?.(err) || fallback
}
return res
}) as anySources: src/jsx/dom/components.ts:7-23
Conversely, the Suspense component expects thrown values to be promises. Its DOM_ERROR_HANDLER implementation validates that the caught error is an instance of Promise; if a non-promise error is thrown, it bypasses suspension and rethrows the error. When a valid promise is caught, it attaches a .finally(retry) listener to trigger a re-render attempt once the asynchronous resource resolves, returning the fallback element immediately.
export const Suspense: FC<PropsWithChildren<{ fallback: any }>> = (({
children,
fallback,
}: any) => {
const res = Fragment({ children })
;(res as any)[DOM_ERROR_HANDLER] = (err: any, retry: () => void) => {
if (!(err instanceof Promise)) {
throw err
}
err.finally(retry)
return fallback
}
return res
}) as anySources: src/jsx/dom/components.ts:25-38
| Component Symbol | Target Property | Error Type Handling | Fallback Resolution |
|---|---|---|---|
ErrorBoundary | DOM_ERROR_HANDLER | Rethrows if err instanceof Promise; invokes onError(err) for synchronous errors. | Returns fallbackRender?.(err) or static fallback. |
Suspense | DOM_ERROR_HANDLER | Throws if !(err instanceof Promise); chains err.finally(retry). | Returns static fallback. |
Sources: src/jsx/dom/components.ts:7-38, src/jsx/constants.ts:2
DOM Specific Intrinsic Elements
Document Metadata Hoisting
Hono provides client-side functional components for document metadata tags—specifically title, script, style, link, and meta—that intercept standard rendering to manage head elements dynamically. The internal helper documentMetadataTag queries document.head to locate existing tags and checks for deduplication keys defined in deDupeKeyMap.
const documentMetadataTag = (
tag: string,
props: Props,
preserveNodeType: PreserveNodeType | undefined,
supportSort: boolean,
supportBlocking: boolean
) => {
if (props?.itemProp) {
return {
tag,
props,
type: tag,
ref: props.ref,
}
}
const head = document.head
let { onLoad, onError, precedence, blocking, ...restProps } = props
let element: HTMLElement | null = null
let created = false
const deDupeKeys = deDupeKeyMap[tag]
const deDupeByKey = shouldDeDupeByKey(tag, supportSort)Sources: src/jsx/dom/intrinsic-element/components.ts:58-81
When deduplication matches an existing element, the component updates or reuses the node instead of appending a duplicate. For stylesheets and scripts, options like precedence and blocking='render' can coordinate asset insertion order and suspend rendering via use(promise) until stylesheets or blocking resources load.
if (supportBlocking && blocking === 'render') {
const key = deDupeKeyMap[tag][0]
if (key && props[key]) {
const value = props[key]
const promise = (blockingPromiseMap[value] ||= new Promise<Event>((resolve, reject) => {
insert(element as HTMLElement)
element!.addEventListener('load', resolve)
element!.addEventListener('error', reject)
}))
use(promise)
}
}Sources: src/jsx/dom/intrinsic-element/components.ts:223-234
| Intrinsic Component | Deduplication Strategy | Preserve Node Type (preserveNodeType) | Blocking Support |
|---|---|---|---|
title | Checked via documentMetadataTag (non-SVG) | undefined | No |
script | Checked if src and async are present | 1 (preserve self) | Yes |
style | Checked if href and precedence are present | 2 (preserve self and children) | Yes |
link | Checked if stylesheet without disable/precedence omissions | 1 (preserve self) | Yes |
meta | Checked via documentMetadataTag | undefined | No |
Sources: src/jsx/dom/intrinsic-element/components.ts:257-316
Note
If a metadata component specifies an itemProp attribute, it bypasses head-hoisting and deduplication entirely, returning a standard intrinsic element node.
Sources: src/jsx/dom/intrinsic-element/components.ts:65-72
Form Actions and Dedicated Hooks
Client DOM hooks provide state management primitives tailored for forms and asynchronous action handlers. The FormContext object maintains a FormStatus state containing a pending boolean flag, submitted data, HTTP method (get or post), and the action handler.
type FormStatus =
| {
pending: false
data: null
method: null
action: null
}
| {
pending: true
data: FormData
method: 'get' | 'post'
action: string | ((formData: FormData) => void | Promise<void>)
}
export const FormContext: Context<FormStatus> = createContext<FormStatus>({
pending: false,
data: null,
method: null,
action: null,
})Sources: src/jsx/dom/hooks/index.ts:11-29
Active actions are tracked in a module-level actions set via registerAction, ensuring pending promises can be awaited to coordinate optimistic updates. The useFormStatus hook consumes FormContext, while useOptimistic uses the active actions list to automatically revert optimistic states once all pending promises settle.
const actions: Set<Promise<unknown>> = new Set()
export const registerAction = (action: Promise<unknown>) => {
actions.add(action)
action.finally(() => actions.delete(action))
}
/**
* This hook returns the current form status
* @returns FormStatus
*/
export const useFormStatus = (): FormStatus => {
return useContext(FormContext)
}
/**
* This hook returns the current state and a function to update the state optimistically
* The current state is updated optimistically and then reverted to the original state when all actions are resolved
* @param state
* @param updateState
* @returns [T, (action: N) => void]
*/
export const useOptimistic = <T, N>(
state: T,
updateState: (currentState: T, action: N) => T
): [T, (action: N) => void] => {
const [optimisticState, setOptimisticState] = useState(state)
if (actions.size > 0) {
Promise.all(actions).finally(() => {
setOptimisticState(state)
})
} else {
setOptimisticState(state)
}Sources: src/jsx/dom/hooks/index.ts:31-63
Warning
The useActionState hook assigns the PERMALINK symbol property onto the returned action wrapper function to track permalink strings across form submissions.
Sources: src/jsx/dom/hooks/index.ts:79-90
Related
On This Page
OverviewClient Entry Points and RootsPublic Mounting APIs and Root ManagementJSX Runtime and Compilation SetupDOM Rendering and Node ReconciliationReconciliation Engine and Node ConstructionChild Indexing and DOM MutationCore State and Lifecycle HooksStash Indices and State HooksDependency Resolution and Effect SchedulingConcurrent and Store HooksTransitions and View TransitionsDeferred ValuesClient Context and Error HandlingScoped Context PropagationError Boundary and Suspense CoordinationDOM Specific Intrinsic ElementsDocument Metadata HoistingForm Actions and Dedicated HooksRelated