@qwik.dev/router 2.0.0-beta.37 → 2.0.0-beta.39

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.
Files changed (35) hide show
  1. package/lib/adapters/cloudflare-pages/vite/index.mjs +2 -2
  2. package/lib/adapters/shared/vite/index.d.ts +3 -4
  3. package/lib/adapters/shared/vite/index.mjs +158 -143
  4. package/lib/chunks/dev-preloaded-route-loader.mjs +3 -0
  5. package/lib/chunks/fs.mjs +6 -2
  6. package/lib/chunks/head.qwik.mjs +814 -93
  7. package/lib/chunks/request-body-limit.mjs +37 -0
  8. package/lib/chunks/request-path.mjs +2 -2
  9. package/lib/chunks/routes.mjs +47 -0
  10. package/lib/chunks/server-error.mjs +19 -0
  11. package/lib/chunks/system.mjs +2 -1
  12. package/lib/chunks/use-functions.qwik.mjs +1 -1
  13. package/lib/chunks/{worker-thread.qwik.mjs → user-response.mjs} +1053 -1749
  14. package/lib/chunks/worker-thread.mjs +317 -0
  15. package/lib/index.d.ts +89 -55
  16. package/lib/index.qwik.mjs +178 -100
  17. package/lib/middleware/azure-swa/index.mjs +13 -9
  18. package/lib/middleware/cloudflare-pages/index.mjs +2 -2
  19. package/lib/middleware/deno/index.d.ts +2 -0
  20. package/lib/middleware/deno/index.mjs +2 -1
  21. package/lib/middleware/netlify-edge/index.mjs +2 -2
  22. package/lib/middleware/node/index.d.ts +9 -1
  23. package/lib/middleware/node/index.mjs +28 -3
  24. package/lib/middleware/request-handler/index.d.ts +9 -10
  25. package/lib/middleware/request-handler/index.mjs +26 -1105
  26. package/lib/middleware/vercel-edge/index.mjs +2 -2
  27. package/lib/ssg/index.d.ts +2 -10
  28. package/lib/ssg/index.mjs +10 -50
  29. package/lib/vite/index.d.ts +8 -0
  30. package/lib/vite/index.mjs +406 -117
  31. package/package.json +5 -4
  32. package/lib/chunks/action-handler.mjs +0 -782
  33. package/lib/chunks/deepFreeze.qwik.mjs +0 -608
  34. package/lib/chunks/error-handler.mjs +0 -57
  35. package/lib/chunks/not-found-wrapper.qwik.mjs +0 -25
