```markdown
---
title: MouseEnter/MouseLeave events for overlapping views are raised incorrectly
severity: bug
area: input/mouse
tags: mouse-enter, overlapping-views, event-contract
---

## Summary

When the mouse moves between overlapping views (e.g. a sub‑view that sits on top of its parent), `MouseEnter` is called multiple times on the parent even though the mouse never left the parent’s bounding rectangle, and `MouseLeave` may be skipped when the mouse moves from the child back to an uncovered part of the parent.

## Steps to reproduce

1. Create a parent view and a child sub‑view that is positioned inside the parent (overlapping arrangement).
2. Move the mouse from an area covered **only** by the parent into the child sub‑view, then back into an area covered **only** by the parent.
3. Observe the sequence of `OnMouseEnter` / `OnMouseLeave` calls.

## Expected behavior

- A view receives `MouseEnter` exactly once when the mouse moves from “outside the view” to “inside the view”.
- A view receives `MouseLeave` exactly once when the mouse leaves its bounds.
- Overlapping sub‑views must not cause spurious re‑entry on ancestor views that remain under the cursor.
  Concretely: moving the cursor from a parent‑only area into a child sub‑view should **not** fire a new `MouseEnter` on the parent, because the parent was already under the mouse. Similarly, moving from the child back to a parent‑only area should **not** fire a new `MouseEnter` on the parent.

## Current behavior

- The parent view receives duplicate `MouseEnter` calls when the mouse transitions between parent‑only and child‑overlay regions.
- `MouseLeave` may be missing when the mouse moves off the child without leaving the parent.

## Suspected cause

The internal cache of views under the mouse (`_cachedViewsUnderMouse`) in `Application.RaiseMouseEnterLeaveEvents` appears to be handled incorrectly. The current logic seems to re‑process views that were already under the mouse, causing repeated enter events and incorrectly skipping leave events.

## Acceptance criteria

- Fix `RaiseMouseEnterLeaveEvents` so that a view is only treated as “newly under the mouse” if it was **not** present in the cached set from the previous position.
- `MouseLeave` is raised for views that are in the cache but no longer under the mouse.
- Moving the mouse between an overlapping child and its parent (while staying inside the parent’s bounds) does not generate any extra `MouseEnter` / `MouseLeave` calls on the parent.
- Existing adjacent‑view and no‑view‑under‑mouse scenarios continue to work as before.
```


```markdown
**Title:** `Canceled` property should be on `Dialog`, not just `FileDialog`, and Esc/cancel should set it

**Description:**
Currently, the `Canceled` property only exists on `FileDialog`. This makes it impossible for generic dialogs (or custom dialogs derived from `Dialog`) to know whether the user dismissed the dialog by canceling (e.g., pressing Esc or clicking a Cancel button). Esc is already bound to `Command.QuitToplevel` in `Dialog`, but it does not set `Canceled`. Even in `FileDialog`, clicking the Cancel button calls `Application.RequestStop()` without setting `Canceled`.

**Expected behavior:**
- A `Canceled` property (default `true`) should be available on the base `Dialog` class.
- When a dialog is dismissed via the Esc key, `Canceled` must be set to `true`.
- When a `FileDialog`’s Cancel button is clicked, `Canceled` must be set to `true`.
- Like other properties in the framework, `Canceled` should throw `ObjectDisposedException` if accessed after the dialog has been disposed, but only when the `DEBUG_IDISPOSABLE` preprocessor symbol is defined.
- The property should remain free to use for dialogs that aren’t specifically file dialogs.

**Impact:**
Currently there is no reliable way to check, after a generic dialog closes, whether the user canceled out of it. This forces callers to duplicate cancellation tracking logic in every dialog subclass.
```

