@solidjs/vite-plugin 3.0.0-next.29 → 3.0.0-next.30

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.
@@ -45,24 +45,54 @@ var babel__namespace = /*#__PURE__*/_interopNamespaceDefault(babel);
45
45
  * different URL than the one node saw — the dev middlewares use it to
46
46
  * restore the configured Vite `base` that the dev/preview base middleware
47
47
  * stripped, so the handler always sees production-shaped URLs.
48
+ *
49
+ * Handles plain HTTP/1 *and* the HTTP/2 compat API: Vite's dev server uses
50
+ * `http2.createSecureServer({ allowHTTP1: true })` whenever `server.https`
51
+ * is set without a proxy, so under https the middlewares receive
52
+ * `Http2ServerRequest`s. The h2/protocol/abort techniques here are
53
+ * reimplemented from srvx's Node adapter (github.com/h3js/srvx,
54
+ * src/adapters/_node) — reference, not copied code.
48
55
  */
49
- function webRequestFromNode(req, urlPath) {
50
- const url = new URL(urlPath ?? req.url ?? '/', `http://${req.headers.host || 'localhost'}`);
56
+ function webRequestFromNode(req, urlPath, res) {
57
+ // TLS sockets (https and h2) expose `encrypted`; a Request whose url says
58
+ // http: on a TLS connection breaks secure-cookie logic, absolute
59
+ // redirects, and origin checks in application code.
60
+ const protocol = req.socket?.encrypted ? 'https' : 'http';
61
+ // HTTP/2 has no Host header — the authority travels in the `:authority`
62
+ // pseudo-header instead.
63
+ const host = req.headers.host ?? req.headers[':authority'] ?? 'localhost';
64
+ const url = new URL(urlPath ?? req.url ?? '/', `${protocol}://${host}`);
51
65
  const headers = new Headers();
52
66
  for (const [key, value] of Object.entries(req.headers)) {
53
67
  if (value === undefined) continue;
68
+ // HTTP/2 pseudo-headers (:method, :path, :authority, :scheme) are not
69
+ // legal field names — Headers#append throws a TypeError on them.
70
+ if (key[0] === ':') continue;
54
71
  if (Array.isArray(value)) {
55
72
  for (const item of value) headers.append(key, item);
56
73
  } else {
57
74
  headers.append(key, value);
58
75
  }
59
76
  }
77
+ // Surface client disconnects as the request's AbortSignal so handlers can
78
+ // cancel work (streamed SSR renders, in-flight fetches). The response's
79
+ // 'close' fires on normal completion too; `writableEnded` distinguishes a
80
+ // finished response from a client that went away.
81
+ let signal;
82
+ if (res) {
83
+ const controller = new AbortController();
84
+ res.once('close', () => {
85
+ if (!res.writableEnded) controller.abort();
86
+ });
87
+ signal = controller.signal;
88
+ }
60
89
  const method = req.method || 'GET';
61
90
  const body = method === 'GET' || method === 'HEAD' ? undefined : node_stream.Readable.toWeb(req);
62
91
  return new Request(url, {
63
92
  method,
64
93
  headers,
65
94
  body,
95
+ signal,
66
96
  // undici requires half-duplex for streamed request bodies.
67
97
  ...(body ? {
68
98
  duplex: 'half'
@@ -77,7 +107,11 @@ async function sendWebResponse(res, response) {
77
107
  if (key !== 'set-cookie') res.setHeader(key, value);
78
108
  });
79
109
  if (cookies && cookies.length) res.setHeader('set-cookie', cookies);
80
- if (!response.body) {
110
+ // HEAD gets the head only — and the body must be *cancelled*, not pumped:
111
+ // node discards HEAD body writes, so streaming a long (or endless) body
112
+ // into the void just burns the render. (Technique from srvx.)
113
+ if (!response.body || res.req?.method === 'HEAD') {
114
+ response.body?.cancel().catch(() => {});
81
115
  res.end();
82
116
  return;
83
117
  }
@@ -1034,7 +1068,7 @@ function serverFunctions(options = {}, internal = {}) {
1034
1068
  nativeEvent: req
1035
1069
  }
1036
1070
  };
1037
- const response = internal.ssrHandler ? await handler.handleRequest(webRequestFromNode(req, dispatchUrl), dispatchOptions) : await handler.handleServerFunctionRequest(webRequestFromNode(req, dispatchUrl), dispatchOptions);
1071
+ const response = internal.ssrHandler ? await handler.handleRequest(webRequestFromNode(req, dispatchUrl, res), dispatchOptions) : await handler.handleServerFunctionRequest(webRequestFromNode(req, dispatchUrl, res), dispatchOptions);
1038
1072
  await sendWebResponse(res, response);
1039
1073
  })().catch(error => {
1040
1074
  if (error instanceof Error) server.ssrFixStacktrace(error);
@@ -1828,7 +1862,7 @@ function startServe(options, internal = {}) {
1828
1862
  // server-function endpoint) and hands the URL to application
1829
1863
  // code, so restore the base — the deployed production handler
1830
1864
  // receives base-prefixed URLs and preview must match it.
1831
- const response = await handler.handleRequest(webRequestFromNode(req, joinBase(base, req.url || '/')),
1865
+ const response = await handler.handleRequest(webRequestFromNode(req, joinBase(base, req.url || '/'), res),
1832
1866
  // Same event extension the dev middleware and a production
1833
1867
  // Node entry pass: the raw Node request as `nativeEvent`.
1834
1868
  {
@@ -1885,7 +1919,7 @@ function startServe(options, internal = {}) {
1885
1919
  // the configured `base` from req.url; restore it so the app
1886
1920
  // sees the same URLs in dev as in production (where the
1887
1921
  // deployed handler receives base-prefixed requests).
1888
- const response = await handler.handleRequest(webRequestFromNode(req, joinBase(base, req.url || '/')), {
1922
+ const response = await handler.handleRequest(webRequestFromNode(req, joinBase(base, req.url || '/'), res), {
1889
1923
  devHead,
1890
1924
  pageRequest,
1891
1925
  // The raw Node request on the event, matching what a