hono-svelte 0.6.1 → 0.6.2

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 CHANGED
@@ -1,277 +1,299 @@
1
- # Changelog
2
-
3
- ## 0.6.1
4
-
5
- - **fix(shell): nested layouts nest outermost first.** With a root
6
- `layout.svelte` plus `<dir>/layout.svelte`, the server wrapped the page in
7
- the root layout first, so the directory layout ended up outside.
8
- - **fix(vite): client pages mount the whole layout chain.** The client entry
9
- imported every layout but only mounted `Layout0`; inner layouts vanished.
10
- Client entries are never server-rendered, so the chain is now mounted
11
- (no hydration of an empty root).
12
- - **fix(vite): components imported by static pages and layouts keep their
13
- `<style>`.** Only the page/layout files were compiled with
14
- `css: "injected"` for the server; a layout's nav or a component-library
15
- component lost its CSS on zero-JS pages.
16
- - **fix(handlers): `errorHandler` keeps the headers of `HTTPException`
17
- responses** — `basicAuth`/`bearerAuth` 401s now carry `WWW-Authenticate`.
18
- - **fix(shell): prefetch.** `prefetch: "all"` no longer preloads static pages
19
- (no JS chunk → 404); `"hover"` dedupes its links (it looked for
20
- `rel="prefetch"` but created `modulepreload`) and maps `/` → `index` and
21
- `/dir` → `dir/index`.
22
- - **fix(shell):** the global stylesheet fallback follows `assetsBase`; a
23
- throwing `nonce()` is logged instead of silently dropping the CSP nonce.
24
- - **fix(vite):** `pagesDir` outside the Vite root (including another drive
25
- on Windows) fails with a clear English error.
26
- - **test:** `test/regressions.test.ts` (one test per fix) and a Playwright
27
- contract suite in `e2e/` that runs the same browser tests against
28
- hono-svelte on Node, on Bun, and against SvelteKit as the reference. CI
29
- adds the e2e job and a peer-dependency matrix (declared minimums and
30
- latest within the majors).
31
-
32
- ## 0.6.0
33
-
34
- - **feat(shell): `htmlAttributes` option.** Only `lang` could be set on
35
- `<html>`, so themes driven by a root attribute (DaisyUI `data-theme`,
36
- `class="dark"`) needed CSS workarounds. `htmlAttributes` takes a record
37
- or `(c) => record` (per-request, e.g. from a cookie); values are escaped,
38
- invalid names (and `lang`, which has its own option) throw.
39
-
40
- - **feat(handlers): error pages get `{ status, message }`; JSON for API
41
- paths.** `notFoundHandler`/`errorHandler` rendered the page without any
42
- data, always with 404/500, and had no way to keep API routes on JSON.
43
- Now: pages receive `{ status, message }` (merged under `opts.data`);
44
- `errorHandler` uses the status carried by the error (`HTTPException`
45
- and friends), exposes the error message only for 4xx (`expose` to
46
- customize, `message` for the generic text) and logs only 5xx; new
47
- `json: (c) => boolean` answers `{ message }` JSON with the same status.
48
- `notFoundHandler` takes a `message`. `ErrorPageData` type exported.
49
-
50
- - **feat: `getPage()` from `hono-svelte/page`.** Layouts had no way to
51
- know which route rendered them (active-link logic needed the route
52
- threaded through every page's `data`). `getPage()` returns `{ path,
53
- route }` (request pathname + Hono route pattern, e.g. `/users/:id`) in
54
- server-rendered components and client pages alike: the shell provides it
55
- as Svelte context on SSR and writes `data-hs-path`/`data-hs-route` on the
56
- root element, which the generated client entry passes as `context` to
57
- `mount`/`hydrate`. New `./page` export; `PageInfo` type exported from the
58
- main entry.
59
-
60
- - **feat(shell): global stylesheet resolved from the manifest
61
- (`stylesEntry`).** With `assets: manifest`, the global CSS kept pointing at
62
- the unhashed `/static/styles.css`, so hashed client builds needed a
63
- hand-written `stylesHref: "/" + manifest["src/styles.css"].file`. The
64
- shell now resolves it from the manifest. New `stylesEntry` option
65
- (default `"src/styles.css"`) names the source file for both modes: dev
66
- links `/<stylesEntry>`, prod the hashed file (fallback
67
- `/static/styles.css`). `stylesHref` still wins when set.
68
-
69
- - **feat(types): `Entry` type exported from `hono-svelte`.** The generated
70
- `.d.ts` now also augments the package (`interface Register { entries }`),
71
- so helpers that forward an entry to `c.render` can be typed with
72
- `import type { Entry } from "hono-svelte"` instead of importing the
73
- generated file by relative path. Without the `.d.ts`, `Entry` is
74
- `string`. A new `test/types.test.ts` runs `tsc` against the generated
75
- declarations.
76
-
77
- - **fix(shell): the page's `<svelte:head>` title and description win.**
78
- The shell always emitted its default `<title>` first and browsers use the
79
- first one, so a static page's `<svelte:head><title>` was ignored (and
80
- `shell({ head })` descriptions were duplicated). The document now keeps a
81
- single title — `c.render(entry, { title })` > page/layout `<svelte:head>`
82
- > `shell({ title })` — and a page description (`<svelte:head>` or
83
- `head.description`) removes the one from `shell({ head })`.
84
-
85
- - **fix(assets): CSS of shared chunks is linked in production.** Vite
86
- attaches a module's CSS to the chunk that owns it, so a layout or
87
- component shared by several pages gets its own chunk + CSS file.
88
- `manifestCssFor`/`manifestImportsFor` (and therefore `shell({ assets:
89
- manifest })`) only read the entry chunk: pages rendered without their
90
- layout's styles. Both now walk the static `imports` graph (shared chunks
91
- first, cycles handled, `dynamicImports` skipped). `ViteManifestChunk`
92
- gains the optional `src`, `name` and `dynamicImports` fields.
93
- - **fix(vite): static pages and layouts keep their `<style>` after `vite
94
- build`.** Server-rendered components have no client bundle, but with
95
- vite-plugin-svelte's default `emitCss: true` the compiler emits their CSS
96
- as an import that SSR drops — zero-JS pages shipped unstyled. `pages()`
97
- now loads static pages and layouts for the server with
98
- `css: "injected"` (merged into an existing `<svelte:options>`, explicit
99
- `css` respected), so `render()` returns the styles in `head`. New
100
- `test/build.test.ts` exercises the real `vite build --ssr` pipeline.
101
-
102
- - **fix(scaffold): `isSafeAppPath` normalizes absolute targets.** An
103
- absolute path such as `/app/../evil.ts` passed the in-root prefix check
104
- on POSIX (only relative targets went through `resolve()`). Every target is
105
- now resolved before the comparison.
106
- - **CI green on main**: the Windows-only `isSafeAppPath` assertions run only
107
- on win32 (drive letters are relative segments on POSIX); the nested-layout
108
- test uses its temp dir as the Vite root (it only passed on Windows because
109
- tmpdir sits on another drive); the `example` job installs Bun, which its
110
- build script calls.
111
-
112
- ## 0.5.1
113
-
114
- - **Scaffold hardening** (`hono-svelte init`):
115
- - the generated `package.json` pins the security floors
116
- (`hono ^4.13.8`, `svelte ^5.57.0`, `vite ^8.3.0`) — the previous
117
- `^4`/`^5`/`^8` ranges resolved versions with known advisories; a
118
- regression test keeps the floors in sync with the package peers.
119
- - `init` writes an initial `src/hono-svelte-entries.d.ts` stub
120
- (plain-string renderer): `tsc` right after `init` used to fail on
121
- `c.render(entry, props)` (2 args vs hono's 1-arg default renderer)
122
- because the typed dts only exists after the first vite run;
123
- `pages({ dts: true })` replaces the stub with the typed union on the
124
- first `dev`/`build`.
125
- - **`c.render()` coexistence contract**: 6 tests pin the shell against
126
- hono's renderer semantics — `c.render(string)` (default renderer) is
127
- reinterpreted as an entry name under the shell (strict 500); renderer
128
- middlewares on the same path pattern are last-registered-wins; and
129
- **path-scoped coexistence works**: default-render routes and
130
- jsxRenderer-style middlewares keep working next to a `shell()` mounted on
131
- its own path (`app.use("/pages/*", shell())`). Documented in the README
132
- ("Coexistence with other renderers").
133
- - **CI**: new `scaffold` job smoke-tests `init --full` end to end (install
134
- with the local package via `file:` → typecheck → build), guarding the
135
- inline templates against drift from the (unpublished) playground example.
136
-
137
- ## 0.5.0
138
-
139
- - **Per-page data typing (opt-in, `pages({ dts: true })`)**: declare
140
- `export type Data` (or `interface Data`) in a page's `<script module>` and
141
- the generated `.d.ts` adds a typed overload per page —
142
- `c.render("dashboard", { data })` is checked by `tsc` (a missing/mistyped
143
- field fails the typecheck instead of shipping `undefined`). The declaration
144
- is inlined verbatim, so it must be self-contained; types that reference
145
- imports are skipped with a one-time warning and keep
146
- `Record<string, unknown>` data. `RenderProps` is now generic
147
- (`RenderProps<TData = Record<string, unknown>>`) and `pages().dataTypes()`
148
- exposes the extractions. The dts also regenerates when a page's `Data`
149
- type changes (dev watcher + `buildStart`).
150
- - **Neutral `<body>`**: the shell no longer hardcodes DaisyUI classes
151
- (`bg-base-200 min-h-screen text-base-content`) — it emits a plain,
152
- CSS-framework-agnostic document. Style `body`/`html` from your global
153
- stylesheet instead (`body { background: var(--color-base-200); ... }`, or
154
- `@apply` with Tailwind — the playground does exactly that in
155
- `styles.css`). **Breaking** for apps relying on the hardcoded classes.
156
- - **`shell({ ssrFallback })`**: when SSR of a page throws at request time,
157
- log the error and fall back to client rendering (entry script) instead of
158
- a 500. Default `false` keeps the previous behavior.
159
- - `prefetch: "all"` / `"hover"` now emit `rel="modulepreload"` (prefetch on a
160
- module URL could double-fetch in some browsers; modulepreload also warms
161
- the entry's import graph).
162
- - `dataLimit` warning fires once per **(page, limit)** instead of once per
163
- process — a second oversized page is reported too, with the entry name in
164
- the message.
165
- - **Removed dead stubs**: `versionsMatch()` (always returned `true`), the
166
- unused `hasClient` export from the ssr-manifest stub/virtual manifest, and
167
- the no-op `checkPagesDir`. **Breaking: `versionsMatch` is no longer
168
- exported.**
169
- - Fully typed handlers/middleware: `notFoundHandler()` / `errorHandler()`
170
- match Hono's `NotFoundHandler`/`ErrorHandler` signatures and `shell()` no
171
- longer uses `any` internally.
172
- - **CLI: `hono-svelte add page <name>`** — scaffolds
173
- `src/pages/<name>.svelte` (static by default, skip-if-exists, traversal
174
- refused). `isValidPageName()` / `newPageFile()` exported for tests.
175
- - Metadata: `engines` now declares `bun >= 1.4` and `node >= 26`, the
176
- `./package.json` export subpath was added, and TypeScript 7 builds the
177
- package.
178
- - **Security floors in `peerDependencies`**: `hono ^4.13.8`, `svelte ^5.57.0`,
179
- `vite ^8.3.0` — every published advisory sits outside these floors
180
- (hono < 4.12.12 and 4.12.0–4.12.33, patched in 4.12.34; svelte ≤ 5.55.6,
181
- patched in 5.55.7; vite ≤ 8.0.15, patched in 8.0.16; transitive `devalue`
182
- resolves to the patched 5.9.2, which also closes CVE-2026-81176). The
183
- versions installed in the repo and in the example are the latest published
184
- and none is affected by a known advisory — the ranges just avoid
185
- advertising vulnerable versions to consumers and scanners.
186
- - CI: ubuntu + windows runners (Windows-only regressions like the old
187
- `isSafeAppPath` bug are caught now), coverage via `@vitest/coverage-v8`
188
- with thresholds (`npm test` runs it), all on the officially supported
189
- runtimes (Node 26, TS 7, Vite 8, Bun 1.4+).
190
- - `hono-svelte init` now scaffolds TypeScript 7 + `@types/node` 26 in the
191
- generated app.
192
-
193
- ## 0.4.2
194
-
195
- - `hono-svelte init` now scaffolds a complete runnable app (works with
196
- `npx`, `bunx` and `pnpm dlx` — writes text files only, no install/network).
197
- Minimal flavor (default): `package.json`, `tsconfig.json`, `.gitignore`,
198
- `vite.config.ts` (with `pages({ dts: true })`), two pages (`index` zero-JS,
199
- `hello` with `data`), `src/routes/index.ts`, `src/styles.css`.
200
- `init --full` adds auth (cookie session), dashboard with nested layout,
201
- and typed RPC (`hc<AppType>`) — `/auth` → login → `/dashboard`.
202
- Generated scripts (`dev`/`build`/`typecheck`/`start`) run under npm and bun.
203
- `InitFlavor` type exported.
204
-
205
- ## 0.4.1
206
-
207
- - Fix `hono-svelte init` refusing every file on Windows: `isSafeAppPath()`
208
- now normalizes `\` vs `/` and compares case-insensitively on win32
209
- (path traversal still blocked).
210
- - Fix `bin` path without `./` prefix — npm was silently stripping the CLI
211
- from the published tarball.
212
-
213
- ## 0.4.0
214
-
215
- - **Typed `c.render` entries (opt-in)**: `pages({ dts })` writes a `.d.ts` file
216
- with the entry union (`HonoSvelteEntries`) + `declare module "hono"`
217
- augmentation. `true` writes `src/hono-svelte-entries.d.ts` (relative to the
218
- Vite root); a string sets a custom path. Regenerated on `buildStart` and by
219
- the dev watcher when entries change. `dtsPath()` / `writeDts()` exposed on
220
- the plugin. The playground wires it (`pages({ dts: true })` +
221
- `/// <reference path="./hono-svelte-entries.d.ts" />` in `env.d.ts`), so a
222
- typo in `c.render("...")` fails `tsc` instead of 500ing at runtime.
223
- `data` stays `Record<string, unknown>` (per-page payload types are a future
224
- feature).
225
- - **Layouts, single source of truth**: `<dir>/layout.svelte` wraps pages
226
- automatically (SSR inside-out + client hydrate, `layouts()` /
227
- `layoutChain()` to inspect, `layouts: false` = legacy ignore). Pages must
228
- NOT import their layout manually — the plugin fails fast at build time
229
- (`manually imports ... but the layout is applied automatically`) instead of
230
- rendering the layout twice. Layout props (`title`, `description`) come from
231
- the route via `c.render(entry, { data })`, with `title = "Dashboard"`
232
- fallback in the layout. Rule shared by plugin and shell: static means
233
- "no `<script>`", layouts don't matter; `__clientEntries` travels in the
234
- virtual manifest so the shell picks the SSR branch for static+layouts pages.
235
- - **Runtime fixes**: static `import.meta.env.PROD/DEV` access (Vite module
236
- runner forbids dynamic access — dev 500 fixed); static
237
- `createRawSnippet` import in layout entries (no duplicated Svelte runtime —
238
- `Illegal invocation` fixed); layout children snippet renders `<div>` wrapper
239
- (empty render broke hydration — `Cannot set properties of null` fixed);
240
- bundled server defaults to prod so plain `node ./dist/index.js` works on
241
- Windows with no `NODE_ENV` prefix.
242
- - `c.render(entry, { status, headers, head })`: per-render status/headers
243
- (merged over shell defaults) and CSP-safe per-page head tags
244
- (`description`, `canonical`, `og`, `twitter`, `meta`, `extra`).
245
- - SSR static pages now receive `data` as component props (plus the embedded JSON).
246
- - Strict entries: unknown entryNames throw a dev-friendly error (`Available pages: ...`, generic in prod); `shell({ strict: false })` disables; `availableEntries()` exposes the list.
247
- - `pages().types()` / `typeDeclarations()` generate the entry union + hono module snippet.
248
- - Nested layouts: `<dir>/layout.svelte` wraps pages (SSR inside-out, `layouts()`/`layoutChain()`, `layouts: false` = legacy).
249
- - `notFoundHandler()` / `errorHandler()` for conventional `404.svelte` / `500.svelte` pages.
250
- - `hono-svelte init|doctor` CLI (`bin`) with `--app` flag.
251
- - `pages().validateConfig()` fails fast on missing svelte plugin / pagesDir.
252
- - `shell({ assets })` accepts a Vite manifest (hashed URLs, CSS, shared-chunk preload) with fallback; `preload`/`prefetch` controls; `styles` extra CSS.
253
- - `shell({ nonce })` CSP support on shell tags; `svelte/server` CSP passthrough for SSR.
254
- - Cache helpers: `immutableHeaders()`, `CACHE_IMMUTABLE`, `CACHE_NO_STORE`.
255
- - `data` size one-time warning (`shell({ dataLimit })`, default 100KB).
256
-
257
- ## 0.3.1
258
-
259
- - Package metadata: repository/homepage/author, publishConfig, LICENSE + CHANGELOG in files.
260
- - No code changes since 0.3.0.
261
-
262
- ## 0.3.0
263
-
264
- - Fully in-memory package: entries and manifest are virtual modules (no files written to disk).
265
- - Automatic zero-JS: `.svelte` pages without `<script>` render on the server (`svelte/server`)
266
- and download no JS on the client.
267
- - Deterministic opaque IDs derived from the entryName (no salt, no env, no disk).
268
- - Zero config: the shell resolves the SSR map on its own (relative import redirected by the plugin).
269
- - Test suite (vitest) and CI (typecheck + tests + build + publint).
270
-
271
- ## 0.2.0
272
-
273
- - Opaque IDs (`r-` + hash) replace configurable `rootId`/`dataId`.
274
-
275
- ## 0.1.0
276
-
1
+ # Changelog
2
+
3
+ ## 0.6.2
4
+
5
+ - **fix(shell): unknown-entry errors never list the app's pages in
6
+ production** (as documented for `knownEntries`); dev keeps the list.
7
+ - **refactor:** the layout chain lives in one module (`src/layouts.ts`)
8
+ shared by the Vite plugin and the shell — the 0.6.1 nesting bug came from
9
+ two diverging copies. Removed dead code (`isDev`, an unused prefetch
10
+ parameter).
11
+ - **perf(shell):** the Vite manifest is resolved once per shell and its
12
+ per-entry CSS/preload lists are cached, instead of walking the chunk graph
13
+ on every request.
14
+ - **getPage().route** keeps reading the request's own `routePath`: the
15
+ `hono/route` helper breaks with duplicate hono copies (found by the new
16
+ playground e2e run). A test guards against runtime hono imports.
17
+ - **Node 24 (LTS) supported**: `engines.node` is `>=24` (package and
18
+ `init` template); CI runs Node 24 and 26.
19
+ - **CI/release:** `release.yml` publishes tags with npm trusted publishing
20
+ (OIDC, provenance) and creates the GitHub release from this file;
21
+ Dependabot for npm and actions; coverage floors raised to the measured
22
+ level; e2e adds `vite` dev and the playground app (sign-in, session,
23
+ dashboard).
24
+
25
+ ## 0.6.1
26
+
27
+ - **fix(shell): nested layouts nest outermost first.** With a root
28
+ `layout.svelte` plus `<dir>/layout.svelte`, the server wrapped the page in
29
+ the root layout first, so the directory layout ended up outside.
30
+ - **fix(vite): client pages mount the whole layout chain.** The client entry
31
+ imported every layout but only mounted `Layout0`; inner layouts vanished.
32
+ Client entries are never server-rendered, so the chain is now mounted
33
+ (no hydration of an empty root).
34
+ - **fix(vite): components imported by static pages and layouts keep their
35
+ `<style>`.** Only the page/layout files were compiled with
36
+ `css: "injected"` for the server; a layout's nav or a component-library
37
+ component lost its CSS on zero-JS pages.
38
+ - **fix(handlers): `errorHandler` keeps the headers of `HTTPException`
39
+ responses** — `basicAuth`/`bearerAuth` 401s now carry `WWW-Authenticate`.
40
+ - **fix(shell): prefetch.** `prefetch: "all"` no longer preloads static pages
41
+ (no JS chunk → 404); `"hover"` dedupes its links (it looked for
42
+ `rel="prefetch"` but created `modulepreload`) and maps `/` → `index` and
43
+ `/dir` → `dir/index`.
44
+ - **fix(shell):** the global stylesheet fallback follows `assetsBase`; a
45
+ throwing `nonce()` is logged instead of silently dropping the CSP nonce.
46
+ - **fix(vite):** `pagesDir` outside the Vite root (including another drive
47
+ on Windows) fails with a clear English error.
48
+ - **test:** `test/regressions.test.ts` (one test per fix) and a Playwright
49
+ contract suite in `e2e/` that runs the same browser tests against
50
+ hono-svelte on Node, on Bun, and against SvelteKit as the reference. CI
51
+ adds the e2e job and a peer-dependency matrix (declared minimums and
52
+ latest within the majors).
53
+
54
+ ## 0.6.0
55
+
56
+ - **feat(shell): `htmlAttributes` option.** Only `lang` could be set on
57
+ `<html>`, so themes driven by a root attribute (DaisyUI `data-theme`,
58
+ `class="dark"`) needed CSS workarounds. `htmlAttributes` takes a record
59
+ or `(c) => record` (per-request, e.g. from a cookie); values are escaped,
60
+ invalid names (and `lang`, which has its own option) throw.
61
+
62
+ - **feat(handlers): error pages get `{ status, message }`; JSON for API
63
+ paths.** `notFoundHandler`/`errorHandler` rendered the page without any
64
+ data, always with 404/500, and had no way to keep API routes on JSON.
65
+ Now: pages receive `{ status, message }` (merged under `opts.data`);
66
+ `errorHandler` uses the status carried by the error (`HTTPException`
67
+ and friends), exposes the error message only for 4xx (`expose` to
68
+ customize, `message` for the generic text) and logs only 5xx; new
69
+ `json: (c) => boolean` answers `{ message }` JSON with the same status.
70
+ `notFoundHandler` takes a `message`. `ErrorPageData` type exported.
71
+
72
+ - **feat: `getPage()` from `hono-svelte/page`.** Layouts had no way to
73
+ know which route rendered them (active-link logic needed the route
74
+ threaded through every page's `data`). `getPage()` returns `{ path,
75
+ route }` (request pathname + Hono route pattern, e.g. `/users/:id`) in
76
+ server-rendered components and client pages alike: the shell provides it
77
+ as Svelte context on SSR and writes `data-hs-path`/`data-hs-route` on the
78
+ root element, which the generated client entry passes as `context` to
79
+ `mount`/`hydrate`. New `./page` export; `PageInfo` type exported from the
80
+ main entry.
81
+
82
+ - **feat(shell): global stylesheet resolved from the manifest
83
+ (`stylesEntry`).** With `assets: manifest`, the global CSS kept pointing at
84
+ the unhashed `/static/styles.css`, so hashed client builds needed a
85
+ hand-written `stylesHref: "/" + manifest["src/styles.css"].file`. The
86
+ shell now resolves it from the manifest. New `stylesEntry` option
87
+ (default `"src/styles.css"`) names the source file for both modes: dev
88
+ links `/<stylesEntry>`, prod the hashed file (fallback
89
+ `/static/styles.css`). `stylesHref` still wins when set.
90
+
91
+ - **feat(types): `Entry` type exported from `hono-svelte`.** The generated
92
+ `.d.ts` now also augments the package (`interface Register { entries }`),
93
+ so helpers that forward an entry to `c.render` can be typed with
94
+ `import type { Entry } from "hono-svelte"` instead of importing the
95
+ generated file by relative path. Without the `.d.ts`, `Entry` is
96
+ `string`. A new `test/types.test.ts` runs `tsc` against the generated
97
+ declarations.
98
+
99
+ - **fix(shell): the page's `<svelte:head>` title and description win.**
100
+ The shell always emitted its default `<title>` first and browsers use the
101
+ first one, so a static page's `<svelte:head><title>` was ignored (and
102
+ `shell({ head })` descriptions were duplicated). The document now keeps a
103
+ single title — `c.render(entry, { title })` > page/layout `<svelte:head>`
104
+ > `shell({ title })` — and a page description (`<svelte:head>` or
105
+ `head.description`) removes the one from `shell({ head })`.
106
+
107
+ - **fix(assets): CSS of shared chunks is linked in production.** Vite
108
+ attaches a module's CSS to the chunk that owns it, so a layout or
109
+ component shared by several pages gets its own chunk + CSS file.
110
+ `manifestCssFor`/`manifestImportsFor` (and therefore `shell({ assets:
111
+ manifest })`) only read the entry chunk: pages rendered without their
112
+ layout's styles. Both now walk the static `imports` graph (shared chunks
113
+ first, cycles handled, `dynamicImports` skipped). `ViteManifestChunk`
114
+ gains the optional `src`, `name` and `dynamicImports` fields.
115
+ - **fix(vite): static pages and layouts keep their `<style>` after `vite
116
+ build`.** Server-rendered components have no client bundle, but with
117
+ vite-plugin-svelte's default `emitCss: true` the compiler emits their CSS
118
+ as an import that SSR drops — zero-JS pages shipped unstyled. `pages()`
119
+ now loads static pages and layouts for the server with
120
+ `css: "injected"` (merged into an existing `<svelte:options>`, explicit
121
+ `css` respected), so `render()` returns the styles in `head`. New
122
+ `test/build.test.ts` exercises the real `vite build --ssr` pipeline.
123
+
124
+ - **fix(scaffold): `isSafeAppPath` normalizes absolute targets.** An
125
+ absolute path such as `/app/../evil.ts` passed the in-root prefix check
126
+ on POSIX (only relative targets went through `resolve()`). Every target is
127
+ now resolved before the comparison.
128
+ - **CI green on main**: the Windows-only `isSafeAppPath` assertions run only
129
+ on win32 (drive letters are relative segments on POSIX); the nested-layout
130
+ test uses its temp dir as the Vite root (it only passed on Windows because
131
+ tmpdir sits on another drive); the `example` job installs Bun, which its
132
+ build script calls.
133
+
134
+ ## 0.5.1
135
+
136
+ - **Scaffold hardening** (`hono-svelte init`):
137
+ - the generated `package.json` pins the security floors
138
+ (`hono ^4.13.8`, `svelte ^5.57.0`, `vite ^8.3.0`) — the previous
139
+ `^4`/`^5`/`^8` ranges resolved versions with known advisories; a
140
+ regression test keeps the floors in sync with the package peers.
141
+ - `init` writes an initial `src/hono-svelte-entries.d.ts` stub
142
+ (plain-string renderer): `tsc` right after `init` used to fail on
143
+ `c.render(entry, props)` (2 args vs hono's 1-arg default renderer)
144
+ because the typed dts only exists after the first vite run;
145
+ `pages({ dts: true })` replaces the stub with the typed union on the
146
+ first `dev`/`build`.
147
+ - **`c.render()` coexistence contract**: 6 tests pin the shell against
148
+ hono's renderer semantics — `c.render(string)` (default renderer) is
149
+ reinterpreted as an entry name under the shell (strict 500); renderer
150
+ middlewares on the same path pattern are last-registered-wins; and
151
+ **path-scoped coexistence works**: default-render routes and
152
+ jsxRenderer-style middlewares keep working next to a `shell()` mounted on
153
+ its own path (`app.use("/pages/*", shell())`). Documented in the README
154
+ ("Coexistence with other renderers").
155
+ - **CI**: new `scaffold` job smoke-tests `init --full` end to end (install
156
+ with the local package via `file:` → typecheck → build), guarding the
157
+ inline templates against drift from the (unpublished) playground example.
158
+
159
+ ## 0.5.0
160
+
161
+ - **Per-page data typing (opt-in, `pages({ dts: true })`)**: declare
162
+ `export type Data` (or `interface Data`) in a page's `<script module>` and
163
+ the generated `.d.ts` adds a typed overload per page —
164
+ `c.render("dashboard", { data })` is checked by `tsc` (a missing/mistyped
165
+ field fails the typecheck instead of shipping `undefined`). The declaration
166
+ is inlined verbatim, so it must be self-contained; types that reference
167
+ imports are skipped with a one-time warning and keep
168
+ `Record<string, unknown>` data. `RenderProps` is now generic
169
+ (`RenderProps<TData = Record<string, unknown>>`) and `pages().dataTypes()`
170
+ exposes the extractions. The dts also regenerates when a page's `Data`
171
+ type changes (dev watcher + `buildStart`).
172
+ - **Neutral `<body>`**: the shell no longer hardcodes DaisyUI classes
173
+ (`bg-base-200 min-h-screen text-base-content`) — it emits a plain,
174
+ CSS-framework-agnostic document. Style `body`/`html` from your global
175
+ stylesheet instead (`body { background: var(--color-base-200); ... }`, or
176
+ `@apply` with Tailwind — the playground does exactly that in
177
+ `styles.css`). **Breaking** for apps relying on the hardcoded classes.
178
+ - **`shell({ ssrFallback })`**: when SSR of a page throws at request time,
179
+ log the error and fall back to client rendering (entry script) instead of
180
+ a 500. Default `false` keeps the previous behavior.
181
+ - `prefetch: "all"` / `"hover"` now emit `rel="modulepreload"` (prefetch on a
182
+ module URL could double-fetch in some browsers; modulepreload also warms
183
+ the entry's import graph).
184
+ - `dataLimit` warning fires once per **(page, limit)** instead of once per
185
+ process — a second oversized page is reported too, with the entry name in
186
+ the message.
187
+ - **Removed dead stubs**: `versionsMatch()` (always returned `true`), the
188
+ unused `hasClient` export from the ssr-manifest stub/virtual manifest, and
189
+ the no-op `checkPagesDir`. **Breaking: `versionsMatch` is no longer
190
+ exported.**
191
+ - Fully typed handlers/middleware: `notFoundHandler()` / `errorHandler()`
192
+ match Hono's `NotFoundHandler`/`ErrorHandler` signatures and `shell()` no
193
+ longer uses `any` internally.
194
+ - **CLI: `hono-svelte add page <name>`** — scaffolds
195
+ `src/pages/<name>.svelte` (static by default, skip-if-exists, traversal
196
+ refused). `isValidPageName()` / `newPageFile()` exported for tests.
197
+ - Metadata: `engines` now declares `bun >= 1.4` and `node >= 26`, the
198
+ `./package.json` export subpath was added, and TypeScript 7 builds the
199
+ package.
200
+ - **Security floors in `peerDependencies`**: `hono ^4.13.8`, `svelte ^5.57.0`,
201
+ `vite ^8.3.0` — every published advisory sits outside these floors
202
+ (hono < 4.12.12 and 4.12.0–4.12.33, patched in 4.12.34; svelte ≤ 5.55.6,
203
+ patched in 5.55.7; vite ≤ 8.0.15, patched in 8.0.16; transitive `devalue`
204
+ resolves to the patched 5.9.2, which also closes CVE-2026-81176). The
205
+ versions installed in the repo and in the example are the latest published
206
+ and none is affected by a known advisory — the ranges just avoid
207
+ advertising vulnerable versions to consumers and scanners.
208
+ - CI: ubuntu + windows runners (Windows-only regressions like the old
209
+ `isSafeAppPath` bug are caught now), coverage via `@vitest/coverage-v8`
210
+ with thresholds (`npm test` runs it), all on the officially supported
211
+ runtimes (Node 26, TS 7, Vite 8, Bun 1.4+).
212
+ - `hono-svelte init` now scaffolds TypeScript 7 + `@types/node` 26 in the
213
+ generated app.
214
+
215
+ ## 0.4.2
216
+
217
+ - `hono-svelte init` now scaffolds a complete runnable app (works with
218
+ `npx`, `bunx` and `pnpm dlx` — writes text files only, no install/network).
219
+ Minimal flavor (default): `package.json`, `tsconfig.json`, `.gitignore`,
220
+ `vite.config.ts` (with `pages({ dts: true })`), two pages (`index` zero-JS,
221
+ `hello` with `data`), `src/routes/index.ts`, `src/styles.css`.
222
+ `init --full` adds auth (cookie session), dashboard with nested layout,
223
+ and typed RPC (`hc<AppType>`) — `/auth` → login → `/dashboard`.
224
+ Generated scripts (`dev`/`build`/`typecheck`/`start`) run under npm and bun.
225
+ `InitFlavor` type exported.
226
+
227
+ ## 0.4.1
228
+
229
+ - Fix `hono-svelte init` refusing every file on Windows: `isSafeAppPath()`
230
+ now normalizes `\` vs `/` and compares case-insensitively on win32
231
+ (path traversal still blocked).
232
+ - Fix `bin` path without `./` prefix — npm was silently stripping the CLI
233
+ from the published tarball.
234
+
235
+ ## 0.4.0
236
+
237
+ - **Typed `c.render` entries (opt-in)**: `pages({ dts })` writes a `.d.ts` file
238
+ with the entry union (`HonoSvelteEntries`) + `declare module "hono"`
239
+ augmentation. `true` writes `src/hono-svelte-entries.d.ts` (relative to the
240
+ Vite root); a string sets a custom path. Regenerated on `buildStart` and by
241
+ the dev watcher when entries change. `dtsPath()` / `writeDts()` exposed on
242
+ the plugin. The playground wires it (`pages({ dts: true })` +
243
+ `/// <reference path="./hono-svelte-entries.d.ts" />` in `env.d.ts`), so a
244
+ typo in `c.render("...")` fails `tsc` instead of 500ing at runtime.
245
+ `data` stays `Record<string, unknown>` (per-page payload types are a future
246
+ feature).
247
+ - **Layouts, single source of truth**: `<dir>/layout.svelte` wraps pages
248
+ automatically (SSR inside-out + client hydrate, `layouts()` /
249
+ `layoutChain()` to inspect, `layouts: false` = legacy ignore). Pages must
250
+ NOT import their layout manually — the plugin fails fast at build time
251
+ (`manually imports ... but the layout is applied automatically`) instead of
252
+ rendering the layout twice. Layout props (`title`, `description`) come from
253
+ the route via `c.render(entry, { data })`, with `title = "Dashboard"`
254
+ fallback in the layout. Rule shared by plugin and shell: static means
255
+ "no `<script>`", layouts don't matter; `__clientEntries` travels in the
256
+ virtual manifest so the shell picks the SSR branch for static+layouts pages.
257
+ - **Runtime fixes**: static `import.meta.env.PROD/DEV` access (Vite module
258
+ runner forbids dynamic access — dev 500 fixed); static
259
+ `createRawSnippet` import in layout entries (no duplicated Svelte runtime —
260
+ `Illegal invocation` fixed); layout children snippet renders `<div>` wrapper
261
+ (empty render broke hydration — `Cannot set properties of null` fixed);
262
+ bundled server defaults to prod so plain `node ./dist/index.js` works on
263
+ Windows with no `NODE_ENV` prefix.
264
+ - `c.render(entry, { status, headers, head })`: per-render status/headers
265
+ (merged over shell defaults) and CSP-safe per-page head tags
266
+ (`description`, `canonical`, `og`, `twitter`, `meta`, `extra`).
267
+ - SSR static pages now receive `data` as component props (plus the embedded JSON).
268
+ - Strict entries: unknown entryNames throw a dev-friendly error (`Available pages: ...`, generic in prod); `shell({ strict: false })` disables; `availableEntries()` exposes the list.
269
+ - `pages().types()` / `typeDeclarations()` generate the entry union + hono module snippet.
270
+ - Nested layouts: `<dir>/layout.svelte` wraps pages (SSR inside-out, `layouts()`/`layoutChain()`, `layouts: false` = legacy).
271
+ - `notFoundHandler()` / `errorHandler()` for conventional `404.svelte` / `500.svelte` pages.
272
+ - `hono-svelte init|doctor` CLI (`bin`) with `--app` flag.
273
+ - `pages().validateConfig()` fails fast on missing svelte plugin / pagesDir.
274
+ - `shell({ assets })` accepts a Vite manifest (hashed URLs, CSS, shared-chunk preload) with fallback; `preload`/`prefetch` controls; `styles` extra CSS.
275
+ - `shell({ nonce })` CSP support on shell tags; `svelte/server` CSP passthrough for SSR.
276
+ - Cache helpers: `immutableHeaders()`, `CACHE_IMMUTABLE`, `CACHE_NO_STORE`.
277
+ - `data` size one-time warning (`shell({ dataLimit })`, default 100KB).
278
+
279
+ ## 0.3.1
280
+
281
+ - Package metadata: repository/homepage/author, publishConfig, LICENSE + CHANGELOG in files.
282
+ - No code changes since 0.3.0.
283
+
284
+ ## 0.3.0
285
+
286
+ - Fully in-memory package: entries and manifest are virtual modules (no files written to disk).
287
+ - Automatic zero-JS: `.svelte` pages without `<script>` render on the server (`svelte/server`)
288
+ and download no JS on the client.
289
+ - Deterministic opaque IDs derived from the entryName (no salt, no env, no disk).
290
+ - Zero config: the shell resolves the SSR map on its own (relative import redirected by the plugin).
291
+ - Test suite (vitest) and CI (typecheck + tests + build + publint).
292
+
293
+ ## 0.2.0
294
+
295
+ - Opaque IDs (`r-` + hash) replace configurable `rootId`/`dataId`.
296
+
297
+ ## 0.1.0
298
+
277
299
  - First version: `shell()` middleware + `pages()` plugin with disk-generated entries.