@jk2908/solas 0.16.2 → 0.16.3

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,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.3
4
+
5
+ ### Fixes
6
+
7
+ - Server actions invoked from the browser now request the RSC representation (`accept: text/x-component`). The server previously answered with an HTML document, which surfaced as React's "Connection closed" error (#412) and left the UI un-updated even though the action ran.
8
+ - Static responses carry `Content-Length`, so prerendered HTML, CSS and client assets are no longer sent chunked.
9
+
10
+ ### Changed
11
+
12
+ - The client hydrates inside `startTransition`, so the browser can paint the server markup before React walks the tree.
13
+ - Production builds pin `process.env.NODE_ENV` to the build mode and minify server bundles. This removes the development React branches and reduces the deployable output size.
14
+
3
15
  ## 0.16.2
4
16
 
5
17
  ### Changed
package/dist/index.js CHANGED
@@ -232,7 +232,7 @@ function solas(c) {
232
232
  }
233
233
  const plugin = {
234
234
  name: Config.NAME,
235
- async config(viteConfig) {
235
+ async config(viteConfig, env) {
236
236
  const pkg = JSON.parse(fsSync.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));
237
237
  if (typeof pkg.name !== 'string' || pkg.name.length === 0) {
238
238
  throw new Error(`Missing ${Config.NAME} package name`);
@@ -276,6 +276,12 @@ function solas(c) {
276
276
  viteConfig.define['import.meta.env.VITE_APP_URL'] = JSON.stringify(resolvedUrl);
277
277
  viteConfig.define['import.meta.env.SOLAS_VERSION'] = JSON.stringify(pkg.version);
278
278
  viteConfig.define['import.meta.env.SOLAS_ADAPTER'] = JSON.stringify(config.adapter?.name ?? '');
279
+ // production builds can drop the development React branches and ship
280
+ // minified server bundles; dev keeps readable output and HMR
281
+ if (env?.command === 'build') {
282
+ viteConfig.define['process.env.NODE_ENV'] = JSON.stringify(env.mode);
283
+ viteConfig.build.minify ??= true;
284
+ }
279
285
  viteConfig.optimizeDeps ??= {};
280
286
  viteConfig.optimizeDeps.exclude = [
281
287
  ...new Set([
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { StrictMode, Suspense, useCallback, useState, useTransition } from 'react';
2
+ import { StrictMode, Suspense, startTransition, useCallback, useState, useTransition, } from 'react';
3
3
  import { hydrateRoot } from 'react-dom/client';
4
4
  import { createFromFetch, createFromReadableStream, createTemporaryReferenceSet, encodeReply, setServerCallback, } from '@vitejs/plugin-rsc/browser';
5
5
  import { getDocumentNonce } from '../../utils/get-document-nonce.js';
@@ -35,7 +35,10 @@ export async function browser() {
35
35
  setServerCallback(async (id, args) => {
36
36
  const url = new URL(window.location.href);
37
37
  const temporaryReferences = createTemporaryReferenceSet();
38
- const headers = new Headers({ 'x-rsc-action-id': id });
38
+ const headers = new Headers({
39
+ accept: 'text/x-component',
40
+ 'x-rsc-action-id': id,
41
+ });
39
42
  const nonce = getDocumentNonce();
40
43
  if (nonce)
41
44
  headers.set('x-solas-nonce', nonce);
@@ -50,8 +53,12 @@ export async function browser() {
50
53
  throw data;
51
54
  return data;
52
55
  });
53
- hydrateRoot(document, _jsx(StrictMode, { children: _jsx(A, {}) }), {
54
- formState: payload.formState,
56
+ // hydrate as a transition so the browser can paint the server markup
57
+ // before React starts walking the tree
58
+ startTransition(() => {
59
+ hydrateRoot(document, _jsx(StrictMode, { children: _jsx(A, {}) }), {
60
+ formState: payload.formState,
61
+ });
55
62
  });
56
63
  if (import.meta.hot) {
57
64
  import.meta.hot.on?.('rsc:update', async () => {
@@ -288,9 +288,13 @@ export class HttpRouter {
288
288
  }
289
289
  // get mime type from original path, not compressed variant
290
290
  const mimeType = assets.mimeType(asset.path);
291
- const res = new Response(await assets.readBuffer(resolved), {
291
+ const buffer = await assets.readBuffer(resolved);
292
+ const res = new Response(buffer, {
292
293
  headers: {
293
294
  'Content-Type': headers['Content-Type'] ?? mimeType,
295
+ // a static file has a known size; sending it lets the client finish
296
+ // the response without waiting for a chunked terminator
297
+ 'Content-Length': String(buffer.byteLength),
294
298
  },
295
299
  });
296
300
  for (const [key, value] of Object.entries(headers)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jk2908/solas",
3
- "version": "0.16.2",
3
+ "version": "0.16.3",
4
4
  "description": "A Vite + React meta-framework exploring streaming, Server Components, and partial prerendering. Designed for simplicity and lightness",
5
5
  "keywords": [
6
6
  "framework",