Serenity 10.2.0 Release Notes (2026-02-16)
These release notes detail the significant changes made in Serenity and StartSharp from version 10.1.0 to 10.2.0. For a complete list of changes, please refer to the Serenity Change Log.
New Serenity Developer Tools Extension for Chrome
We are excited to introduce the Serenity Developer Tools extension for Chrome. This browser extension is designed to help developers inspect and debug Serenity applications more efficiently.
Key features include:
- Inspect Serenity grid state, columns, and data
- View and debug widget registrations
- Examine dialog and editor configurations
- Scroll selected grid elements into view for easier debugging
The extension can be downloaded from the Chrome Web Store. For more information, see the README in the Serenity repository.
New DENY Special Permission
A new special permission key DENY has been introduced that denies access to everyone, including super admins. This joins the existing special permission keys * (public/unrestricted access) and ? (any logged-in user).
To make these easier to reference, a new SpecialPermissionKeys class has been added:
using Serenity;
// SpecialPermissionKeys contains:
// "*" - SpecialPermissionKeys.Public (public/unrestricted access)
// "?" - SpecialPermissionKeys.LoggedIn (any authenticated user)
// "DENY" - SpecialPermissionKeys.Deny (deny all access, even super admins)
// Example: deny access to a page or service for everyone
[PageAuthorize(SpecialPermissionKeys.Deny)]
public class MyRestrictedPage : Controller
{
// This page is inaccessible to everyone
}
The DENY permission is particularly useful when you have a page or service that should not be accessible in certain environments or configurations. For example, you can use it to conditionally disable features based on configuration:
if (featureDisabled)
{
// Dynamically return the DENY permission to block access
return SpecialPermissionKeys.Deny;
}
File Read Access Control (Experimental)
We have added new infrastructure for controlling file read access, currently used by the experimental SecureUploadFileResponder. This allows you to secure file downloads by checking entity-level permissions in addition to simple file-path-based permissions.
FileReadAccessAttribute
A new attribute that can be placed on row properties to specify file read access control:
public class MyFileRow
{
// Restrict file access to users with a specific permission
[FileReadPermission("MyModule:FileAccess")]
public string FilePath { get; set; }
// Allow public access to the file
[FileReadPermission(SpecialPermissionKeys.Public)]
public string PublicFilePath { get; set; }
// Require bypass permission (default) for admin override
[FileReadPermission("MyModule:RestrictedFile", AllowBypass = true)]
public string RestrictedFilePath { get; set; }
}
FileReadAccessSettings
The FileReadAccessSettings class provides configuration options for file read access control in appsettings.json:
{
"FileReadAccess": {
"BypassPermission": "Administration:General",
"DefaultPermission": "*",
"PathPermissions": "^public/:*;^temporary/:*;^uploads/:MyModule:FileAccess",
"ReturnForbidResult": false
}
}
Key configuration options:
- BypassPermission: A permission that bypasses all file read access checks (e.g., for administrators)
- DefaultPermission: Permission to check when no
FileReadAccessAttributeis present (default is"*") - PathPermissions: Regex-based path matching patterns with associated permissions; evaluated in order, first match wins. Format:
"pattern:permission"entries separated by semicolons - MissingMetadataPermission: Permission to check when file metadata is missing or cannot be read
- ReturnForbidResult: Whether to return 403 (Forbidden) instead of 404 (Not Found)
SecureUploadFileResponder
A new experimental SecureUploadFileResponder in Serenity.Pro.Extensions that checks for related entity access and optionally permissions while returning files from FilePage/Read endpoints. Instead of granting access to all files based on a single permission, the secure responder:
- Checks path-based permissions configured in
FileReadAccessSettings.PathPermissions - If the path doesn't match any configured pattern, it looks up the row that owns the file
- Checks entity-level access control (read permission of the row)
- Optionally validates
FileReadAccessAttributeon the row property
To use the secure responder in Startup.cs:
// Register the SecureUploadFileResponder instead of the default file responder
services.AddSingleton<IFileResponder, SecureUploadFileResponder>();
This is currently experimental and may see further refinements in upcoming releases.
UploadPathHelper Improvements
TemporaryFilePrefix and IsTemporaryFile
Instead of hardcoding the "temporary/" prefix for temporary upload files, we have standardized it:
// Instead of checking filename.StartsWith("temporary/")
if (UploadPathHelper.IsTemporaryFile(filename))
{
// File is a temporary upload
}
// Instead of "temporary/" + Guid.NewGuid()
var tempPath = UploadPathHelper.TemporaryFilePrefix + Guid.NewGuid().ToString("N");
Thumbnail Metadata Improvements
We have significantly improved the thumbnail handling system:
- New thumbnail filename constants:
ThumbBaseSuffix("_t"),ThumbExtension(".jpg"),SizedThumbFormat("_t{0}x{1}"), andMetaFileExtension(".meta") for consistency. GetThumbnailNamenow supports sized thumbnails: You can pass optional width/height parameters to generate thumbnails with specific dimensions:
// Old behavior (default thumb with _t suffix)
var thumb = UploadPathHelper.GetThumbnailName("uploads/file.jpg");
// Returns: uploads/file_t.jpg
// New: sized thumbnail
var sizedThumb = UploadPathHelper.GetThumbnailName("uploads/file.jpg", 100, 200);
// Returns: uploads/file_t100x200.jpg
TryParseThumbSuffixnow returns full base paths including the folder, not just the filename. This makes it easier to locate the primary file from a thumbnail path.- New
FileMetadataKeys: AddedIsThumbnailandPrimaryFileExtensionmetadata keys. Thumbnails are now tagged with metadata:IsThumbnail = "true"— marks the file as a thumbnailPrimaryFileExtension— stores the extension of the primary file (e.g.,".jpg",".png")
This helps in reliably finding the primary file from a thumbnail, even for legacy thumbnails that lack metadata.
New Helper Methods in UploadStorageExtensions
GetPrimaryFileFromThumb: Given a thumbnail path, returns the primary file path by checking metadata and falling back to legacy conventions.GetThumbnailFiles: Given a source file path, returns all associated thumbnail files. This is now used internally byDiskUploadStorageduring copy/delete operations.
jQuery 4.0.0 Update
jQuery has been updated to version 4.0.0 (served via Serenity.Assets for compatibility).
Note: jQuery 4.0.0 includes several breaking changes. Most notably,
$.isArray,$.isFunction,$.isNumeric,$.isPlainObject, and several other deprecated methods have been removed. If your project uses these methods directly, you should either:
- Update your code to use native alternatives (e.g.,
Array.isArray()instead of$.isArray())- Pin jQuery to version 3.7.0 via npm if you encounter issues
The jQuery version served via Serenity.Assets is 4.0.0, but you can continue using 3.7.0 by installing it via npm and configuring your import map accordingly.
Removal of Unused Global Type Helpers
Several unused global type helpers (Action, Predicate, Func, IComparable, IComparer, IEquatable, IEqualityComparer, etc.) have been removed from corelib. These were legacy type aliases that sometimes clashed with other global type declarations.
If you were using any of these global types, you will need to define them locally or use the appropriate native TypeScript equivalents.
Additionally, ESNext.Disposable has been added to the domwise tsconfig.json to satisfy @preact/signals-core requirements.
ServerTypingsGenerator Improvements
The ServerTypingsGenerator now has improved package name resolution:
- When encountering an assembly name like
Company.Package, it now tries more package name patterns:@company/package(scoped npm style)company.package(dot-separated style)
- It parses
package.jsonfiles for assembly-to-package name mappings, making it more likely to find the correct npm package for a given assembly. - An
AssemblyToPackageNamedictionary is also available for manual mappings, but the automatic parsing covers most common cases.
CSharpSyntaxRules.EscapeIfKeyword
A new EscapeIfKeyword helper has been added to CSharpSyntaxRules to avoid generating C# identifiers that conflict with reserved keywords. This fixes issues when:
- C# keywords like
decimal,int, etc. are used as.cshtmlfile names or other identifier positions - Generator output now prefixes such names with
@and uppercases the first letter to conform to C# identifier syntax rules
// Previously generated code with keyword conflict:
public class decimal // Error: 'decimal' is a keyword
// Now correctly generated:
public class Decimal
initNProgress Helper
A new initNProgress function has been added to @serenity-is/corelib that automatically sets up NProgress (the top-of-page progress bar) to show during AJAX requests:
import { initNProgress } from "@serenity-is/corelib";
// Initialize NProgress to show during AJAX requests
// Automatically shows after 200ms delay on ajaxStart, hides on ajaxStop
initNProgress(nprogress);
This is now used in the StartSharp pro-theme.ts script init instead of manual NProgress setup, simplifying the initialization code:
// Before (in pro-theme.ts):
const NProgress = (globalThis as any).NProgress;
if (NProgress && NProgress.start && NProgress.done && typeof document !== 'undefined') {
let npt: number;
Fluent.on(document, "ajaxStart", function () {
clearTimeout(npt);
npt = setTimeout(NProgress.start, 200);
});
Fluent.on(document, "ajaxStop", function () {
clearTimeout(npt);
NProgress.done();
});
}
// After:
import { initNProgress } from "@serenity-is/corelib";
if (!initNProgress()) {
setTimeout(initNProgress, 0);
}
If NProgress is already available as a global (e.g., loaded via the global bundle), initNProgress() works without any arguments. Otherwise, you can pass the NProgress instance explicitly.
AutoColumnWidthMixin CSP Fix
The AutoColumnWidthMixin has been reworked to properly handle strict Content Security Policy (CSP) environments. Previously, it constructed column cell content using HTML strings via innerHTML, which could violate CSP script-src or style-src directives.
The fix changes the approach to build cell nodes directly using DOM APIs instead of HTML string concatenation:
// Before (using HTML strings - CSP issue):
sb.push('<div class="slick-cell autocolumnwidthcalc">');
sb.push(childHtml);
// ...
row.innerHTML = sb.join('');
// After (using DOM APIs - CSP safe):
const node = document.createElement('div');
node.classList.add('slick-cell', 'autocolumnwidthcalc');
applyFormatterResultToCellNode(ctx, fmtResult, node);
// ...
row.append(...cellNodes);
This also makes it compatible with enableHtmlRendering = false (the new default since 9.0.0).
Header Menu Modal Z-Index Fix
Fixed an issue where the header menu dropdown appeared under modal dialogs. Since Bootstrap modals have a z-index of 1055 by default, the header menu (which had no explicit z-index) would be hidden behind the modal overlay.
/* Fix: added z-index to header menu dropdown */
.s-header-menu.dropdown-menu {
z-index: 9999;
}
Bug Fixes (10.1.1 through 10.1.6)
The following bug fixes from the 10.1.x patch releases are also included in 10.2.0:
PageAuthorizeAttribute Equality Fix (10.1.1)
Fixed a StackOverflowException in PageAuthorizeAttribute's default Equals implementation when permission keys are the same for the action and controller, causing Swagger to fail. Equals and GetHashCode are now properly overridden.
New GridUtils.addQuickSearch Method (10.1.2)
A new GridUtils.addQuickSearch method with named arguments has been introduced, replacing the deprecated addQuickSearchInput and addQuickSearchInputCustom methods:
import { GridUtils } from "@serenity-is/corelib";
GridUtils.addQuickSearch(grid, container, {
field: "Name",
placeholder: "Search...",
onSearch: (value, done) => {
// custom search logic
done();
}
});
Shift+Wheel for Horizontal Scrolling (10.1.2)
SleekGrid now handles Shift+Wheel for horizontal scrolling.
CheckTreeEditor/CheckLookupEditor Fix (10.1.2)
Fixed formatting issues with CheckTreeEditor/CheckLookupEditor and the quick search indicator.
Sortable(false) Fix (10.1.3)
Fixed an issue where a column with Sortable(false) was incorrectly included in the server-side generated sorted column list.
PropertyProcessor: DefaultSummaryType (10.1.4)
Added a DefaultSummaryType setting to PropertyProcessorOptions that allows overriding the default aggregate type for numeric columns:
services.Configure<PropertyProcessorOptions>(options =>
options.DefaultSummaryType = SummaryType.None);
This sets the default to None but still allows users to choose an aggregate type via the column header menu.
Summary Footer Fix (10.1.4)
Fixed an issue where the summary footer value was not shown the first time when no columns have summaries and one is set via the column header menu.
DateOnly Field Fix (10.1.5)
Fixed an issue with DateOnly field due to an extra quote in the format string. Also reverted Sergen to generate DateTime fields as before for SQL date columns, as DateOnlyField is not yet stable.
Header Filter Dropdown Fix (10.1.6)
Fixed an issue where header filter dropdown contents were not emptied before appending new search results.
Validation Rule Disposal Fix
Added a disposing listener to the target element during addValidationRule to resolve an issue where editors with custom validation rules were disposed in inline grid editors while validation callbacks were still executing. Also added null coalescing to DateEditor and DateTimeEditor get_value methods for this.domNode? to avoid similar issues.
MVC File Generation with C# Keywords
Fixed an issue with generated MVC files when C# keywords like decimal are used as .cshtml file names. The generator now properly escapes these via CSharpSyntaxRules.EscapeIfKeyword.
StartSharp Specific Changes
- SecureUploadFileResponder is now integrated in
Startup.cs(experimental) - initNProgress used in
pro-theme.tsinstead of manual NProgress setup - SpecialPermissionKeys used in
OpenIdScopePermissionWrapper - Header menu z-index fixed for modal dialog compatibility
- AutoColumnWidthMixin CSP fix applied
- GridUtils.addQuickSearch used in relevant grids
Package Updates
Updated NPM packages:
@serenity-is/tsbuildto 10.1.6@preact/signals-coreto 1.12.2 / 1.13.0@preact/signalsto 2.6.2preactto 10.28.3@tiptapto 3.18.0 / 3.19.0vitestto 4.0.18esbuildto 0.27.3jspdfto 4.1.0jsdomto 28.0.0@floating-ui/domto 1.7.5globto 13.0.1
Updated NuGet packages:
- System/ASP.NET Core packages to 10.0.3
Microsoft.Buildto 18.3.3FluentMigratorto 8.0.1MailKitto 4.15.0Microsoft.Data.SqlClientto 6.1.4Oracle.ManagedDataAccess.Coreto 23.26.100coverlet.collectorto 8.0.0Selenium.WebDriverto 4.40.0CSharpMinifierto 2.2.0
Upgrading to 10.2.0
- Update NuGet packages to 10.2.0
- Update npm packages:
@serenity-is/tsbuildto 10.1.6
- Review jQuery usage: If you use
$.isArray,$.isFunctionor other deprecated jQuery methods, update them to native alternatives or pin jQuery to 3.7.0 via npm - Review global type helpers: If you were using the removed global type helpers (
Action,Predicate, etc.), define them locally or use native TypeScript equivalents - Consider using
initNProgress: If you have custom NProgress setup code, you can replace it withinitNProgress()from@serenity-is/corelib - Review file access security: If you use custom file serving, consider the new
SecureUploadFileResponderandFileReadAccessSettings(experimental) - Replace
"temporary/"hardcoding: UseUploadPathHelper.TemporaryFilePrefixandUploadPathHelper.IsTemporaryFile()instead - Check for C# keyword identifiers: If you have
.cshtmlfiles or identifiers using C# keywords (likedecimal.cshtml), they will now be properly escaped - Review CSP compatibility: If you have a strict CSP, the
AutoColumnWidthMixinfix ensures grid auto-column-width functionality works withoutunsafe-inline