Skip to content

Releases: pyxle-dev/pyxle

v0.4.4

12 Jun 09:33
cefa504

Choose a tag to compare

  • Fix: cross-page hash links scroll to their anchor. Client-side navigation to /page#section changed the page but stayed pinned to the top (only the URL carried the hash); a full load scrolled correctly. The router now jumps to the top and then scrolls to the anchor as soon as the next page's DOM commits, matching native behaviour — including for unknown anchors, which stay at the top.

Full changelog: https://pyxle.dev/docs/changelog

v0.4.3

12 Jun 08:16
fd17c46

Choose a tag to compare

  • Security: hardened HEAD sanitisation (XSS). Dynamic HEAD values/callables that interpolate loader data into raw head strings — the documented dynamic-meta-tags recipe — could previously inject markup because attribute values weren't escaped (a " in a <meta content> broke out of the attribute). Head elements are now parsed and rebuilt from a strict tag allowlist with every attribute value HTML-escaped, on* handlers dropped, javascript:/vbscript:/data: URLs neutralised, <meta http-equiv="refresh"> and non-head tags rejected, and any markup injected after a breakout discarded. Inline <script>/<style> head content remains supported as trusted author code. Self-closing head tags now serialise canonically as />.
  • Security: CSRF exempt paths match on segment boundaries. csrf.exemptPaths previously used a bare string prefix, so exempting /api/webhooks also exempted an adjacently-named sibling like /api/webhooks-admin. Exemptions now match a path exactly or at a / boundary.
  • Security: oversized no-JS form POSTs fail loud. A form-encoded POST whose body exceeded the CSRF buffer cap (1 MB) was silently truncated and replayed to your handler. Such requests now return 413 and ask for the token via the X-CSRF-Token header instead of corrupting the payload.
  • Fix: <Script>/<Image> boolean attributes written as strings. defer="false", async="no", priority="0", lazy="false" and similar quoted-falsy forms were coerced to True (the opposite of the source). They now coerce correctly.
  • Fix: custom csrf.cookieName / csrf.headerName now reach the client runtime. Configuring either name made every action POST fail with 403 — the middleware set and validated the cookie under the configured name while useAction/<Form> kept reading a literal pyxle-csrf cookie and sending a literal x-csrf-token header. Non-default names are now injected into the document shell as window.__PYXLE_CSRF_COOKIE__ / window.__PYXLE_CSRF_HEADER__ and the client runtime resolves them with the defaults as fallback, in both dev and production builds.
  • Fix: no double loader run on hover-then-click navigation. When a <Link> was prefetched on hover/viewport and then clicked, the click raced a second request and ran the page's @server loader twice. The click now reuses the in-flight prefetch, and a navigation-sequence token discards a superseded prefetch so a slow one can't render a stale page over a newer click.
  • Multi-core serving: pyxle serve --workers N. Production serving can now run N independent server processes on one port (each with its own SSR pool and event loop) — throughput scales with cores, no load balancer or shared state to configure. See Deployment → Multi-core (worker processes).
  • Sync API endpoints. A plain def endpoint(request) in pages/api/ now runs in Starlette's threadpool instead of failing at request time — blocking database drivers and sync SDKs no longer require manual asyncio.to_thread wrapping. Sync get/post/… methods on HTTPEndpoint classes are threadpooled too, and HTTPEndpoint classes dispatch correctly alongside route hooks. See API Routes → Sync endpoints and blocking calls.
  • In-memory static asset cache. When serving a production build, small static files (≤1 MB each, 32 MB total per process) are loaded into memory at startup and served without filesystem I/O or threadpool hops — several-fold higher static throughput. Conditional requests (ETag/If-Modified-Since → 304) and cache headers behave exactly as before; larger files keep streaming from disk.

Full changelog: https://pyxle.dev/docs/changelog

v0.4.2

09 Jun 09:15
d2a3b85

Choose a tag to compare

