@qwik.dev/router 2.0.0-beta.34 → 2.0.0-beta.35

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.
@@ -1,5 +1,5 @@
1
1
  import { parentPort } from 'node:worker_threads';
2
- import { isDev, _UNINITIALIZED, _deserialize, _verifySerializable, _serialize } from '@qwik.dev/core/internal';
2
+ import { isDev, _UNINITIALIZED, _deserialize, _verifySerializable, _serialize, _getAsyncLocalStorage } from '@qwik.dev/core/internal';
3
3
  import { withLocale, inlinedQrl } from '@qwik.dev/core';
4
4
  import { A as AbortMessage$1 } from './redirect-handler.mjs';
5
5
  import { isServer } from '@qwik.dev/core/build';
@@ -1616,8 +1616,11 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
1616
1616
  });
1617
1617
  pipeSource = readable.pipeThrough(capture);
1618
1618
  }
1619
+ let pipeError;
1619
1620
  const pipe = pipeSource.pipeTo(writableStream, {
1620
1621
  preventClose: true
1622
+ }).catch((error) => {
1623
+ pipeError = error;
1621
1624
  });
1622
1625
  const stream = writable.getWriter();
1623
1626
  const status = requestEv.status();
@@ -1648,6 +1651,9 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
1648
1651
  await stream.close();
1649
1652
  await pipe;
1650
1653
  }
