@netrojs/vono 0.0.1 → 0.1.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/dist/server.js CHANGED
@@ -192,7 +192,7 @@ async function resolveComponent(comp) {
192
192
  }
193
193
  return comp;
194
194
  }
195
- async function renderPage(route, data, url, params, appLayout) {
195
+ async function renderPage(route, data, url, params, appLayout, dev) {
196
196
  const layout = route.layout !== void 0 ? route.layout : appLayout;
197
197
  const PageComp = await resolveComponent(route.page.component);
198
198
  const routeComp = layout ? defineComponent({
@@ -211,6 +211,7 @@ async function renderPage(route, data, url, params, appLayout) {
211
211
  });
212
212
  app.use(router);
213
213
  await router.isReady();
214
+ if (dev) return renderToString(app);
214
215
  return renderToWebStream(app);
215
216
  }
216
217
  function buildResponseStream(headHtml, bodyStream, tailHtml) {
@@ -306,8 +307,11 @@ function createVono(config) {
306
307
  assets.styles,
307
308
  config.htmlAttrs
308
309
  );
309
- const bodyStream = await renderPage(route, data, pathname, params, config.layout);
310
- const stream = buildResponseStream(head, bodyStream, tail);
310
+ const body = await renderPage(route, data, pathname, params, config.layout, isDev);
311
+ if (isDev) {
312
+ return c.html(head + body + tail, 200);
313
+ }
314
+ const stream = buildResponseStream(head, body, tail);
311
315
  return c.body(stream, 200, {
312
316
  "Content-Type": "text/html; charset=UTF-8",
313
317
  "Transfer-Encoding": "chunked",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netrojs/vono",
3
- "version": "0.0.1",
3
+ "version": "0.1.1",
4
4
  "description": "Full-stack Hono + Vue 3 framework \u2014 Streaming SSR, SPA, code splitting, SEO, middleware",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/server.ts CHANGED
@@ -192,13 +192,37 @@ async function resolveComponent(comp: Component | ((...a: unknown[]) => unknown)
192
192
  * against the correct route and never emits a spurious
193
193
  * "[Vue Router warn]: No match found for location with path '/'" warning.
194
194
  */
195
+ /**
196
+ * Builds a fresh Vue SSR app + router per request and renders the page body.
197
+ *
198
+ * DEV MODE — returns a `string` via `renderToString`.
199
+ * `@hono/vite-dev-server` proxies requests through Vite's Connect middleware
200
+ * pipeline. That pipeline does not flush a `ReadableStream` — the browser
201
+ * hangs waiting for bytes that never arrive, then reports
202
+ * "localhost refused to connect" after the idle timeout fires.
203
+ * `renderToString` buffers the full HTML and returns it as a plain string
204
+ * which Hono serialises to a normal HTTP response — no streaming needed.
205
+ *
206
+ * PRODUCTION — returns a `ReadableStream<Uint8Array>` via `renderToWebStream`.
207
+ * Lower TTFB: the browser receives `<head>` (CSS links, preload hints,
208
+ * critical scripts) while Vue is still rendering the `<body>`.
209
+ *
210
+ * Vue Router warning fix:
211
+ * `createMemoryHistory()` starts at '/'. The router performs an internal
212
+ * startup navigation to that initial location before any routes are matched.
213
+ * If the only registered route is e.g. '/about', Vue Router emits:
214
+ * "[Vue Router warn]: No match found for location with path '/'"
215
+ * Fix: call `memHistory.replace(url)` BEFORE constructing the router so its
216
+ * startup navigation always resolves against the correct, matched route.
217
+ */
195
218
  async function renderPage(
196
219
  route: ResolvedRoute,
197
220
  data: object,
198
221
  url: string,
199
222
  params: Record<string, string>,
200
223
  appLayout: LayoutDef | undefined,
201
- ): Promise<ReadableStream<Uint8Array>> {
224
+ dev: boolean,
225
+ ): Promise<ReadableStream<Uint8Array> | string> {
202
226
  const layout = route.layout !== undefined ? route.layout : appLayout
203
227
 
204
228
  // Resolve async component loaders — critical for SSR correctness
@@ -217,16 +241,8 @@ async function renderPage(
217
241
  const app = createSSRApp({ render: () => h(RouterView) })
218
242
  app.provide(DATA_KEY, data)
219
243
 
220
- // ── Vue Router warning fix ────────────────────────────────────────────────
221
- // createMemoryHistory() initialises its location to '/'. When the router
222
- // is constructed it performs an internal navigation to that initial location.
223
- // If the only registered route is e.g. '/about', no match is found and
224
- // Vue Router emits a warning even though the subsequent router.push('/about')
225
- // succeeds perfectly.
226
- //
227
- // Fix: call history.replace(url) BEFORE constructing the router. The router
228
- // then sees the correct initial location and its startup navigation succeeds
229
- // without warnings. No separate router.push() is required.
244
+ // Initialise history at the request URL before creating the router so its
245
+ // startup navigation resolves immediately without a "[Vue Router warn]".
230
246
  const memHistory = createMemoryHistory()
231
247
  memHistory.replace(url)
232
248
 
@@ -236,11 +252,11 @@ async function renderPage(
236
252
  })
237
253
  app.use(router)
238
254
 
239
- // router.isReady() resolves once the initial navigation (to `url`) completes.
240
255
  await router.isReady()
241
256
 
242
- // renderToWebStream streams body chunks as Uint8Array lower TTFB vs
243
- // renderToString (which buffers the entire body before responding).
257
+ // Dev: buffered string works correctly inside @hono/vite-dev-server.
258
+ // Prod: streaming flushes <head> to the browser before <body> is ready.
259
+ if (dev) return renderToString(app)
244
260
  return renderToWebStream(app)
245
261
  }
246
262
 
@@ -389,10 +405,18 @@ export function createVono(config: VonoOptions): VonoApp {
389
405
  config.htmlAttrs,
390
406
  )
391
407
 
392
- // Render the body asynchronously while the head is already on the wire
393
- const bodyStream = await renderPage(route, data, pathname, params, config.layout)
394
- const stream = buildResponseStream(head, bodyStream, tail)
408
+ const body = await renderPage(route, data, pathname, params, config.layout, isDev)
409
+
410
+ // Dev: body is a plain string — return a normal buffered HTML response.
411
+ // @hono/vite-dev-server cannot flush a ReadableStream through Vite's
412
+ // Connect pipeline; using c.html() avoids the hanging-connection issue.
413
+ if (isDev) {
414
+ return c.html(head + (body as string) + tail, 200)
415
+ }
395
416
 
417
+ // Production: body is a ReadableStream — stream head + body + tail for
418
+ // the lowest possible TTFB.
419
+ const stream = buildResponseStream(head, body as ReadableStream<Uint8Array>, tail)
396
420
  return c.body(stream, 200, {
397
421
  'Content-Type': 'text/html; charset=UTF-8',
398
422
  'Transfer-Encoding': 'chunked',
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Netro Solutions
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.