0.4.2

  • Live dev-server reconciliation. Editing a .pyxl in pyxle dev now applies route-shape changes without a restart — rename/add/remove a @server loader or @action, add or delete a page, wrap a page in a layout, change the head. Editing pyxle.config.json prints a clear 'restart to apply' warning instead of being silently ignored.
  • pyxle check works on a clean install. The JSX checker's parser deps are bundled into a self-contained extractor (via pyxle-langkit), so pyxle check runs after pip install 'pyxle-framework[langkit]' with no extra npm setup.
  • Locale-independent SSR. The Python↔Node SSR transport pins UTF-8, so emoji / non-BMP characters in components or loader data no longer crash rendering under a non-UTF-8 locale (LANG=C).
  • Smoother first run. pyxle init generates a gitignored .env.local with a random dev PYXLE_SECRET_KEY; the scaffold requirements.txt declares pyxle-framework itself; pyxle install warns on PEP 668 with venv guidance.

Upgrade: pip install --upgrade pyxle-framework

v0.4.1

08 Jun 10:24
2fe61c8

Choose a tag to compare

0.4.1

  • No more double loader run on first load. The page you land on is seeded into the client navigation cache from the server render, so the active nav link's prefetch resolves from cache instead of re-fetching — the loader runs once, not twice. Back/forward navigation to the page is instant for the same reason.
  • Per-route navigation-cache TTL. A route's cache TTL now also governs how long its prefetched/seeded data stays fresh client-side. Routes without a cache entry default to 2 minutes (raised from 30s), tunable via the new navigation.defaultPrefetchTtl config option.

Upgrade: pip install --upgrade pyxle-framework

v0.4.0 — Edge caching, layout loaders, hardened errors

08 Jun 06:10
964b722

Choose a tag to compare

What's new in 0.4.0

  • Edge caching. Declare cacheable routes in pyxle.config.json::cache and pages are served Cache-Control: public, s-maxage=N (plus stale-while-revalidate) so a CDN or reverse proxy can absorb traffic instead of your origin. The per-user CSRF cookie is automatically omitted from cacheable responses.
  • Layout & template loaders. A layout.pyxl or template.pyxl can declare its own @server loader; its result lands on the component's data prop, just like a page — so shared UI (nav bars, the signed-in user, the framework version) loads once per request without repeating the loader in every page.
  • Hardened production errors. SSR render failures — including the SPA-navigation JSON path — are sanitized in the response (no exception type, message, or path leaks to the client) while the full error is written to the server log for operators.
  • Faster static serving. The static-asset middleware indexes public/client paths up front, skipping a per-request filesystem stat + exception on every dynamic request.

Upgrade: pip install --upgrade pyxle-framework

Full notes: see the Changelog.

v0.3.0 — Plugin system + framework polish

03 May 16:54
0456ec8

Choose a tag to compare

Highlights

First-class plugin system

Compose apps via pyxle.config.json::plugins — Django-style INSTALLED_APPS. Plugins register services through a PluginContext, hook into the ASGI lifespan, and contribute middleware.

{
  "plugins": [
    "pyxle-db",
    {
      "name": "pyxle-auth",
      "settings": { "cookieDomain": ".example.app" }
    }
  ]
}

Resolve services Django-style:

from pyxle_auth import get_auth_service

@action
async def sign_in(request):
    auth = get_auth_service()
    ...

See: Plugins guide, Plugins API reference.

First-party plugins (separate packages)

  • pyxle-db — SQLite-first database with migrations, WAL, connection pooling.
  • pyxle-auth — Email+password sessions, argon2id, sliding expiration, per-IP/per-email rate limits.

