Hotfix release for the 1.9.17 version _This is a hotfix release for the 1.9.17 version, the 1.9.17 version has a few bugs that have been fixed in this version._
Auto-generated CRUD endpoints (
POST /api/models/{table}
,
PUT /api/models/{table}/{id}
) were returning
422 Unprocessable Content
on every request body submission.
Root cause:
from __future__ import annotations
in
models.py
caused Python to defer all type annotations as strings. FastAPI's
get_typed_signature
resolved the
data
parameter to the string
"PydanticModel"
instead of the actual Pydantic class, treating it as a query parameter instead of a request body.
Fix:
Replaced decorator-based route registration with functional registration. Endpoint functions are now defined without annotations on
data
, and
__annotations__
is manually patched with the resolved Pydantic class before registering the route:
async def create_item(data):
...
create_item.__annotations__['data'] = PydanticModel
router.post(f"/{table}")(create_item)
Clicking a button in an SPA route (e.g. "Add Product") fired the event handler twice , causing double submissions and double counter increments.
Root cause:
When the SPA router navigates to a route, it injects
app_{slug}.js
into the DOM. On re-navigation, the same
addEventListener
is called again on the same element — the browser does not deduplicate listeners with anonymous function references.
Fix:
Added a dataset-based guard to every generated
addEventListener
call. Before attaching a listener, the code checks for a
data-dars-evt-{event}
attribute. If it already exists, the listener is skipped:
if (!el.dataset.darsEvt_click) {
el.dataset.darsEvt_click = "1";
el.addEventListener("click", async function(event) { ... });
}
dars dev
Backend Output Visibility
The
dars dev
command was suppressing all backend output via
--log-level warning
, making it impossible to see HTTP request logs, errors, or user
print()
statements from the backend.
Fix:
Changed log level to
info
and added a background filter thread that suppresses only uvicorn startup bloat (
Started server process
,
Application startup complete
, etc.) while passing all other output through with a
[backend]
prefix for clear visual distinction:
[backend] INFO: 127.0.0.1:52341 - "GET /api/models/products HTTP/1.1" 200
[backend] INFO: 127.0.0.1:52342 - "POST /api/models/products HTTP/1.1" 201
Database Layer, Server Actions, Route Types, Guards, Middleware System & Auth Simplification _Major full-stack expansion: built-in ORM, server-side actions, security middleware, and a simplified decorator-based auth system._
pip install --upgrade dars-framework
Dars now ships with a complete built-in database layer for SQLite:
DarsModel
— Declarative model base class with auto-detected fields
TextField
,
IntegerField
,
FloatField
,
BooleanField
,
DateTimeField
,
JSONField
,
ForeignKey
ModelManager
— Per-model query API:
all()
,
get()
,
filter()
,
count()
,
create()
,
delete()
Database
— Thread-safe SQLite connection manager with WAL mode, migration tracking, and raw SQL support
register_model_api()
— Auto-generate full CRUD REST endpoints (
GET/POST/PUT/DELETE
) for all registered models
class Product(DarsModel):
__tablename__ = "products"
name = TextField(nullable=False)
price = IntegerField(default=0)
db = Database("app.db")
db.register(Product)
db.create_all()
# Query
Product.objects.filter(price=0)
A new
@server_action
decorator system that registers Python functions as API endpoints callable from client-side events:
from dars.backend.actions import server_action, call_server
@server_action
def greet(name: str, count: int = 1) -> list:
return [f"Hello {name}! x{i}" for i in range(count)]
Button("Greet", on_click=call_server("greet", name="World", count=3))
@server_action(auth_required=True, roles=["admin"])
discover_actions("backend.api")
POST /api/actions/{action_name}
New
RouteType
enum system for SPA-level route protection:
RouteType.PUBLIC
— No auth required (default)
RouteType.SSR
— Server-side rendered
RouteType.PRIVATE
— Requires authentication, redirects to login
RouteType.PROTECTED
— Requires authentication AND specific roles
@route("/admin", route_type=RouteType.PROTECTED, roles=["admin"])
def admin_panel():
return Page(...)
@requires_auth
The authentication system has been fundamentally simplified:
@requires_auth
— Now a proper FastAPI route decorator that auto-injects
request.state.user
. Can be used bare (
@requires_auth
) or with custom callback/secret (
@requires_auth(verify_credentials_callback=fn, secret="...")
).
@requires_role("admin")
— Role-based access control decorator compatible with any FastAPI route.
verify_credentials_callback
and
secret
are passed to
@requires_auth
, it auto-registers the auth config with a predictable
auth_id
(e.g.,
auth_dashboard
for
def dashboard()
).
app.setup_auth()
— Global auth configuration with optional
auth_id
and custom
login_page
.
/_dars/auth/{auth_id}/login
,
/me
,
/logout
,
/refresh
generated automatically for each scheme.
SessionManager
+
InMemorySessionStore
with
SessionStore
protocol for custom backends.
XSRF-TOKEN
cookie/header validation and refresh token rotation.
A complete middleware pipeline for FastAPI/Starlette:
DarsMiddleware
— Abstract base class with
before_request
/
after_response
lifecycle hooks
AuthMiddleware
— JWT Bearer/cookie validation with CSRF protection and multi-auth cookie detection
SecurityHeadersMiddleware
— CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, XSS-Protection
CORSMiddleware
— Configurable CORS with origin matching, credential support, and preflight handling
RateLimitMiddleware
— Sliding-window per-IP/per-user rate limiter with burst support
LoggingMiddleware
— Structured request/response logging with body and header capture
CompressionMiddleware
— Gzip response compression for text-based content types
MiddlewareChain
— Compose multiple middlewares into a single Starlette middleware
register_default_middlewares()
— One-call setup of the full middleware stack
Production-Grade Authentication: Multi-Auth, Secure Cookies & Server-Side Security _This is a major update that has been in preparation and development for a long time._
pip install --upgrade dars-framework
Dars now ships with a complete, production-ready authentication system that is completely isolated from the VDOM .
XSRF-TOKEN
cookie and header validation for all mutating requests (
POST
,
PUT
,
DELETE
,
PATCH
).
You can now register multiple independent authentication configurations in the same app, each with its own secret, callback, and scoped cookies.
dars_access_token_admin
).
@requires_auth
(Inline)
: Simply add the decorator below your route. Auto-registers the auth scheme with an ID derived from the function name.
Page.setup_auth()
(Component-level)
: Configure auth directly on your
Page
components.
App.setup_auth()
(Global)
: Register an auth configuration globally across your app.
updateVRefFromResponse
updateVRefFromResponse
now supports a
key
parameter, allowing you to easily extract deeply nested fields from JSON API responses using dot-notation. This makes Dars fully server-compatible using standard JSON responses.
# Extracts response["user"]["username"] and stores it in the VRef
updateVRefFromResponse(".user-name", key="response.user.username")
Unified fullstack dev:
dars devnow starts frontend and backend together whenbackendEntryis configured.dars dev --backendis deprecated.
pip install --upgrade dars-framework
dars dev
now launches the frontend preview server and backend SSR/API server together when
backendEntry
is configured in
dars.config.json
.
dars dev --backend
remains supported for compatibility, but it now prints a deprecation warning.
Desktop exporter removed, future
dars-desktopwith PyQt6 + WebEngine, and continued web exporter stability
pip install --upgrade dars-framework
The legacy desktop export path has been removed from the core Dars framework. This makes the framework leaner and keeps the main package focused on web application export, build, and SSR workflows.
format: "desktop"
export path is no longer supported in
dars-framework
.
dars-desktop
.
dars-framework
now prioritizes stable web export, SPA/SSR workflows, and the improved
dars-bundler
pipeline.
dars-desktop
dars-framework
and desktop-specific packaging logic.
dars-bundler: Standalone Rust Minifier, Static Site Router Optimization & Unified Config
pip install --upgrade dars-framework
Dars now ships with
dars-bundler
, a standalone cross-platform binary written in Rust that replaces the entire
rjsmin
/
rcssmin
/ Vite/esbuild minification pipeline. It requires
zero external dependencies
— no Node.js, npm, or Vite installation needed on the developer's machine.
Under the hood, dars-bundler uses:
The binary is discovered automatically in this priority order:
DARS_BUNDLER_PATH
environment variable (user override)
PATH
dars/bundler/
inside the framework package (shipped with Dars)
DarsBundler/
repo
target/debug|release
build
Pre-built binaries for Windows (amd64) , Linux (amd64) , and macOS (amd64 + arm64) are released via GitHub Actions on every tagged release.
dars.config.json
— Single
minify
Key
The old dual-key minification configuration (
defaultMinify
+
viteMinify
) has been replaced by a single, unified key:
{
"minify": true
}
true
(default) — dars-bundler runs after export, minifying all JS and CSS files in-place.
false
— minification is skipped entirely.
Old keys are accepted for backward compatibility but are no longer the source of truth. Existing projects do not need to update their configs immediately.
All 13
dars.config.json
files in the framework's own repos have been updated to use the new format.
When exporting a purely static site (no SPA routes), Dars now automatically:
router.js
from the generated
lib/
directory entirely — shaving ~11 KB from the cold load.
dars.min.js
on-the-fly
to strip the
import { ... } from "./router.js"
statement and the
router:
key from the exported
Dars
object, ensuring no broken import references at runtime.
SPA projects are unaffected — the full router is still included when
app._spa_routes
is populated.
The
dars export
and
dars build
commands previously contained ~90 lines of duplicated, branching environment-variable logic (
DARS_VITE_MINIFY
,
DARS_DEFAULT_MINIFY
,
DARS_DEFAULT_MINIFY_ONLY_FALLBACK
, etc.) wired across three separate code paths. This has been replaced with a single, clean block:
minify_enabled = cfg.get('minify', cfg.get('defaultMinify', True))
os.environ['DARS_MINIFY'] = '1' if minify_enabled else '0'
The
--no-minify
CLI flag continues to work on both
dars export
and
dars build
.
| Old key | Status | Replacement |
|---|---|---|
viteMinify
|
Deprecated (backward-compat read) |
minify
|
defaultMinify
|
Deprecated (backward-compat read) |
minify
|
Secure Asynchronous SSR Hydration,
useVRefHook, Pure SPA Shells & CLI Lifecycle Hardening
pip install --upgrade dars-framework
The Dars Server Protocol (DSP) payload handling has been fundamentally redesigned to prioritize security and DOM cleanliness.
<script id="__DARS_DSP_DATA__">
) has been entirely removed from the SSR HTML source. Pre-rendered pages are now shipped with pure, semantic HTML.
mode: "same-origin"
and
credentials: "same-origin"
to prevent external interception.
router.js
now correctly identifies and preserves the visibility of the hydrated
.dars-page
root container.
app.js
)
app.js
no longer incorrectly bundles VRef and reactive state bindings (
window.__DARS_VREF_VALUES__
) from all compiled routes. It has been refactored into a pristine, lightweight bootloader that _exclusively_ contains the SPA route map and framework initialization logic.
app_{slug}.js
files (like
app_did.js
) now retain complete, isolated control over their specific states, preventing namespace pollution and duplicate execution on navigation.
The CLI process management for
dars preview
(specifically with Uvicorn backends) has been aggressively optimized.
Ctrl+C
no longer results in a hanging terminal or "zombie" background processes waiting for WebSockets to close.
taskkill /F /T
on Windows,
SIGKILL
on Unix) combined with an immediate
os._exit(0)
, ensuring the backend server is dismantled instantly.
useVRef()
— Reactive VRef Consumer Hook
The VRef ecosystem is now complete with the introduction of
useVRef()
, closing the reactive loop alongside
setVRef
(define) and
updateVRef
(mutate).
from dars.all import *
# Define initial state
price = setVRef(19.99, ".item-price")
qty = setVRef(2, ".item-qty")
# Consume reactively — initial value is resolved at SSR time (no flash!)
Text(text=useVRef(V(".item-price")))
Text(text=useVRef(V(".item-price").float() * V(".item-qty").int())) # "39.98"
Button("Checkout", disabled=useVRef(V(".item-qty").int() == 0))
# Mutate to trigger reactive updates everywhere
Button("+", on_click=updateVRef(".item-qty", V(".item-qty").int() + 1))
useVRef
pre-resolves the initial value at build time by looking up the bound selector in the
setVRef
registry -- the server HTML already has the correct value.
V()
expression tree and automatically extracts every CSS selector used. At runtime, whenever any of those selectors is updated via
updateVRef()
, the binding re-evaluates and patches the DOM instantly -- no manual
dependencies
list required.
dScript
,
RawJS
, or plain strings via the
callbacks
parameter (single value or list). They fire every time the binding re-evaluates, enabling side-effects like logging or chained updates.
# Callbacks fire every time .value-stuff changes
Text(text=useVRef(
V(".value-stuff"),
callbacks=log("value-stuff changed!")
))
V()
expression,
MathExpression
,
BooleanExpression
, or plain literal.
window.__DARS_VREF_VALUES__
registry, keeping it in sync with any subsequent
updateVRef
calls.
VRefBinding
objects were iterated by key instead of by value, causing the reactive JS block to be silently omitted from the output bundle.
Production-Grade Fullstack: useFetch, FormValidator, Each, JsonStore, UploadPipeline, SecurityHeaders & .env Support
pip install --upgrade dars-framework
useFetch
— Declarative Data Fetching Hook
A new
useFetch
hook provides a fully Pythonic way to fetch data from APIs and bind the response to reactive VRefs — no JavaScript required.
from dars.all import *
trigger, loading, data, error = useFetch(
"/api/tasks",
method="GET",
on_success=runSequence(
updateVRef(".loading", False),
updateVRefFromResponse(".tasks-data"),
),
on_error=updateVRef(".error", True),
)
page.add_script(trigger) # auto-run on page load
(trigger_script, loading_vref, data_vref, error_vref)
— all pure Python objects
Show
,
Each
, and
updateVRefFromResponse
for zero-boilerplate reactive UIs
network_request
DAP op
updateVRefFromResponse
— Store Fetch Response into VRef
New helper that stores the API response from a
useFetch
on_success
context directly into a VRef selector. Works seamlessly with
Each
for runtime list rendering.
on_success=runSequence(
updateVRef(".loading", False),
updateVRefFromResponse(".tasks-data"), # stores ctx.response → VRef
)
Each
— Runtime List Rendering from VRef
The
Each
component now fully supports runtime VRef items (e.g. from
useFetch
). Pass a
VRefValue
as
items
and a render function — the exporter generates an HTML template at compile time, and the browser substitutes real item values at runtime.
Each(
items=tasks_vref, # VRefValue from setVRef([])
render=lambda t: Container(
Text(t.get("title", "__item_title__")),
Text(f"#{t.get('id', '__item_id__')}"),
style="flex items-center gap-2 p-2 border rounded bg-white",
),
)
dom_each_render
substitutes
__item_<field>__
placeholders with real values
{tasks:[...]}
,
{items:[...]}
,
{data:[...]}
)
done_class
placeholder supported for conditional styling (e.g. strikethrough for completed items)
Fixed:
VRefValue
objects passed as
items
no longer cause
TypeError: 'VRefValue' object is not iterable
at export time.
FormValidator
— Client-Side Form Validation
Declarative form validation with dual client/server enforcement. Rules are declared once in Python and evaluated both server-side and client-side via DAP.
validator = FormValidator({
"title": [required(), min_length(3), max_length(100)],
"email": [required(), email()],
})
# validated_submit: validates first, only submits if all rules pass
submit_action = validator.validated_submit(
url="/api/tasks",
form_data=collect_form(title=V("#title")),
on_success=runSequence(clearInput("title"), fetch_trigger),
on_error=setText("submit-error", "Error submitting."),
)
Available rules:
required()
,
min_length(n)
,
max_length(n)
,
pattern(regex)
,
email()
,
min_value(n)
,
max_value(n)
,
custom(fn)
Fixed:
validated_submit
now correctly blocks the network request when any validation rule fails. Previously, the submit fired unconditionally after validation.
Fixed:
conditional
DAP op now properly resolves DAP expressions (e.g.
bool_expr
,
transform
) as the condition — previously it only evaluated pre-resolved boolean values.
Fixed:
transform
DAP op now supports
length
,
is_email
, and
test_pattern
methods needed by the validator runtime.
JsonStore
— File-Backed Key-Value Store
Thread-safe, atomic-write JSON persistence for rapid prototyping and small-scale backends.
from dars.all import *
store = JsonStore("data.json", default={"tasks": []})
store.set("tasks", [{"id": 1, "title": "Hello"}])
tasks = store.get("tasks")
store.delete("tasks")
store.clear()
.tmp
then
os.replace()
threading.Lock
on all mutating operations
ValueError
with a descriptive message on malformed JSON
UploadPipeline
— Server-Side File Upload Handler
Validates MIME type and file size, sanitises filenames, and saves uploads to a configurable directory.
from dars.all import *
pipeline = UploadPipeline(
upload_dir="uploads",
allowed_types=["image/png", "image/jpeg"],
max_size_bytes=10 * 1024 * 1024,
)
pipeline.create_endpoint(app, path="/api/upload")
sanitize_filename()
removes path traversal (
../
,
./
) and unsafe characters
SecurityHeadersMiddleware
— HTTP Security Headers
Injects five security headers into every response without overwriting existing ones.
from dars.all import *
ssr.use_security_headers()
# or manually:
app.add_middleware(SecurityHeadersMiddleware, csp="default-src 'self'", hsts=True)
Default headers:
X-Content-Type-Options
,
X-Frame-Options
,
X-XSS-Protection
,
Referrer-Policy
,
Permissions-Policy
. Optional
Content-Security-Policy
and
Strict-Transport-Security
.
DarsEnv
—
.env
File Support
DarsEnv
now loads
.env
files automatically at config load time.
from dars.env import DarsEnv
DarsEnv.load() # loads .env silently if present
api_key = DarsEnv.get("API_KEY") # os.environ.get with default
secret = DarsEnv.require("SECRET") # raises KeyError if missing
os.environ
keys
#
comments
load_config()
before reading
dars.config.json
SSRApp
— Production-Ready SSR Backend Helper
The
SSRApp
class (used in
backend/api.py
) now exposes clean methods for CORS, security headers, file uploads, and custom routes:
ssr = SSRApp(dars_app, prefix="/api/ssr")
ssr.use_cors(origins=["http://localhost:4000"], credentials=True)
ssr.use_security_headers()
ssr.use_upload(upload_dir="uploads", allowed_types=["image/png"], max_size_bytes=10_485_760)
# In production, serve the exported frontend files with built-in SPA 404 fallback:
if not DarsEnv.is_dev():
ssr.use_spa_fallback()
app = ssr.fastapi_app
fullstack
Renaming
dars preview
Revamp
: The
preview
command UI has been completely redesigned with a beautiful
rich
terminal UI, detailing project mode, target directories, and the backend server. It now interactively asks to start the server and automatically opens your browser. Graceful
taskkill
shutdown has been added to prevent orphaned background processes.
--type fullstack
: The
dars init --type ssr
command has been renamed to
dars init --type fullstack
to better reflect the complete SPA + SSR + API nature of the scaffolded backend.
Hybrid Stability, Anti-Flash System & CLI UX Overhaul
pip install --upgrade dars-framework
Resolved critical race conditions and hydration bugs that affected projects using a mix of Server-Side Rendering and Single Page Application routing.
window.__DARS_HYDRATED_PATH__
to ensure the router only hydrates the page if the current path matches the pre-rendered content. This prevents "blank screens" or incorrect content display when deep-linking into sub-routes.
registerConfig
call is made during the boot sequence, preventing state corruption in SPA shells.
dars-ready
)
To provide a premium feel, we've implemented a robust visibility management system that hides the page during the delicate hydration phase to prevent "Flash of Unstyled Content" (FOUC).
dars-ready
Attribute
: The framework now manages a
dars-ready
attribute on the root element. Visibility is automatically triggered once the runtime is ready.
dars preview
v2
The
dars preview
command has been significantly improved to be more intuitive and configuration-aware.
dars preview
without any arguments. It will automatically detect your
outdir
from
dars.config.json
(falling back to
./dist
).
--port
/
-p
support to specify the server port. The command also respects the
"port"
setting in your project's configuration file.
dars dev
command now more reliably propagates port settings to the underlying application process.
dars generate
)
Accelerate your development workflow with the new code generation commands. Scaffold components and pages instantly with automatic project integration.
dars generate component <name>
: Quickly create new reusable FunctionComponents.
dars generate page <name>
: Scaffold new pages (
Static, SPA or SSR
) with pre-filled templates.
/backend
infrastructure and update
dars.config.json
for you.
-y
flag (e.g.,
dars g page Contact -y
) to automatically add imports and register the new page in your
main.py
file, linking it to your application instantly.
Modular Animation Engine, Scroll Triggers & Zero-Jitter Handoffs
pip install --upgrade dars-framework
anim.js
)
The core animation system has been completely decoupled from the monolithic
dars.min.js
runtime into a dedicated
anim.js
module. This provides a cleaner architecture, better caching, and lays the groundwork for future advanced animation plugins.
Added a suite of new Web Animations API-based triggers that run autonomously on the client. These features use
IntersectionObserver
to trigger animations the exact moment an element enters the viewport.
animateOnView
: Trigger CSS keyframe animations when an element scrolls into view.
staggerOnView
: Sequence animations across multiple elements with a defined delay, triggered when the first element becomes visible.
scrollProgress
: Tie CSS properties directly to the scroll percentage of the page (e.g. fading out the hero section on scroll).
runOnView
/
classOnView
: Execute JS callbacks or toggle classes based on viewport intersection.
Implemented a bulletproof handoff mechanism between the Web Animations API (WAAPI) and native CSS transitions.
The Problem:
Historically, using
fill: forwards
in WAAPI locks CSS properties, breaking
:hover
states. If you cancel the animation and apply inline styles, it triggers a "phantom" CSS transition, causing visual jitter (especially with matrix interpolation on 3D transforms).
The Solution:
The new engine uses a specialized
_setStylesWithoutTransition
helper that:
transition: none !important
.
void el.offsetHeight
) to commit the changes silently.
Result:
Flawless CSS
:hover
effects immediately after an entrance animation, with zero jitter or layout thrashing.
Continuing our commitment to security, the new animation triggers are built entirely without
eval()
or
new Function()
. Python payloads compile to strict JavaScript object references and IIFEs, completely mitigating dynamic string execution vulnerabilities.
sequence()
were being compiled as raw JavaScript strings, resulting in
<...dScript object...>
memory references output to the DOM. They are now correctly serialized into pure DAP JSON payloads.
dap.js
:
dispatch()
,
sequence()
, and
delay()
commands inside the browser runtime are now fully
async
/
await
capable. This resolves an issue where delayed actions within a sequence were firing synchronously.
display: block
Layout Shift Fix
: Removed hardcoded
display: block
from core functions like
fadeIn
,
slideIn
,
scaleIn
,
dom_show
, and
dom_toggle
. They now clear the
display
style (i.e.
display: ""
), allowing inline-block elements (like Buttons) to retain their native layout without unwanted line breaks.
Relative Import Paths for Static Deployments
pip install --upgrade dars-framework
All JavaScript imports inside the framework's runtime resources (
dars/exporters/web/resources/
) have been updated to use
relative paths
(e.g.,
./dap.js
) instead of absolute paths (e.g.,
lib/dap.js
).
The Problem:
When deploying a Dars application to GitHub Pages (or any host that serves from a subpath like
https://user.github.io/my-project/
), the browser resolved non-relative paths against the
host's base URL
instead of the application's directory:
❌ https://user.github.io/lib/dap.js → 404
✅ https://user.github.io/my-project/lib/dap.js → OK
This caused
dap.js
,
dompurify.js
, and other runtime scripts to fail loading with
404
errors on any subpath-based deployment.
The Fix:
All
import
and
<script src="...">
references within the exported runtime files now use
./
relative paths, ensuring correct resolution regardless of the hosting base URL:
- import { ActionProtocol } from "lib/dap.js";
+ import { ActionProtocol } from "./dap.js";
Impact:
dars/exporters/web/resources/dars.min.js
— Relative import paths
dars/exporters/web/resources/dap.js
— Relative import paths
./
prefix for local imports
Configurable Dev Port, Local DOMPurify & Runtime Resource Optimization
pip install --upgrade dars-framework
You can now customize the port used by the
dars dev
preview server directly in your project configuration or via CLI:
dars.config.json
: Added a new
"port"
field (default:
8000
).
CLI Override
: Use
--port
or
-P
to override the configuration at runtime.
dars dev --port 4000
Automatic Propagation : The CLI now correctly propagates the port setting to the underlying application process.
To improve load times and reliability, especially in offline or restricted environments, we have moved core runtime dependencies from CDNs to local assets:
dompurify.js
.
resources/
directory in the web exporter and copied to the
/lib
directory of the final export.
The minification pipeline has been extended to ensure all runtime assets are as lean as possible:
rjsmin
Integration
: All JavaScript resources, including
dompurify.js
and the Dars runtime, are now minified using the Python-native
rjsmin
during the export process.
js_lib.py
has been retired in favor of a file-based resource system. This allows for better code splitting and easier maintenance of the Dars runtime components.
rTimeCompile
logic to reliably detect the project root and configuration, ensuring that custom settings are respected even when starting the app from different working directories.
Documentation Corrections & Complete App Class Docstring
pip install --upgrade dars-framework
Fixed documentation inconsistencies across all component modules:
class_name
Documentation
: Corrected to reflect that it contains
regular CSS class names
(not utility classes) for standard HTML class attributes.
style
Documentation
: Updated to clarify that it contains
CSS utility classes
(Tailwind-like syntax) for convenience styling.
Added a comprehensive docstring to the
App
class with:
Premium Utility Styles, DAP Reactivity Fixes & Documentation Overhaul
pip install --upgrade dars-framework
We've significantly expanded the utility-first styling system to bring it closer to a better developer experience, adding many features:
bg-gradient-to-{dir}
,
from-{color}
,
via-{color}
, and
to-{color}
. Internally uses a modern CSS variable architecture (
--tw-gradient-stops
).
ring
,
ring-{n}
,
ring-{color}
,
ring-opacity-{n}
, and
ring-offset-{n}
.
text-
prefix is now intelligent. It automatically switches between
font-size
and
color
based on the provided value (e.g.,
text-xl
vs
text-indigo-500
).
divide-x
and
divide-y
to easily add borders between child elements.
accent-{color}
,
caret-{color}
,
line-clamp-{n}
, and expanded support for specific border sides (e.g.,
border-t-2
,
border-x-4
).
shadow-{color}
.
updateVRef
Reactivity
: Resolved a critical issue where components using
ValueRef
(via
setVRef
) were not consistently re-rendering when updated through Dars Action Protocol (DAP) scripts.
Secure Action Protocol (DAP) & Zero-Eval Runtime Hardening
pip install --upgrade dars-framework
new Function()
Building on the milestone of v1.8.9, v1.9.0 achieves a 100% "Zero-Eval" runtime for all dynamic event execution.
event
and
element
(the
this
context) without using
eval()
or
new Function()
.
Introduced a centralized
Command Registry
in the browser runtime (
dars.min.js
). This moves the framework from "sending code strings" to "sending structured commands".
dispatch(action, context)
function ensures that actions are processed as structured data objects
{op, args}
, eliminating the risk of arbitrary code execution.
The browser runtime now includes a comprehensive library of registered commands, covering almost all utility functions in
utils_ds.py
:
alert
,
confirm
(with DAP-driven
on_ok
/
on_cancel
callbacks), and
log
.
navigate
,
reload
,
history_back
,
history_forward
.
dom_show
,
dom_hide
,
dom_toggle
,
dom_focus
,
dom_blur
,
dom_reflow
.
dom_set_text
,
dom_set_html
(sanitized via DOMPurify),
dom_set_style
,
dom_set_attr
,
class_add
,
class_remove
,
class_toggle
.
storage_set
,
storage_remove
,
storage_clear
, and
storage_get
(with direct state-update mapping).
fetch
support with success and error handlers.
vref_update
and
vref_get
.
Fixed several long-standing issues with Prism.js integration and global asset management:
Native String Concatenation & Math Fixes
pip install --upgrade dars-framework
Fixed a major regression in
MathExpression
where using the
+
operator aggressively coerced all operands into
parseFloat()
. This behavior broke string concatenation, resulting in
0
or
NaN
when attempting to combine strings and
ValueRef
values.
(left + right)
.
.float()
or
.int()
on the
ValueRef
(e.g.
V(".num1").float() + V(".num2").float()
).
State V2 Reactivity Hardening & FunctionComponent Fixes
pip install --upgrade dars-framework
The Dars runtime state manager has been decoupled from strict physical DOM bindings, enabling robust reactivity for headless and virtual components:
change()
handler now persists new state values to the internal
__reactiveRegistry
before attempting to locate a DOM element. This ensures that virtual states update reliably even when no matching element ID exists.
increment
and
decrement
methods would always evaluate from
0
when bound to headless states. The
startLoop()
runtime function now checks the state registry if a target element is not found, allowing seamless mathematical operations in the background.
Resolved multiple issues affecting
useDynamic
and reactive bindings inside
@FunctionComponent
trees:
app.add_page
) generation. This ensures that all
useDynamic
bindings nested inside FunctionComponents are successfully collected and exported into the reactive Javascript bundle.
_generate_reactive_bindings_js
) that caused silent failures (missing closing braces) when exporting a project containing exclusively FunctionComponent bindings without any standard built-in bindings.
Ultimate Security & Reactivity Hardening: Removal of Eval/New Function & Native JS Compilation
[!IMPORTANT] SECURITY ADVISORY : v1.8.9 achieves a major milestone by removing
eval()andnew Function()from the core client-side runtime (dars.min.js). However, the web framework (as seen in certain SSR/Fullstack exports) is not yet 100% free ofnew Function()and_executeExternalScriptfor specific dynamic execution flows. This will be fully addressed in the upcoming Dars Flight Protocol (DFP) release.
pip install --upgrade dars-framework
We have completely overhauled how Dars executes dynamic code in the browser.
eval()
and
new Function()
have been eliminated from the runtime (
dars.min.js
).
await
Support
: You can now use
await
directly within any event handler or transformation script.
The
dScript
compiler is now a core framework utility, moving complex resolution logic from the browser to the build/export phase.
V()
,
MathExpression
, and
BooleanExpression
are now compiled into clean, native JavaScript code strings.
compile_val
logic to ensure that complex structures (lists, dicts) containing reactive objects are correctly translated into executable JS literals, resolving previous "RawJS is not serializable" warnings.
Fixed several long-standing issues with the reactivity pipeline:
NaN
errors in calculators. The compiler now correctly handles the
+
operator, favoring native JS concatenation for strings and addition for numbers.
url()
and
transform()
helpers to use a structured concatenation model, eliminating
SyntaxError: Unexpected identifier
issues caused by nested backticks in template literals.
The initial rendering engine (
_elFromVNode
) is now asynchronous-aware:
compile_val
now recursively handles nested collections, ensuring all parts of a complex prop are correctly compiled.
ValueRef
string representation to integrate seamlessly with the new native compiler.
Critical Security Update: Dars Server Protocol (DSP) & SSR Hydration Fix
[!CAUTION] SECURITY WARNING : Versions <= v1.8.7 are considered deprecated and NOT recommended for production use. v1.8.8 addresses critical security surfaces by temporarily removing experimental Server Components. Upgrading is mandatory.
pip install --upgrade dars-framework
Introduced a new unified protocol for transmitting VDOM snapshots, component states, and reactive bindings from the server to the client. This ensures that SSR-rendered pages are hydrated with full parity to client-side renders.
Resolved critical issues where reactive bindings (
useDynamic
) and
VRef
bindings were not correctly executed after initial server rendering.
As part of security hardening, the experimental "Dars Server Components" feature (using
use_server=True
) has been removed from this version.
Dars Server Components & FastAPI Integration
pip install --upgrade dars-framework
v1.8.7 introduces first-class Server Components , allowing individual components to be fully rendered on the server while maintaining client-side interactivity.
use_server=True
to any component inheriting from the base
Component
class.
Button("Server Rendered Button", use_server=True, on_click=...)
The new version of
create_dars_app
plugin provides tight integration with FastAPI, making it easier than ever to build full-stack SSR applications.
Scaffold a complete SSR project and then add Server Components support in seconds:
dars init my-app --type ssr
This template sets up:
create_dars_app
.
Environment Management & File Upload Component
pip install --upgrade dars-framework
New
DarsEnv
class provides a standard way to check the current environment mode:
DarsEnv.dev
: returns
True
during development (
dars dev
), and
False
during production builds (
dars build
/
dars export
).
This allows you to write conditional logic in your components:
from dars.env import DarsEnv
Link(target="/docs" if DarsEnv.dev else "https://example.domain.com/env", text="Docs")
A new
FileUpload
component is now available in
dars.components.advanced
:
<input type="file">
with a custom, styleable interface.
accept
,
multiple
, and hidden input handling.
on_change
events.
from dars.components.advanced import FileUpload
FileUpload(
label="Upload Document",
accept=".pdf",
on_change=log("File uploaded")
)
Outlet improvements + SSR lazy-load placeholders + SPA router hardening
pip install --upgrade dars-framework
outlet_id
Nested routing now supports targeting a specific outlet in a parent layout:
Outlet(outlet_id="main" | "sidebar" | ...)
app.add_page(..., outlet_id="...")
outletId
per route so the client router can mount the child route into the correct outlet.
Outlet(placeholder=...)
Outlet
can render an optional placeholder while the child route region is empty (e.g. SSR lazy-load or SPA navigation).
If
placeholder
is not provided, the outlet remains empty.
New API:
app.set_loading_state(loadingComp, onErrorComp)
Exporters and the SSR backend render these as static HTML placeholders and expose them to the SPA router. This keeps state/events safe and avoids breaking hydration.
The SPA router now treats paths with trailing slashes as equivalent:
/dashboard
and
/dashboard/
match the same route
This prevents incorrect 404 redirects when a user navigates to a valid route with a trailing slash.
Python-native minification + major Utility Styles upgrade
pip install --upgrade dars-framework
v1.8.4 upgrades the default minification pipeline to use real, battle-tested Python minifiers:
rjsmin
rcssmin
This makes builds and exports work reliably in pure-Python environments.
viteMinify
mode preserved
If you enable
viteMinify: true
in
dars.config.json
, Dars can still use Vite/esbuild
optionally
when available.
When tools are not installed, Dars falls back to the Python minifiers automatically.
dars.min.js
The embedded runtime bundle (
dars.min.js
) is now treated as a special case:
export
output).
rjsmin
, regardless of
viteMinify
.
prop-[value]
)
The utility system now supports Tailwind-like arbitrary properties :
style="background-image-[linear-gradient(90deg,_rgba(0,0,0,.35),_#00ffcc)]"
style="padding-[calc(1rem_+_2vw)]"
style="color-[var(--brand-color)]"
style="--brand-color-[#00ffcc]"
Also includes background gradient support via
bg-[linear-gradient(...)]
(maps to
background-image
).
filter
/
backdrop-filter
/
transform
Multiple filter/transform utilities now compose instead of overwriting:
style="filter-[blur(6px)] filter-[brightness(120%)]"
text-[#hex]
/
text-[rgba(...)]
being interpreted as
font-size
instead of
color
.
border-top-[...]
incorrectly becoming
border-color: top-[...]
.
The LandingPage navbar now uses
style="..."
utility strings instead of large inline style dicts,
improving consistency and providing a real-world example of the upgraded styling system.
Critical Build Fix
pip install --upgrade dars-framework
Unexpected token 'export'
In some environments, the JS minification pipeline (Vite/esbuild) could emit ESM output ending with
export default ...
inside
app.js
. Since exported pages load
app.js
as a classic script, browsers would fail to parse it with:
Uncaught SyntaxError: Unexpected token 'export'
v1.8.3 fixes this by forcing the minifier output format to
IIFE
for browser scripts, preventing ESM
export
statements from being generated during build.
Bug Fixes & Utility System Improvements
pip install --upgrade dars-framework
The
setTimeout
utility function in
utils_ds.py
has been updated to properly return a Promise, enabling correct chaining with
.then()
operations. This fixes JavaScript syntax errors that occurred when using sequential animations or delayed operations.
Improved the animation chaining system to handle missing DOM elements gracefully, preventing runtime errors when referenced elements don't exist in the component tree.
Updated CSS media queries for better handling of text overflow on small screens, ensuring content remains readable across all device sizes without cutting off important information.
Style System Optimization & SSR-Aware Registry
pip install --upgrade dars-framework
v1.8.1 introduces the first phase of a new style optimization system focused on reducing inline CSS while keeping full compatibility with Dars reactivity and dynamic operations.
style={...}
or Tailwind-like strings in
style="..."
are now:
.dars-s-<hash>
.
style
blocks.
The original
class_name
remains fully respected and is appended
after
the generated
dars-s-*
class, so user classes (and external CSS frameworks) retain override power.
The optimized styles are accumulated into a central registry and injected in the
<head>
as:
<link rel="stylesheet" href="runtime_css.css" />
<style id="dars-style-registry">
/* .dars-s-* rules here */
</style>
<link rel="stylesheet" href="styles.css" />
Order is carefully chosen so that:
runtime_css.css
provides the base UI tokens and default component styling.
#dars-style-registry
contains all extracted
.dars-s-*
rules (including those coming from
hover_style
/
active_style
phases on future releases).
styles.css
(hover/active styles +
app.add_global_style()
+ user CSS files) comes last, ensuring user styles can override the framework-generated ones.
The new style pipeline now runs consistently across all export modes:
Single page & multipage :
HTMLCSSJSExporter.export
collects static styles from the component tree before rendering.
#dars-style-registry
block in the head.
SPA export (
_export_spa
)
:
html
.
__DARS_SPA_CONFIG__
now includes a
styles
field containing the CSS for that route.
_injectStyles(routeName, styles)
so that SSR/SPA navigations share the same optimized classes.
SSR backend (
dars.backend.ssr
)
:
SSRRenderer.render_route
uses a
deep copy
of each route's root tree to avoid mutating the original components when collecting styles.
.dars-s-*
classes, and the resulting CSS is injected into the SSR HTML head using
#dars-style-registry
.
/api/ssr/<route>
) now returns a
styles
field alongside
html
,
vdom
,
events
, etc., so the SPA router can inject the same registry CSS on client-side navigations.
The embedded JS runtime (
dars/js_lib.py
→
DARS_MIN_JS
) has been updated to be style-optimization aware without breaking existing behavior:
Dars.change({ id, dynamic: true, style: {...} })
and state rules that manipulate
attrs.style
continue to write directly to
el.style[...]
.
style
attribute, so elements whose base styles were moved to
.dars-s-*
remain fully reactive.
attrs.class
preserve internal
dars-*
classes (including
.dars-s-*
) and only replace user classes, ensuring the optimization never gets wiped by state changes.
/api/ssr/...
and now respects the
styles
payload from the backend.
_injectStyles(routeName, styles)
on every SSR navigation so that optimized classes stay active even after client-side route changes.
These changes are designed to be
backwards compatible
for projects that used only
style
/
class_name
and dynamic state. The main effect you will notice in v1.8.1 is smaller, cleaner HTML with fewer repeated inline styles, especially for static or Tailwind-like styling.
Advanced Multimedia Components & Electron Security Baseline
pip install --upgrade dars-framework
v1.8.0 introduces two new first-class components in the basic library:
Video
: wrapper around
<video>
Audio
: wrapper around
<audio>
Both are fully reactive and integrate with the existing hooks system:
State
+
useDynamic
for:
src
autoplay
muted
loop
controls
plays_inline
(Video)
useValue
for non-reactive initial values.
VRef
(setVRef/useVRef) can target
src
and other props when needed.
Example:
from dars.all import *
from dars.hooks.value_helpers import V
media_state = State(
"media",
current_video="/media/intro.mp4",
current_audio="/media/theme1.mp3",
autoplay_video=False,
muted_video=True,
loop_audio=True,
)
@route("/", index=True)
def index():
return Page(
Container(
Video(
src=useDynamic("media.current_video"),
poster="/media/poster.jpg",
width="720",
controls=True,
autoplay=useDynamic("media.autoplay_video"),
muted=useDynamic("media.muted_video"),
preload="metadata",
),
Button(
"Toggle Mute",
on_click=media_state.muted_video.set(
(V("media.muted_video").bool() == True).then(False, True)
),
),
Button(
"Toggle Autoplay",
on_click=media_state.autoplay_video.set(
(V("media.autoplay_video").bool() == True).then(False, True)
),
),
Text("Audio actual:"),
Text(useDynamic("media.current_audio")),
Audio(
src=useDynamic("media.current_audio"),
controls=True,
loop=useDynamic("media.loop_audio"),
preload="auto",
),
)
)
The web exporter has been extended so that
useDynamic
bindings on boolean attributes behave correctly:
State
value changes, the runtime:
autoplay
,
muted
,
loop
,
controls
,
playsinline
.
el.autoplay
,
el.muted
, etc.).
useDynamic
) no longer count as _truthy_ defaults:
controls=True
,
plays_inline=True
remain active by default.
autoplay
,
loop
,
muted
are off by default unless explicitly set by state or by a literal
True
.
This ensures that Video/Audio behave predictably both on initial render and during reactive updates.
The HTML/CSS/JS exporter now supports a convention-based media folder :
media/
directory, Dars will:
media/
into the export
output_path
.
src="/media/..."
used in
Image
,
Video
or
Audio
will point to real files in the exported build.
This makes it straightforward to ship videos, audio tracks and posters alongside your static export.
To keep desktop builds secure and reproducible, v1.8.0 introduces an Electron security baseline :
39.2.6
:
dars/templates/desktop/template/backend/package.json
init
desktop scaffolds and
init --update
flows.
dars doctor
gains version-awareness:
MIN_SAFE_ELECTRON = "39.2.6"
.
dars doctor --all --yes
, Dars will install/update Electron globally via Bun as
electron@39.2.6
and
electron-builder@latest
.
dars dev
for desktop projects now warns if your installed Electron is below the baseline and suggests:
dars doctor --all --yes
This keeps both templates and global tooling aligned with a reviewed Electron version.
Several quality-of-life fixes improve desktop (Electron) development:
App.rTimeCompile
desktop branch:
dars core/js_bridge
:
electron_dev_spawn
now sets
ELECTRON_DISABLE_SECURITY_WARNINGS=true
for dev runs.
Result: smoother desktop dev cycle with clear logs and no accidental web preview server when working on Electron apps.
LandingPage/documentation/markdown/components.md
now documents:
Video
and
Audio
components.
State
,
useDynamic
,
V()
and VRefs.
media/
folder convention for assets.