@@ -0,0 +1,317 @@
1
+ import { _serialize } from '@qwik.dev/core/internal';
2
+ import { parentPort } from 'node:worker_threads';
3
+ import { r as runQwikRouter, a as resolveRequestHandlers, b as renderQwikMiddleware } from './user-response.mjs';
4
+ import { b as trimInternalPathname } from './request-path.mjs';
5
+ import { I as getRouteLoaders, J as getRouteLoaderValues, l as loadRoute } from './head.qwik.mjs';
6
+
7
+ const staticWorkerThreadDeps = {
8
+ loadRoute,
9
+ renderQwikMiddleware,
10
+ resolveRequestHandlers,
11
+ trimInternalPathname,
12
+ runQwikRouter
13
+ };
14
+ const createNoopWritableStream = () => new WritableStream();
15
+ function isRedirectMessage(value) {
16
+ return typeof value === "object" && value !== null && value.constructor?.name === "RedirectMessage";
17
+ }
18
+ async function requestHandlerForSsg(serverRequestEv, opts, deps) {
19
+ const { render, checkOrigin, qwikRouterConfig } = opts;
20
+ if (!qwikRouterConfig) {
21
+ throw new Error("qwikRouterConfig is required.");
22
+ }
23
+ const pathname = deps.trimInternalPathname(serverRequestEv.url.pathname);
24
+ if (pathname === "/.well-known" || pathname.startsWith("/.well-known/")) {
25
+ return null;
26
+ }
27
+ const { routes, serverPlugins, cacheModules } = qwikRouterConfig;
28
+ const loadedRoute = await deps.loadRoute(routes, cacheModules, pathname);
29
+ const requestHandlers = deps.resolveRequestHandlers(serverPlugins, loadedRoute, serverRequestEv.request.method, checkOrigin ?? true, deps.renderQwikMiddleware(render));
30
+ if (qwikRouterConfig.fallthrough && loadedRoute.$notFound$ && !pathname.endsWith("/404.html")) {
31
+ return null;
32
+ }
33
+ const rebuildRouteInfo = async (url) => {
34
+ const cleanPathname = deps.trimInternalPathname(url.pathname);
35
+ const loadedRoute2 = await deps.loadRoute(routes, cacheModules, cleanPathname);
36
+ const requestHandlers2 = deps.resolveRequestHandlers(serverPlugins, loadedRoute2, serverRequestEv.request.method, checkOrigin ?? true, deps.renderQwikMiddleware(render));
37
+ return {
38
+ loadedRoute: loadedRoute2,
39
+ requestHandlers: requestHandlers2
40
+ };
41
+ };
42
+ return deps.runQwikRouter(serverRequestEv, loadedRoute, requestHandlers, rebuildRouteInfo, qwikRouterConfig.basePathname);
43
+ }
44
+ async function workerRender(sys, opts, staticRoute, pendingPromises, callback, deps) {
45
+ const url = new URL(staticRoute.pathname, opts.origin);
46
+ const result = {
47
+ type: "render",
48
+ pathname: staticRoute.pathname,
49
+ url: url.href,
50
+ ok: false,
51
+ error: null,
52
+ filePath: null,
53
+ contentType: null,
54
+ resourceType: null
55
+ };
56
+ try {
57
+ let routeWriter = null;
58
+ let closeResolved;
59
+ const closePromise = new Promise((closePromiseResolve) => {
60
+ closeResolved = closePromiseResolve;
61
+ });
62
+ const request = new Request(url);
63
+ const requestCtx = {
64
+ mode: "static",
65
+ locale: void 0,
66
+ url,
67
+ request,
68
+ env: {
69
+ get(key) {
70
+ return sys.getEnv(key);
71
+ }
72
+ },
73
+ platform: sys.platform,
74
+ getClientConn: () => {
75
+ return {};
76
+ },
77
+ getWritableStream: (status, headers, _, _r, requestEv) => {
78
+ const isNotFoundPage = url.pathname.endsWith("/404.html");
79
+ result.ok = status >= 200 && status < 300 || isNotFoundPage && status === 404;
80
+ if (!result.ok) {
81
+ return createNoopWritableStream();
82
+ }
83
+ result.contentType = (headers.get("Content-Type") || "").toLowerCase();
84
+ const isHtml = result.contentType.includes("text/html");
85
+ const routeFilePath = sys.getRouteFilePath(url.pathname, isHtml);
86
+ if (isHtml) {
87
+ result.resourceType = "page";
88
+ }
89
+ const hasRouteWriter = isHtml ? opts.emitHtml !== false : true;
90
+ const writeLoaderDataEnabled = isHtml && opts.emitData !== false;
91
+ const stream = new WritableStream({
92
+ async start() {
93
+ try {
94
+ if (hasRouteWriter || writeLoaderDataEnabled) {
95
+ await sys.ensureDir(routeFilePath);
96
+ }
97
+ if (hasRouteWriter) {
98
+ routeWriter = sys.createWriteStream(routeFilePath);
99
+ routeWriter.on("error", (e) => {
100
+ console.error(e);
101
+ routeWriter = null;
102
+ result.error = {
103
+ message: e.message,
104
+ stack: e.stack
105
+ };
106
+ });
107
+ }
108
+ } catch (e) {
109
+ console.error("Error during stream start", staticRoute.pathname, e);
110
+ routeWriter = null;
111
+ result.error = {
112
+ message: String(e),
113
+ stack: e.stack || ""
114
+ };
115
+ }
116
+ },
117
+ write(chunk) {
118
+ try {
119
+ if (routeWriter) {
120
+ routeWriter.write(Buffer.from(chunk.buffer));
121
+ }
122
+ } catch (e) {
123
+ console.error("Error during stream write", staticRoute.pathname, e);
124
+ routeWriter = null;
125
+ result.error = {
126
+ message: String(e),
127
+ stack: e.stack || ""
128
+ };
129
+ }
130
+ },
131
+ async close() {
132
+ const writePromises = [];
133
+ try {
134
+ if (writeLoaderDataEnabled && !isNotFoundPage) {
135
+ const routeLoaders = getRouteLoaders(requestEv);
136
+ const loaderValues = getRouteLoaderValues(requestEv);
137
+ const manifestHash = opts.manifest?.manifestHash || "dev";
138
+ for (const loader of routeLoaders) {
139
+ if (loader.__cacheControl !== "immutable") {
140
+ continue;
141
+ }
142
+ const data = loaderValues[loader.__id];
143
+ if (data === void 0) {
144
+ continue;
145
+ }
146
+ const loaderFilePath = sys.getLoaderFilePath(url.pathname, loader.__id, manifestHash);
147
+ const loaderWriter = sys.createWriteStream(loaderFilePath);
148
+ loaderWriter.on("error", (e) => {
149
+ console.error(e);
150
+ result.error = {
151
+ message: e.message,
152
+ stack: e.stack
153
+ };
154
+ });
155
+ const serialized = await deps.serialize({
156
+ d: data
157
+ });
158
+ loaderWriter.write(serialized);
159
+ writePromises.push(new Promise((resolve) => {
160
+ result.filePath = routeFilePath;
161
+ loaderWriter.end(resolve);
162
+ }));
163
+ }
164
+ }
165
+ if (routeWriter) {
166
+ writePromises.push(new Promise((resolve) => {
167
+ result.filePath = routeFilePath;
168
+ routeWriter.end(resolve);
169
+ }).finally(closeResolved));
170
+ }
171
+ if (writePromises.length > 0) {
172
+ await Promise.all(writePromises);
173
+ }
174
+ } catch (e) {
175
+ console.error("Error during stream close", staticRoute.pathname, e);
176
+ routeWriter = null;
177
+ result.error = {
178
+ message: String(e),
179
+ stack: e.stack || ""
180
+ };
181
+ }
182
+ }
183
+ });
184
+ return stream;
185
+ }
186
+ };
187
+ const promise = requestHandlerForSsg(requestCtx, opts, deps).then((rsp) => {
188
+ if (rsp != null) {
189
+ return rsp.completion.then((r) => {
190
+ if (routeWriter) {
191
+ return closePromise.then(() => r);
192
+ }
193
+ return r;
194
+ });
195
+ }
196
+ }).then((e) => {
197
+ if (e !== void 0) {
198
+ if (isRedirectMessage(e)) {
199
+ return;
200
+ }
201
+ if (e instanceof Error) {
202
+ result.error = {
203
+ message: e.message,
204
+ stack: e.stack
205
+ };
206
+ } else {
207
+ result.error = {
208
+ message: String(e),
209
+ stack: void 0
210
+ };
211
+ }
212
+ console.error("Error during request handling", staticRoute.pathname, e);
213
+ }
214
+ }).catch((e) => {
215
+ console.error("Unhandled error during request handling", staticRoute.pathname, e);
216
+ result.error = {
217
+ message: String(e),
218
+ stack: e.stack || ""
219
+ };
220
+ }).finally(() => {
221
+ pendingPromises.delete(promise);
222
+ callback(result);
223
+ });
224
+ pendingPromises.add(promise);
225
+ } catch (e) {
226
+ console.error("Error during render", staticRoute.pathname, e);
227
+ if (e instanceof Error) {
228
+ result.error = {
229
+ message: e.message,
230
+ stack: e.stack
231
+ };
232
+ } else {
233
+ result.error = {
234
+ message: String(e),
235
+ stack: void 0
236
+ };
237
+ }
238
+ callback(result);
239
+ }
240
+ }
241
+ async function workerThread(sys) {
242
+ const opts = sys.getOptions();
243
+ const pendingPromises = /* @__PURE__ */ new Set();
244
+ const deps = {
245
+ ...staticWorkerThreadDeps,
246
+ serialize: _serialize
247
+ };
248
+ process.on("uncaughtException", (e) => {
249
+ console.error("Worker uncaught exception (suppressed):", e.message);
250
+ });
251
+ process.on("unhandledRejection", (e) => {
252
+ console.error("Worker unhandled rejection (suppressed):", e instanceof Error ? e.message : e);
253
+ });
254
+ const onMessage = async (msg) => {
255
+ switch (msg.type) {
256
+ case "render": {
257
+ return new Promise((resolve) => {
258
+ workerRender(sys, opts, msg, pendingPromises, resolve, deps).catch((e) => {
259
+ console.error("Error during render", msg.pathname, e);
260
+ resolve({
261
+ type: "render",
262
+ pathname: msg.pathname,
263
+ url: "",
264
+ ok: false,
265
+ error: {
266
+ message: e instanceof Error ? e.message : String(e),
267
+ stack: e instanceof Error ? e.stack : void 0
268
+ },
269
+ filePath: null,
270
+ contentType: null,
271
+ resourceType: null
272
+ });
273
+ });
274
+ });
275
+ }
276
+ case "close": {
277
+ if (pendingPromises.size) {
278
+ const promises = Array.from(pendingPromises);
279
+ pendingPromises.clear();
280
+ await Promise.all(promises);
281
+ }
282
+ return {
283
+ type: "close"
284
+ };
285
+ }
286
+ }
287
+ };
288
+ parentPort?.on("message", async (msg) => {
289
+ try {
290
+ parentPort?.postMessage(await onMessage(msg));
291
+ } catch (e) {
292
+ if (msg.type === "render") {
293
+ const error = e instanceof Error ? e : new Error(String(e));
294
+ parentPort?.postMessage({
295
+ type: "render",
296
+ pathname: msg.pathname,
297
+ url: "",
298
+ ok: false,
299
+ error: {
300
+ message: error.message,
301
+ stack: error.stack
302
+ },
303
+ filePath: null,
304
+ contentType: null,
305
+ resourceType: null
306
+ });
307
+ } else {
308
+ console.error("Worker message handler error", e);
309
+ }
310
+ }
311
+ if (msg.type === "close") {
312
+ parentPort?.close();
313
+ }
314
+ });
315
+ }
316
+
317
+ export { workerThread };
package/lib/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  /// <reference path="./modules.d.ts" />
2
2
 