Framework fixes (surfaced by Pyxle Cloud work)

  • WebSocket endpointspages/api/*.py can export async def websocket(ws) for live updates and chat.
  • Client navigation cache with TTL + invalidation — loader payloads cached 30s; call invalidate(url) on the client or return invalidate_routes(response, ...) from an @action to refresh list views after mutations.
  • ActionError is auto-imported in any .pyxl with an @action.
  • <Head> coerces multi-part <title> children into a single string.
  • SSR worker pins LANG=en-US.UTF-8 by default (override via PYXLE_SSR_LOCALE) — fixes hydration mismatches from toLocaleString().
  • Vite resolver prefers pinned versionspyxle build runs npm install before falling back to npx --yes vite.

Install

pip install --upgrade pyxle-framework

v0.2.4

21 Apr 06:52
81e73ee

Choose a tag to compare

Pyxle Kit (the new pyxle/client reference demo) exercises every
client component in one place, so this release implements or
hardens the ones that were still stubs or racy on hydration.

Highlights

SSR pathname plumbing

The worker pool now propagates the request path into the Node SSR
runtime, which sets globalThis.__PYXLE_CURRENT_PATHNAME__ for the
duration of the render. This eliminates:

  • hydration mismatches on usePathname() (e.g. active-link highlighting)
  • <Form> / useAction action-URL mismatches between SSR and client

Client components

  • <Head> — client useEffect rewrite. Adopts SSR-rendered head
    nodes by key attribute (no duplicates on hydration), restores the
    previous <title> on unmount.
  • <Script> — full implementation, not a stub. Strategies
    afterInteractive (default), lazyOnload, and beforeInteractive;
    module-level promise dedup; cooperation with bootstrap-preloaded
    scripts; onLoad / onError; inline children via script.textContent.
  • <Image>placeholder=\"blur\" with blurDataURL or
    placeholderColor, fallbackSrc for graceful recovery on broken
    primary src, onLoad / onError with cache-hit detection, and a
    data-pyxle-image-state attribute for CSS-only fade transitions.
    Also fixes a hydration race: a terminal-error on an SSR-rendered
    src (e.g. a 404 avatar) is now recovered via the post-mount
    complete && naturalWidth === 0 check, so fallbackSrc kicks in
    even when the native error event fired before React hydrated.
  • usePathname() — reads the SSR pathname global so the first
    client render matches SSR, then subscribes to framework navigation
    events for reactive updates.

Documentation

Resolved the 8-item docs/framework parity audit:

  • Documented usePathname() in reference/client-api.md
  • Removed stale dev:css / build:css references across quick-start,
    project-structure, faq, deployment, and the pyxle init hint — the
    scaffold's actual flow is PostCSS-via-Vite, auto-run by pyxle dev
  • Updated the quick-start landing-page description and the
    project-structure loader example to match the scaffold's real output
  • Added source_absolute_path, content_hash, and head_elements to
    the RouteContext table in the middleware guide
  • Clarified the pyxle build chain description in the deployment guide

Tests

1141 passing (+14 new).

v0.2.3

15 Apr 18:11
b72f1bc

Choose a tag to compare

v0.2.3 — Release Notes

🐛 Bug Fix

SSR props no longer break JSON.parse on pages containing <!-- or -->

The inline-JSON escaper (pyxle.ssr._escape.escape_inline_json) previously rewrote HTML-comment sequences with backslash escapes that are not valid JSON:

  • <!--<\!--\! is not a legal JSON escape
  • -->--\>\> is not a legal JSON escape

Any page whose props contained a literal --> (very common in docs that quote shell output, code examples, or arrow markers) would fail at hydration with:

[Pyxle] Failed to parse initial props
SyntaxError: Bad escaped character in JSON

The fix swaps the problematic replacements for Unicode escapes — which are valid JSON — and still neutralise the HTML-tokenizer sequences:

  • <!--\u003c!--
  • -->--\u003e

🧪 Tests

  • Added a parametrised test_output_is_valid_json_after_escape that round-trips escaped output through json.loads to catch this class of regression.
  • 1127 total tests, all passing.

📦 Install

pip install --upgrade pyxle-framework==0.2.3

🙏 Migration

None. This is a pure bug fix — existing apps just need to redeploy with 0.2.3 to get correct behaviour.

v0.2.2 — Release Notes

15 Apr 14:40
89a9864

Choose a tag to compare

v0.2.1

13 Apr 04:59
1f09d4d

Choose a tag to compare

Full Changelog: v0.2.0...v0.2.1