@elurjs/kit 2.4.10 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +313 -0
- package/README.md +248 -15
- package/dist/lib/action/index.cjs +1 -1
- package/dist/lib/action/index.js +1 -1
- package/dist/lib/{action-DZHihU3Z.cjs → action-BiE4tEty.cjs} +2 -2
- package/dist/lib/{action-DZHihU3Z.cjs.map → action-BiE4tEty.cjs.map} +1 -1
- package/dist/lib/{action-BB6N5yD-.js → action-CXVk8rWt.js} +3 -3
- package/dist/lib/{action-BB6N5yD-.js.map → action-CXVk8rWt.js.map} +1 -1
- package/dist/lib/adapter-DNsjXvq5.js +160 -0
- package/dist/lib/adapter-DNsjXvq5.js.map +1 -0
- package/dist/lib/adapter-Dz2hzR36.cjs +3 -0
- package/dist/lib/adapter-Dz2hzR36.cjs.map +1 -0
- package/dist/lib/adapters/bun.cjs +2 -2
- package/dist/lib/adapters/bun.cjs.map +1 -1
- package/dist/lib/adapters/bun.js +17 -9
- package/dist/lib/adapters/bun.js.map +1 -1
- package/dist/lib/adapters/index.d.cts +34 -0
- package/dist/lib/adapters/index.d.ts +34 -0
- package/dist/lib/adapters/netlify.cjs +1 -1
- package/dist/lib/adapters/netlify.js +7 -7
- package/dist/lib/adapters/node.cjs +5 -5
- package/dist/lib/adapters/node.cjs.map +1 -1
- package/dist/lib/adapters/node.js +20 -12
- package/dist/lib/adapters/node.js.map +1 -1
- package/dist/lib/adapters/vercel.cjs +1 -1
- package/dist/lib/adapters/vercel.js +8 -8
- package/dist/lib/build/build.d.cts +40 -0
- package/dist/lib/build/build.d.ts +40 -0
- package/dist/lib/build/document-shell.d.cts +47 -1
- package/dist/lib/build/document-shell.d.ts +47 -1
- package/dist/lib/build/vite-build.d.cts +25 -2
- package/dist/lib/build/vite-build.d.ts +25 -2
- package/dist/lib/cache/adapter.d.cts +41 -0
- package/dist/lib/cache/adapter.d.ts +41 -0
- package/dist/lib/cache/index.cjs +2 -0
- package/dist/lib/cache/index.cjs.map +1 -0
- package/dist/lib/cache/index.d.cts +5 -0
- package/dist/lib/cache/index.d.ts +5 -0
- package/dist/lib/cache/index.js +113 -0
- package/dist/lib/cache/index.js.map +1 -0
- package/dist/lib/cache/invalidation.d.cts +36 -0
- package/dist/lib/cache/invalidation.d.ts +36 -0
- package/dist/lib/cache/redis-adapter.d.cts +49 -0
- package/dist/lib/cache/redis-adapter.d.ts +49 -0
- package/dist/lib/cache-DGhTYyqh.js +50 -0
- package/dist/lib/cache-DGhTYyqh.js.map +1 -0
- package/dist/lib/cache-X1n4Ossu.cjs +2 -0
- package/dist/lib/cache-X1n4Ossu.cjs.map +1 -0
- package/dist/lib/cli/output.d.cts +58 -0
- package/dist/lib/cli/output.d.ts +58 -0
- package/dist/lib/cli/ports.d.cts +20 -0
- package/dist/lib/cli/ports.d.ts +20 -0
- package/dist/lib/cli.cjs +2559 -1488
- package/dist/lib/cli.cjs.map +1 -1
- package/dist/lib/cli.d.cts +21 -0
- package/dist/lib/cli.d.ts +21 -0
- package/dist/lib/cli.js +2558 -1488
- package/dist/lib/cli.js.map +1 -1
- package/dist/lib/config/index.cjs +1 -1
- package/dist/lib/config/index.cjs.map +1 -1
- package/dist/lib/config/index.d.cts +79 -0
- package/dist/lib/config/index.d.ts +79 -0
- package/dist/lib/config/index.js +21 -14
- package/dist/lib/config/index.js.map +1 -1
- package/dist/lib/{errors-ePYxDlhU.js → errors-j4cg9VN3.js} +24 -30
- package/dist/lib/{errors-ePYxDlhU.js.map → errors-j4cg9VN3.js.map} +1 -1
- package/dist/lib/errors-kSl9yLkg.cjs +2 -0
- package/dist/lib/{errors-0X7pCFNU.cjs.map → errors-kSl9yLkg.cjs.map} +1 -1
- package/dist/lib/hydrate-BSLuI0Nh.cjs +2 -0
- package/dist/lib/hydrate-BSLuI0Nh.cjs.map +1 -0
- package/dist/lib/hydrate-BaS7-d8C.js +133 -0
- package/dist/lib/hydrate-BaS7-d8C.js.map +1 -0
- package/dist/lib/idiomorph.esm-BOOTOg1r.cjs +2 -0
- package/dist/lib/idiomorph.esm-BOOTOg1r.cjs.map +1 -0
- package/dist/lib/idiomorph.esm-CXuY-EHB.js +371 -0
- package/dist/lib/idiomorph.esm-CXuY-EHB.js.map +1 -0
- package/dist/lib/index.cjs +1 -1
- package/dist/lib/index.cjs.map +1 -1
- package/dist/lib/index.d.cts +8 -6
- package/dist/lib/index.d.ts +8 -6
- package/dist/lib/index.js +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/interpolation-plugin-C0dTd69H.cjs +58 -0
- package/dist/lib/interpolation-plugin-C0dTd69H.cjs.map +1 -0
- package/dist/lib/{interpolation-plugin-Wgb1j4pT.js → interpolation-plugin-K2qE69tF.js} +95 -126
- package/dist/lib/interpolation-plugin-K2qE69tF.js.map +1 -0
- package/dist/lib/invalidation-CFpNQUv6.cjs +2 -0
- package/dist/lib/invalidation-CFpNQUv6.cjs.map +1 -0
- package/dist/lib/invalidation-DXhdE9wj.js +64 -0
- package/dist/lib/invalidation-DXhdE9wj.js.map +1 -0
- package/dist/lib/island/generate-entry.d.cts +40 -2
- package/dist/lib/island/generate-entry.d.ts +40 -2
- package/dist/lib/island/hydrate.d.cts +19 -6
- package/dist/lib/island/hydrate.d.ts +19 -6
- package/dist/lib/island/index.cjs +1 -1
- package/dist/lib/island/index.d.cts +2 -1
- package/dist/lib/island/index.d.ts +2 -1
- package/dist/lib/island/index.js +1 -1
- package/dist/lib/island/island.d.cts +15 -0
- package/dist/lib/island/island.d.ts +15 -0
- package/dist/lib/island-CTBnsOhI.js +59 -0
- package/dist/lib/island-CTBnsOhI.js.map +1 -0
- package/dist/lib/island-kfQEKRe_.cjs +2 -0
- package/dist/lib/island-kfQEKRe_.cjs.map +1 -0
- package/dist/lib/node-http-DVFcYeBS.js +723 -0
- package/dist/lib/node-http-DVFcYeBS.js.map +1 -0
- package/dist/lib/node-http-Uct7DhmN.cjs +13 -0
- package/dist/lib/node-http-Uct7DhmN.cjs.map +1 -0
- package/dist/lib/rolldown-runtime-BIInC43l.cjs +1 -0
- package/dist/lib/rolldown-runtime-Dy4uBu1J.js +11 -0
- package/dist/lib/router/client.cjs +1 -1
- package/dist/lib/router/client.cjs.map +1 -1
- package/dist/lib/router/client.d.cts +55 -8
- package/dist/lib/router/client.d.ts +55 -8
- package/dist/lib/router/client.js +298 -97
- package/dist/lib/router/client.js.map +1 -1
- package/dist/lib/router/redirects.d.cts +34 -0
- package/dist/lib/router/redirects.d.ts +34 -0
- package/dist/lib/runtime/handler.d.cts +59 -0
- package/dist/lib/runtime/handler.d.ts +59 -0
- package/dist/lib/runtime/index.cjs +1 -1
- package/dist/lib/runtime/index.d.cts +2 -1
- package/dist/lib/runtime/index.d.ts +2 -1
- package/dist/lib/runtime/index.js +4 -4
- package/dist/lib/runtime/logger.d.cts +47 -0
- package/dist/lib/runtime/logger.d.ts +47 -0
- package/dist/lib/runtime/node-http.d.cts +11 -1
- package/dist/lib/runtime/node-http.d.ts +11 -1
- package/dist/lib/runtime-ChE4flX3.cjs +2 -0
- package/dist/lib/runtime-ChE4flX3.cjs.map +1 -0
- package/dist/lib/runtime-OMyz5yE-.js +905 -0
- package/dist/lib/runtime-OMyz5yE-.js.map +1 -0
- package/dist/lib/seo/index.cjs +1 -10
- package/dist/lib/seo/index.d.cts +1 -0
- package/dist/lib/seo/index.d.ts +1 -0
- package/dist/lib/seo/index.js +2 -41
- package/dist/lib/seo/sitemap-from-routes.d.cts +24 -0
- package/dist/lib/seo/sitemap-from-routes.d.ts +24 -0
- package/dist/lib/{shared-CfqOLsKj.cjs → shared-CP0Dc3cp.cjs} +72 -47
- package/dist/lib/shared-CP0Dc3cp.cjs.map +1 -0
- package/dist/lib/{shared-DU3iQgr7.js → shared-D8w-neq2.js} +87 -58
- package/dist/lib/shared-D8w-neq2.js.map +1 -0
- package/dist/lib/sitemap-from-routes-CaBoiGvE.js +77 -0
- package/dist/lib/sitemap-from-routes-CaBoiGvE.js.map +1 -0
- package/dist/lib/sitemap-from-routes-DbcLLL4G.cjs +13 -0
- package/dist/lib/sitemap-from-routes-DbcLLL4G.cjs.map +1 -0
- package/dist/lib/ssr/render.d.cts +8 -2
- package/dist/lib/ssr/render.d.ts +8 -2
- package/dist/lib/ssr/server.d.cts +7 -0
- package/dist/lib/ssr/server.d.ts +7 -0
- package/dist/lib/ssr/stream-response.d.cts +30 -0
- package/dist/lib/ssr/stream-response.d.ts +30 -0
- package/dist/lib/ssr/stream.d.cts +16 -1
- package/dist/lib/ssr/stream.d.ts +16 -1
- package/dist/lib/{stream-DmZjYqHm.cjs → stream-Cg-xYG82.cjs} +3 -3
- package/dist/lib/stream-Cg-xYG82.cjs.map +1 -0
- package/dist/lib/stream-D-ytogdu.js +106 -0
- package/dist/lib/stream-D-ytogdu.js.map +1 -0
- package/dist/lib/vite/index.cjs +1 -1
- package/dist/lib/vite/index.cjs.map +1 -1
- package/dist/lib/vite/index.d.cts +11 -0
- package/dist/lib/vite/index.d.ts +11 -0
- package/dist/lib/vite/index.js +101 -94
- package/dist/lib/vite/index.js.map +1 -1
- package/package.json +25 -8
- package/dist/lib/errors-0X7pCFNU.cjs +0 -2
- package/dist/lib/hydrate-CJbUWdXu.js +0 -126
- package/dist/lib/hydrate-CJbUWdXu.js.map +0 -1
- package/dist/lib/hydrate-D6MR24mz.cjs +0 -2
- package/dist/lib/hydrate-D6MR24mz.cjs.map +0 -1
- package/dist/lib/interpolation-plugin-TWDch8nn.cjs +0 -39
- package/dist/lib/interpolation-plugin-TWDch8nn.cjs.map +0 -1
- package/dist/lib/interpolation-plugin-Wgb1j4pT.js.map +0 -1
- package/dist/lib/node-http-DRAUhO0c.js +0 -617
- package/dist/lib/node-http-DRAUhO0c.js.map +0 -1
- package/dist/lib/node-http-DhxguYyz.cjs +0 -13
- package/dist/lib/node-http-DhxguYyz.cjs.map +0 -1
- package/dist/lib/runtime-CrH5X-Es.cjs +0 -2
- package/dist/lib/runtime-CrH5X-Es.cjs.map +0 -1
- package/dist/lib/runtime-Ds2-wH6P.js +0 -520
- package/dist/lib/runtime-Ds2-wH6P.js.map +0 -1
- package/dist/lib/seo/index.cjs.map +0 -1
- package/dist/lib/seo/index.js.map +0 -1
- package/dist/lib/shared-CfqOLsKj.cjs.map +0 -1
- package/dist/lib/shared-DU3iQgr7.js.map +0 -1
- package/dist/lib/stream-CpOG052V.js +0 -104
- package/dist/lib/stream-CpOG052V.js.map +0 -1
- package/dist/lib/stream-DmZjYqHm.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,319 @@ All notable changes to Elur Kit are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.5.1]
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **`elur-kit build` never generated image variants** — the CLI bundle
|
|
13
|
+
(`dist/lib/cli.js`) had `import("sharp")` rewritten by Vite's
|
|
14
|
+
optional-peer-dep transform into a stub that always throws
|
|
15
|
+
(`Could not resolve "sharp"`), because `sharp` was missing from the
|
|
16
|
+
externals in `vite.cli.config.ts`. `loadSharp()` caught the error and
|
|
17
|
+
permanently returned `null`, so `processImageBatch` fell back to
|
|
18
|
+
manifest-only entries (`variants: []`) and every `image()` emitted a
|
|
19
|
+
plain `<img>` instead of a `<picture>` with WebP/AVIF sources. `sharp`
|
|
20
|
+
is now external in the CLI build, so the optional peer resolves from
|
|
21
|
+
the consuming project's `node_modules` at runtime.
|
|
22
|
+
|
|
23
|
+
## [2.5.0]
|
|
24
|
+
|
|
25
|
+
All changes are additive and backwards-compatible: existing projects work
|
|
26
|
+
unchanged without touching their config.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- **Structured request logging** — the unified web handler
|
|
31
|
+
(`createWebHandler`, used by the dev server, preview server, and the
|
|
32
|
+
generated Node/Bun servers) now creates a `StructuredLogger` per request
|
|
33
|
+
instead of bare `console.error` calls. Error logs are emitted as JSON in
|
|
34
|
+
production (readable `[LEVEL]` text in dev) with structured fields
|
|
35
|
+
(`path`, `method`, `route`, `error`, `stack`), sensitive-header
|
|
36
|
+
redaction, and a request ID taken from the incoming `X-Request-ID`
|
|
37
|
+
header or generated per request. Configure via
|
|
38
|
+
`defineConfig({ logger: { level: "debug" | "info" | "warn" | "error" } })`
|
|
39
|
+
(default: `debug` in dev, `info` in production).
|
|
40
|
+
- **`X-Request-ID` response header** on every handler response, enabling
|
|
41
|
+
request correlation between clients, proxies, and logs.
|
|
42
|
+
- **`Server-Timing` response header** with per-phase timings (`action`,
|
|
43
|
+
`render-endpoint`, `api`, `ssr`), visible in browser DevTools → Network
|
|
44
|
+
→ Server Timing.
|
|
45
|
+
- **Redirects, rewrites, and route headers** — the previously internal
|
|
46
|
+
`src/router/redirects.ts` module is now wired into the handler pipeline.
|
|
47
|
+
Configure via `defineConfig({ redirects, rewrites, headers })`:
|
|
48
|
+
- `redirects: [{ from, to, status? }]` are evaluated before all routing
|
|
49
|
+
and return a `Location` response (default status `308`; also `301`,
|
|
50
|
+
`302`, `307`).
|
|
51
|
+
- `rewrites: [{ from, to }]` rewrite the pathname transparently (the
|
|
52
|
+
browser URL does not change); the rewritten path drives static
|
|
53
|
+
serving, SSR, API routes, and the ISR cache key.
|
|
54
|
+
- `headers: [{ path, headers }]` merge custom headers into responses
|
|
55
|
+
matching the public (pre-rewrite) path; they may override security
|
|
56
|
+
headers, while `Server-Timing`/`X-Request-ID` always win.
|
|
57
|
+
- Patterns support literal segments, `:param`, `*` wildcards, and
|
|
58
|
+
`:param*` catch-alls, with `:param` interpolation in destinations.
|
|
59
|
+
- Works in dev, preview, and the generated Node/Bun servers. The
|
|
60
|
+
Vercel/Netlify bundled handlers do not pick this up yet.
|
|
61
|
+
- **Public exports** — `StructuredLogger`, `createRequestLogger`, and the
|
|
62
|
+
`LogLevel`/`LogEntry`/`ServerTimingMetric` types are now exported from
|
|
63
|
+
the package root.
|
|
64
|
+
- **Pluggable ISR cache adapters in the handler** — the unified web
|
|
65
|
+
handler now stores rendered pages through the `CacheAdapter` interface
|
|
66
|
+
instead of the legacy cache module. Configure via
|
|
67
|
+
`defineConfig({ cache: { adapter } })` (e.g. Redis or Cloudflare KV);
|
|
68
|
+
when omitted, a filesystem adapter rooted at `cache.dir` is created and
|
|
69
|
+
shared per directory. Page entries are keyed with `cacheKey(pathname)`
|
|
70
|
+
(SHA-256), the same scheme used by path-based invalidation.
|
|
71
|
+
- **Cache policy tags are now honored** — `tags` declared in a route's
|
|
72
|
+
`cache` policy (`page.data.ts`) are stored with the cache entry, so
|
|
73
|
+
tag-based invalidation works end to end.
|
|
74
|
+
- **Automatic cache invalidation from actions** — after a successful
|
|
75
|
+
action run, `invalidateTags`/`invalidatePaths` declared via
|
|
76
|
+
`defineAction()` are dispatched to the connected cache adapter
|
|
77
|
+
(`defaultInvalidator`). Actions that return `fail(...)` do not
|
|
78
|
+
invalidate anything.
|
|
79
|
+
- **Stale-while-revalidate for ISR pages** — stale cache entries are
|
|
80
|
+
served immediately while the page re-renders in the background, instead
|
|
81
|
+
of blocking the request on a full re-render.
|
|
82
|
+
- **New `@elurjs/kit/cache` subpath** — exports `CacheAdapter`,
|
|
83
|
+
`createFsCacheAdapter`, `createRedisCacheAdapter`,
|
|
84
|
+
`createCloudflareKVCacheAdapter`, `getWithSWR`, `cacheKey`, the cache
|
|
85
|
+
policy helpers, and the invalidation primitives.
|
|
86
|
+
- **Automatic sitemap generation in `build`** — when `site` is configured
|
|
87
|
+
in `defineConfig`, the build now writes `sitemap.xml` from the scanned
|
|
88
|
+
route manifest (dynamic routes, error pages and internal namespaces are
|
|
89
|
+
excluded; sites over 50,000 URLs are split into `sitemap-N.xml` files
|
|
90
|
+
with a `sitemap.xml` index). An existing `sitemap.xml` — copied from
|
|
91
|
+
`public/` or written by an integration `build` hook — always takes
|
|
92
|
+
precedence and is never overwritten. `generateSitemapFromRoutes` and its
|
|
93
|
+
options type are re-exported from `@elurjs/kit/seo`.
|
|
94
|
+
- **`--verbose` / `--quiet` CLI flags** — override `logger.level` from the
|
|
95
|
+
config: `--verbose` maps to `debug`, `--quiet` maps to `error` (only
|
|
96
|
+
errors are printed; `--quiet` wins if both are passed).
|
|
97
|
+
- **Real streaming SSR in the unified handler (opt-in, experimental)** —
|
|
98
|
+
`defineConfig({ streaming: true })` enables true streaming for dynamic
|
|
99
|
+
routes that declare a `loading` boundary: the document shell + loading
|
|
100
|
+
fallback are flushed immediately, the page render continues in the
|
|
101
|
+
background, and the resolved content arrives as a `<template>` chunk that
|
|
102
|
+
swaps the boundary in-place. Details:
|
|
103
|
+
- Wired into `createWebHandler` (new `streaming` and `capabilities`
|
|
104
|
+
options), so `dev`, `preview`, `start` and the generated Node/Bun
|
|
105
|
+
servers all stream through the same code path. Hosts that declare
|
|
106
|
+
`capabilities.streaming: false` degrade to buffered rendering, and the
|
|
107
|
+
CLI `adapter` command validates the combination at build time.
|
|
108
|
+
- Streamed responses send `Content-Type: text/html` early, no
|
|
109
|
+
`Content-Length`, `X-Accel-Buffering: no` (nginx must not buffer) and
|
|
110
|
+
`Cache-Control: no-store`. `Server-Timing`'s `ssr` metric measures
|
|
111
|
+
time-to-shell.
|
|
112
|
+
- **ISR interaction**: streamed pages bypass the cache entirely (never
|
|
113
|
+
read, never written); buffered routes keep the existing ISR behavior.
|
|
114
|
+
- **Abort handling**: client disconnects cancel the stream via
|
|
115
|
+
`request.signal`; late background renders are discarded. The Node
|
|
116
|
+
server now writes responses with the new `sendWebResponse()` helper
|
|
117
|
+
(`@elurjs/kit/runtime`), which forwards chunks as they are produced
|
|
118
|
+
with backpressure and cancels the upstream stream on socket close.
|
|
119
|
+
- **Mid-stream failures**: a loader redirect `Response` navigates via an
|
|
120
|
+
inline script chunk; a render error swaps the boundary for an
|
|
121
|
+
inline-styled `role="alert"` notice instead of a dead spinner.
|
|
122
|
+
- `documentShell` now delimits the `#app` content with explicit
|
|
123
|
+
`<!--elur:app:start-->`/`<!--elur:app:end-->` markers (invisible HTML
|
|
124
|
+
comments), replacing the fragile `<div id="app">` regex extraction;
|
|
125
|
+
`extractAppBody()` is exported for adapters. `createStreamingResponse`
|
|
126
|
+
and `createBufferedResponse` are now exported from the package root.
|
|
127
|
+
- **User middleware in the unified handler** — `src/middleware.ts` now runs
|
|
128
|
+
in `dev`, `preview` and `start` (previously only the legacy SSR server ran
|
|
129
|
+
it). Semantics: after redirects/rewrites and the internal endpoints,
|
|
130
|
+
before routing; a returned `Response` short-circuits through the standard
|
|
131
|
+
finalize step (security headers, `X-Request-ID`, `Server-Timing` still
|
|
132
|
+
apply); `next({ headers })` merges into the downstream request and
|
|
133
|
+
`next({ locals })` is exposed to API routes as `ctx.locals`. Middleware
|
|
134
|
+
errors return a sanitized 500 instead of crashing the request. The
|
|
135
|
+
generated Node/Bun adapter servers do not run the middleware file yet.
|
|
136
|
+
- **`RedirectRule` / `RewriteRule` / `RouteHeadersRule` type exports** from
|
|
137
|
+
the package root, so config rule arrays can be annotated outside
|
|
138
|
+
`defineConfig`.
|
|
139
|
+
- **0% JavaScript by default (per-page script gating)** — `documentShell`
|
|
140
|
+
no longer emits the client entry unconditionally. In the default
|
|
141
|
+
`js: "modern"` mode the shell inspects the rendered body for
|
|
142
|
+
`data-elur-island` markers: pages without islands ship **no client JS**
|
|
143
|
+
when the router is disabled, or **only the router chunk** when it is on.
|
|
144
|
+
Streaming shells are exempt (the body is not known when the shell is
|
|
145
|
+
sent, so streamed routes always emit the entry). Opt out entirely with
|
|
146
|
+
`defineConfig({ js: "legacy" })`, which restores the unconditional
|
|
147
|
+
combined entry.
|
|
148
|
+
- **Split client entries (hydration vs router)** — the generated client
|
|
149
|
+
entry is now hydrate-only and the router lives in its own module
|
|
150
|
+
(`.elur/router.ts` → `/_elur/router.js`). The CLI decides the layout
|
|
151
|
+
before rendering pages: projects with a two-input
|
|
152
|
+
`vite.client.config.*` (or no client config at all — a default
|
|
153
|
+
two-input config is now synthesized) get the split; single-input
|
|
154
|
+
configs keep the router embedded in `entry-client.js` and the shell
|
|
155
|
+
emits it whenever islands exist *or* the router is enabled. Wired
|
|
156
|
+
through `ElurConfig.router`, `BuildConfig.router`,
|
|
157
|
+
`GenerateEntryOptions.router`, `WebHandlerOptions.router`,
|
|
158
|
+
`AdapterOptions.router`/`js`, `documentShell` (`routerEntry`,
|
|
159
|
+
`routerEnabled`), and the generated adapter SSR entries. The example
|
|
160
|
+
`vite.client.config.ts` now uses two named inputs.
|
|
161
|
+
- **`<link rel="modulepreload">` for emitted entries** — every module
|
|
162
|
+
script the shell emits (hydration entry and/or router chunk) also gets
|
|
163
|
+
a `modulepreload` link so the fetch starts during HTML parsing.
|
|
164
|
+
- **Navigation lifecycle events** — the client router dispatches
|
|
165
|
+
`elur:navigate-start`, `elur:navigate-end` and `elur:navigate-error`
|
|
166
|
+
with `{ pathname, search, fromCache, popstate }`, plus
|
|
167
|
+
`elur:before-render` right before the `#app` swap (with
|
|
168
|
+
`detail.persisted` listing the nodes that will survive). Islands are
|
|
169
|
+
now disposed on `elur:before-render` — while still attached — instead
|
|
170
|
+
of on `elur:rendered`.
|
|
171
|
+
- **`data-elur-persist` element persistence** — mark an element with
|
|
172
|
+
`data-elur-persist="key"` and the router moves the *same live node*
|
|
173
|
+
into the matching position of the new page (matched by attribute
|
|
174
|
+
value) using `Element.moveBefore()` when available
|
|
175
|
+
(`replaceWith` fallback). Island state, media playback and scroll
|
|
176
|
+
positions survive navigations; islands inside persisted subtrees are
|
|
177
|
+
skipped by the navigation cleanup. When an island inside a persisted
|
|
178
|
+
node would receive different props, an `elur:persist-props-changed`
|
|
179
|
+
event bubbles from its marker. `cleanupHydratedIslands({ except })`
|
|
180
|
+
supports exclusion lists.
|
|
181
|
+
- **Complete SPA render payload** — `/__elur-js/render` now returns
|
|
182
|
+
`{ title, body, head, data, actions, clearActionErrorCookie }` in every
|
|
183
|
+
runtime (unified handler, generated Node/Bun adapters, dev server and
|
|
184
|
+
the legacy `createSsrServer`): `head` carries the managed
|
|
185
|
+
`data-elur-head` tags (title/meta/OG/Twitter), `data`/`actions` carry
|
|
186
|
+
the serialized contents of `#elur-data`/`#elur-actions`, and
|
|
187
|
+
`clearActionErrorCookie` is also relayed as the
|
|
188
|
+
`X-Elur-Action-Clear-Cookie` response header. Loader-thrown
|
|
189
|
+
`Response`s propagate as first-class responses. After a navigation the
|
|
190
|
+
router refreshes the inert `#elur-data`/`#elur-actions` scripts, merges
|
|
191
|
+
the managed head tags, and re-executes inline `<script>` elements in
|
|
192
|
+
the new body (external `src` scripts are deduplicated across
|
|
193
|
+
navigations; `data-elur-no-reload` opts out).
|
|
194
|
+
- **Router robustness** — the prefetch cache is now a bounded LRU (32
|
|
195
|
+
entries, 30 s TTL), `history.scrollRestoration` is set to `manual`
|
|
196
|
+
with per-entry scroll save/restore on back/forward, prefetching starts
|
|
197
|
+
on `pointerdown` in addition to hover/focus (and opt-in viewport via
|
|
198
|
+
`data-prefetch="viewport"`), and constrained networks are respected:
|
|
199
|
+
`Save-Data` and `slow-2g`/`2g` effective connection types skip prefetch
|
|
200
|
+
unless the link forces it with `data-prefetch="always"` (or
|
|
201
|
+
`prefetch(path, search, { force: true })` is called directly).
|
|
202
|
+
- **Optional DOM morphing (`router.morph`)** — `defineConfig({ router: {
|
|
203
|
+
morph: true } })` swaps `#app` with idiomorph-based DOM morphing
|
|
204
|
+
instead of a wholesale `replaceChildren` (experimental). Hydrated
|
|
205
|
+
islands and `data-elur-persist` subtrees are treated as opaque. New
|
|
206
|
+
dependency: `idiomorph` — loaded via dynamic `import()` only when the
|
|
207
|
+
flag is on, so it stays out of the served bytes when morph is off
|
|
208
|
+
(a hard dependency is required because bundlers must resolve the
|
|
209
|
+
specifier at build time even when the chunk is lazy).
|
|
210
|
+
- **Optional loading indicator (`router.loadingIndicator`)** — a minimal
|
|
211
|
+
top progress bar appears only when a navigation takes longer than
|
|
212
|
+
~200 ms, driven by the lifecycle events; it honors
|
|
213
|
+
`prefers-reduced-motion` (static bar, no trickle) and never flashes on
|
|
214
|
+
cache hits.
|
|
215
|
+
- **Speculation Rules (`router.speculation`)** —
|
|
216
|
+
`defineConfig({ router: { speculation: "prefetch" | "prerender" } })`
|
|
217
|
+
emits a `<script type="speculationrules">` block with document rules
|
|
218
|
+
(`eagerness: "moderate"`, internal same-origin links excluding actions,
|
|
219
|
+
downloads, `target`/`data-no-router`/`data-no-speculation` links) on
|
|
220
|
+
statically built pages. Chromium-only progressive enhancement; other
|
|
221
|
+
browsers ignore it.
|
|
222
|
+
|
|
223
|
+
### Deprecated
|
|
224
|
+
|
|
225
|
+
- **`createSsrServer`** (`@elurjs/kit`) — the legacy standalone SSR server
|
|
226
|
+
pipeline. All CLI commands (`dev`, `preview`, `start`) now run through
|
|
227
|
+
`createWebHandler`; the legacy server remains exported for backward
|
|
228
|
+
compatibility and will be removed in a future major release.
|
|
229
|
+
- **`renderStreamingPage`** — the legacy shell + client-fetch streaming
|
|
230
|
+
approach, superseded by `createStreamingResponse` (real streaming).
|
|
231
|
+
|
|
232
|
+
### Changed
|
|
233
|
+
|
|
234
|
+
- **`elur-kit start` runs on the unified Web handler** — it previously used
|
|
235
|
+
the legacy `createSsrServer` pipeline. It now shares the dev/preview path:
|
|
236
|
+
startup banner with Local/Network URLs, busy-port fallback, per-request
|
|
237
|
+
structured logging with `Server-Timing`/`X-Request-ID`, security headers,
|
|
238
|
+
redirects/rewrites/route headers, pluggable ISR cache, user middleware, and
|
|
239
|
+
opt-in streaming SSR. `start` now requires a previous `elur-kit build`
|
|
240
|
+
(it fails fast with a clear message when `dist/` is missing) instead of
|
|
241
|
+
silently rendering everything on demand.
|
|
242
|
+
- **CLI output formatting** — build, dev, preview, and adapter messages
|
|
243
|
+
now use a consistent format (`✓` success, `→` info, `!` warning, `✗`
|
|
244
|
+
error, `[tag]` lifecycle events) with ANSI colors when stdout is a TTY
|
|
245
|
+
(disabled via `NO_COLOR` or when piped; no new dependencies). The build
|
|
246
|
+
summary now includes the total duration plus page/island/file counts.
|
|
247
|
+
The fatal request-error catch in the dev/preview server now logs through
|
|
248
|
+
the structured logger (with `path`, `method`, `error`, `stack`) instead
|
|
249
|
+
of a bare `console.error`.
|
|
250
|
+
- **Dev/preview/start startup banner** — the dev, preview and start servers
|
|
251
|
+
now print an Astro-style banner with the kit name and version and the
|
|
252
|
+
bound URLs:
|
|
253
|
+
`→ Local: http://…` plus `→ Network: http://<LAN-IP>:…` when the
|
|
254
|
+
machine has a non-internal IPv4 address. The banner reflects the port
|
|
255
|
+
actually bound after the busy-port fallback.
|
|
256
|
+
- **Build phase timings and file sizes** — `elur-kit build` now reports
|
|
257
|
+
each phase with a checkmark and its duration (`transform`, `scan`,
|
|
258
|
+
`pages`, `images`, `integrations`, `sitemap`, `manifest`,
|
|
259
|
+
`client bundle`; phases that don't run are omitted) via a new optional
|
|
260
|
+
`BuildConfig.onPhase(name, durationMs)` observer, and lists the
|
|
261
|
+
generated files with their sizes (aligned, Vite-style). With more than
|
|
262
|
+
20 files, only the 10 largest are shown followed by a
|
|
263
|
+
`… and N more` summary. With `--quiet`, the client bundle step is now
|
|
264
|
+
silent too (new `quiet` option on `buildClientBundle`, which also sets
|
|
265
|
+
Vite's `logLevel: "silent"`).
|
|
266
|
+
- **ISR storage backend** — `createWebHandler` no longer uses the legacy
|
|
267
|
+
`getCachedHtml`/`setCachedHtml` module for page caching (it remains
|
|
268
|
+
exported for backward compatibility). Existing on-disk cache entries use
|
|
269
|
+
the same SHA-256-of-pathname key, but entries written by older versions
|
|
270
|
+
lack tag metadata, so they simply miss tag-based invalidation until
|
|
271
|
+
re-rendered. What gets cached (cacheability gates, TTL rules) is
|
|
272
|
+
unchanged.
|
|
273
|
+
|
|
274
|
+
### Fixed
|
|
275
|
+
|
|
276
|
+
- **Dead legacy config lookup removed** — `findConfigFile` carried a legacy
|
|
277
|
+
config-name fallback that could never fire (its file list was identical to
|
|
278
|
+
the preferred one) and referenced pre-rename names that are no longer
|
|
279
|
+
supported. Config files are now exclusively `elur.config.ts/js/mjs`.
|
|
280
|
+
- **Missing type declarations for `@elurjs/kit/cache`** — the subpath
|
|
281
|
+
shipped its runtime bundle but no `index.d.ts`/`index.d.cts` because
|
|
282
|
+
`src/cache/index.ts` was absent from the declaration build inputs
|
|
283
|
+
(publint error). The `./cache` subpath is now fully typed (including
|
|
284
|
+
`redis-adapter`).
|
|
285
|
+
- **Infinite restart loop when the dev port is busy** — with port 3000
|
|
286
|
+
occupied, the dev supervisor restarted the crashed worker forever on the
|
|
287
|
+
same `EADDRINUSE`. The dev and preview servers now retry on the next
|
|
288
|
+
port (up to 20 candidates) with a clear warning
|
|
289
|
+
(`! Puerto 3000 ocupado, usando 3001`), and the startup message shows
|
|
290
|
+
the port actually bound. If the whole range is busy, the worker exits
|
|
291
|
+
with a dedicated code (78) that the supervisor treats as fatal instead
|
|
292
|
+
of restarting. Fallback also applies to an explicit `--port` (with the
|
|
293
|
+
same warning).
|
|
294
|
+
- **Actions defined with `defineAction()` crashed at the action
|
|
295
|
+
endpoint** — `handleActionRequest` invoked actions as `action(...args)`
|
|
296
|
+
without building an `ActionContext`, so any action using the default
|
|
297
|
+
`concurrency: "latest"` failed with `ctx undefined` (`ctx.signal`
|
|
298
|
+
access). Defined actions (detected via their `__elurAction` metadata)
|
|
299
|
+
now receive `(input, ctx)` with `ctx.request`, `ctx.signal` (from the
|
|
300
|
+
request), `ctx.idempotencyKey` (from the `Idempotency-Key` header),
|
|
301
|
+
and empty `params`/`locals`. Legacy plain actions keep the `(...args)`
|
|
302
|
+
call convention unchanged.
|
|
303
|
+
- **Head metadata lost on SPA navigations in production** — the
|
|
304
|
+
production render endpoint (`/__elur-js/render`) only returned
|
|
305
|
+
`{ title, body }`, so pages navigated via the client router kept stale
|
|
306
|
+
`<head>` tags and the consumed action-error cookie was never cleared.
|
|
307
|
+
The endpoint now ships the full payload and the cookie is relayed as
|
|
308
|
+
`Set-Cookie` (HTML mode) / `X-Elur-Action-Clear-Cookie` (JSON mode).
|
|
309
|
+
- **`directive: "load"` did not hydrate on load** — the generated client
|
|
310
|
+
entry wrapped the whole hydration pass in `requestIdleCallback`, so
|
|
311
|
+
`load` islands were really `idle` islands. The entry now calls
|
|
312
|
+
`hydrateIslands()` immediately (module scripts are already deferred);
|
|
313
|
+
`idle`/`visible` keep their deferred scheduling inside the hydrator.
|
|
314
|
+
- **Islands were disposed after the DOM swap** — the generated entry
|
|
315
|
+
listened only for `elur:rendered` (post-swap), so disposers ran on
|
|
316
|
+
detached DOM and could not read live state. Cleanup now runs on
|
|
317
|
+
`elur:before-render`, dispatched before `#app` is replaced, with a
|
|
318
|
+
compat fallback on `elur:rendered` for hosts that only emit the old
|
|
319
|
+
event (e.g. the streaming swap chunk).
|
|
320
|
+
|
|
8
321
|
## [2.4.10]
|
|
9
322
|
|
|
10
323
|
### Fixed
|