3
- import type { AsyncSignal } from '@qwik.dev/core';
3
+ import type { AbortMessage } from '@qwik.dev/router/middleware/request-handler';
4
+ import type { CacheControl } from '@qwik.dev/router/middleware/request-handler';
4
5
  import { Component } from '@qwik.dev/core';
6
+ import type { ComputedSignal } from '@qwik.dev/core';
5
7
  import { Cookie } from '@qwik.dev/router/middleware/request-handler';
6
8
  import { CookieOptions } from '@qwik.dev/router/middleware/request-handler';
7
9
  import { CookieValue } from '@qwik.dev/router/middleware/request-handler';
@@ -24,6 +26,7 @@ import { RequestEventLoader } from '@qwik.dev/router/middleware/request-handler'
24
26
  import { RequestHandler } from '@qwik.dev/router/middleware/request-handler';
25
27
  import type { ResolveSyncValue } from '@qwik.dev/router/middleware/request-handler';
26
28
  import type { SerializationStrategy } from '@qwik.dev/core/internal';
29
+ import type { ServerError } from '@qwik.dev/router/middleware/request-handler';
27
30
  import type { Signal } from '@qwik.dev/core';
28
31
  import type * as v from 'valibot';
29
32
  import type { ValueOrPromise } from '@qwik.dev/core';