1654
+ if (pipeError) {
1655
+ throw pipeError;
1656
+ }
1651
1657
  if (eTagCacheKey && cacheChunks && cacheChunks.length > 0) {
1652
1658
  const totalLength = cacheChunks.reduce((sum, chunk) => sum + chunk.length, 0);
1653
1659
  const combined = new Uint8Array(totalLength);
@@ -2103,11 +2109,12 @@ function createRequestEvent(...args) {
2103
2109
 
2104
2110
  let _asyncRequestStore;
2105
2111
  if (isServer) {
2106
- import('node:async_hooks').then((module) => {
2107
- _asyncRequestStore = new module.AsyncLocalStorage();
2108
- }).catch((err) => {
2109
- console.warn("\n=====================\n Qwik Router Warning:\n AsyncLocalStorage is not available, continuing without it.\n This impacts concurrent async server calls, where they lose access to the ServerRequestEv object.\n=====================\n\n", err);
2110
- });
2112
+ const AsyncLocalStorage = _getAsyncLocalStorage();
2113
+ if (AsyncLocalStorage) {
2114
+ _asyncRequestStore = new AsyncLocalStorage();
2115
+ } else {
2116
+ console.warn("\n=====================\n Qwik Router Warning:\n AsyncLocalStorage is not available, continuing without it.\n This impacts concurrent async server calls, where they lose access to the ServerRequestEv object.\n=====================\n\n");
2117
+ }
2111
2118
  }
2112
2119
 
2113
2120
  const ESCAPE_HTML = /[&<>]/g;
@@ -26,6 +26,7 @@ const ErrorBoundary = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((p
26
26
  return /* @__PURE__ */ _jsxSorted(Slot, null, null, null, 3, "bA_1");
27
27
  }, "ErrorBoundary_component_pOa6vjtC7ik"));
28
28
 
29
+ const __vite_import_meta_env__ = {};
29
30
  const Link = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((props) => {
30
31
  const nav = useNavigate();
31
32
  const loc = useLocation();
@@ -117,7 +118,7 @@ const Link = /* @__PURE__ */ componentQrl(/* @__PURE__ */ inlinedQrl((props) =>
117
118
  handler?.(event, anchorRef2.value);
118
119
  }
119
120
  }
120
- const isProdOrTest = !isDev || undefined ;
121
+ const isProdOrTest = !isDev || __vite_import_meta_env__?.TEST;
121
122
  if (isProdOrTest && anchorRef2.value) {
122
123
  if (prefetchBundleProp2 === "visible" || // deprecated prop below, remove in favor of prefetchBundle
123
124
  prefetchProp2 === "js" || prefetchProp2 === true) {
@@ -18,12 +18,15 @@ function fallbackOrigin(req) {
18
18
  function computeOrigin(req, opts) {
19
19
  return opts?.getOrigin?.(req) ?? opts?.origin ?? process.env.ORIGIN ?? fallbackOrigin(req);
20
20
  }
21
- function isIgnoredError(message = "") {
22
- const ignoredErrors = [
23
- "The stream has been destroyed",
24
- "write after end"
25
- ];
26
- return ignoredErrors.some((ignored) => message.includes(ignored));
21
+ function isClientAbortWriteError(error) {
22
+ if (!(error instanceof Error)) {
23
+ return false;
24
+ }
25
+ const code = error.code;
26
+ if (code === "EPIPE" || code === "ECONNRESET" || code === "ERR_STREAM_DESTROYED" || code === "ERR_STREAM_WRITE_AFTER_END") {
27
+ return true;
28
+ }
29
+ return false;
27
30
  }
28
31
  const invalidHeadersPattern = /^:(method|scheme|authority|path)$/i;
29
32
  function normalizeUrl(url, base) {
@@ -34,38 +37,75 @@ function getUrl(req, origin) {
34
37
  }
35
38
  function createNodeResponseSink(res) {
36
39
  let closed = res.closed || res.destroyed;
40
+ let responseError;
41
+ let resolveClosed;
37
42
  const closedPromise = closed ? Promise.resolve() : new Promise((resolve) => {
43
+ resolveClosed = resolve;
38
44
  res.once("close", () => {
39
45
  closed = true;
40
46
  resolve();
41
47
  });
42
48
  });
49
+ res.on("error", (error) => {
50
+ if (responseError === void 0) {
51
+ responseError = error;
52
+ }
53
+ if (!closed) {
54
+ closed = true;
55
+ resolveClosed();
56
+ }
57
+ });
58
+ const shouldPropagateResponseError = () => responseError !== void 0 && !isClientAbortWriteError(responseError);
43
59
  const write = (chunk) => {
44
60
  if (closed || res.closed || res.destroyed) {
61
+ if (shouldPropagateResponseError()) {
62
+ return Promise.reject(responseError);
63
+ }
45
64
  return;
46
65
  }
47
66
  return new Promise((resolve, reject) => {
48
67
  let settled = false;
68
+ let onDrain;
69
+ const cleanupDrain = () => {
70
+ if (onDrain) {
71
+ res.off("drain", onDrain);
72
+ onDrain = void 0;
73
+ }
74
+ };
49
75
  const finish = () => {
50
76
  if (settled) {
51
77
  return;
52
78
  }
53
79
  settled = true;
80
+ cleanupDrain();
54
81
  setImmediate(resolve);
55
82
  };
83
+ const finishClosed = () => {
84
+ closed = true;
85
+ finish();
86
+ };
56
87
  const fail = (error) => {
57
88
  if (settled) {
58
89
  return;
59
90
  }
60
91
  settled = true;
92
+ closed = true;
93
+ cleanupDrain();
61
94
  reject(error);
62
95
  };
63
- closedPromise.then(finish);
96
+ closedPromise.then(() => {
97
+ if (shouldPropagateResponseError()) {
98
+ fail(responseError);
99
+ return;
100
+ }
101
+ finish();
102
+ });
103
+ let ok;
64
104
  try {
65
- res.write(chunk, (error) => {
105
+ ok = res.write(chunk, (error) => {
66
106
  if (error) {
67
- if (isIgnoredError(error.message)) {
68
- finish();
107
+ if (isClientAbortWriteError(error)) {
108
+ finishClosed();
69
109
  return;
70
110
  }
71
111
  fail(error);
@@ -74,22 +114,69 @@ function createNodeResponseSink(res) {
74
114
  finish();
75
115
  });
76
116
  } catch (error) {
77
- if (error instanceof Error && isIgnoredError(error.message)) {
78
- finish();
117
+ if (isClientAbortWriteError(error)) {
118
+ finishClosed();
79
119
  return;
80
120
  }
81
121
  fail(error);
122
+ return;
123
+ }
124
+ if (settled) {
125
+ return;
126
+ }
127
+ if (ok) {
128
+ finish();
129
+ } else {
130
+ onDrain = () => {
131
+ onDrain = void 0;
132
+ finish();
133
+ };
134
+ res.once("drain", onDrain);
82
135
  }
83
136
  });
84
137
  };
85
138
  const close = () => {
86
139
  if (closed || res.closed || res.destroyed) {
140
+ if (shouldPropagateResponseError()) {
141
+ return Promise.reject(responseError);
142
+ }
87
143
  return;
88
144
  }
89
- return new Promise((resolve) => {
90
- res.end(() => {
145
+ return new Promise((resolve, reject) => {
146
+ let settled = false;
147
+ const finish = () => {
148
+ if (settled) {
149
+ return;
150
+ }
151
+ settled = true;
152
+ closed = true;
91
153
  resolve();
154
+ };
155
+ const fail = (error) => {
156
+ if (settled) {
157
+ return;
158
+ }
159
+ settled = true;
160
+ closed = true;
161
+ reject(error);
162
+ };
163
+ closedPromise.then(() => {
164
+ if (shouldPropagateResponseError()) {
165
+ fail(responseError);
166
+ return;
167
+ }
168
+ finish();
92
169
  });
170
+ res.once("finish", finish);
171
+ try {
172
+ res.end();
173
+ } catch (error) {
174
+ if (isClientAbortWriteError(error)) {
175
+ finish();
176
+ return;
177
+ }
178
+ fail(error);
179
+ }
93
180
  });
94
181
  };
95
182
  return {
@@ -123,6 +210,9 @@ async function fromNodeHttp(url, req, res, mode, getClientConn) {
123
210
  };
124
211
  const body = req.method === "HEAD" || req.method === "GET" ? void 0 : getRequestBody();
125
212
  const controller = new AbortController();
213
+ const abort = () => {
214
+ controller.abort();
215
+ };
126
216
  const options = {
127
217
  method: req.method,
128
218
  headers: requestHeaders,
@@ -130,9 +220,10 @@ async function fromNodeHttp(url, req, res, mode, getClientConn) {
130
220
  signal: controller.signal,
131
221
  duplex: "half"
132
222
  };
133
- res.on("close", () => {
134
- controller.abort();
135
- });
223
+ req.on("aborted", abort);
224
+ req.on("error", abort);
225
+ res.on("close", abort);
226
+ res.on("error", abort);
136
227
  const serverRequestEv = {
137
228
  mode,
138
229
  url,
@@ -142,7 +233,7 @@ async function fromNodeHttp(url, req, res, mode, getClientConn) {
142
233
  return process.env[key];
143
234
  }
144
235
  },
145
- getWritableStream: (status, headers, cookies) => {
236
+ getWritableStream: (status, headers, cookies, resolve) => {
146
237
  res.statusCode = status;
147
238
  const sink = createNodeResponseSink(res);
148
239
  try {
@@ -159,6 +250,7 @@ async function fromNodeHttp(url, req, res, mode, getClientConn) {
159
250
  } catch (err) {
160
251
  console.error(err);
161
252
  }
253
+ resolve(true);
162
254
  return new WritableStream({
163
255
  write(chunk) {
164
256
  return sink.write(chunk);
@@ -1,5 +1,5 @@
1
1
  import { Q as Q_ROUTE, m as resolveRouteConfig, n as QLOADER_KEY, h as QFN_KEY, j as QACTION_KEY, o as isPromise, k as QDATA_KEY, b as loadRoute } from '../../chunks/routing.qwik.mjs';
2
- import { isDev, _UNINITIALIZED, _deserialize, _verifySerializable, _serialize } from '@qwik.dev/core/internal';
2
+ import { isDev, _UNINITIALIZED, _deserialize, _verifySerializable, _serialize, _getAsyncLocalStorage } from '@qwik.dev/core/internal';
3
3
  import { inlinedQrl } from '@qwik.dev/core';
4
4
  import { R as RedirectMessage, A as AbortMessage } from '../../chunks/redirect-handler.mjs';
5
5
  import { isServer } from '@qwik.dev/core/build';
@@ -1079,8 +1079,11 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
1079
1079
  });
1080
1080
  pipeSource = readable.pipeThrough(capture);
1081
1081
  }
1082
+ let pipeError;
1082
1083
  const pipe = pipeSource.pipeTo(writableStream, {
1083
1084
  preventClose: true
1085
+ }).catch((error) => {
1086
+ pipeError = error;
1084
1087
  });
1085
1088
  const stream = writable.getWriter();
1086
1089
  const status = requestEv.status();
@@ -1111,6 +1114,9 @@ The request origin "${inputOrigin}" does not match the server origin "${origin}"
1111
1114
  await stream.close();
1112
1115
  await pipe;
1113
1116
  }
1117
+ if (pipeError) {
1118
+ throw pipeError;
1119
+ }
1114
1120
  if (eTagCacheKey && cacheChunks && cacheChunks.length > 0) {
1115
1121
  const totalLength = cacheChunks.reduce((sum, chunk) => sum + chunk.length, 0);
1116
1122
  const combined = new Uint8Array(totalLength);
@@ -1291,11 +1297,12 @@ const { renderQwikMiddleware, resolveRequestHandlers} = requestHandlers;
1291
1297
 
1292
1298
  let _asyncRequestStore;
1293
1299
  if (isServer) {
1294
- import('node:async_hooks').then((module) => {
1295
- _asyncRequestStore = new module.AsyncLocalStorage();
1296
- }).catch((err) => {
1297
- console.warn("\n=====================\n Qwik Router Warning:\n AsyncLocalStorage is not available, continuing without it.\n This impacts concurrent async server calls, where they lose access to the ServerRequestEv object.\n=====================\n\n", err);
1298
- });
1300
+ const AsyncLocalStorage = _getAsyncLocalStorage();
1301
+ if (AsyncLocalStorage) {
1302
+ _asyncRequestStore = new AsyncLocalStorage();
1303
+ } else {
1304
+ console.warn("\n=====================\n Qwik Router Warning:\n AsyncLocalStorage is not available, continuing without it.\n This impacts concurrent async server calls, where they lose access to the ServerRequestEv object.\n=====================\n\n");
1305
+ }
1299
1306
  }
1300
1307
 
1301
1308
  function createCacheControl(cacheControl) {
@@ -2275,7 +2275,11 @@ class HtmlTransformPatcher {
2275
2275
  }
2276
2276
  }
2277
2277
  async handleEnd(chunk, encoding, callback) {
2278
- if (typeof encoding === "function") {
2278
+ if (typeof chunk === "function") {
2279
+ callback = chunk;
2280
+ chunk = void 0;
2281
+ encoding = void 0;
2282
+ } else if (typeof encoding === "function") {
2279
2283
  callback = encoding;
2280
2284
  encoding = void 0;
2281
2285
  }
@@ -2293,7 +2297,15 @@ function wrapResponseForHtmlTransform(request, response, server) {
2293
2297
  }
2294
2298
 
2295
2299
  const makeRouterDevMiddleware = (server, ctx) => async (req, res, next) => {
2296
- const mod = await server.ssrLoadModule("src/entry.ssr");
2300
+ let mod;
2301
+ try {
2302
+ mod = await server.ssrLoadModule("src/entry.ssr");
2303
+ } catch (e) {
2304
+ if (e instanceof Error) {
2305
+ server.ssrFixStacktrace(e);
2306
+ }
2307
+ return next();
2308
+ }
2297
2309
  if (!mod.default) {
2298
2310
  console.error("No default export found in src/entry.ssr");
2299
2311
  return next();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qwik.dev/router",
3
3
  "description": "The router for Qwik.",
4
- "version": "2.0.0-beta.34",
4
+ "version": "2.0.0-beta.35",
5
5
  "bugs": "https://github.com/QwikDev/qwik/issues",
6
6
  "dependencies": {
7
7
  "@azure/functions": "3.5.1",
@@ -40,7 +40,7 @@
40
40
  "tsm": "2.3.0",
41
41
  "typescript": "5.9.3",
42
42
  "uvu": "0.5.6",
43
- "@qwik.dev/core": "2.0.0-beta.34"
43
+ "@qwik.dev/core": "2.0.0-beta.35"
44
44
  },
45
45
  "engines": {
46
46
  "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
@@ -169,7 +169,7 @@
169
169
  "main": "./lib/index.qwik.mjs",
170
170
  "peerDependencies": {
171
171
  "vite": ">=6 <9",
172
- "@qwik.dev/core": "^2.0.0-beta.34"
172
+ "@qwik.dev/core": "^2.0.0-beta.35"
173
173
  },
174
174
  "publishConfig": {
175
175
  "access": "public"