hono-svelte 0.3.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 hono-svelte contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # hono-svelte
2
+
3
+ Shell server-side + paginas Svelte multi-entry para apps Hono, 100% in-memory
4
+ (nenhum arquivo gerado em disco) e com zero-JS automatico para paginas estaticas.
5
+ Zero config: o app nao importa nada alem de `shell()` e `pages()`.
6
+
7
+ - `hono-svelte` — middleware `shell()`: registra `c.render(entry, { title?, data? })`.
8
+ - `data` (pequeno, publico, serializavel) vai embutido no HTML
9
+ (`<script type="application/json">` com id opaco) e chega na pagina via `$props()`.
10
+ - Dados privados, grandes ou mutaveis: buscar via RPC (`hc<AppType>`) apos o mount.
11
+ - Zero config: o shell descobre sozinho quais paginas sao estaticas e
12
+ renderiza no servidor via `svelte/server`, sem import nenhum no app.
13
+ - `hono-svelte/vite` — plugin `pages()`: cada `*.svelte` em `pagesDir`
14
+ (exceto parciais) vira um entry virtual com `mount()` automatico.
15
+ Nada e escrito em disco.
16
+
17
+ ## Uso
18
+
19
+ ```ts
20
+ // vite.config.ts
21
+ import { pages } from "hono-svelte/vite";
22
+ const appPages = pages(); // defaults: src/pages
23
+ // input: { ...appPages.input(), styles: "src/styles.css" }
24
+ // plugins: [tailwindcss(), svelte(), appPages, ...] em TODOS os modos
25
+ // (client, serve e server build) - o modo server precisa de appPages para
26
+ // o shell enxergar o manifesto in-memory.
27
+ ```
28
+
29
+ ```ts
30
+ // servidor
31
+ import { shell } from "hono-svelte";
32
+
33
+ app.use("/*", shell({ title: "Meu App", lang: "pt-BR" }));
34
+ app.get("/dashboard", (c) => c.render("dashboard", { data: { plan: "pro" } }));
35
+ ```
36
+
37
+ ```svelte
38
+ <!-- pagina estatica: sem <script> => zero-JS, HTML vem pronto do servidor -->
39
+ <main>...</main>
40
+ ```
41
+
42
+ ## Zero-JS automatico
43
+
44
+ Pagina sem `<script>` no `.svelte` nao gera entry client: o `shell()` renderiza
45
+ o HTML no servidor e nenhum `<script>` e enviado ao cliente. Para forcar client
46
+ JS em uma pagina especifica: `pages({ alwaysClient: ["index"] })`.
47
+ Para override manual do mapa de SSR: `shell({ ssrPages })` (opcional).
48
+
49
+ ## IDs
50
+
51
+ `root`/`data` usam ids opacos derivados do `entryName` via `getIds(entryName)`
52
+ (`r-` + hash). Servidor e client derivam o mesmo par sem config, sem disco, sem salt.
53
+
54
+ ## Repo
55
+
56
+ - `/` — o pacote `hono-svelte` (src, test, dist)
57
+ - `/examples/playground` — app Hono de demonstracao, consome o pacote via `file:../..`
58
+
59
+ ## Tipagem do `c.render`
60
+
61
+ Augmente o `ContextRenderer` no SEU app (padrao do Hono) — crie um `env.d.ts`:
62
+
63
+ ```ts
64
+ import type { RenderProps } from "hono-svelte";
65
+
66
+ declare module "hono" {
67
+ interface ContextRenderer {
68
+ (entryName: string, props?: RenderProps): Response | Promise<Response>;
69
+ }
70
+ }
71
+ ```
package/dist/ids.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export type SharedIds = {
2
+ rootId: string;
3
+ dataId: string;
4
+ };
5
+ export declare function getIds(entryName: string): SharedIds;
package/dist/ids.js ADDED
@@ -0,0 +1,22 @@
1
+ // IDs opacos derivados do entryName. Puro, sem dependencias de runtime.
2
+ // (funciona em Node, Workers, Deno, Bun). Nao e segredo de estado:
3
+ // apenas evita expor dados do servidor no HTML.
4
+ function hashString(input) {
5
+ let h1 = 0xdeadbeef;
6
+ let h2 = 0x41c6ce57;
7
+ for (let i = 0; i < input.length; i++) {
8
+ const ch = input.charCodeAt(i);
9
+ h1 = Math.imul(h1 ^ ch, 2654435761);
10
+ h2 = Math.imul(h2 ^ ch, 1597334677);
11
+ }
12
+ h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
13
+ h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
14
+ const n = 4294967296 * (2097151 & h2) + (h1 >>> 0);
15
+ return n.toString(16).padStart(12, '0').slice(0, 8);
16
+ }
17
+ export function getIds(entryName) {
18
+ return {
19
+ rootId: 'r-' + hashString(entryName + ':root'),
20
+ dataId: 'r-' + hashString(entryName + ':data'),
21
+ };
22
+ }
@@ -0,0 +1,24 @@
1
+ import type { Next } from "hono";
2
+ export { getIds } from "./ids.js";
3
+ export type { SharedIds } from "./ids.js";
4
+ export type SsrPageLoader = () => Promise<{
5
+ default: unknown;
6
+ }>;
7
+ export type RenderProps = {
8
+ title?: string;
9
+ data?: Record<string, unknown>;
10
+ };
11
+ export type ShellOptions = {
12
+ title?: string;
13
+ lang?: string;
14
+ assetsBase?: string;
15
+ stylesHref?: string | ((isProd: boolean) => string);
16
+ head?: string;
17
+ /** Override manual do mapa entryName -> loader (opcional; sem isso o shell
18
+ * resolve o mapa automaticamente via ssr-manifest). */
19
+ ssrPages?: Record<string, SsrPageLoader>;
20
+ };
21
+ export type ShellContext = {
22
+ render: (entryName: string, props?: RenderProps) => Response | Promise<Response>;
23
+ };
24
+ export declare function shell(options?: ShellOptions): (c: any, next: Next) => Promise<void>;
package/dist/index.js ADDED
@@ -0,0 +1,84 @@
1
+ import { getIds } from "./ids.js";
2
+ import { ssrPages as autoSsrPages } from "./ssr-manifest.js";
3
+ import { devEntryUrl } from "./virtual.js";
4
+ export { getIds } from "./ids.js";
5
+ function escapeAttr(value) {
6
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
7
+ }
8
+ function isValidEntryName(entryName) {
9
+ if (!entryName || entryName.length > 200)
10
+ return false;
11
+ if (entryName.startsWith("/") || entryName.startsWith("."))
12
+ return false;
13
+ if (entryName.includes("\\"))
14
+ return false;
15
+ const parts = entryName.split("/");
16
+ return parts.every((p) => p.length > 0 && p !== "." && p !== ".." && !p.includes(String.fromCharCode(0)));
17
+ }
18
+ function serializePageData(data, dataId) {
19
+ if (!data)
20
+ return "";
21
+ let json;
22
+ try {
23
+ json = JSON.stringify(data);
24
+ }
25
+ catch {
26
+ throw new Error("hono-svelte: props.data precisa ser serializavel em JSON");
27
+ }
28
+ if (json === undefined)
29
+ return "";
30
+ json = json.replace(/</g, "\\u003c");
31
+ const lt = String.fromCharCode(60);
32
+ return lt + 'script type="application/json" id="' + dataId + '">' + json + lt + "/script>";
33
+ }
34
+ export function shell(options = {}) {
35
+ const titleDefault = options.title ?? "App";
36
+ const lang = options.lang ?? "en";
37
+ const assetsBase = (options.assetsBase ?? "/static").replace(/\/$/, "");
38
+ const head = options.head ?? "";
39
+ const resolveStyles = typeof options.stylesHref === "function"
40
+ ? options.stylesHref
41
+ : () => options.stylesHref;
42
+ // Zero config: com pages() ativo, este modulo e redirecionado para o
43
+ // manifesto in-memory pelo resolveId do plugin (enforce: "pre").
44
+ const ssrPages = options.ssrPages ?? autoSsrPages;
45
+ return async function shellMiddleware(c, next) {
46
+ c.setRenderer(async (entryName, props) => {
47
+ if (!isValidEntryName(entryName)) {
48
+ throw new Error(`hono-svelte: entryName invalido: ${JSON.stringify(entryName)}`);
49
+ }
50
+ const ids = getIds(entryName);
51
+ const title = props?.title ?? titleDefault;
52
+ const isProd = import.meta.env?.PROD ?? false;
53
+ const stylesHref = resolveStyles(isProd) ?? (isProd ? "/static/styles.css" : "/src/styles.css");
54
+ const dataHtml = props?.data ? serializePageData(props.data, ids.dataId) : "";
55
+ const lt = String.fromCharCode(60);
56
+ const loader = ssrPages[entryName];
57
+ let bodyHtml = "";
58
+ let ssrHead = "";
59
+ if (loader !== undefined) {
60
+ const mod = await loader();
61
+ const { render } = await import("svelte/server");
62
+ const rendered = render(mod.default);
63
+ bodyHtml = rendered.html;
64
+ ssrHead = rendered.head ?? "";
65
+ }
66
+ let scriptHtml = "";
67
+ if (loader === undefined) {
68
+ const src = isProd ? `${assetsBase}/${entryName}.js` : devEntryUrl(entryName);
69
+ scriptHtml = lt + `script type="module" src="${escapeAttr(src)}">` + lt + "/script>";
70
+ }
71
+ return c.html(`<!doctype html><html lang="${escapeAttr(lang)}"><head><title>${escapeAttr(title)}</title>` +
72
+ `<meta charset="utf-8" /><meta content="width=device-width, initial-scale=1" name="viewport" />` +
73
+ (head ? head : "") +
74
+ `<link rel="stylesheet" href="${escapeAttr(stylesHref)}" />` +
75
+ ssrHead +
76
+ scriptHtml +
77
+ `</head>` +
78
+ `<body class="bg-base-200 min-h-screen text-base-content"><div id="${ids.rootId}">${bodyHtml}</div>` +
79
+ dataHtml +
80
+ `</body></html>`);
81
+ });
82
+ await next();
83
+ };
84
+ }
@@ -0,0 +1,3 @@
1
+ export declare const ssrPages: Record<string, () => Promise<{
2
+ default: unknown;
3
+ }>>;
@@ -0,0 +1,6 @@
1
+ // Stub estatico usado pelo shell quando o plugin hono-svelte/vite (pages())
2
+ // nao esta ativo no bundler: nenhuma pagina estatica, todos os entries
3
+ // sao client-side. Com pages() ativo, o resolveId do plugin (enforce: "pre")
4
+ // redireciona o import relativo "./ssr-manifest.js" do shell para o
5
+ // manifesto virtual gerado em memoria.
6
+ export const ssrPages = {};
@@ -0,0 +1,7 @@
1
+ export declare const ENTRY_PREFIX = "virtual:hono-svelte/entry/";
2
+ export declare const ENTRY_RESOLVED_PREFIX: string;
3
+ export declare const MANIFEST_ID = "virtual:hono-svelte/manifest";
4
+ export declare const MANIFEST_RESOLVED: string;
5
+ export declare function entryVirtualId(entryName: string): string;
6
+ export declare function entryResolvedId(entryName: string): string;
7
+ export declare function devEntryUrl(entryName: string): string;
@@ -0,0 +1,19 @@
1
+ // IDs dos modulos virtuais compartilhados entre o plugin (hono-svelte/vite)
2
+ // e o shell (hono-svelte). Nada e escrito em disco: entries e manifesto
3
+ // sao servidos in-memory como virtual modules.
4
+ const NULL = String.fromCharCode(0);
5
+ export const ENTRY_PREFIX = "virtual:hono-svelte/entry/";
6
+ export const ENTRY_RESOLVED_PREFIX = NULL + ENTRY_PREFIX;
7
+ export const MANIFEST_ID = "virtual:hono-svelte/manifest";
8
+ export const MANIFEST_RESOLVED = NULL + MANIFEST_ID;
9
+ export function entryVirtualId(entryName) {
10
+ return ENTRY_PREFIX + entryName;
11
+ }
12
+ export function entryResolvedId(entryName) {
13
+ return ENTRY_RESOLVED_PREFIX + entryName;
14
+ }
15
+ // URL de dev para o <script type="module"> gerado pelo shell.
16
+ // \0 vira __x00__ na URL servida pelo dev server do Vite.
17
+ export function devEntryUrl(entryName) {
18
+ return "/@id/" + entryResolvedId(entryName).replace(NULL, "__x00__");
19
+ }
package/dist/vite.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { type Plugin } from "vite";
2
+ export type PagesOptions = {
3
+ pagesDir?: string;
4
+ ignore?: string[];
5
+ /** entryNames que sempre geram client JS, mesmo sem <script>. */
6
+ alwaysClient?: string[];
7
+ generatedHeader?: string;
8
+ };
9
+ export type PagesPlugin = Plugin & {
10
+ input: () => Record<string, string>;
11
+ entries: () => string[];
12
+ staticEntries: () => string[];
13
+ hasClient: (entryName: string) => boolean;
14
+ };
15
+ export declare function pages(options?: PagesOptions): PagesPlugin;
package/dist/vite.js ADDED
@@ -0,0 +1,193 @@
1
+ import { globSync, readFileSync } from "node:fs";
2
+ import { relative, resolve } from "node:path";
3
+ import { getIds } from "./ids.js";
4
+ import { ENTRY_PREFIX, ENTRY_RESOLVED_PREFIX, MANIFEST_ID, MANIFEST_RESOLVED, } from "./virtual.js";
5
+ import { createFilter } from "vite";
6
+ const DEFAULT_IGNORE = ["**/layout.svelte", "**/_*.svelte"];
7
+ function normalizeSlashes(p) {
8
+ return p.replace(/\\/g, "/");
9
+ }
10
+ function entryNameFromFile(file) {
11
+ return normalizeSlashes(file).slice(0, -".svelte".length);
12
+ }
13
+ export function pages(options = {}) {
14
+ const pagesDir = resolve(options.pagesDir ?? "src/pages");
15
+ const ignore = options.ignore ?? DEFAULT_IGNORE;
16
+ const alwaysClient = new Set(options.alwaysClient ?? []);
17
+ const header = options.generatedHeader ?? "// @generated - hono-svelte, nao editar.";
18
+ const filter = createFilter(["**/*.svelte"], ignore, { resolve: pagesDir });
19
+ let cachedPages = null;
20
+ let cachedInput = {};
21
+ let viteRoot = null;
22
+ function computePages() {
23
+ const files = globSync("**/*.svelte", { cwd: pagesDir })
24
+ .map(normalizeSlashes)
25
+ .filter((f) => filter(pagesDir + "/" + f))
26
+ .sort();
27
+ const seen = new Set();
28
+ const result = [];
29
+ for (const file of files) {
30
+ const entryName = entryNameFromFile(file);
31
+ if (seen.has(entryName)) {
32
+ throw new Error(`hono-svelte: entry duplicado: ${entryName}`);
33
+ }
34
+ seen.add(entryName);
35
+ const absFile = resolve(pagesDir, file);
36
+ // Zero-JS automatico: pagina sem <script> nao tem interatividade,
37
+ // entao o shell renderiza no servidor e o cliente nao baixa JS.
38
+ const hasScript = /<script[\s>]/i.test(readFileSync(absFile, "utf8"));
39
+ result.push({ entryName, file, absFile, isStatic: !hasScript && !alwaysClient.has(entryName) });
40
+ }
41
+ return result;
42
+ }
43
+ function refresh(log = true) {
44
+ cachedPages = computePages();
45
+ cachedInput = {};
46
+ let jsCount = 0;
47
+ for (const page of cachedPages) {
48
+ if (page.isStatic)
49
+ continue;
50
+ cachedInput[page.entryName] = ENTRY_RESOLVED_PREFIX + page.entryName;
51
+ jsCount++;
52
+ }
53
+ if (log) {
54
+ const staticCount = cachedPages.length - jsCount;
55
+ console.log("[hono-svelte] " + jsCount + " pagina(s) com JS + " + staticCount + " estatica(s), tudo in-memory");
56
+ }
57
+ }
58
+ function listPages() {
59
+ if (!cachedPages)
60
+ refresh(false);
61
+ return cachedPages;
62
+ }
63
+ function stateKey() {
64
+ return listPages()
65
+ .map((p) => (p.isStatic ? "S:" : "C:") + p.entryName)
66
+ .join("|");
67
+ }
68
+ function pageImportPath(page) {
69
+ const root = viteRoot ?? process.cwd();
70
+ const rel = relative(root, page.absFile);
71
+ if (rel.startsWith("..")) {
72
+ throw new Error("hono-svelte: pagesDir precisa estar dentro do root do Vite");
73
+ }
74
+ return "/" + normalizeSlashes(rel);
75
+ }
76
+ function manifestSource() {
77
+ const all = listPages();
78
+ const client = all.filter((p) => !p.isStatic).map((p) => p.entryName);
79
+ const staticPages = all.filter((p) => p.isStatic);
80
+ const loaders = staticPages.map((p) => " " + JSON.stringify(p.entryName) + ": () => import(" + JSON.stringify(pageImportPath(p)) + "),");
81
+ return [
82
+ header + " gerado em memoria pelo plugin - nao editar.",
83
+ "",
84
+ "export const clientEntries = " + JSON.stringify(client) + ";",
85
+ "",
86
+ "export const staticEntries = " + JSON.stringify(staticPages.map((p) => p.entryName)) + ";",
87
+ "",
88
+ "export const ssrPages = {",
89
+ ...(loaders.length > 0 ? loaders : [" // nenhuma pagina estatica detectada"]),
90
+ "};",
91
+ "",
92
+ "export function hasClient(entryName) {",
93
+ " return !Object.prototype.hasOwnProperty.call(ssrPages, entryName);",
94
+ "}",
95
+ "",
96
+ ].join("\n");
97
+ }
98
+ function entrySource(page) {
99
+ const ids = getIds(page.entryName);
100
+ return (header + " Origem: " + page.file + "\n" +
101
+ 'import { mount } from "svelte";\n' +
102
+ "import Page from " + JSON.stringify(pageImportPath(page)) + ";\n" +
103
+ "const target = document.getElementById(" + JSON.stringify(ids.rootId) + ");\n" +
104
+ "const raw = document.getElementById(" + JSON.stringify(ids.dataId) + ")?.textContent;\n" +
105
+ "const props = raw ? JSON.parse(raw) : {};\n" +
106
+ "if (target) mount(Page, { target, props });\n");
107
+ }
108
+ const plugin = {
109
+ name: "hono-svelte-pages",
110
+ // pre: precisa rodar ANTES do vite:resolve para interceptar o import
111
+ // relativo do shell ("./ssr-manifest.js") e redirecionar ao manifesto.
112
+ enforce: "pre",
113
+ configResolved(config) {
114
+ viteRoot = config.root;
115
+ },
116
+ buildStart() {
117
+ refresh();
118
+ },
119
+ resolveId(source) {
120
+ // Manifesto virtual: explicito (virtual:hono-svelte/manifest, retrocompat)
121
+ // ou o import relativo do proprio shell. Sem pages() ativo, o import
122
+ // relativo resolve o stub estatico dist/ssr-manifest.js.
123
+ if (source === MANIFEST_ID || source === "./ssr-manifest.js")
124
+ return MANIFEST_RESOLVED;
125
+ if (source.startsWith(ENTRY_PREFIX)) {
126
+ return ENTRY_RESOLVED_PREFIX + source.slice(ENTRY_PREFIX.length);
127
+ }
128
+ // ids ja resolvidos (\0...) passam direto para o load()
129
+ if (source === MANIFEST_RESOLVED || source.startsWith(ENTRY_RESOLVED_PREFIX)) {
130
+ return source;
131
+ }
132
+ return null;
133
+ },
134
+ load(id) {
135
+ if (id === MANIFEST_RESOLVED)
136
+ return manifestSource();
137
+ if (id.startsWith(ENTRY_RESOLVED_PREFIX)) {
138
+ const entryName = id.slice(ENTRY_RESOLVED_PREFIX.length);
139
+ const page = listPages().find((p) => p.entryName === entryName);
140
+ if (!page) {
141
+ throw new Error("hono-svelte: pagina nao encontrada para entry: " + entryName);
142
+ }
143
+ return entrySource(page);
144
+ }
145
+ return null;
146
+ },
147
+ configureServer(server) {
148
+ server.watcher.add(pagesDir);
149
+ const isPagePath = (file) => {
150
+ const rel = normalizeSlashes(relative(pagesDir, file));
151
+ return rel.length > 0 && !rel.startsWith("..") && rel.endsWith(".svelte");
152
+ };
153
+ const onMaybeStructural = () => {
154
+ const before = stateKey();
155
+ cachedPages = null;
156
+ refresh();
157
+ if (stateKey() !== before) {
158
+ const mod = server.moduleGraph.getModuleById(MANIFEST_RESOLVED);
159
+ if (mod)
160
+ server.moduleGraph.invalidateModule(mod);
161
+ server.ws.send({ type: "full-reload" });
162
+ }
163
+ };
164
+ server.watcher.on("add", (file) => {
165
+ if (isPagePath(file))
166
+ onMaybeStructural();
167
+ });
168
+ server.watcher.on("unlink", (file) => {
169
+ if (isPagePath(file))
170
+ onMaybeStructural();
171
+ });
172
+ server.watcher.on("change", (file) => {
173
+ if (isPagePath(file))
174
+ onMaybeStructural();
175
+ });
176
+ },
177
+ input() {
178
+ listPages();
179
+ return cachedInput;
180
+ },
181
+ entries() {
182
+ return listPages().map((p) => p.entryName);
183
+ },
184
+ staticEntries() {
185
+ return listPages().filter((p) => p.isStatic).map((p) => p.entryName);
186
+ },
187
+ hasClient(entryName) {
188
+ const page = listPages().find((p) => p.entryName === entryName);
189
+ return page ? !page.isStatic : false;
190
+ },
191
+ };
192
+ return plugin;
193
+ }
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "hono-svelte",
3
+ "version": "0.3.0",
4
+ "description": "Shell server-side + multi-entry Svelte pages for Hono apps",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ },
11
+ "./vite": {
12
+ "types": "./dist/vite.d.ts",
13
+ "default": "./dist/vite.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.json",
22
+ "typecheck": "tsc --noEmit -p tsconfig.json",
23
+ "prepublishOnly": "npm run build && npm test && npx publint",
24
+ "test": "vitest run"
25
+ },
26
+ "peerDependencies": {
27
+ "hono": "^4",
28
+ "svelte": "^5",
29
+ "vite": "^6 || ^7 || ^8"
30
+ },
31
+ "devDependencies": {
32
+ "@sveltejs/vite-plugin-svelte": "^7.3.0",
33
+ "@types/node": "^22.7.0",
34
+ "publint": "^0.3.24",
35
+ "svelte": "^5.57.0",
36
+ "typescript": "^5.6.0",
37
+ "vite": "^8.3.0",
38
+ "vitest": "^5.0.1"
39
+ },
40
+ "engines": {
41
+ "node": ">=22"
42
+ },
43
+ "license": "MIT",
44
+ "sideEffects": false,
45
+ "keywords": [
46
+ "hono",
47
+ "svelte",
48
+ "vite",
49
+ "ssr",
50
+ "zero-js",
51
+ "middleware"
52
+ ]
53
+ }