Moved `Canceled` from `FileDialog` to `Dialog` (because that's just a good idea and it provides a good case for having a property test for Disposal on get/set).


The driver event subscription and unsubscription are asymmetric and poorly named, which makes the teardown path fragile and hard to maintain.

Currently:
- `InitState` subscribes to `Driver` events (SizeChanged, KeyDown, KeyUp, MouseEvent), but its name doesn’t communicate that it’s solely about driver events.
- There is no corresponding `Unsubscribe*` method. In `ResetState`, the four event handlers are unsubscribed one by one in an inline block before the driver is ended.

This duplication creates two problems:
1. If the set of subscribed events ever changes (added/removed), developers must remember to update the unsubscription block in `ResetState` by hand. Forgetting to do so would leave dangling event handlers attached to a driver that is about to be disposed or replaced, causing potential memory leaks or unexpected behavior.
2. The name `InitState` suggests broader initialization work, but the method only wires up driver events. This is misleading when reading the code or when writing test helpers that need to manually subscribe to driver events without full initialization.

**Desired behavior**
- Rename `InitState` to `SubscribeDriverEvents` so the purpose is clear.
- Create a matching `UnsubscribeDriverEvents` method that cleanly removes all four event handlers.
- Use `SubscribeDriverEvents` as soon as possible after the driver is initialized (preferably right after `Driver!.Init()` in `InternalInit`) to avoid any window where a driver is active but events aren’t connected.
- Use `UnsubscribeDriverEvents` in `ResetState` (and any other teardown/scenario where the driver is being detached) instead of the current inline unsubscription block.
- All existing callers of `InitState` (including test helpers and unit tests) should be updated to call `SubscribeDriverEvents`, and the null‑driver guard that throws `ArgumentNullException` must remain enforced when the subscription method is invoked without a driver.

**Impact / risk**
Without a dedicated unsubscribe method, any future change to the driver’s event surface risks introducing stale handlers in teardown paths. The rename makes the API self‑explanatory, and the symmetry between subscribe and unsubscribe improves maintainability and testability.

Rename to SubscribeDriverEvents and add UnsubscribeDriverEvents method.


We need the orientation change pipeline to follow the standard `Changing`/`Changed` event pattern consistently. Currently, the order of virtual method calls vs. event invocations in `OrientationHelper` is not clearly defined, and some views (like `RadioGroup`) rely on a temporary subclass to prevent orientation changes instead of using the provided cancellation mechanism.

**Request**

1. In `OrientationHelper`, ensure the `Orientation` setter calls methods in this order:
   - `_owner.OnOrientationChanging(newValue, currentValue)` – gives the owning view the first chance to approve or cancel the change. If it returns `true`, the change is aborted and no events are raised.
   - Raise the `OrientationChanging` event with a cancelable `CancelEventArgs`. If `Cancel` is set to `true`, the change is aborted.
   - If not canceled, store the new orientation on the helper and update `_owner.Orientation` if needed.
   - `_owner.OnOrientationChanged(oldValue, newValue)` – called after the value is committed.
   - Raise the `OrientationChanged` event (non‑cancelable).

2. Remove the `RadioGroupHorizontal` class from `RadioGroup`. Instead, any view that wishes to prevent orientation changes should do so by returning `true` from `OnOrientationChanging` or by canceling the event from an external handler.

3. Update `RadioGroup` to use `OrientationHelper` properly: it should wire `OrientationHelper.OrientationChanging` and `OrientationHelper.OrientationChanged` to its own public events, and not provide a dummy `OnOrientationChanging` that ignores the request. The `OnOrientationChanged` override should call `SetupKeyBindings()` and `SetContentSize()` as before.

4. Add XML documentation remarks to `OrientationHelper` explaining that view authors should raise their own `IOrientation` events by chaining from the helper’s events, and describing the guaranteed invocation order.

**Acceptance criteria**
- Changing `Orientation` on any view that uses `OrientationHelper` invokes `OnOrientationChanging` before any external handlers.
- Returning `true` from `OnOrientationChanging` or setting `Cancel = true` in the `OrientationChanging` event completely prevents the change (value stays the same, no `Changed` events fire).
- `OnOrientationChanged` is called after the value is committed and before the `OrientationChanged` event.
- `RadioGroupHorizontal` no longer exists; preventing orientation changes works through the standard cancellation flow.


**Request: Cap MessageBox dimensions to console bounds when auto-sizing**

`MessageBox.Query` (and `ErrorQuery`) dynamically size the dialog based on the message text, title, and buttons when no explicit width/height are provided. For messages containing extremely long words (e.g., a continuous string of thousands of characters without spaces), `TextFormatter.MaxWidth` can return a width that exceeds the physical console width, and the derived height can similarly exceed the console height. The resulting window is larger than the screen, causing clipping and rendering artifacts.

**Expected behavior:**
In the auto-size path (`QueryFull` where `width == 0 & height == 0`), the computed width and height of the message box must be capped to `Application.Driver.Cols` and `Application.Driver.Rows` respectively.

**Specifics:**
- The width used for word-wrapping (`textWidth`) should be clamped to `Application.Driver.Cols`.
- The overall message box height (`msgboxHeight`) should be clamped to `Application.Driver.Rows`.
- The final dialog width (`d.Width`) should be clamped to `Application.Driver.Cols`.

This ensures the message box always fits within the console, regardless of message length or word-break characteristics. The existing logic for considering title length, button width, and padding should be preserved; only the final values need the upper bound.

Fixes #2053. MessageBox.Query still not wrapping with v1.8.0 on windows/net6.0.


**Subject:** Focus inconsistencies when toggling `CanFocus`, `Visible`, and during `HasFocusChanged` events cause label tests to fail

### Context
Labels are not focusable by default (`CanFocus = false`). Several unit tests verify that when a Label’s `CanFocus` is changed at runtime, or when a Label is shown/hidden while focus is changing, focus behaves predictably. These tests are currently failing.

### Acceptance Criteria

1. **`CanFocus` toggling**
   - Setting `CanFocus = true` on a subview must immediately give it focus **if** its `SuperView` already has focus and no subview is currently focused.
   - Setting `CanFocus = false` on the currently focused subview must immediately remove focus from that subview (focus remains with the `SuperView` or moves to another focusable subview).

2. **Keyboard navigation after runtime `CanFocus` change**
   - When a view becomes focusable (`CanFocus` set to `true`), the keyboard navigation (Tab key) must treat it as a valid stop.
   - Pressing Tab when a newly focusable view is the next in order must advance focus to it.

3. **Mouse click on a Label**
   - A Label with `CanFocus = false` must **not** steal focus on click; focus must pass to the next focusable subview (existing behavior).
   - A Label with `CanFocus = true` must receive focus on click, as any focusable view does.

4. **`Visible` toggling**
   - Setting `Visible = true` on a focusable view should auto-focus it **only** when:
     - The view has no `SuperView` (i.e., it is a Toplevel), **or**
     - The view’s `SuperView` itself has focus, is `Visible`, is `Enabled`, and has no focused subview (`Focused is null`).
   - If the `SuperView` does **not** have focus, setting a subview’s `Visible = true` must **not** cause an automatic focus change.

5. **`HasFocusChanged` side‑effects safety**
   - While processing `HasFocus = false`, if the `HasFocusChanged` event handler sets `HasFocus` back to `true` (or otherwise re‑establishes focus on the same view), the focus‑removal logic must abort immediately without updating the `SuperView`’s `_previouslyMostFocused` or performing further state changes that assume focus was lost.
   - When `HasFocus` transitions to `false`, the `HasFocusChanged` event may be used to hide the currently focused subview and show another. After this transition, all subviews must reflect the lost focus correctly (e.g., the previously focused subview’s `HasFocus` must be `false`), and no assertion failures or crashes should occur.

### Notes
- These requirements touch code in `View.Navigation.cs` (focus change propagation), `View.cs` (`CanFocus`, `Visible` setters), and the general focus‑change notification path.
- The existing Label mouse‑click passthrough behavior (`MouseClick_SetsFocus_OnNextSubview`) must remain intact.


```
Bug: Modifying `Border.Thickness.Top` silently does nothing

When I set `someView.Border.Thickness.Top = 4` the border thickness does not update and the layout remains unchanged. The assignment appears to succeed but has no visible effect.

Steps to reproduce:
- Create a view with a border, e.g. `var win = new Window { BorderStyle = LineStyle.Double };`
- Call `Application.Begin(win);`
- After initialization, attempt `win.Border.Thickness.Top = 3;`
- Force a redraw or inspect the layout; the border top thickness stays at its previous value.

The root cause is that `Thickness` is a reference type (class). The property `Border.Thickness` returns a reference to the underlying `_thickness` object, so mutating a field directly modifies the object in place without calling the property setter. Thus the setter’s layout-invalidation logic (e.g. `Parent.SetNeedsLayout()`) never runs.

Expected behavior:
`Thickness` should be an immutable value type so that any attempt to change a dimension requires going through the property setter (i.e. `win.Border.Thickness = win.Border.Thickness with { Top = 3 }` or constructing a new `Thickness`). This ensures the setter is always invoked, which triggers re-layout and notifies listeners.

Desired changes:
1. Convert `Thickness` from a `class` to a `record struct` to obtain value semantics and automatic equality members.
2. Remove the custom equality, hash code, and operator implementations that are now provided by the record struct.
3. Evaluate whether the `validate` method (that threw on negative widths) is still needed; if not removed, ensure its intent is preserved elsewhere (e.g. in the constructor or property setters that accept `Thickness`).
4. Update all call sites that currently mutate a `Thickness` field through a property getter (like `Border.Thickness.Top = value`) to replace it with a full assignment using a `with` expression or constructor. This is critical for the change to be correct—without it, such sites would either not compile (if fields become init-only or readonly) or would modify a copy of the struct without effect.
5. Ensure no other assignments bypass the setter in a way that could silently ignore mutations.

This change will fix the silent-failure bug and simplify the type’s contract by making `Thickness` behave as an immutable value.```


## TextValidateField crashes or fails in generic viewer scenarios when no mask is provided or when cast to `ITextValidateProvider`

### Context

The `TextValidateField<T>` view is used in the **All View Tester** scenario, which dynamically enumerates all views, inspects their properties, and calls interface methods. Two problems emerge:

1.  **Null reference exceptions when the default constructor is used** – The default constructor `TextValidateField()` never creates an `ITextValidateProvider`, but other methods (`Text`, `IsValid`, `Redraw`, `ProcessKey`, `PositionCursor`, etc.) assume the provider is always non‑null. This causes crashes when a mask is not supplied (e.g., when the tester creates views programmatically without arguments).
2.  **The view does not implement `ITextValidateProvider`** – The tester (and any generic code) expects to interact with the text‑validate field through this interface directly on the view, but `TextValidateField<T>` does not implement it. Attempts to cast to `ITextValidateProvider` fail with an `InvalidCastException`.

### Desired behavior (acceptance criteria)

1.  **Safe provider‑less state**
    When `TextValidateField<T>` is instantiated with the default constructor (or in any state where the internal `ITextValidateProvider` is `null`):
    *   `Redraw` must not throw; instead it renders the message: `Error: ITextValidateProvider not set!`
    *   `ProcessKey` must ignore all keystrokes (return `true`) without an exception.
    *   `Text` getter must return `ustring.Empty`, the `Text` setter must safely return without effect.
    *   `IsValid` must return `false`.
    *   `Mask` getter must return `ustring.Empty`.

2.  **View implements `ITextValidateProvider`**
    The `TextValidateField<T>` class itself must implement `ITextValidateProvider`, passing all calls through to the inner provider when it exists:
    *   `Cursor(int pos)`, `CursorLeft(int)`, `CursorRight(int)`, `CursorStart()`, `CursorEnd()`, `Delete(int)`, `InsertAt(char, int)` forward immediately.
    *   `Text`, `Mask`, `IsValid`, and `Fixed` properties delegate to the inner provider.
    *   When the inner provider is `null`, all methods/properties must return safe defaults (e.g., `0` or `false` for cursors/deletes, `ustring.Empty` for texts). For `Fixed`, throwing `NotImplementedException` is acceptable for now, but the fact that the interface is present guarantees no `InvalidCastException`.

3.  **No regressions**
    All existing behavior **when a mask is supplied** (i.e., the provider is correctly created) must continue to work exactly as before.

### Externally visible constraints

*   The `TextValidateField<T>` generic constraint must change from `where T : ITextValidateProvider` to `where T : class` because `T` is used only for `Activator.CreateInstance`; the interface implementation is now on the view itself.
*   Any method that previously referenced `provider` without a null guard must include one.
*   The “providerless” error string must be plainly visible as `"Error: ITextValidateProvider not set!"` and appear at the view’s origin (`Move(0,0)`).

Fixes #1251. Fixes TextValidateProvider exception on the All View Tester scenario. (#1252)


Add support for rounded corners in `LineCanvas`. Currently, when multiple lines meet at a corner and any line uses `BorderStyle.Rounded` (with non-zero length), the corner is still rendered as a non-rounded single-corner glyph. The `ConsoleDriver` already exposes rounded corner properties (`ULRCorner`, `URRCorner`, `LLRCorner`, `LRRCorner`), but they are not used.

Update the corner-rune selection logic so that if any intersecting line has `BorderStyle.Rounded` and `Length != 0`, the rounded corner variant is used, analogous to how `BorderStyle.Double` already overrides corners.
- T-junctions and crosshair intersections should remain unchanged (no rounded variants exist for those).
- A line with zero length should not influence corner rounding decisions.


# FillRect corrupts wide characters when overlapping (e.g., MessageBox borders over CJK text)

## Summary
When `FillRect` draws over an area that contains double-width (CJK) characters, the wide characters are not correctly invalidated or replaced, leading to visual corruption. This manifests most commonly when a `MessageBox` border is rendered on top of Chinese/Japanese/Korean button labels or content, causing garbled text or remnants of the original wide glyphs.

## Observed behavior
- Drawing a vertical border character (`│`) via `FillRect` over part of a wide character (either its first or second column) leaves the wide character partially visible or shows replacement characters at the wrong position.
- Adjacent cells are not marked dirty (`IsDirty = true`) when a wide glyph’s second half is overwritten, so the terminal may not re‑render the area correctly.
- The `OutputBase.Write` method leaves the second cell of a wide grapheme as dirty (`IsDirty = true`) after the wide character has been fully rendered, causing unnecessary redraws.

## Expected behavior
1. **FillRect correctness:** When `FillRect` overwrites any column of a double‑width character, the entire wide glyph must be invalidated. The cell where the replacement character is written should receive the new content, and the adjacent cell (the other half of the old wide glyph) must be marked dirty so the terminal fully repaints the area.
2. **AddRune/AddStr correctness:** Writing a single‑width character at a column that is the second cell of a wide glyph should replace that wide glyph with `Rune.ReplacementChar` in the first column and mark both involved cells as dirty.
3. **OutputBase.Write:** After processing a wide grapheme during the redraw step, the cell immediately following it (the second half) should have its `IsDirty` flag set to `false` to prevent redundant re‑output.
4. The `FillRect(Rectangle, char)` convenience overload should be removed or replaced by the `Rune`‑based version, because raw `char` cannot represent the full range of Unicode code points and does not correctly handle wide character semantics.

## Steps to reproduce (conceptual)
1. Fill an output buffer with a line of Chinese text (e.g., `你好呀`).
2. Use `FillRect` to draw a single‑column rectangle (like `│`) at a position that overlaps one half of a wide character.
3. Observe that the wide character is not fully replaced and the surrounding cells are not properly marked dirty.

## Additional context
This is critical for any UI that uses borders or overlays on top of locales with double‑width characters. The issue is tracked in #4466. A correct fix needs to change `OutputBufferImpl.FillRect`, the internal `AddGrapheme` helpers, and `OutputBase.Write`.

**Desired outcome:** `FillRect` should robustly handle overlap with wide characters, producing clean output with no visual artifacts. The internal dirty‑flag management must guarantee that the terminal re‑draws the correct cells.

Fixes #4466 - `FillRect` Corrupts Wide Characters When Overlapping (#4486)


The `Toplevel.Ready` member is currently a plain `Action` field. This exposes it to unsafe usage patterns: callers can assign handlers directly, overwriting any previous subscriptions, and can also set it to `null` to clear handlers after invocation. For example, the UICatalog application currently subscribes with a lambda and then sets `_top.Ready = null` inside the lambda to ensure one-shot behavior. This will break once `Ready` is a proper event, and the clearing pattern is not safe in a multi-subscriber scenario anyway.

Convert `Toplevel.Ready` from a public field to an `event`. As part of this, fix all existing call sites that rely on assignment or null-clearing so they use `+=` and `-=` with named methods instead of anonymous handlers that self-nullify. The UICatalog one-shot behavior should be replaced by unsubscribing a named handler from within the handler. Any unit tests that assign `Ready` directly (e.g., `t.Ready = ...` or object initializers like `new OpenDialog { Ready = ... }`) must be updated to subscribe with `+=`. Ensure the `OnReady` invocation inside `Toplevel` remains unchanged and continues to work with the new event declaration.


**Title:** OrientationHelper: prevent redundant change events & fix RadioGroup crash on empty horizontal layout

**Background**
`OrientationHelper` is the standard implementation backing `IOrientation` for views. It fires `OrientationChanging`/`OrientationChanged` and calls `OnOrientationChanging`/`OnOrientationChanged` whenever its `Orientation` property is set—even if the value hasn’t actually changed. This can cause unnecessary work in layouts and items collections that react to orientation changes.

Additionally, `RadioGroup.PositionCursor` accesses `_horizontal[_cursor].pos` when the group is in `Horizontal` orientation, but does not guard against an empty `_horizontal` list, leading to an `IndexOutOfRangeException` when there are no radio labels.

**Requested changes**

1. **OrientationHelper setter – idempotency**
   If the new `Orientation` value is equal to the current one, skip all event invocations and callback calls. This should avoid spurious `OrientationChanging`/`OrientationChanged` events and `OnOrientationChanging`/`OnOrientationChanged` calls when nothing actually changes.

2. **RadioGroup cursor safety**
   In the `Orientation.Horizontal` branch of `RadioGroup.PositionCursor`, only compute the position from `_horizontal` when the list contains at least one element. If it is empty, the method should return without dereferencing the list (preserving the previous behavior for non-empty cases).

3. **Null-safety on owner propagation**
   Inside `OrientationHelper`, after the orientation is updated and the helper writes back to `_owner.Orientation`, guard the assignment with a null check on `_owner`. This prevents a `NullReferenceException` if the property setter is invoked when the owner has not been assigned (defensive improvement).

4. **Typo fix**
   Correct the parameter descriptions in `IOrientation.OnOrientationChanging` from “orienation” to “orientation”.

**Acceptance criteria**
- Setting `OrientationHelper.Orientation` to its current value causes no `OrientationChanging`/`OrientationChanged` events and no calls to `OnOrientationChanging`/`OnOrientationChanged`.
- A `RadioGroup` with `Orientation = Horizontal` and zero items does not throw when `PositionCursor` is invoked.
- `OrientationHelper` correctly skips the `_owner.Orientation` assignment when `_owner` is null.
- The doc comments on `IOrientation` use correct spelling.


When a `Border` object is passed to `MessageBox.Query` or `ErrorQuery`, the dialog does **not** use it – the default `Dialog` border appears instead.
The provided border’s style, colors, `Effect3D`, and title are all ignored.

### Required behavior

- `MessageBox` must apply the supplied `Border` to the created `Dialog`.
- The border’s `Title` must be set from the dialog’s title.
- The `Dialog` class should support being constructed directly with a `Border` so that the border can be supplied during initialization.

The fix must also ensure that switching between “Query” and “Error” modes (via color scheme changes) updates the border colors accordingly when a shared `Border` object is used.

Fix the border not respecting the user settings.


**Title:** `GetViewsUnderLocation` incorrectly orders adornment subviews when a child view overlaps a parent’s adornment area

**Description**
When a parent view’s adornment (e.g., `Border`) contains interactive subviews (like a button or expander) and a child view is positioned such that its own adornment extends into the same screen location, `View.GetViewsUnderLocation` returns the child’s adornment as the deepest view instead of the parent’s adornment subview. This breaks hit-testing for mouse clicks and other input that depends on the returned depth order.

**Steps to reproduce**
1. Create a parent view with a non‑zero `Border` thickness and add a child `View` (e.g., an `ExpanderButton`) to that `Border`.
2. Add a child view to the parent that is positioned with negative `X`/`Y` (so that its `Border` rectangle overlaps the parent’s `Border` subview).
3. Call `GetViewsUnderLocation` at the screen coordinate where the overlap occurs.

**Expected behavior**
The returned list of views at the location must be ordered from root to deepest (topmost) according to the actual visual stacking. The deepest element should be the subview of the parent’s adornment (e.g., the `ExpanderButton`), because adornments are drawn *after* child content and chid adornments. Thus the parent’s `Border` (and its children) is visually above the child’s `Border`.

**Actual behavior**
The deepest view is the child’s `Border` (an adornment of the child view). The parent’s adornment subview appears earlier in the list, causing input to target the wrong element.

**Acceptance criteria**
- `GetViewsUnderLocation` must always return the correct topmost view when an adornment of the parent has child views.
- The ordering must respect that parent adornments (Margin, Border, Padding) and their subviews are visually stacked *above* child adornments.
- The fix must not regress existing ordering for cases where no adornment subviews are present, or where child views do not overlap parent adornment areas.
- The behavior must be independent of the exact thickness values of the adornments and work correctly with any combination of `ViewportSettingsFlags` that expose adornment rectangles.

**Notes**
The issue manifests specifically when a child view’s layout extends outside the parent’s content area, causing its adornment to invade the parent’s adornment region. The internal traversal in `GetViewsAtLocation` currently adds parent adornments to the result *before* processing subviews, but it does not traverse into the children of those adornments in a way that ensures they end up deeper in the result list. The traversal needs to push adornment views onto the stack so that their own subviews are visited later than sibling child view adornments.

Fixes #4493 - `GetViewsUnderLocation` does not return subviews of Adornment if Parent has a Subview under Adornment (#4507)


**Title:** ListView default `SelectedItem` should be `-1` (no selection) instead of `0`

**Description**
When a `ListView` is created, its `SelectedItem` property defaults to `0`, which means the first item is automatically highlighted and considered selected. This causes several problems:

1. **Unwanted selection** – A fresh `ListView` should not pre‑select an item before any user interaction. The initial state should indicate “no active selection”.
2. **Empty source** – When the data source is `null` or contains zero items, `SelectedItem == 0` is invalid and can lead to out‑of‑range errors or visual artifacts.
3. **Combobox integration** – The `ComboBox` internally uses a `ListView` for its drop‑down. With the old default, the drop‑down always appeared to have the first item pre‑selected, even when the text field was empty. The expected behavior is that no item is selected until the user picks one, and the text field remains empty.
4. **Keyboard search (`CollectionNavigator`)** – When no item is selected and the user presses a letter to cycle through matching items, the current implementation starts searching from the current item (index `0`). It should start from “nothing selected” (index `-1`) and jump to the first match in the list.

**Expected Behavior**
- `ListView.SelectedItem` should be `-1` by default (both in the constructor and after `Source` is assigned).
- `TopItem` must never become negative. When `SelectedItem` is `-1`, `TopItem` should be `0`.
- The `ComboBox` drop‑down should have no selection when it is first opened, and its `Text` should remain empty until the user actively selects an item.
- When the internal `ListView` source has no items, `SelectedItem` must be forced to `-1`.
- The `CollectionNavigator` used for key‑based search should accept a start index of `-1` and correctly find the first matching item in the list.

**Impact**
Without this fix, controls behave incorrectly when they logically should have no selection, leading to user confusion, visual inconsistency, and possible crashes when operating on empty collections.

Fixes #2451. ListView SelectedItem should be -1 by default.


**Title:** Fixes for view layout initialization and invalidation propagation

**Description:**

Several layout bugs cause views to be drawn incorrectly or not respond to changes after construction. These are especially visible in the UICatalog `AllViewsTester` scenario where the EventLog panel uses a `Dim.Fill` that depends on another view’s frame width.

The observed problems and required invariants:

1. **New views are not laid out by default.**
   The `NeedsLayout` internal flag starts `false`, so a newly constructed view may be skipped by the first layout pass. A view should always be considered as needing layout until it is actually laid out.

2. **`SetNeedsLayout` short-circuits incorrectly when the view already needs layout.**
   Even if the view’s own `NeedsLayout` is `true`, changes in children or adornments may still require the parent/superview to be laid out again. The early return prevents proper upward propagation, meaning ancestor views do not get re-laid-out when a child’s layout changes.
   *Constraint:* Propagation to the `SuperView` must not cause infinite recursion. It should only call `SuperView.SetNeedsLayout()` if `SuperView.NeedsLayout` is currently `false`.

3. **`SetNeedsLayout` propagation for `Adornment` is wrong.**
   When called on an `Adornment`, it should propagate to the `Adornment.Parent`, but only when the parent does not already need layout (to avoid redundant work and infinite loops). The current code either propagates unconditionally or returns early for non-adornments before handling the parent correctly.

4. **`EndInit` ordering may skip the initial layout for complex dependency chains.**
   The method currently calls `SetNeedsLayout()` before the explicit `Layout()`, which can cause the view to be laid out with stale dimensions or not at all when `Dim` expressions refer to other views’ post-layout frames. The expected order is: perform `Layout()` first, then call `SetNeedsLayout()` to mark any follow-up work.

5. **The `AllViewsTester` scenario works around layout bugs with a manual `_eventLog.Layout()` call.**
   The correct way for a `Dim` callback to indicate that its computed value depends on layout state that may change is to throw `LayoutException`. The layout system catches that exception and retries, ensuring correct final results.
   Replace the manual `Layout()` call in the scenario with a `throw new LayoutException(...)` so that the scenario exercises the normal layout retry mechanism.

**Acceptance criteria:**
- After `EndInit`, all views are laid out and visible without external manual `Layout()` calls.
- Modifying a child’s position/size triggers appropriate layout of ancestors and adornments, with no infinite recursion or missed updates.
- The `AllViewsTester` scenario renders the EventLog panel correctly using `LayoutException` and without the manual `Layout()` workaround.
- `SetNeedsLayout` behaves correctly for both ordinary views and adornments, respecting the propagation guard against redundant calls.


**Title:** Add global timeout to test driver and fix race condition during application startup

**Component:** `TerminalGuiFluentAssertions` test helpers (`With.A<T>`, `GuiTestContext`, `FakeInput`)

**Symptom / behavior:**
Tests using the fluent test context (`With.A<T>()`) can hang the test runner indefinitely if the Terminal.Gui application fails to start, blocks forever in its input loop, or never shuts down. Additionally, on slower machines or under load tests sometimes flake because the context constructor returns before the Toplevel is fully initialized — the test may try to interact with the UI while the background thread is still inside early boot code (e.g., `v2.Init`).

**Request:**
1. **Configurable hard-stop timeout** – Introduce a global timeout (e.g., a static `TimeSpan` property on the `With` helper, defaulting to 30 seconds) that limits how long `FakeInput<T>.Run` and the `GuiTestContext` background thread are allowed to wait. If the timeout elapses, the background thread and any blocking waits should be cancelled forcefully, and the test should fail with a descriptive message indicating the application did not shut down gracefully.
2. **Boot synchronization** – After spawning the background thread that calls `v2.Init` then `Application.Run<T>()`, the `GuiTestContext` constructor must wait for the boot sequence to reach a point where the Toplevel is ready for interaction (at minimum after `v2.Init` has completed). Use a bounded wait with a short timeout (e.g., 5 seconds) and fail early with a `TimeoutException` if booting does not finish in time.
3. **Clear failure on timeout** – When the hard-stop token has been cancelled at disposal time (i.e., the test timed out or the application didn’t stop gracefully), throw a clear exception explaining the situation so that test failures are obvious and actionable.

**Non‑obvious constraints:**
- The timeout must apply to both the boot phase and the steady‑state `FakeInput.Run` wait; a stalled input loop after boot should also be killed.
- The boot synchronization must be signaled inside the background thread *after* any one‑time initialization (e.g., after `v2.Init`) but before the blocking `Application.Run`. The test‑harness thread must not proceed before this signal is received (or the timeout fires).
- The global timeout should be configurable so that individual test suites can set a longer/shorter duration if needed, but the default must be safe for CI environments.
- The implementation must be cancellation‑safe: no orphaned background threads or native handles leaked if the test is aborted via the new timeout.

**Acceptance criteria (natural language):**
- A test that deliberately never calls `Application.RequestStop()` (or that blocks in `Application.Run` forever) fails within the configured global timeout instead of hanging.
- A test that boots correctly continues to work as before, without regressions in startup speed or reliability.
- The failure message when a timeout occurs clearly states that the application timed out or did not shut down gracefully.

Add timeout to tests and fix race condition to boot


Make `MainLoop` independently testable. Decouple the main loop driver from the console driver: `FakeDriver` should no longer implement `IMainLoopDriver`; instead, lift the existing `NetMainLoop` logic out so it can be used with a fake console for tests. `NetMainLoop` must accept a custom `Func<ConsoleKeyInfo>` to read keys, defaulting to `Console.ReadKey`, and throw if none is provided. Provide public `Application.Init` overloads that accept an `IMainLoopDriver`; when a custom `ConsoleDriver` is supplied, a `mainLoopDriver` argument must be required, not optional.

Add coverage for `MainLoop` idle handlers: they must be invoked each iteration, support duplicate registrations, be removable, and be automatically removed when the handler returns `false`. Timers must fire approximately after the specified interval, respect removal, and stop repeating when their callback returns `false`. `Invoke` must dispatch actions as an idle handler. Also cover `EventsPending` behavior with and without waiting.


When using `FindOperator` instances (e.g. `IsNull()`, `In(...)`) as the condition value for a `ManyToOne` or owner-side `OneToOne` relation in `findOptions.where`, TypeORM should map the operator directly to the underlying foreign key column and generate the expected SQL (e.g. `"authorId" IS NULL` or `"authorId" IN (...)`) — similar to how plain values already work. The affected relation types are those where the foreign key lives on the current entity’s table.

**Edge cases / safety:**

- **Non-applicable relation types:** For `OneToMany`, `ManyToMany`, and non-owner `OneToOne` relations, using a `FindOperator` should continue to be rejected with a clear error message indicating the relation kind is not supported.
- **Composite/OR conditions:** The fix must work correctly when multiple `FindOperator` conditions for the same relation appear inside an array of where-clauses (OR semantics).
- **Other `FindOperator` kinds:** The change should cover all `FindOperator`-based conditions, not only `IsNull` and `In`. The direct column comparison behavior should be uniformly applied.
- There is no need to join the related entity’s table; the comparison operates on the FK column of the owning side.

fix: allow where IsNull for ManyToOne relations (#9031)


**Title:** `save()` throws `Cannot update entity because entity id is not set` when only `@Column({ update: false })` fields have changed

**Description**
When an entity contains a column marked `@Column({ update: false })` (i.e. `isUpdate: false` in the column metadata) and the application modifies *only* the value of that non‑updatable column before calling `repository.save()` or `manager.save()`, TypeORM throws an error:

```
Cannot update entity because entity id is not set in the entity.
```

The entity exists and has a primary key; the error is misleading. The expected behaviour is that saving an entity where no *updatable* column has actually changed should be a no‑op – no UPDATE query should be issued and no error should be thrown.

Additionally, when *both* updatable and non‑updatable columns are changed on an existing entity, `save()` should still perform an UPDATE but only persist the new values of the updatable columns, leaving the non‑updatable column’s database value unchanged.

**Acceptance criteria / invariants**

1. **Only non‑updatable columns changed (`update: false`)** – `save()` must not attempt to execute an UPDATE statement. The operation should complete without error and the database record should remain as before.
2. **Both updatable and non‑updatable columns changed** – `save()` must issue an UPDATE that only sets the updatable columns; the stored value of the non‑updatable column must stay unchanged.
3. **Original behaviour unchanged** – When at least one updatable column changes (with or without non‑updatable changes), the update must still happen exactly as it did before.
4. The fix should operate solely on the decision of whether an UPDATE is necessary; do not alter the way column values are collected or the UPDATE query itself.

**Possible area of investigation**
The persistence logic that decides whether an entity “must be updated” currently appears to look at raw change maps without distinguishing columns that cannot be updated. That check should additionally verify that at least one of those change maps belongs to a column whose `isUpdate` metadata flag is `true`.

fix: ignore changes for columns with `update: false` in persistence (#10250)


Title: Repeated filesystem lookups for `package.json` during `importOrRequireFile` degrade performance with many files

When TypeORM loads many files—for example, when scanning for entity files or applying migrations—it calls `getNearestPackageJson` for each imported file path. That helper traverses the directory tree upward on every invocation, issuing `stat` and `readFile` calls for every directory in the chain, even when the nearest `package.json` for a given directory has already been determined in a previous call. The result is a noticeable slowdown on projects with large numbers of entities or migration files.

Expected behavior:
The result of `getNearestPackageJson` should be cached per-directory for the lifetime of the process. Once a directory has been resolved (whether a `package.json` is found or not), subsequent lookups for files in that directory or its subdirectories should reuse the cached result and avoid redundant filesystem operations.

Requirements / constraints:
- Both positive and negative results must be cached (i.e., directories where no `package.json` exists up to the root as well as directories where one was found).
- The cache must be bounded in size. A simple LRU or similar eviction strategy is acceptable to prevent unbounded memory growth in long-running processes that may encounter a very large number of unique directories.
- The caching layer must be transparent: it must not change the semantics of `importOrRequireFile` or any consumer that relies on the nearest `package.json` to decide between ESM and CommonJS.

Context:
This performance issue was discussed in #4136. The repeated filesystem calls become especially visible when using TypeORM’s migration tools with many migration files, but any path that invokes `importOrRequireFile` on many file paths will benefit from caching.

perf: Cache package.json location between getNearestPackageJson invocations (#11580)


```
Fix: PostgreSQL array column default values cause unnecessary schema migration queries

When an entity defines a column with `array: true` and a default value (e.g., `[]`, `["a","b","c"]`, `"{}"`, `"{a,b,c}"`), running `schema:log` or `migration:generate` after the table already exists results in non-empty up/down queries, even though the database schema matches the entity. The PostgreSQL driver's default-value normalization does not produce the same representation that is read during table introspection, so the schema builder incorrectly detects a change and emits redundant SQL.

After synchronizing the database schema once, subsequent diff operations should show zero pending changes for these columns. The normalization for array defaults must be consistent with how PostgreSQL returns column defaults in information_schema (e.g., without extra type casts that are stripped during introspection). This applies to both ordinary array columns and enum array columns, and should handle defaults given as arrays as well as defaults given as already-formatted strings (`"{}"`, `"{a,b,c}"`). Affects the PostgreSQL driver only.
```

fix: Array type default value should not generate SQL commands without change (#7409)


## Feature Request: Support Oracle Implicit Results in Query Runner

### Problem

Oracle PL/SQL procedures can return implicit result sets via `DBMS_SQL.RETURN_RESULT`. When such a procedure is executed through `queryRunner.query()`, the rows from the returned cursor are silently lost. The caller receives no data for the implicit results, even though the underlying Oracle driver includes them in the response.

### Desired Behavior

When a call to `queryRunner.query()` executes a PL/SQL block that invokes `DBMS_SQL.RETURN_RESULT` to return one or more cursors, the resulting `QueryResult` should:

- Include the rows from those implicit result sets in the `records` property (as an array of rows).
- Include the `implicitResults` value in the `raw` property so the raw Oracle response is preserved.

If multiple implicit result sets are returned, they should be represented as an array of arrays (one array of rows per cursor).

### Constraints

- The change must not alter behavior for existing use cases: normal `SELECT` queries continue to return data via `rows`, and procedures with `OUT` bind parameters continue to return results in `outBinds`.
- When `implicitResults` is present in the driver response, it should take precedence over `rows` and `outBinds` for populating `records` only if those are not already populated (i.e., the driver response for implicit results does not typically include `rows` directly, but `implicitResults` is the primary data carrier).
- The implementation should be confined to the Oracle query runner (`OracleQueryRunner`) and treat `implicitResults` similarly to the existing handling of `rows` and `outBinds`.

### Acceptance Criteria

- Calling `queryRunner.query()` with an Oracle stored procedure that uses `DBMS_SQL.RETURN_RESULT` returns the rows of the implicit cursor as the query result (observable via `result.records`).
- The raw driver output containing `implicitResults` is exposed via `result.raw`.
- Existing behavior for regular queries and procedures with out binds is unchanged.

feat: support Oracle Implicit Results (#8050)


**Title:** Relation ID mapping fails when relation belongs to a nested embedded entity

**Repro scenario:**
1. Define a top-level entity that has an embedded column (via `@Column(() => SomeEmbedded)`).
2. Inside the embedded class, define a relation (e.g., `@OneToOne` with `eager: true` or `@ManyToOne`) pointing to an own entity.
3. Save a parent instance (including the relation) and then retrieve it with `find`/`findOne`.

**Observed behaviour:**
- The embedded object's relation field is either `undefined` or `null`, even though the database row contains the foreign-key value.
- When the relation is loaded eagerly, the nested entity data is not attached to the embedded object as expected.
- Updates to the relation's property on a fetched embedded object are not persisted correctly.

**Expected behaviour:**
Relation-id data (and eagerly-joined relation data) should be correctly mapped into the nested embedded structure. Intermediate embedded objects that contain relations must be initialised as necessary during raw-result → entity transformation, so callers can traverse the object graph without encountering missing intermediates.

**Constraints / safety:**
- Must handle arbitrary nesting depth of embedded columns (not just one level).
- Must not break existing flat / non-nested relation-id mapping.
- The fix should apply to both relation-id mapping and eager-relation population (they likely share a property-path traversal).
- No change to the public API; the transformation must remain transparent to data-source/repository users.

fix: handle relation ids in nested embedded entities (#11942)


**Title:** mysql/mariadb: JSON column values are double-parsed when using the mysql2 connector package

---

**Description**

When a user configures a MySQL/MariaDB data source with `connectorPackage: "mysql2"`, TypeORM incorrectly applies `JSON.parse` to the value returned by the underlying driver in `prepareHydratedValue`. The mysql2 driver already deserialises JSON columns, so the value arrives as a JavaScript object, array, number, boolean, or null rather than as a JSON string. Applying `JSON.parse` to an already‑deserialised value leads to runtime errors (e.g. “Unexpected token … in JSON”), needlessly stringifying and re‑parsing, or other unintended transformations.

The same logic breaks when a JSON column stores a plain string value. mysql2 returns such a string directly (not wrapped in JSON quotes), so `JSON.parse` on it either throws (if the string is not valid JSON) or produces a double‑wrapping result (if the string happens to be valid JSON). For the classic `mysql` connector the existing behaviour of parsing every stringified JSON value is correct, because that driver always returns JSON columns as unparsed strings.

**Expected behaviour**

*   **When the connector is classic `mysql`** – keep the current behaviour of unconditionally parsing a JSON‑string value.
*   **When the connector is `mysql2`**:
    1. Values that are *not* strings (objects, arrays, numbers, booleans, null) must be used directly without any parsing.
    2. String values must be *attempted* to be parsed as JSON. If parsing succeeds (the driver returned a JSON‑encoded representation), the parsed result is used. If parsing fails, the string is used as‑is (it represents a plain string stored as a JSON scalar).

**Connector detection**

The driver needs to reliably determine whether the underlying connector is `mysql` or `mysql2`. The `connectorPackage` option is the primary source. However, users can also provide a custom `driver` instance without specifying `connectorPackage`. In that scenario the driver must infer the connector type by inspecting properties of the loaded module (e.g., `version`, `Connection.prototype.execute`). Emit a warning when a custom driver is passed without `connectorPackage`, because correct JSON handling depends on knowing which library is in use.

**Edge cases to verify**

Round‑trip consistency must be preserved for JSON columns containing:

*   nested objects and arrays
*   complex structures (mixed types, deeply nested)
*   string primitives (including empty strings, strings containing quotes)
*   numeric primitives (integers, floats)
*   boolean primitives
*   `null`

All the above should be stored and retrieved with their correct JavaScript type and value, regardless of which MySQL connector package is used.

fix: JSON parsing for mysql2 client library (#8319) (#11659)


```
Title: `connection-to-datasource` codemod ignores deep‑path imports (e.g. `typeorm/driver/sap/…`)

### Description
The `connection-to-datasource` transform (packages/codemod) is meant to rewrite TypeORM v0 `Connection`-related symbols and paths to their v1 `DataSource` equivalents.
Right now it only acts on files that contain **exact** `import { … } from "typeorm"` statements.
Files that import from sub‑paths like:

```ts
import type { SapConnectionOptions } from "typeorm/driver/sap/SapConnectionOptions"
import type { SqliteConnectionOptions } from "typeorm/driver/sqlite/SqliteConnectionOptions"
```

are silently skipped.  No symbols are renamed, and no module paths are updated.

### Expected behaviour
1. The transform should recognise **any** import whose source is either `"typeorm"` or a path starting with `"typeorm/"`.
2. Imported identifiers that match a known rename mapping (e.g. `SapConnectionOptions` → `SapDataSourceOptions`) must be renamed **both** in the import specifier and in all *usage* sites.
3. The module path itself must be updated when the final segment is a renamed symbol (e.g. `typeorm/driver/sap/SapConnectionOptions` → `typeorm/driver/sap/SapDataSourceOptions`).
   In addition, there are **cross‑directory moves** that cannot be handled by a simple file‑name swap.  For example, the `sqlite/` directory was removed; `SqliteConnectionOptions` should be rewritten to `typeorm/driver/better-sqlite3/BetterSqlite3DataSourceOptions`.
4. The same logic must apply to CommonJS `require(…)` calls where identifiers are destructured:

   ```ts
   const { Connection: LegacyConn } = require("typeorm")
   const { SapConnectionOptions: LegacySap } = require("typeorm/driver/sap/SapConnectionOptions")
   ```

   The destructured keys, the local aliases, and the require path must all be rewritten.
5. Deep paths whose last segment does **not** correspond to any rename key (e.g. `typeorm/driver/sap/ThingsConnectionHelper`) must be **left untouched** – the transform should not alter paths or specifiers that it doesn’t recognise.

### Additional notes
- The early‑exit guard (`fileImportsFrom(root, j, "typeorm")`) currently only checks for the exact string `"typeorm"`.  That guard is the reason files with only deep‑path imports are skipped entirely.  The fix should ensure the guard (or its equivalent) lets through `"typeorm/"…` imports so the transform can process them.
- The transform already has a map of symbol renames (`typeRenames`).  Use that as the source of truth for what to rename; do **not** hard‑code additional symbol lists elsewhere.

### Acceptance criteria (behavioural)
- A file that imports `SapConnectionOptions` from `typeorm/driver/sap/SapConnectionOptions` has the import rewritten to `SapDataSourceOptions` from `typeorm/driver/sap/SapDataSourceOptions` and any usage of that type is updated.
- A file that imports `SqliteConnectionOptions` from `typeorm/driver/sqlite/SqliteConnectionOptions` is rewritten to `BetterSqlite3DataSourceOptions` from `typeorm/driver/better-sqlite3/BetterSqlite3DataSourceOptions`.
- A file that imports a non‑mapped symbol from a deep path (like `something` from `typeorm/driver/sap/ThingsConnectionHelper`) is **not** modified by the transform.
- CommonJS `require` destructuring works identically for both exact and deep‑path sources.
- Existing exact‑source `import` / `require` behaviour is unchanged.
```

fix(codemod): recognize typeorm deep-path imports (#12382)


**Title:** Upsert/merge queries on STI child entities use wrong alias for discriminator column condition

**Description:**
When using `.insert().into(ChildEntity).values(...).orUpdate(...)` (or the merge equivalent) with entities that use single-table inheritance (`@TableInheritance`, `@ChildEntity`), the generated SQL’s `WHERE` condition for the discriminator column uses `mainAlias.name` instead of the actual table identifier the rest of the query uses. This causes invalid SQL or “column not found” errors, particularly visible in drivers that rely on merge/upsert (e.g., PostgreSQL, CockroachDB, MSSQL) and when the table has a custom schema.

**Expected behavior:**
The upsert/merge condition expression must use the same table reference (table name or query alias) that the surrounding query uses. In the case of a child entity, the discriminator column condition should be prefixed with the alias/name that is actually selected for the upsert target, not the global `mainAlias.name` which may differ from the alias expected in the upsert clause.

**Constraints:**
- Must work with all supported drivers that implement upsert/merge (PostgreSQL, CockroachDB, MSSQL, etc.).
- Must correctly handle entities using custom database schemas.
- Must not break non-STI upsert queries.
- The fix should avoid introducing new regressions in other parts of `INSERT … ON CONFLICT … DO UPDATE` or `MERGE …` generation.

**Acceptance criteria:**
- A single-table inheritance child entity upsert using `.orUpdate()` produces valid SQL where the discriminator column condition references the same table alias as the `INSERT`/`MERGE` target.
- The query executes successfully without “column does not exist” or “could not be bound” SQL errors.
- The upsert correctly inserts or updates rows as expected, including when a custom schema is used.

fix: enhance upsert functionality for proper sql generation with table alias (#11915)


Fix SQLite: `migration:generate` and `schema:sync` do not detect changes to the allowed values of a `simple-enum` column.
When a `@Column({ type: "simple-enum", enum: MyEnum })` field has its enum members updated (for example, a new enum constant is added), the driver must flag the column as changed so that a new `CHECK` constraint is produced in the next migration or sync.

The column-change detection logic in the SQLite driver should compare the existing table column's enum values (a string array) with the entity metadata's enum values (normalized to strings). If the two lists differ, treat the column as changed, analogously to how other column attributes (type, length, unique, etc.) are already handled.

Safety/edge-case notes:
- Enum values stored in the database CHECK constraint are strings; the metadata may contain numeric or mixed enum values, so always convert them to strings before comparison.
- The comparison should be order-preserving or order-agnostic, whichever naturally matches the existing array-comparison utilities used elsewhere in the driver’s change-detection path.

fix: resolve issue with "simple-enum" synchronization in SQLite (#9716)


# Bug: SQLite loses user-specified unique constraint names on schema reload

**Platform/surface**: TypeORM SQLite driver (affects `AbstractSqliteQueryRunner` → `loadTables`).

**Symptom**:
When a SQLite table is created via synchronisation or migrations with a custom unique constraint name—for example
`@Unique("customer_email_unique", ["email"])`—the custom name is discarded when the table metadata is later reloaded (e.g. during `getTable()`, migration diff, or `schema:log`). Instead, a name generated by the naming strategy is used. This causes false schema changes and can lead to duplicate constraint creation.

**Root cause**:
SQLite’s `PRAGMA index_list` and `sqlite_master` do not surface the constraint name of a `UNIQUE` index created via `CONSTRAINT … UNIQUE`. The current code always falls back to `NamingStrategyInterface#uniqueConstraintName` without attempting to recover the user-specified name.

**Expected behaviour**:
After schema creation, the `TableUnique` objects returned by the table-loading path should preserve the original custom names (when they are available in the `CREATE TABLE` SQL). If a mapping cannot be determined, falling back to the naming strategy is acceptable.

**Acceptance criteria / invariants**:
- A `@Unique("my_name", ["col"])` decorator leads to a `TableUnique` with `name == "my_name"` after synchronisation and reload on SQLite.
- Compound unique constraints with custom names are similarly preserved.
- Constraints without an explicit name (or where the name cannot be recovered) must still receive a generated name, maintaining existing behaviour.
- Other drivers must not regress; the fix should be confined to the SQLite table-loading logic (and not affect MySQL/Postgres/etc). MySQL’s path, where uniques are exposed as indices, must still work correctly as verified by the test suite.

**Implementation hint**:
Since SQLite stores the original `CREATE TABLE` statement in `sql_master`, parsing it for `CONSTRAINT "name" UNIQUE (...)` may provide the necessary mapping. Ensure the parsing is defensive and only used to enhance the naming, never to break existing code.

fix: @Unique constraint is not created with specified name


**Feature Request: `Or` find operator for `where` clause conditions**

The `where` option of repository `.find*()` methods already supports an `And` operator that combines multiple operator conditions using `AND`. We need an analogous `Or` operator that combines conditions with `OR`.

```ts
// Example usage
const posts = await dataSource.getRepository(Post).findBy({
  title: Or(Equal("About #2"), ILike("About%")),
});
```

Expected generated SQL (PostgreSQL syntax for illustration):
```sql
SELECT * FROM "post" WHERE "title" = 'About #2' OR "title" ILIKE 'About%'
```

**Requirements**
- Provide an `Or` function (importable from `typeorm`) that accepts a variadic list of `FindOperator` instances.
- The operator should produce `OR`-joined SQL expressions, just as `And` produces `AND`-joined expressions.
- `Or` must integrate with the existing where-clause machinery such that it works correctly when combined with other properties in the `where` object, and with other wrapping operators like `Not` (e.g., `Not(Or(...))`).
- The overall semantics should mirror `And`: each argument is evaluated independently, and the whole expression evaluates to `true` if **any** argument condition is true.
- Edge cases: nesting `Or` inside another `Or`, or mixing `Or` with `And`/`Not`, should behave predictably — the SQL generation should respect operator precedence appropriately.
- Include a corresponding entry in the find options documentation (`docs/find-options.md`) under "Combining Advanced Options".

Use the existing `And` implementation as a reference for the pattern; the changes needed are analogous but with `OR` logic instead of `AND`.


```
Issue: `synchronize()` drops and recreates `json` columns in MariaDB

Severity: Data loss

When `connection.synchronize()` (or `synchronize: true` on startup) runs against a MariaDB database, any column declared with `type: "json"` is dropped and recreated on every synchronization, even when the entity definition has not changed. This destroys all data stored in those columns.

Steps to reproduce:
1. Define an entity with a `json` column:
   ```ts
   @Entity()
   export class MyEntity {
       @PrimaryColumn()
       id!: number;

       @Column({ type: "json" })
       data!: any;
   }
   ```
2. Insert rows containing JSON data.
3. Run `connection.synchronize()` a second time.
4. All previously stored JSON values are gone; the column was dropped and recreated.

Expected behavior:
`synchronize()` should recognize that the column already exists with a compatible type and leave it untouched, preserving data.

Additional notes:
- The problem is specific to MariaDB; the same schema works correctly on MySQL.
- MariaDB implements the `json` type as `LONGTEXT` under the hood. The schema comparison logic likely sees a mismatch between the declared type and the actual database type and decides to drop/recreate the column.
- The fix should ensure that, for MariaDB, columns of type `json` are mapped to MariaDB's underlying representation so that schema synchronization treats them as stable.
```

fix: .synchronize() drops json column on mariadb (#5391)


Fix the `relation-count` v1 codemod transform:

1. The TODO message for `@RelationCount` removal currently recommends migrating to `loadRelationCountAndMap()`, but that API was also removed in v1. Update the message to direct users to `@VirtualColumn` with a sub-query (as in the upgrading guide).

2. The transform currently only flags the `@RelationCount` decorator. It should also flag direct calls to the removed `loadRelationCountAndMap()` method on a query builder, inserting a similar TODO comment.

3. For `loadRelationCountAndMap()` call sites, attach the TODO comment to the nearest enclosing statement-like AST node (anything ending with `Statement`, plus `VariableDeclaration`, `ExportDefaultDeclaration`, `ExportNamedDeclaration`, and class property definitions) so that jscodeshift’s printer preserves the comment in output.

4. Multiple chained `.loadRelationCountAndMap(...)` calls on the same statement should produce exactly one TODO comment, not one per call.

5. Remove the early guard that skips files without a direct `typeorm` import, because `loadRelationCountAndMap()` can appear in files that only receive a query builder via a helper and never import from `typeorm` themselves. The existing decorator and import-removal logic already handles the absence of a direct import gracefully, so the guard is unnecessary.

fix(codemod): correct relation-count guidance and flag loadRelationCountAndMap (#12374)


When two separate entity inheritance hierarchies (each using `@TableInheritance`) have child entities that declare the same discriminator value via `@ChildEntity`, TypeORM incorrectly raises an error:

*Entities X and Y have the same discriminator values. Make sure they are different while using the @ChildEntity decorator.*

This happens even though the child entities belong to different database tables and unrelated inheritance trees. Discriminator values should only need to be unique within a single table’s inheritance set, not globally. Unrelated hierarchies should be allowed to reuse the same discriminator value.

Acceptance criteria:
- Given two unrelated root entities (each with its own table and STI hierarchy), and each has a child entity marked `@ChildEntity('internal')`, schema creation and entity metadata validation must succeed without throwing a duplicate discriminator error.
- The duplicate discriminator error must still be thrown when two child entities that belong to the same inheritance tree (the same table) share the same discriminator value.
- The change must not affect other entity metadata validation rules (e.g., eager relations, dependencies).

fix: single table inheritance returns the same discriminator value error for unrelated tables where their parents extend from the same entity  (#8525)


Please fix `OrmUtils.deepCompare` so that it correctly compares `Map` and `Set` instances. Currently passing a `Map` or `Set` to `deepCompare` does not produce correct equality results because the internal comparison treats them as plain objects, which fails to compare their actual contents.

**Expected behavior:**

- Two `Map` objects should be considered equal if they contain the same key-value pairs (with values compared recursively, using the same deep-comparison rules applied to plain objects, arrays, primitives, etc.).
- Two `Set` objects should be considered equal if they contain the same elements (also compared recursively).

Equality of `Map` and `Set` instances should work when they are nested inside objects, arrays, or each other.

**AC:**

- `deepCompare(new Map(), new Map())` returns `true`.
- `deepCompare(new Map([["a", 1]]), new Map([["a", 1]]))` returns `true`.
- `deepCompare(new Map([["a", 1]]), new Map([["a", 2]]))` returns `false`.
- `deepCompare(new Set(), new Set())` returns `true`.
- `deepCompare(new Set([1, 2]), new Set([1, 2]))` returns `true`.
- `deepCompare(new Set([1]), new Set([1, 2]))` returns `false`.
- Recursive comparison works for Maps and Sets whose values/entries contain other Maps, Sets, objects, arrays, etc.

Make sure the fix does not break existing comparison behavior for plain objects, arrays, and primitives.

fix: fix up map objects comparison (#10990)


**Summary:**
When using the `setLock` method on a `SelectQueryBuilder` backed by Microsoft SQL Server, the lock hint (`WITH (NOLOCK)`, `WITH (HOLDLOCK, ROWLOCK)`, etc.) is only applied to the first table (the one after `FROM`). Joined tables do not receive the lock hint, making it impossible to build queries that lock all involved tables correctly on MSSQL.

**Expected behavior:**
For MSSQL connections, if a lock mode is set on the query builder, the corresponding lock hint must appear after **every** table alias in the query:

- After the FROM table
- After each table introduced via `innerJoin`, `leftJoin`, `innerJoinAndSelect`, `leftJoinAndSelect`, and the junction tables implicitly created for many-to-many relations
- The lock hint should be inserted immediately after the alias and before any `ON` condition (or end of the join clause if no condition).

The lock hints follow MSSQL conventions:
- `dirty_read` → ` WITH (NOLOCK)`
- `pessimistic_read` → ` WITH (HOLDLOCK, ROWLOCK)`
- `pessimistic_write` → ` WITH (UPDLOCK, ROWLOCK)`

**Examples of problematic SQL vs corrected SQL:**

*Without joins:*
`SELECT * FROM Cart c WITH (NOLOCK) WHERE …`  (already works)

*With one join (currently broken):*
Produced: `SELECT * FROM Cart c WITH (NOLOCK) INNER JOIN CartItems ci ON …`
Expected: `SELECT * FROM Cart c WITH (NOLOCK) INNER JOIN CartItems ci WITH (NOLOCK) ON …`

*Many-to-many with junction table (currently broken):*
The generated SQL should include the lock hint on both the junction table and the target table.

**Constraints / edge cases:**
- Only activate this behavior for the MSSQL driver; other drivers must remain unaffected.
- If no lock mode is set, no hint should be appended anywhere.
- The lock hint is a **table-level** hint in MSSQL; it must appear for each table reference, including the junction table in many-to-many joins.
- The behavior must work uniformly for both relation-based joins (e.g., `innerJoinAndSelect("cart.cartItems", …)`) and direct table-name joins (e.g., `innerJoin(User, "user", "user.ID = cart.UNID")`).

feat: add WITH (lock) clause for MSSQL select with join queries (#8507)


In Postgres, when extracting column defaults from `column_default`, the logic that strips the trailing type-cast suffix (e.g. `'draft'::enum_typename`) does not handle type names that contain a dash (`-`). This happens when the table name includes a dash, because enum type names derived from the table name also contain that dash.

The symptom: for an enum column with a default value on a table like `module-foo_table_x`, the parsed default includes leftover cast characters instead of the clean literal `'draft'`.

Change the cast-stripping to remove **any** `::` suffix, regardless of the characters in the type name, so that the extracted default matches what was actually supplied.

fix: Incorrect enum default value when table name contains dash character (#9685)


**Title:** `save()` does not return generated primary keys for cascaded related entities

**Description:**
When an entity is saved with `cascade: true` on a relation, and the related entity has a generated primary key (e.g., `@PrimaryGeneratedColumn`), the generated value is not populated on the returned object. The parent's generated key is set, but the child's key remains `undefined`.

**Expected behavior:**
After saving a parent with a cascaded child, both the parent's and the child's generated primary keys should be present on the returned entity.

**Steps to reproduce:**
1. Define a parent entity with a `@OneToOne` (or `@ManyToOne`) relation to a child entity, with `cascade: true`.
2. Both entities use `@PrimaryGeneratedColumn`.
3. Create a new parent, assign a new child, and call `repository.save(parent)`.
4. Check `returnedEntity.child.generatedId` — it is `undefined`.

The issue also occurs when the relation’s foreign key column is not yet set (undefined id) and the child is inserted first via cascade. The generated map from the child’s insert operation is lost or overwritten, so the returned parent entity never receives the child’s generated key.

fix: save does not return id, save does not return generated (#7336)


The HTTP client currently accumulates headers from every response in a redirect chain, so the final `Response::headers()` can contain stale values from intermediate responses (e.g., `301`).
Only the headers of the final response must be preserved; headers from upstream redirects must be discarded.

**Required behaviour**:
- When the low-level header parser encounters a new HTTP status-line (e.g. `HTTP/1.1 301 …`) during a redirect, it must clear any previously stored headers before processing subsequent header lines.
- After a redirect, the `Response::headers()` map must reflect exactly the headers of the last (final) response, not a merge of all responses in the chain.

**Relevant code areas**:
- `clients::http::RequestImpl::parse_header` – the place where incoming header lines are parsed and inserted into the response’s header map.
- The redirect handling within `clients::http::Client`/`Request` that follows `Location` headers.

bug httpclient: ignore 1st response headers on redirect (#1041)


**Title**: Cannot interleave regular queries with portal fetches or use multiple portals concurrently in a single transaction

**Severity**: High
**Component**: storages/postgres, pq-extra
**Labels**: bug, extended-query-protocol, portal

### Description
When a transaction holds an open portal (created via `MakePortal`) and partially fetches rows, executing any other query (e.g., `Execute("SELECT 1")`) or binding a second portal causes the connection to hang, return incorrect state, or produce runtime errors. The problem occurs with libpq 14+ but can also affect earlier versions.

### Steps to reproduce
1. Begin a transaction.
2. Create a portal for a query that returns multiple rows (e.g., `SELECT generate_series(1,10)`).
3. Fetch a subset of rows (e.g., 1 row) without completing the portal.
4. Execute any other SQL statement in the same transaction.
5. Observe that the subsequent query never completes or the connection state becomes invalid.

Similarly, creating two portals and interleaving fetches between them also fails.

### Expected behavior
- It should be possible to mix regular queries and portal fetches within the same transaction.
- Multiple portals should be able to coexist and be used in an interleaved manner (fetch from portal A, fetch from portal B, etc.).
- The connection should correctly return to an idle state after a portal bind, allowing other work to proceed.

### Observed behavior
- After a portal bind and partial fetch, the connection remains stuck in a non-idle state, blocking further commands.
- Runtime errors from portal name collisions when an empty name is supplied.

### Impact
- Users cannot use portals for cursor-like streaming while also executing other business logic queries in the same transaction.
- Pipeline/parallel portal patterns are impossible.
- Workarounds (e.g., always draining a portal before any other query) break many common usage patterns.

### Root cause analysis (internal notes)
The custom protocol workaround (`pq_extra_defs`, `pq_portal_funcs`, `pq_workaround`) did not properly handle the **Bind Complete** message from the server. A portal `Bind` command was marked with the generic `PGQUERY_EXTENDED` class, so the parser did not treat it as a self-contained operation. Consequently, after a bind, the connection remained in a busy state, preventing subsequent commands.

Additionally:
- The portal fetch termination logic incorrectly continued fetching when `n_rows == 0` was passed to retrieve remaining rows.
- Supplying an empty portal name could cause collisions because multiple unnamed portals share the same internal name, leading to “portal already exists” runtime errors.

### Required fixes
1. Introduce a distinct query class for portal bind operations (e.g., `PGXQUERY_BIND`) so the parser can recognize when a bind command is complete.
2. Update `pqxParseInput3` to handle the `'2'` (Bind Complete) message specifically: when a command with `PGXQUERY_BIND` class receives this message, advance the command queue and set `asyncStatus` to `PGASYNC_IDLE`, allowing subsequent queries to proceed.
3. Adjust `Portal::Impl` so that fetching with `n_rows == 0` correctly terminates the portal (already partially done; add check for `!n_rows` in addition to `fetched != n_rows`).
4. In `Transaction::MakePortal`, when an empty portal name is provided, automatically generate a unique name (e.g., a UUID) to avoid collisions.

### Related
TAXICOMMON-4482

bug pq-extra: fix portal binds for libpq14 (#2405)


```
[TAXICOMMON-4923] Resolver should directly parse bracketed IPv6 addresses

### Context
`clients::dns::Resolver::Resolve()` currently cannot handle raw IPv6 addresses
written in the bracket notation `[<IPv6>]` (as used in URLs). The HTTP client
works around this in `RequestState::ResolveTargetAddress()` by short‑circuiting
resolution when the host starts with `[`. This bypasses resolver statistics,
caching and logging, and it only helps HTTP, not other resolver consumers.

### What needs to happen
Teach the resolver to recognise an exact `[<IPv6>]` form and return the
parsed `engine::io::Sockaddr` directly, throwing `NotResolvedException` on
malformed input. Then remove the workaround from the HTTP client so that all
codepaths use the resolver uniformly.

### Detailed requirements
* When `Resolve()` receives a string that starts with `[`, require the format
  to be exactly `[<IPv6>]` with **no** trailing characters (such as a port).
  Examples of valid input: `"[::1]"`, `"[::ffff:127.0.0.1]"`.
* Convert the inner address using `inet_pton(AF_INET6, ...)`. On success push
  the resulting `engine::io::Sockaddr` into the returned `AddrVector`.
* If the string is empty, does not start with `[`, or the bracket expression is
  malformed (missing `]`, extra characters after `]`, invalid hex, etc.) **do
  not** return anything from this new path. For the `[`‑prefixed malformed
  cases throw a `clients::dns::NotResolvedException` with a descriptive message.
* The new parsing step must be performed **after** the file‑cache lookup and
  **before** querying the network cache in `Resolve()`. This preserves the
  existing precedence order and avoids unnecessary network calls.
* Remove the early‑return workaround in
  `clients::http::RequestState::ResolveTargetAddress()` that tests for a
  leading `[`; the HTTP client should go through the resolver like any other
  client.

### Out of scope
* Handling of embedded port numbers (`[::1]:80`) – these must still throw.
* Zone‑ID / scope‑ID support.
* Expanding hostnames that contain `:` without brackets.

### Acceptance criteria
* `resolver.Resolve("[::1]")` returns the IPv6 loopback address as a single
  sockaddr.
* `resolver.Resolve("[::1]")` does **not** generate a network query.
* `resolver.Resolve("[::1]:80")` throws `NotResolvedException`.
* `resolver.Resolve("[localhost]")` throws `NotResolvedException`.
* The HTTP client’s direct `[` check is gone; all resolution statistics
  (lookup source counters) continue to be updated correctly.
```


Remove URL-decoding of Cookie header fields during request parsing. Per RFC 6265, cookie names and values are not URL-encoded; the current decoding (including the `DecodeMode::Cookie` that treated `+` literally) unnecessarily mangles the data. Store the raw bytes directly into the request cookies map.

Additionally, introduce early validation of cookie name and value when constructing a `Cookie` object, based on the RFC 6265 grammar. Reject invalid characters (control characters, separators, high bytes) and enforce rules around quoting and empty names, so that malformed cookies are surfaced immediately rather than passed through.

feat server: remove UrlDecode for cookies (#1601)


After updating to a newer yaml-cpp (commit 96f5c887+), `Node::as<T>(fallback)` throws a type mismatch exception even when a fallback value is provided, instead of returning the fallback. This breaks our `IsConvertibleToArithmetic` detection (used by `IsBool`, `IsInt`, etc.) and causes schema parsing errors for scalars like `additionalProperties: false` (“Expected YAML type 'objectValue', but found 'scalarValue'”).

Replace the internal conversion check and value extraction so that arithmetic convertibility of a YAML node is tested **without throwing an exception**. Use `YAML::convert<T>::decode` directly. Maintain the existing rule that nodes with an explicit `!` or `tag:yaml.org,2002:str` tag are never considered arithmetic, even if they contain a valid number or `true`/`false`.

The fix must ensure the following work correctly:
- `IsBool()`/`IsInt()`/`IsInt64()`/`IsUInt64()`/`IsDouble()` return `true` for a parsed unquoted scalar like `false`, `true`, `42`, `3.14`.
- `IsBool()` returns `false` for a quoted `"false"`.
- `As<T>()` succeeds for such scalars when `Is*` is true, and throws `TypeMismatchException` (not a generic yaml-cpp exception) when the type does not match or the scalar is explicitly tagged as a string.
- YAML config schema parsing that inspects boolean fields (e.g., `additionalProperties: false`) no longer throws an unexpected type error.

fix: compatibility with the new yaml-cpp version


Add a truncation indicator to `ugrpc::ToLimitedDebugString` and the error details formatter so that clients can distinguish between a naturally short output and a forcefully trimmed one.

**Background**
Currently, when a protobuf message exceeds the size limit, the output is silently truncated with no visible sign. This leads to misleading logs: a message that happens to fit vs. one that was cut off look identical.

**Requirements**

- After any truncation **due to the limit**, append the string `...(truncated)` preceded by a newline, so the final output ends with `\n...(truncated)`.
- If the truncated prefix is too short to meaningfully include the marker (i.e., the prefix length ≤ 15), output only `...(truncated)`.
- When `limit == 0`, return exactly `...(truncated)`.
- Empty messages (no fields) must still return `<EMPTY>` – the marker must never appear for truly empty messages.
- For the error details helper (`GetErrorDetails`-like function with a `max_size`), the marker must appear only in the error details part when that part is truncated, while the surrounding format (`code: ..., error message: ...`) remains intact. If the details part is empty, no marker should be added.
- If the output fits within the limit (no truncation), the marker **must not** be present.

**Non-functional notes**

- The marker behavior must hold regardless of whether the underlying protobuf version supports early stopping or falls back to full serialization.
- Update the documentation in `protobuf_logging.hpp` to describe the new indicator and the edge cases.

feat grpc: add truncation indicator to grpc logging


# Socket::Release() must invalidate FdControl before returning the fd

`Socket::Release()` is a `&&`-qualified method that transfers the underlying file descriptor to the caller and ends the socket’s lifetime. Currently it releases the internal `FdControl` without invalidating it first. This is unsafe: the `FdControl` holds libev watchers (read / write) that reference the released fd. If that fd number is later reused—for example by a new `Socket` or a completely unrelated file—the stale watchers can fire and attempt I/O on the wrong resource, leading to data corruption, use-after-free, or unexpected errors.

The required fix is to invalidate the `FdControl` (via `Invalidate()`) before returning the fd to the caller. This must happen:
- after retrieving the fd value (so we still have the correct fd to return),
- before the `FdControl` is destroyed,
- and before the caller closes or reuses the fd.

Invalidation should wake any tasks that are currently waiting on the socket with an appropriate error (as already implemented by `FdControl::Invalidate`), and stop all event watchers so they cannot fire later.

No new APIs are needed; just adjust the `Release()` implementation in `src/engine/io/socket.cpp` to call `Invalidate()` on the `FdControl` returned by `fd_control_.release()` and then return the previously captured fd.

bug io: invalidate FdControl on Socket release (#218)


Optimize case-insensitive string comparison and hashing for HTTP header maps.

**Context**
`StrIcaseEqual` and `StrIcaseHash` (case-insensitive ASCII comparators/hashers) are used in every HTTP request/response to look up headers. Profiles indicate they are a significant bottleneck.

**Requirements**

1. **Case-insensitive equality (`StrIcaseEqual`)**
   - Treat ASCII uppercase letters (`A`–`Z`) as equivalent to their lowercase counterparts (`a`–`z`). All other byte values compare as-is.
   - For equal strings, accelerate comparison significantly (target ~2–3× faster) by avoiding full lowercasing passes.
   - Provide an *early exit* when a mismatch is detected; the common case is equality in hash-map lookups, so maximize speed for that path.
   - A mild slowdown for very short unequal strings is acceptable.
   - When SSE2 is available use vectorised operations; provide a correct scalar fallback for other platforms.

2. **Case-insensitive hashing (`StrIcaseHash`)**
   - The existing hash is a SipHash1-3 variant. Do **not** change the hash result; only improve the implementation.
   - Speed up hashing for short strings (< 8 bytes) and for typical header lengths (8–64 bytes).
   - Vectorise case-lowering using SSE2 when available; keep a correct scalar fallback.
   - The internal leftover-byte handling for lengths that are not a multiple of 8/16 should be efficient and correct for all byte values.

3. **General constraints**
   - All functions remain `noexcept` and work with `std::string_view`.
   - Changes must be confined to the existing implementation utilities (e.g., `utils::impl::byte_utils`).
   - Correctness must hold for arbitrary byte sequences (0–255), not merely alphabetic ones.


Classic workers (type=classic) served via the dev server should not receive any ESM‑specific injections. Specifically:

- Do not inject `import.meta.env`
- Do not inject HMR code
- When the `injectQuery` helper is required (e.g. to process dynamic imports), do **not** emit an ESM `import` statement. Instead, inline the helper as a regular function definition so it remains compatible with classic scripts.

The dynamic import path rewriting must continue to work for classic workers in dev mode; a classic worker doing `import(path)` should resolve the asset correctly and the injected helper must not break the worker.

fix(worker): prevent inject esm in classic workers (#14918)


## Inconsistent handling of CJS re-exports between dev and build

When a source file re-exports named members from a CommonJS dependency using an `export { ... } from 'cjs-dep'` declaration, the dev server’s on-the-fly CJS import transformation does not rewrite these statements. The production build, however, does transform them (via Rollup’s CommonJS interop), leading to a behavioural mismatch: code that works in production fails or behaves differently in development.

### Acceptance criteria

Given a CommonJS module (e.g. `react`) that is pre-bundled and served from `.vite/`, the dev server’s CJS import transform must handle all of the following re-export declarations so that the runtime behaviour matches the production build.

1. **Named export from CJS**
   - Input: `export { useState, Component } from 'react'`
   - Must rewrite to an import of the CJS bundle, extract the named properties, and re-export them with the same names.

2. **Default re-export**
   - Input: `export { default } from 'react'`
   - Must apply the default‑interop logic (`__esModule` check) and re-export the result as the default export.

3. **Default re-export with alias**
   - Input: `export { default as React } from 'react'`
   - Must apply the default‑interop logic and re-export the result under the alias `React`.

4. **Re-export a named member as default**
   - Input: `export { Component as default } from 'react'`
   - Must extract the named property `Component` from the CJS module and re-export it as the default export.

5. **Re-export with local aliases**
   - Input: `export { useState as useStateAlias } from 'react'`
   - Must extract the property and re-export under the alias.

6. **`export *` / `export * as` from CJS**
   - Input: `export * from 'react'` and `export * as react from 'react'`
   - These must remain **untouched** (no transformation) because CJS modules do not have statically known named exports and cannot be faithfully re‑exported with a wildcard.

In all transformed cases the generated code must be valid ESM, must not introduce unintended global variables, and must produce the same runtime behaviour as the production build’s Rollup output. The existing handling of `import` declarations (regular, default, namespace imports) must continue to work unchanged.

fix(transformCjsImport): make dev and build get the same result (#5745)


As a maintainer ticket:

**Feature:** Add `vite:afterUpdate` HMR event that fires when an update has been fully applied

**Context**
Vite’s client HMR API (`import.meta.hot.on`) currently exposes a `vite:beforeUpdate` event that fires right before an update is applied. There is no corresponding event for *after* the update completes. This forces tools and framework integrations to resort to brittle workarounds (e.g. `MutationObserver` to detect CSS link tag swaps in the playground) to know when new styles or modules are actually in place.

**Request**
Introduce a built-in `vite:afterUpdate` event that is dispatched to all HMR listeners after every update from a single `update` payload has been fully applied:

- For JS updates: after the fetched module has been executed and any accept callbacks have run.
- For CSS updates: after the new `<link>` tag has been loaded (`load` / `error`) and the old tag has been removed (i.e. the stylesheet swap is complete).

The event should carry the same `UpdatePayload` data as `vite:beforeUpdate`, allowing listeners to inspect which modules were updated.

**Observable behaviour**
- `import.meta.hot.on('vite:afterUpdate', callback)` must be callable and will be invoked once after each HMR update batch completes.
- The callback must fire **after** all updates in the update payload have finished, including async CSS link replacements.
- Ordering: `vite:beforeUpdate` → apply updates → `vite:afterUpdate` (per payload).
- No spurious extra fires; one `vite:afterUpdate` per HMR update message that actually applies changes.

This will allow framework authors and dev tool authors to reliably react to completed HMR updates (e.g. trigger state resets, notify devtools, or finally remove the MutationObserver-based workarounds currently used in tests).

feat: add `vite:afterUpdate` event (#9810)


We need `transformWithEsbuild` to be usable outside of the ViteDevServer, and its handling of `tsconfigRaw` options should merge with the project’s tsconfig rather than fully replacing it.

**Problem**

- `transformWithEsbuild` is currently only exposed as a method on a `ViteDevServer` instance. This makes it impossible to use in build plugins, script utilities, or any context where a server has not been created.
- When calling `transformWithEsbuild` and providing a `tsconfigRaw` **object** with specific compiler options (e.g. `{ compilerOptions: { useDefineForClassFields: false } }`), the implementation previously used that object as the complete `tsconfigRaw`, fully disregarding the file’s own `tsconfig.json`. This means any other settings inherited from the project (like `importsNotUsedAsValues: "preserve"`) are lost, producing incorrect output. Passing `tsconfigRaw` as a **string** already performs a full override, but the object form should allow partial overrides by merging with the resolved tsconfig.

**Expected Behavior**

1. A named export `transformWithEsbuild` should be available directly from `vite` (e.g. `import { transformWithEsbuild } from 'vite'`), independent of any server instance.
2. The existing server method should remain for backwards compatibility but be marked as deprecated, delegating to the standalone function.
3. When `options.tsconfigRaw` is an **object**, its `compilerOptions` should be **merged** with the effective compiler options determined by the file’s `tsconfig.json`. In other words, only the explicitly provided keys should override the project defaults; unspecified keys should retain their project values.
4. When `options.tsconfigRaw` is a **string**, it should completely replace the tsconfig resolution (existing behavior is acceptable).

**Impact**

Without a standalone export, this utility is inaccessible in many legitimate use cases outside of the dev server. Without correct merging, partial `tsconfigRaw` overrides break transformations that rely on project-level TypeScript settings, leading to subtle, hard-to-diagnose bugs.

**Suggested Implementation Notes (non‑blocking)**

- The merging should happen only when a `tsconfigRaw` object is provided. If a string is given, it should be used verbatim.
- The merging must respect the “meaningful fields” already listed in the current code (`jsxFactory`, `jsxFragmentFactory`, `useDefineForClassFields`, `importsNotUsedAsValues`) and any others that esbuild consumes from `tsconfig.json`.
- Edge cases: a `tsconfigRaw` of `{ compilerOptions: {} }` should leave all project settings intact; `tsconfigRaw: { compilerOptions: { useDefineForClassFields: false } }` should only change that one flag while preserving e.g. `importsNotUsedAsValues`.

feat: export transformWithEsbuild (#4882)


# Bug Report: `import.meta.resolve` not detected when written across multiple lines in Vite config

**Title**: `import.meta.resolve` fails when formatted across multiple lines in Vite config

**Environment**: Vite (latest)

**Description**:

When authoring a Vite configuration file that uses `import.meta.resolve()`, the feature works correctly as long as the call is written on a single line. However, if the code is formatted so that `import.meta` and `.resolve(...)` appear on separate lines—for example:

```ts
export default {
  resolved: import.meta
    .resolve('../some/path'),
}
```

Vite fails to properly handle the `import.meta.resolve` call. The config loading/bundling logic does not recognize the expression, which leads to one of the following problems:

- The bundled config output lacks the necessary injected support for `import.meta.resolve`, causing a runtime error or an undefined value.
- The resolution does not produce the expected file URL.

**Expected behavior**:

Any valid `import.meta.resolve(...)` expression should be recognized and processed correctly by Vite’s config bundling step, regardless of whitespace—including line breaks and indentation—between `import.meta` and `.resolve`. The behavior should be identical to the single-line case.

**Impact**:

This prevents developers from formatting config files according to common style guides (e.g., Prettier may break long `import.meta.resolve` calls across lines). It forces all such calls onto a single line, reducing readability and maintainability.

**Suggested fix**:

The detection of `import.meta.resolve` in config files should use a pattern that allows arbitrary whitespace between the `import.meta` token and the `.resolve` member access. A regular expression such as `/import\.meta\s*\.\s*resolve/` would match any whitespace including newlines, ensuring multiline formatting works correctly.

fix: detect `import.meta.resolve` when formatted across multiple lines (#21312)


We need to fix the SSR transform’s handling of class property keys that happen to share a name with an imported binding. Currently, when a non‑computed `PropertyDefinition` key matches an import, the transform hoists a `const` declaration for that name and treats the key as if it were a reference. This is incorrect: uncomputed keys are plain property names, not identifier references, and they should never trigger import hoisting or rewriting.

The behavior causes unnecessary `const` declarations that may shadow the import and can lead to confusing or broken output, especially when the same name appears in the property value or elsewhere in the module.

### Expected behavior
- For a class body containing a `PropertyDefinition` with a non‑computed key (i.e. `computed === false`), the key identifier must **not** be treated as a reference to its imported binding.
- No `const` hoisting should be performed for the key.
- The identifier in the key position should be left untouched; only the value expression (if present) should be processed normally for import references.
- Computed property keys (`computed: true`) remain expressions and **should** still be processed as references.

### Affected code
The core logic lives in:
- `packages/vite/src/node/ssr/ssrTransform.ts`
  – the `isRefIdentifier` helper, which decides whether an identifier is a reference, and
  – the `onIdentifier` callback inside the walker, which currently has a special case for `PropertyDefinition` inside a `ClassBody` that adds a `const` declaration.

### Concrete ask
1. In `isRefIdentifier`, add a rule that returns `false` for an identifier that is the key of a `PropertyDefinition` when `parent.computed` is falsy. This correctly marks it as a non‑reference.
2. Remove the `PropertyDefinition`/`ClassBody` branch from `onIdentifier` (the one that inserts `const` declarations). The `ClassDeclaration` + `superClass` handling should remain.
3. Ensure that property values inside class bodies continue to be transformed as usual, with imports replaced by their SSR bindings.

This will keep the transform aligned with JavaScript semantics where uncomputed keys are inherently static and do not reference variables.

fix(ssr): class property keys hoisting matching imports (#22199)


Virtual modules (resolved ids starting with `\0`) are incorrectly blocked while serving under strict file‑system access restrictions (`server.fs.strict` / `server.fs.deny`). Imports like `import X from 'virtual:foo.svg'` fail with an access‑denied error because the transform middleware applies the same filesystem checks to the final resolved id and its request URL.

Make the transform middleware skip file‑serving access checks for virtual‑module requests so they load regardless of `server.fs` configuration:

- In the early serving check inside `transformMiddleware`, bypass the access check when the request URL begins with `/@id/\0`.
- In the `allowId` callback passed to `transformRequest`, immediately allow any `id` that starts with `\0`, so the deeper load/transform path also skips the access validation.

Only ids and URLs that are part of a genuine virtual module (the internal `\0`‑prefixed id) should be exempted; other requests must still be gated by the configured filesystem allow/deny rules.