Releases: Pyra-js/Pyra
Releases · Pyra-js/Pyra
v0.30.10
Fixed
- All 5 example apps were unrunnable - every
pyra.config.tsinexamples/was missingadapter: createReactAdapter(), causingpyra dev/build/startto fail immediately with a missing-adapter error; all configs now import and set the adapter correctly - Example
package.jsonfiles missing@pyra-js/adapter-react- all 5 example apps now declare@pyra-js/adapter-react: workspace:*as a dependency so the adapter resolves correctly when running from the monorepo - HMR error overlay - compilation and route-scan errors are now broadcast to the browser as
{ type: "error" }WebSocket messages; an in-browser overlay (dark card, error badge, file pill, stack trace, ESC to dismiss) replaces silent console-only failures; overlay auto-dismisses on the next successful update or reload - Parallel compilation guard - concurrent requests to the same uncached route no longer each spawn a duplicate esbuild process; a
pendingBundlesmap inbundler.tsand apendingServerCompilesmap indev-compiler.tsdeduplicate in-flight compiles so late arrivals await the same Promise - Event loop blocking in static file handler -
dev-static.tsservePublicFile()switched fromfs.readFileSynctofs.promises.readFile;dev-server.tsCSS and static file handlers likewise converted tofs.promises.statandfs.promises.readFile, unblocking concurrent requests during large file reads
Changed
<Link>used for all internal navigation in examples - all layout nav links and intra-app page links acrosspyra-blog,routing-showcase,ssg-cache, andmiddleware-authnow use<Link>from@pyra-js/adapter-reactinstead of raw<a href>, enabling client-side SPA navigation without full page reloads- Dead artifacts removed from
examples/- removedKubo/(orphaned dist artifact),basic-ts/(pre-routing SPA era),test-config/(internal test fixture),example.sh(empty file), and 6 stalepyra.config.*.tsfiles (basic,full,library,minimal,mode,react) that described a superseded config API;QUICK_START.mdandUSAGE.mdalso removed as they documented the same obsolete format examples/pyra.config.reference.tsadded — single accurate reference file documenting every realPyraConfigfield with inline comments, including adefineConfigFnmode-aware example at the bottom
v0.30.6
Added
pyraFramerMotion()plugin in@pyra-js/core- solves the Framer Motion + SSR incompatibility at the framework level- Implements the new
headInjectionplugin hook to inject<style id="__pyra_fm">[data-projection-id]{opacity:1!important;transform:none!important}</style>into every SSR page's<head>, keeping all Framer Motion elements visible during SSR and before hydration regardless of theirinitialprop
- Implements the new
<FramerMotionReady>component in@pyra-js/adapter-react, companion component forpyraFramerMotion(); place it in your root layout to remove the SSR override after React hydrates and Framer Motion's ownuseLayoutEffecthas initialized, usingrequestAnimationFrameto ensure correct sequencingheadInjection?(): stringhook on thePyraPlugininterface in@pyra-js/shared- allows plugins to inject arbitrary HTML into<head>on every SSR page render, prepended before the adapter's own head tags; wired into both the streaming and buffered render paths in the dev server (dev-ssr.ts) and production server (prod/prod-server.ts)- Dev server compile cache warming - on startup, the dev server now pre-compiles all route and layout files in the background using
Promise.allSettled, eliminating the ~280ms cold-start delay on the first browser request - SSR-safe hooks in
@pyra-js/adapter-react-useLocation(),useParams(), anduseRouteError()now return safe defaults during server-side rendering instead of crashing withwindow is not defined;useLocation()returns{ pathname: '/', search: '', hash: '', searchParams: new URLSearchParams() }on the server,useParams()returns{}, anduseRouteError()returnsnull
v0.27.12
Full details: CHANGELOG.md
v0.27.8
Added
- React Fast Refresh - component-level hot updates in the dev server replace full-page reloads, preserving React component state, scroll position, and app context on every file save
- New
fast-refresh-plugin.tsin@pyra-js/adapter-react- esbuildonLoadplugin that runsreact-refresh/babelvia@babel/coreon every.tsx/.jsx/.ts/.jsfile outsidenode_modules, inserting$RefreshReg$and$RefreshSig$registration calls; JSX and TypeScript transforms are still handled by esbuild after the plugin returns the modified source getHMRPreamble?(): stringadded to thePyraAdapterinterface in@pyra-js/shared- adapters return a<script type="module">tag that initialises the HMR runtime in the browser before the hydration script's static imports execute, ensuring globals are in place when component registration calls fire/__pyra_refresh_runtimeendpoint in the dev server - lazily resolvesreact-refresh/runtimefrom the user's project root viacreateRequire, bundles it to ESM with esbuild once per session, and serves it withCache-Control: no-cache; returns 404 ifreact-refreshis not installedcanFastRefresh(filePath, routesDir)helper indev-hmr.ts- returnstruefor.tsx/.jsx/.ts/.jsfiles that are not server-only route files (route.*,middleware.*); drives theupdatevsreloaddecision in the file watcherwindow.__pyra_hmr_modules- a regular<script>tag (synchronous, runs before deferred module scripts) injected per page indev-ssr.tslisting all client module URLs (/__pyra/modules/...) for the current page and its layouts; kept in sync after client-side navigations in the hydration scriptwindow.__pyra_refresh— set by the RFR preamble to thereact-refresh/runtimeinstance so the HMR client can callperformReactRefresh()without importing the runtime again@babel/core ^7.24.0andreact-refresh ^0.14.0added as dependencies of@pyra-js/adapter-react
- New
<Head>component in@pyra-js/adapter-react— manage document head tags (title, meta, link, etc.) from within any page or layout component<ClientOnly>component in@pyra-js/adapter-react— suppresses SSR rendering of its children; useful for components that depend on browser-only APIs and would otherwise throw duringrenderToString()<Form>component in@pyra-js/adapter-react— enhanced form element with type-safe props<NavLink>component in@pyra-js/adapter-react— like<Link>but automatically applies an active CSS class when itshrefmatches the current pathname; drops in as a replacement for<Link>in nav bars and sidebars- Routing hooks and utilities in
@pyra-js/adapter-reactuseLocation()- returns the current URL as aURLobject, reactive to client-side navigationsuseRouter()- exposesnavigate(href)and the current params object; thin wrapper aroundwindow.__pyra- Additional routing helpers exported from the package index
- Deployment documentation (
docs/deployment.md)
Changed
- Adapter-agnostic core -
DevServer,build(), andProdServerno longer have any knowledge of React; the adapter must be supplied by the application viapyra.config.ts@pyra-js/adapter-reactremoved from@pyra-js/clidependencies - the CLI no longer bundles the React adapter; projects must install it separately (all templates already do)- Passing
adapter: createReactAdapter()inpyra.config.tsis now the only way to opt into React SSR; omittingadapteris an error forpyra dev,pyra build, andpyra start(existing behaviour, now enforced without a React fallback)
- HMR file watcher now distinguishes between two update strategies for the
changeevent.tsx/.jsx/.ts/.jsfiles that are not server-only route files → broadcasts{ type: 'update' }(triggers React Fast Refresh in the browser)- Everything else (CSS, JSON,
route.*,middleware.*, config files) → broadcasts{ type: 'reload' }(full page reload, same as before) - File
addevents always broadcast{ type: 'reload' }(route graph may have changed)
- HMR client script updated to handle
{ type: 'update' }messages - re-imports all tracked page and layout modules with a?__hmr=<timestamp>cache-bust query, then callswindow.__pyra_refresh.performReactRefresh(); falls back towindow.location.reload()ifwindow.__pyra_refreshis not defined (non-React adapter) or if the re-import throws bundleFile()inpackages/core/src/bundler.tsaccepts an optionalextraPlugins: esbuild.Plugin[]fourth parameter - prepended before the PostCSS plugin; used to pass adapter-provided esbuild plugins (e.g. the RFR transform) to all client-side bundles from both/__pyra/modules/*and/__pyra/styles/*endpoints
Fixed
FormPropstype error in@pyra-js/adapter-react- corrected incorrect prop type annotation that caused a TypeScript compile error when using the<Form>component- Type errors in
PyraAdapterinterface resolved - corrected mismatched signatures that surfaced after the adapter-agnostic refactor
v0.25.2
Added
- Client-side navigation (CSN) - same-layout routes now transition without a full page reload
- New
/_pyra/navigate?path=...JSON endpoint in both dev and prod servers, runs middleware +load(), returns{ data, clientEntry, layoutClientEntries, routeId } - Dev server: synthetic
fakeReqviaObject.create(req)ensuresctx.urlreflects the navigated path soload()reads the correct query params - Prod server: manifest-driven, same response shape as dev
- Middleware redirects (3xx) returned as
{ redirect: location }so the client performs a full navigation to the correct destination <Link>component added to@pyra-js/adapter-react- intercepts same-origin clicks, passes through modifier-key clicks (Cmd/Ctrl/Shift/Alt) for native browser behavior, callswindow.__pyra.navigate()for client-side transitionsgetHydrationScript()in the React adapter now generates a persistentPyraAppshell component usinguseState/useEffectinstead of a one-shothydrateRoot()calluseState(() => InitialComponent)+useState(initialData)hold the current page component and datauseEffectregisterswindow.__pyra.navigate(href)and apopstatehandler for Back/Forward button support- Layout chain comparison: if
nav.layoutClientEntriesdiffers from the boot-timepageLayouts, falls back tolocation.href(full reload) - keeps layout boundaries clean without complex diffing window.scrollTo(0, 0)on each client-side navigation
- New
Changed
- React Compiler wizard prompt updated from
"Enable React Compiler?"to"Enable React Compiler? (beta - adds Babel build step, benefits are client-side only)"- makes the tradeoffs visible before the user opts in
Fixed
- Tailwind CSS scaffolding in
create-pyrafor full-stack projects now prepends@tailwinddirectives to the existingstyle.cssinstead of creating a separateindex.css- preserves all custom template styles (nav, cards, hero, etc.) that were previously lost - Tailwind
index.csswas previously written tosrc/but full-stack layouts import fromsrc/routes/, file is no longer created separately; directives are merged into the existingstyle.cssinsrc/routes/
v0.24.0
Added
- Router and React Compiler options in
create-pyrawizard, new post-copy patching system inpackages/create-pyra/src/patches.tsapplies targeted file modifications after the base template is copied, avoiding a explosion of template directories- React SPA mode now prompts for a client-side router: None, React Router v7 (
react-router: ^7.0.0), or TanStack Router (@tanstack/react-router: ^1.0.0) - React Router patch: rewrites
main.tsxwithcreateBrowserRouter, convertsApp.tsxinto a layout shell with<Outlet />, and createssrc/pages/Home.tsx - TanStack Router patch: rewrites
main.tsxwith a fully code-based router, removesApp.tsx(root route replaces it) - React (both SSR and SPA) now prompts to enable the React Compiler, adds
babel-plugin-react-compilerandesbuild-plugin-babeldevDependencies and rewritespyra.config.ts/jswith a working Pyra plugin block
- React SPA mode now prompts for a client-side router: None, React Router v7 (
- Separator lines in request trace terminal output - each
toDetailedLog()block is now wrapped with a──────────────────────────────────────rule above and below, making individual requests visually distinct in busy dev server output - Compat shim packages (
packages/compat-pyrajs-*/) - new versions of the oldpyrajs-*package names that re-export from@pyra-js/*so existing projects continue to work without code changespyrajs-shared→ re-exports from@pyra-js/sharedpyrajs-core→ re-exports from@pyra-js/corepyrajs-adapter-react→ re-exports from@pyra-js/adapter-reactpyrajs-cli→ re-exports from@pyra-js/cli; includes abin/pyra.mjsshim so thepyrabinary continues to resolve- Each compat package ships a
README.mdwith a deprecation notice and a migration guide
Changed
- All four packages renamed to the
@pyra-jsnpm scopepyrajs-shared→@pyra-js/sharedpyrajs-core→@pyra-js/corepyrajs-adapter-react→@pyra-js/adapter-reactpyrajs-cli→@pyra-js/cli
defineConfigand user-facing types (RequestContext,Middleware,ErrorPageProps,CacheConfig,PrerenderConfig) are now re-exported from@pyra-js/cli- application developers only ever need to import from@pyra-js/cliand never from@pyra-js/shareddirectly- All generated project templates (
create-pyraandpackages/cli) updated to importdefineConfigfrom@pyra-js/cli
Fixed
babel-plugin-react-compilerversion corrected from^19.0.0(non-existent) to^1.0.0- React Compiler reached stable 1.0 in October 2025 under its own versioning scheme
v0.21.23
Fixed
pyra doctorno longer reports "Entry point not found: src/index.ts" in full-stack (file-based routing) projects, the entry check is now skipped when a routes directory is detected, since file-based projects do not use a single entry filepyra doctorTypeScript check no longer triggers Node.js DEP0190 deprecation warning ("Passing args to a child process with shell option true"),runTscCheck()now spawnscmd.exe /c tsc.cmd --noEmiton Windows instead of usingshell: true, eliminating both the warning and the follow-onEINVALspawn error
Security
- Patched GHSA-mw96-cpmx-2vgc (high severity) - Rollup arbitrary file write via path traversal in versions
>=4.0.0 <4.59.0; resolved by adding"rollup": "^4.59.0"topnpm.overridesin the rootpackage.json
Removed
- Deprecated
frameworkfield removed fromPyraConfiginpackages/shared/src/types.ts- theadapterfield fully replaces it and has been the intended API since v0.3
Changed
build.splittingis now read fromconfig.build?.splittinginstead of being hardcoded totrue, applies to both the SSR client build (build-orchestrator.ts) and the SPA build (buildSPA.ts); server build remains hardcoded tofalseas Node.js ESM cannot load split chunks dynamically
Refactored
packages/cli/src/bin.tsextracted into per-command files underpackages/cli/src/commands/commands/dev.ts- exportsDevOptionsanddevCommand()commands/build.ts- exportsBuildOptionsandbuildCommand()commands/start.ts- exportsStartOptionsandstartCommand()commands/init.ts- exportsInitOptionsandinitCommand()bin.tsreduced from ~625 lines to ~155 lines; now a pure wiring file that resolvessilent/colorand delegates to each command function- Follows the same pattern already used by
commands/graph.tsandcommands/doctor.ts
packages/core/src/build.tssplit into focused modules underpackages/core/src/build/build-utils.ts-routeIdToSafeName(),getAncestorDirIds()pure string utilitiesbuild-client.ts-buildClientOutputMap(),buildClientLayoutOutputMap()esbuild metafile correlationbuild-server.ts-buildServerOutputMap(),buildServerMwLayoutOutputMap()server entry path resolutionbuild-manifest.ts-assembleManifest(),buildEmptyManifest(),getMimeType(),DEFAULT_SHELLbuild-prerender.ts-buildPrerenderAssetTags()SSG asset tag generationbuild-report.ts-printBuildReport()and private helpers (estimateGzipSize,getSharedChunks,formatSize,countFilesRecursive)build-orchestrator.ts- mainbuild()functionbuild.tsreplaced with a 3-line re-export shim; all external imports remain unchanged
packages/core/src/prod-server.tssplit into focused modules underpackages/core/src/prod/prod-matcher.ts-MatchResult,buildMatcher()trie-based manifest route matcherprod-assets.ts-buildCacheControlHeader(),getCacheControl(),getContentType(),buildAssetTags()static asset helpersprod-html.ts-DEFAULT_SHELL,getErrorHTML(),get404HTML()HTML string generatorsprod-server.ts-ProdServerclass importing from the three helpers aboveprod-server.tsatsrc/root replaced with a 4-line re-export shim; all external imports remain unchanged
v0.19.0
Added
- CORS support - configure cross-origin resource sharing via the
corsfield inpyra.config.ts- New
CorsConfigtype inpackages/shared/src/types.ts:origin,methods,allowedHeaders,exposedHeaders,credentials,maxAgefields packages/core/src/cors.ts—buildCORSHeaders()andapplyCors()helpers; origin resolution supports wildcard, string, and array allow-lists; automaticOPTIONSpreflight handling- Dev server and production server both apply CORS headers via
applyCors()before routing
- New
- Static HTML CSS injection in dev server — prerendered/static HTML pages now receive
<link rel="stylesheet">tags for co-located CSS imports, consistent with SSR pages - HMR client CSS injection - extracted CSS is now injected into the HMR client bundle so hot-reloaded pages receive stylesheet updates
Refactored
packages/core/src/dev-server.tssplit into eight focused modules underpackages/core/src/dev/dev-compiler.ts— on-demand esbuild compilationdev-hmr.ts— WebSocket HMR server and client injectiondev-dashboard.ts—/_pyradashboard and API endpoint handlersdev-static.ts— static file andpublic/directory servingdev-api.ts— API route dispatch and method validationdev-routes.ts— page route matching and SSR pipeline entrydev-errors.ts— error boundary rendering and 404 handlingdev-ssr.ts— core SSR assembly (load → render → shell → inject assets)- Dead code removed;
dev-server.tsnow re-exports and wires the modules together
Fixed
- TypeScript typecheck errors in
Image.test.tsx— correctedmockContext,renderToHTML, adapter, and React type annotations across multiple test helpers - Image plugin tests - added missing manifest fixture to
image-plugin-tests.ts - Middleware tests - corrected assertions after middleware refactor
v0.18.4
Added
- Module resolution and path alias support via
ResolveConfigis now wired to esbuild in both dev and production buildsbundler.ts: newbuildEsbuildResolveOptions()helper translatesResolveConfigto esbuildalias,resolveExtensions, andmainFieldsoptions; alias values are resolved to absolute paths automaticallydev-server.ts: all fourbundleFile()call sites now passconfig.resolvebuild.ts:buildEsbuildResolveOptions()spread into both client and serveresbuild.build()calls
RequestTracer.setDetail(detail)method - annotates the most recently closed stage with a detail string without opening a new span; used to attach the matched route ID to theroute-matchstage after the router returnsMetricsStore.isActiveBuild()method - returns true whenstartBuild()has been called without a matchingfinishBuild(); used to coordinate build lifecycle across the file watcher and request handler- Build metrics now fully wired in dev server
startBuild()called in thechangeandaddwatcher handlers when a source file is saved- Orphaned builds (two rapid saves with no intervening request) are closed before the next
startBuild()viaisActiveBuild() finishBuild()called after the response is sent for the first routed request following a file change;totalDurationspans the full file-save-to-response cycle
- Dashboard "Recent Requests" section - live table polling
/_pyra/api/tracesevery 2 seconds- Color-coded method badges (GET, POST, PUT, PATCH, DELETE)
- Color-coded status badges by class (2xx, 3xx, 4xx, 5xx)
- Per-request pipeline stage breakdown with slow-stage highlighting (yellow above 50% of total, red above 80%)
- Relative timestamps ("3s ago", "1m ago")
- Matched route ID shown below the URL when the pattern differs from the path
- Docs:
docs/dashboard.md,docs/request-context.md,docs/cookies.md,docs/plugins.mdx
Fixed
- Double
route-matchentry in request traces - the dev server was opening two separate spans for route matching; the second (zero-duration) span is now replaced withtracer.setDetail(), attaching the route ID to the first span and keepingextractRouteId()correct - Dashboard empty state messages used em dashes; replaced with commas for consistency
Changed
- Dashboard stat cards show
--instead of0when no build data exists yet, avoiding misleading zeros on initial load - Build history rows now include a file count column
- Dashboard "Recent Requests" and "Build History" empty states distinguish between the two conditions with separate messages
v0.16.1
Added
-
CSS pipeline fix in dev server -
import './style.css'in layout/page files now works without Flash of Unstyled Contentbundler.ts: newcssOutputCachestores CSS extracted by esbuild separately from the JS bundle; removed the olddocument.createElement('style')injection that caused FOUC- New
getCSSOutput(filePath)export returns cached CSS for a given entry file dev-server.ts: new/__pyra/styles/*endpoint serves extracted CSS as propertext/cssresponseshandlePageRouteInnereagerly callsbundleFile()for each layout + page before assembly, then injects<link rel="stylesheet" href="/__pyra/styles/...">tags into<head>via<!--pyra-head-->style.cssmoved back tosrc/routes/(co-located with the layout) in all full-stack templates;import './style.css'restored as a standard ES module import
-
Package READMEs:
packages/adapter-react/README.md,packages/core/README.md,packages/shared/README.md- each tailored to its audience (end-user, internal/contributor, type reference) -
.vscode/settings.json- excludespackages/cli/templates/**from Tailwind CSS extension scanning, preventing false "unable to load config" errors caused by the extension attempting CommonJSrequire()on ESM-only template config files
Changed
create-pyrarendering mode prompt labels updated from "SSR / SPA" to "Full-stack / Frontend (SPA)" -valuefields ("ssr"/"spa") are unchanged so template selection is unaffectedpackages/clidev:linkscript changed frompnpm link -gtonpm link- now consistent withcreate-pyra'sdev:link, makingnpm link pyrajs-cliwork correctly in generated test projects- Full-stack template CSS redesigned across all six templates (
create-pyraReact/Preact TS/JS andpyrajs-cliReact TS/JS fullstack) to match the Pyra visual identity
Fixed
vitest.workspace.ts: removeddefineWorkspaceimport that broke TypeScript in Vitest 4 (the export was removed in Vitest 4; workspace files now export the array directly)