@@ -38,51 +41,53 @@ export declare type Action<RETURN, INPUT = Record<string, unknown>, OPTIONAL ext
38
41
  * component$(). Like all `use-` functions and methods, it can only be invoked within a
39
42
  * `component$()`.
40
43
  */
41
- (): ActionStore<RETURN, INPUT, OPTIONAL>;
44
+ (): ActionStore<ExcludeControlFlow<RETURN>, INPUT, OPTIONAL>;
42
45
  };
43
46
 
44
47
  /** @public */
45
48
  export declare type ActionConstructor = {
46
- <OBJ extends Record<string, any> | void | null, VALIDATOR extends TypedDataValidator, REST extends [DataValidator, ...DataValidator[]]>(actionQrl: (data: GetValidatorOutputType<VALIDATOR>, event: RequestEventAction) => ValueOrPromise<OBJ>, options: {
47
- readonly id?: string;
49
+ <OBJ extends Record<string, any> | void | null, VALIDATOR extends TypedDataValidator, REST extends [DataValidator, ...DataValidator[]]>(actionQrl: (data: GetValidatorOutputType<VALIDATOR>, event: RequestEventAction) => ValueOrPromise<OBJ>, options: ActionOptions & {
48
50
  readonly validation: [VALIDATOR, ...REST];
49
51
  }): Action<StrictUnion<OBJ | FailReturn<ValidatorErrorType<GetValidatorInputType<VALIDATOR>>> | FailReturn<FailOfRest<REST>>>, GetValidatorInputType<VALIDATOR>, false>;
50
- <OBJ extends Record<string, any> | void | null, VALIDATOR extends TypedDataValidator>(actionQrl: (data: GetValidatorOutputType<VALIDATOR>, event: RequestEventAction) => ValueOrPromise<OBJ>, options: {
51
- readonly id?: string;
52
+ <OBJ extends Record<string, any> | void | null, VALIDATOR extends TypedDataValidator>(actionQrl: (data: GetValidatorOutputType<VALIDATOR>, event: RequestEventAction) => ValueOrPromise<OBJ>, options: ActionOptions & {
52
53
  readonly validation: [VALIDATOR];
53
54
  }): Action<StrictUnion<OBJ | FailReturn<ValidatorErrorType<GetValidatorInputType<VALIDATOR>>>>, GetValidatorInputType<VALIDATOR>, false>;
54
- <OBJ extends Record<string, any> | void | null, REST extends [DataValidator, ...DataValidator[]]>(actionQrl: (data: JSONObject, event: RequestEventAction) => ValueOrPromise<OBJ>, options: {
55
- readonly id?: string;
55
+ <OBJ extends Record<string, any> | void | null, REST extends [DataValidator, ...DataValidator[]]>(actionQrl: (data: JSONObject, event: RequestEventAction) => ValueOrPromise<OBJ>, options: ActionOptions & {
56
56
  readonly validation: REST;
57
57
  }): Action<StrictUnion<OBJ | FailReturn<FailOfRest<REST>>>>;
58
58
  <OBJ extends Record<string, any> | void | null, VALIDATOR extends TypedDataValidator, REST extends [DataValidator, ...DataValidator[]]>(actionQrl: (data: GetValidatorOutputType<VALIDATOR>, event: RequestEventAction) => ValueOrPromise<OBJ>, options: VALIDATOR, ...rest: REST): Action<StrictUnion<OBJ | FailReturn<ValidatorErrorType<GetValidatorInputType<VALIDATOR>>> | FailReturn<FailOfRest<REST>>>, GetValidatorInputType<VALIDATOR>, false>;
59
59
  <OBJ extends Record<string, any> | void | null, VALIDATOR extends TypedDataValidator>(actionQrl: (data: GetValidatorOutputType<VALIDATOR>, event: RequestEventAction) => ValueOrPromise<OBJ>, options: VALIDATOR): Action<StrictUnion<OBJ | FailReturn<ValidatorErrorType<GetValidatorInputType<VALIDATOR>>>>, GetValidatorInputType<VALIDATOR>, false>;
60
60
  <OBJ extends Record<string, any> | void | null, REST extends [DataValidator, ...DataValidator[]]>(actionQrl: (form: JSONObject, event: RequestEventAction) => ValueOrPromise<OBJ>, ...rest: REST): Action<StrictUnion<OBJ | FailReturn<FailOfRest<REST>>>>;
61
- <OBJ>(actionQrl: (form: JSONObject, event: RequestEventAction) => ValueOrPromise<OBJ>, options?: {
62
- readonly id?: string;
63
- }): Action<StrictUnion<OBJ>>;
61
+ <OBJ>(actionQrl: (form: JSONObject, event: RequestEventAction) => ValueOrPromise<OBJ>, options?: ActionOptions): Action<StrictUnion<OBJ>>;
64
62
  };
65
63
 
66
64
  /** @public */
67
65
  declare type ActionConstructorQRL = {
68
- <OBJ extends Record<string, any> | void | null, VALIDATOR extends TypedDataValidator, REST extends [DataValidator, ...DataValidator[]]>(actionQrl: QRL<(data: GetValidatorOutputType<VALIDATOR>, event: RequestEventAction) => ValueOrPromise<OBJ>>, options: {
69
- readonly id?: string;
66
+ <OBJ extends Record<string, any> | void | null, VALIDATOR extends TypedDataValidator, REST extends [DataValidator, ...DataValidator[]]>(actionQrl: QRL<(data: GetValidatorOutputType<VALIDATOR>, event: RequestEventAction) => ValueOrPromise<OBJ>>, options: ActionOptions & {
70
67
  readonly validation: [VALIDATOR, ...REST];
71
68
  }): Action<StrictUnion<OBJ | FailReturn<ValidatorErrorType<GetValidatorInputType<VALIDATOR>>> | FailReturn<FailOfRest<REST>>>, GetValidatorInputType<VALIDATOR>, false>;
72
- <OBJ extends Record<string, any> | void | null, VALIDATOR extends TypedDataValidator>(actionQrl: QRL<(data: GetValidatorOutputType<VALIDATOR>, event: RequestEventAction) => ValueOrPromise<OBJ>>, options: {
73
- readonly id?: string;
69
+ <OBJ extends Record<string, any> | void | null, VALIDATOR extends TypedDataValidator>(actionQrl: QRL<(data: GetValidatorOutputType<VALIDATOR>, event: RequestEventAction) => ValueOrPromise<OBJ>>, options: ActionOptions & {
74
70
  readonly validation: [VALIDATOR];
75
71
  }): Action<StrictUnion<OBJ | FailReturn<ValidatorErrorType<GetValidatorInputType<VALIDATOR>>>>, GetValidatorInputType<VALIDATOR>, false>;
76
- <OBJ extends Record<string, any> | void | null, REST extends [DataValidator, ...DataValidator[]]>(actionQrl: QRL<(data: JSONObject, event: RequestEventAction) => ValueOrPromise<OBJ>>, options: {
77
- readonly id?: string;
72
+ <OBJ extends Record<string, any> | void | null, REST extends [DataValidator, ...DataValidator[]]>(actionQrl: QRL<(data: JSONObject, event: RequestEventAction) => ValueOrPromise<OBJ>>, options: ActionOptions & {
78
73
  readonly validation: REST;
79
74
  }): Action<StrictUnion<OBJ | FailReturn<FailOfRest<REST>>>>;
80
75
  <OBJ extends Record<string, any> | void | null, VALIDATOR extends TypedDataValidator, REST extends [DataValidator, ...DataValidator[]]>(actionQrl: QRL<(data: GetValidatorOutputType<VALIDATOR>, event: RequestEventAction) => ValueOrPromise<OBJ>>, options: VALIDATOR, ...rest: REST): Action<StrictUnion<OBJ | FailReturn<ValidatorErrorType<GetValidatorInputType<VALIDATOR>>> | FailReturn<FailOfRest<REST>>>, GetValidatorInputType<VALIDATOR>, false>;
81
76
  <OBJ extends Record<string, any> | void | null, VALIDATOR extends TypedDataValidator>(actionQrl: QRL<(data: GetValidatorOutputType<VALIDATOR>, event: RequestEventAction) => ValueOrPromise<OBJ>>, options: VALIDATOR): Action<StrictUnion<OBJ | FailReturn<ValidatorErrorType<GetValidatorInputType<VALIDATOR>>>>, GetValidatorInputType<VALIDATOR>, false>;
82
77
  <OBJ extends Record<string, any> | void | null, REST extends [DataValidator, ...DataValidator[]]>(actionQrl: QRL<(form: JSONObject, event: RequestEventAction) => ValueOrPromise<OBJ>>, ...rest: REST): Action<StrictUnion<OBJ | FailReturn<FailOfRest<REST>>>>;
83
- <OBJ>(actionQrl: QRL<(form: JSONObject, event: RequestEventAction) => ValueOrPromise<OBJ>>, options?: {
84
- readonly id?: string;
85
- }): Action<StrictUnion<OBJ>>;
78
+ <OBJ>(actionQrl: QRL<(form: JSONObject, event: RequestEventAction) => ValueOrPromise<OBJ>>, options?: ActionOptions): Action<StrictUnion<OBJ>>;
79
+ };
80
+
81
+ /** @public */
82
+ export declare type ActionOptions = {
83
+ readonly id?: string;
84
+ readonly validation?: DataValidator[];
85
+ /**
86
+ * Route loaders to invalidate after this action completes. The loader hooks' hashes are sent to
87
+ * the client so it knows which loaders to re-fetch. If omitted, ALL route loaders are invalidated
88
+ * (unless `strictLoaders` is enabled globally in the Vite plugin).
89
+ */
90
+ readonly invalidate?: Loader_2<any>[];
86
91
  };
87
92
 
88
93
  /** @public */
@@ -394,6 +399,14 @@ declare interface ErrorBoundaryProps {
394
399
  fallback$?: QRL<(error: any) => any>;
395
400
  }
396
401
 
402
+ /**
403
+ * Drops control-flow signals (`ev.redirect()`, `ev.error()`, etc.) from a loader/action return
404
+ * type: those are thrown, not surfaced as data. `ev.fail()` is plain data and is kept.
405
+ *
406
+ * @public
407
+ */
408
+ export declare type ExcludeControlFlow<T> = Exclude<T, AbortMessage | ServerError>;
409
+
397
410
  declare type Failed = {
398
411
  failed: true;
399
412
  };
@@ -434,6 +447,14 @@ export declare interface FormSubmitSuccessDetail<T> {
434
447
  value: T;
435
448
  }
436
449
 
450
+ /**
451
+ * Returns the current RequestEvent if possible. Only usable on the server, and only during request
452
+ * processing.
453
+ *
454
+ * @public
455
+ */
456
+ export declare const getRequestEvent: (thisArg?: unknown) => RequestEvent | undefined;
457
+
437
458
  /** @public */
438
459
  export declare type GetValidatorInputType<VALIDATOR extends TypedDataValidator> = VALIDATOR extends ValibotDataValidator<infer TYPE> ? v.InferInput<TYPE> : VALIDATOR extends ZodDataValidator<infer TYPE> ? z_2.input<TYPE> : never;
439
460
 
@@ -482,8 +503,8 @@ export declare const Link: Component<LinkProps>;
482
503
  /** @public */
483
504
  export declare interface LinkProps extends AnchorAttributes {
484
505
  /**
485
- * @deprecated Use `prefetchBundle` and `prefetchData` instead for more granular control over what
486
- * is prefetched and when. This prop will be removed in a future major version.
506
+ * @deprecated Use `prefetchBundles` and `prefetchData` instead for more granular control over
507
+ * what is prefetched and when. This prop will be removed in a future major version.
487
508
  *
488
509
  * Legacy prefetch control for this **`Link`**.
489
510
  *
@@ -501,7 +522,7 @@ export declare interface LinkProps extends AnchorAttributes {
501
522
  *
502
523
  * Prefetching will not occur if the user has the **data saver** setting enabled.
503
524
  */
504
- prefetchBundle?: PrefetchStrategy;
525
+ prefetchBundles?: PrefetchStrategy;
505
526
  /**
506
527
  * Controls when Qwik should prefetch and cache route data for this **`Link`** target, including
507
528
  * invoking any **`routeLoader$`**, **`onGet`**, etc.
@@ -534,8 +555,8 @@ declare interface LoadedRoute {
534
555
  $routeBundleNames$?: string[] | undefined;
535
556
  /** Whether this route is a not-found (404) route */
536
557
  $notFound$?: boolean;
537
- /** The error module loader (nearest _E ancestor), for rendering ServerErrors */
538
- $errorLoader$?: ContentModuleLoader;
558
+ /** The nearest _E (error.tsx) boundary's chain to render on a thrown ServerError (in its layouts). */
559
+ $errorLoader$?: ModuleLoader[];
539
560
  /** Merged array of routeLoader$ hashes from all matched nodes (layouts + page) */
540
561
  $loaders$?: string[];
541
562
  /** Runtime-only mapping of routeLoader$ hashes to the matched pathname used for q-loader fetches */
@@ -548,7 +569,7 @@ declare type Loader_2<RETURN> = {
548
569
  * Returns the `Signal` containing the data returned by the `loader$` function. Like all `use-`
549
570
  * functions and methods, it can only be invoked within a `component$()`.
550
571
  */
551
- (): LoaderSignal<RETURN>;
572
+ (): LoaderSignal<ExcludeControlFlow<RETURN>>;
552
573
  };
553
574
  export { Loader_2 as Loader }
554
575
 
@@ -566,24 +587,28 @@ declare type LoaderConstructorQRL = {
566
587
 
567
588
  /** @public */
568
589
  declare type LoaderOptions = {
569
- /** @deprecated Unused */
590
+ /**
591
+ * Explicit loader id, overriding the QRL hash. Pass a distinct value (e.g. `fn.getHash()`) when
592
+ * loaders share a wrapper QRL.
593
+ */
570
594
  readonly id?: string;
571
595
  readonly validation?: DataValidator[];
572
596
  readonly serializationStrategy?: SerializationStrategy;
573
597
  /**
574
- * Time in milliseconds after which the loader data is considered stale. The server derives
575
- * `Cache-Control: max-age` seconds from this value on loader responses.
598
+ * Cache-Control for this loader's JSON responses. The browser HTTP cache is the single source of
599
+ * freshness for loader data: on every client navigation the loaders re-fetch, and this header
600
+ * decides whether the browser serves the fetch from cache, revalidates, or hits the server.
576
601
  *
577
- * On the client, the loader's AsyncSignal `expires` is set to this value. If `poll` is true, the
578
- * signal auto-refetches when expired. If `poll` is false (default), the data is marked stale but
579
- * not auto-refetched.
580
- */
581
- readonly expires?: number;
582
- /**
583
- * When true AND `expires` is set, the loader data is automatically refetched when it expires
584
- * (polling behavior). When false (default), expired data is marked stale but not auto-refetched.
602
+ * Accepts any `cacheControl()` value (`'immutable'`, `'no-cache'`, a max-age number, an options
603
+ * object) or a function of the request event; the function may return `null` to skip the header.
604
+ * A `Cache-Control` header set inside the loader function wins over this option.
605
+ *
606
+ * Defaults to `no-cache` (always revalidate; combine with `eTag` for cheap 304s).
607
+ *
608
+ * The literal value `'immutable'` also marks the loader's data as static, so SSG writes a
609
+ * per-loader JSON file at build time.
585
610
  */
586
- readonly poll?: boolean;
611
+ readonly cacheControl?: CacheControl | ((ev: RequestEvent) => CacheControl | null);
587
612
  /**
588
613
  * Enable ETag-based caching for this loader's JSON responses.
589
614
  *
@@ -632,17 +657,21 @@ declare type LoaderOptions = {
632
657
  */
633
658
  readonly search?: string[];
634
659
  /**
635
- * When true (default), the previous value is kept while the loader re-fetches after navigation,
636
- * so components see stale data until the new response arrives.
660
+ * When true (default), the loader is awaited before SSR renders, so its redirect or error can
661
+ * short-circuit the response and its value is ready for synchronous reads (e.g. in the head).
662
+ *
663
+ * When false, the loader runs in the background without blocking SSR: rendering starts
664
+ * immediately and reading its `.value` suspends until it resolves. A background loader cannot
665
+ * redirect or error the initial SSR response; treat it as a separate request.
637
666
  *
638
- * When false, the value is cleared on re-fetch, causing reads to suspend (show a loading
639
- * boundary). This is useful when showing old data during navigation would be confusing.
667
+ * Setting `false` is experimental and requires adding `experimental: ["blockSSR"]` to your
668
+ * qwikVite plugin options.
640
669
  */
641
- readonly allowStale?: boolean;
670
+ readonly blockSSR?: boolean;
642
671
  };
643
672
 
644
673
  /** @public */
645
- export declare type LoaderSignal<TYPE> = (TYPE extends () => ValueOrPromise<infer VALIDATOR> ? Signal<ValueOrPromise<VALIDATOR>> : Signal<TYPE>) & Pick<AsyncSignal, 'promise' | 'loading' | 'error'>;
674
+ export declare type LoaderSignal<TYPE> = (TYPE extends () => ValueOrPromise<infer VALIDATOR> ? Signal<ValueOrPromise<VALIDATOR>> : Signal<TYPE>) & Pick<ComputedSignal<any>, 'promise' | 'pending' | 'error' | 'loading'>;
646
675
 
647
676
  declare interface MenuModule {
648
677
  readonly default: ContentMenu;
@@ -840,9 +869,9 @@ export declare const QwikRouterMockProvider: Component<QwikRouterMockProps>;
840
869
  /** @public */
841
870
  export declare interface QwikRouterProps {
842
871
  /**
843
- * Enable the ViewTransition API
872
+ * Enable the ViewTransition API on SPA navigation. Opt-in: set to `true` to enable.
844
873
  *
845
- * Default: `true`
874
+ * Default: `false`
846
875
  *
847
876
  * @see https://github.com/WICG/view-transitions/blob/main/explainer.md
848
877
  * @see https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API
@@ -906,7 +935,8 @@ export declare type ResolvedDocumentHead<FrontMatter extends Record<string, any>
906
935
  */
907
936
  export declare const routeAction$: ActionConstructor;
908
937
 
909
- /* Excluded from this release type: routeActionQrl */
938
+ /** @public */
939
+ export declare const routeActionQrl: ActionConstructorQRL;
910
940
 
911
941
  declare type RouteActionResolver = {
912
942
  status: number;
@@ -963,10 +993,13 @@ export declare interface RouteData {
963
993
  _G?: string;
964
994
  /** The JS bundle names for this route (SSR only) */
965
995
  _B?: string[];
966
- /** The not-found (404) module loader for this subtree */
967
- _4?: ContentModuleLoader;
968
- /** The error page module loader for this subtree (error.tsx, takes precedence over _4) */
969
- _E?: ContentModuleLoader;
996
+ /**
997
+ * Not-found (404) boundary: single loader (runtime prepends layouts) or override chain
998
+ * (`404@layout`/`!`).
999
+ */
1000
+ _4?: ContentModuleLoader | ModuleLoader[];
1001
+ /** Error (error.tsx) boundary, same single-or-override-chain shape as `_4`. */
1002
+ _E?: ContentModuleLoader | ModuleLoader[];
970
1003
  /** The parameter name when this node is reached via `_W` or `_A` from the parent */
971
1004
  _P?: string;
972
1005
  /** Prefix for infix params (e.g. "pre" for `pre[slug]post`) — only on `_W` nodes */
@@ -986,7 +1019,7 @@ export declare interface RouteData {
986
1019
  /**
987
1020
  * Define a route loader that fetches data before the route renders.
988
1021
  *
989
- * Route loaders run on the server during SSR and return data as an `AsyncSignal`. On the client,
1022
+ * Route loaders run on the server during SSR and return data as a `ComputedSignal`. On the client,
990
1023
  * loaders automatically re-fetch when the route changes (SPA navigation). Each loader gets its own
991
1024
  * JSON endpoint (`q-loader-{id}.{hash}.json`), so only the loaders present on the target route are
992
1025
  * fetched.
@@ -1001,10 +1034,9 @@ export declare interface RouteData {
1001
1034
  * sent in the request and changes to other params are ignored. During SSR and loader JSON
1002
1035
  * requests, the loader's request event is filtered to those params too. `search: []` means no
1003
1036
  * search params are sent and only route path changes trigger a re-fetch.
1004
- * - `allowStale: false`: Clears the previous value when re-fetching, so components see a loading
1005
- * state instead of stale data during navigation. Useful when old data would be confusing.
1006
1037
  * - `eTag`: Enable ETag-based caching. Can be `true` (auto-hash), a string, or a function.
1007
- * - `expires` / `poll`: Control client-side caching and polling behavior.
1038
+ * - `cacheControl`: Cache-Control for loader JSON responses; the browser HTTP cache controls
1039
+ * client-side freshness. `'immutable'` additionally lets SSG write the loader file.
1008
1040
  *
1009
1041
  * The `strictLoaders` Vite plugin option applies `search: []` globally for all loaders that don't
1010
1042
  * specify an explicit `search` option.
@@ -1028,6 +1060,8 @@ declare type RouteLoaderCtx = {
1028
1060
  pageSearch?: string;
1029
1061
  /** SPA navigation function. Client-only and intentionally omitted from SSR state. */
1030
1062
  goto?: NoSerialize<RouteNavigate>;
1063
+ /** Client manifest hash for q-loader fetch URLs. */
1064
+ manifestHash?: string;
1031
1065
  };
1032
1066
 
1033
1067
  /* Excluded from this release type: routeLoaderQrl */