@qwik.dev/router 2.0.0-beta.29 → 2.0.0-beta.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.
@@ -1,10 +1,27 @@
1
- import { isServer } from '@qwik.dev/core/build';
2
- import { k as isPromise, j as QDATA_KEY, Q as Q_ROUTE, f as QFN_KEY, m as QLOADER_KEY, h as QACTION_KEY, n as resolveRouteConfig, d as loadRoute } from '../../chunks/routing.qwik.mjs';
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, e as loadRoute } from '../../chunks/routing.qwik.mjs';
2
+ import { isDev, _UNINITIALIZED, _deserialize, _verifySerializable, _serialize } from '@qwik.dev/core/internal';
3
3
  import { inlinedQrl } from '@qwik.dev/core';
4
- import { _serialize, isDev, _UNINITIALIZED, _deserialize, _verifySerializable } from '@qwik.dev/core/internal';
5
- import { _asyncRequestStore as _asyncRequestStore$1, ServerError as ServerError$1, RedirectMessage as RedirectMessage$1, RewriteMessage as RewriteMessage$1, AbortMessage as AbortMessage$1 } from '@qwik.dev/router/middleware/request-handler';
4
+ import { R as RedirectMessage, A as AbortMessage } from '../../chunks/redirect-handler.mjs';
5
+ import { isServer } from '@qwik.dev/core/build';
6
6
  import { g as getErrorHtml, m as minimalHtmlResponse } from '../../chunks/error-handler.mjs';
7
7
 
8
+ const IsQData = "@isQData";
9
+ const QDATA_JSON = "/q-data.json";
10
+ function getRouteMatchPathname(pathname) {
11
+ const isInternal = pathname.endsWith(QDATA_JSON);
12
+ if (isInternal) {
13
+ const trimEnd = pathname.length - QDATA_JSON.length + (globalThis.__NO_TRAILING_SLASH__ ? 0 : 1);
14
+ pathname = pathname.slice(0, trimEnd);
15
+ if (pathname === "") {
16
+ pathname = "/";
17
+ }
18
+ }
19
+ return {
20
+ pathname,
21
+ isInternal
22
+ };
23
+ }
24
+
8
25
  const MAX_CACHE_SIZE = typeof globalThis.__SSR_CACHE_SIZE__ === "number" ? globalThis.__SSR_CACHE_SIZE__ : 50;
9
26
  const ssrCache = /* @__PURE__ */ new Map();
10
27
  function resolveETag(eTagExport, headProps) {
@@ -116,460 +133,148 @@ var HttpStatus = /* @__PURE__ */ (function(HttpStatus2) {
116
133
  return HttpStatus2;
117
134
  })({});
118
135
 
119
- function createCacheControl(cacheControl) {
120
- const controls = [];
121
- if (cacheControl === "day") {
122
- cacheControl = 60 * 60 * 24;
123
- } else if (cacheControl === "week") {
124
- cacheControl = 60 * 60 * 24 * 7;
125
- } else if (cacheControl === "month") {
126
- cacheControl = 60 * 60 * 24 * 30;
127
- } else if (cacheControl === "year") {
128
- cacheControl = 60 * 60 * 24 * 365;
129
- } else if (cacheControl === "private") {
130
- cacheControl = {
131
- private: true,
132
- noCache: true
133
- };
134
- } else if (cacheControl === "immutable") {
135
- cacheControl = {
136
- public: true,
137
- immutable: true,
138
- maxAge: 60 * 60 * 24 * 365
139
- };
140
- } else if (cacheControl === "no-cache") {
141
- cacheControl = {
142
- noCache: true
143
- };
144
- }
145
- if (typeof cacheControl === "number") {
146
- cacheControl = {
147
- maxAge: cacheControl,
148
- sMaxAge: cacheControl
149
- };
150
- }
151
- if (cacheControl.immutable) {
152
- controls.push("immutable");
153
- }
154
- if (cacheControl.maxAge) {
155
- controls.push(`max-age=${cacheControl.maxAge}`);
156
- }
157
- if (cacheControl.sMaxAge) {
158
- controls.push(`s-maxage=${cacheControl.sMaxAge}`);
159
- }
160
- if (cacheControl.noStore) {
161
- controls.push("no-store");
162
- }
163
- if (cacheControl.noCache) {
164
- controls.push("no-cache");
165
- }
166
- if (cacheControl.private) {
167
- controls.push("private");
168
- }
169
- if (cacheControl.public) {
170
- controls.push("public");
171
- }
172
- if (cacheControl.staleWhileRevalidate) {
173
- controls.push(`stale-while-revalidate=${cacheControl.staleWhileRevalidate}`);
174
- }
175
- if (cacheControl.staleIfError) {
176
- controls.push(`stale-if-error=${cacheControl.staleIfError}`);
177
- }
178
- return controls.join(", ");
136
+ const RequestEvLoaders = /* @__PURE__ */ Symbol("RequestEvLoaders");
137
+ const RequestEvMode = /* @__PURE__ */ Symbol("RequestEvMode");
138
+ const RequestEvRoute = /* @__PURE__ */ Symbol("RequestEvRoute");
139
+ const RequestEvLoaderSerializationStrategyMap = /* @__PURE__ */ Symbol("RequestEvLoaderSerializationStrategyMap");
140
+ const RequestRouteName = "@routeName";
141
+ const RequestEvSharedActionId = "@actionId";
142
+ const RequestEvSharedActionFormData = "@actionFormData";
143
+ const RequestEvSharedNonce = "@nonce";
144
+ const RequestEvIsRewrite = "@rewrite";
145
+ const RequestEvShareServerTiming = "@serverTiming";
146
+ const RequestEvETagCacheKey = "@eTagCacheKey";
147
+ const RequestEvHttpStatusMessage = "@httpStatusMessage";
148
+ const RequestEvShareQData = "qData";
149
+ function getRequestLoaders(requestEv) {
150
+ return requestEv[RequestEvLoaders];
179
151
  }
180
-
181
- const SAMESITE = {
182
- lax: "Lax",
183
- Lax: "Lax",
184
- None: "None",
185
- none: "None",
186
- strict: "Strict",
187
- Strict: "Strict"
188
- };
189
- const UNIT = {
190
- seconds: 1,
191
- minutes: 1 * 60,
192
- hours: 1 * 60 * 60,
193
- days: 1 * 60 * 60 * 24,
194
- weeks: 1 * 60 * 60 * 24 * 7
195
- };
196
- function tryDecodeUriComponent(str) {
197
- try {
198
- return decodeURIComponent(str);
199
- } catch {
200
- return str;
201
- }
152
+ function getRequestLoaderSerializationStrategyMap(requestEv) {
153
+ return requestEv[RequestEvLoaderSerializationStrategyMap];
202
154
  }
203
- const parseCookieString = (cookieString) => {
204
- const cookie = {};
205
- if (typeof cookieString === "string" && cookieString !== "") {
206
- const cookieSegments = cookieString.split(";");
207
- for (const cookieSegment of cookieSegments) {
208
- const separatorIndex = cookieSegment.indexOf("=");
209
- if (separatorIndex !== -1) {
210
- cookie[tryDecodeUriComponent(cookieSegment.slice(0, separatorIndex).trim())] = tryDecodeUriComponent(cookieSegment.slice(separatorIndex + 1).trim());
155
+ function getRequestRoute(requestEv) {
156
+ return requestEv[RequestEvRoute];
157
+ }
158
+ function getRequestMode(requestEv) {
159
+ return requestEv[RequestEvMode];
160
+ }
161
+ const ABORT_INDEX = Number.MAX_SAFE_INTEGER;
162
+ const isDangerousKey = (k) => k === "__proto__" || k === "constructor" || k === "prototype";
163
+ const isArrayIndexKey = (k) => /^(0|[1-9]\d*)$/.test(k);
164
+ const getArrayPaths = (formData) => {
165
+ const arrayCandidates = /* @__PURE__ */ new Map();
166
+ for (const [name] of formData) {
167
+ const keys = name.split(".");
168
+ let hasDangerousKey = false;
169
+ for (const key of keys) {
170
+ if (isDangerousKey(key)) {
171
+ hasDangerousKey = true;
172
+ break;
173
+ }
174
+ }
175
+ if (hasDangerousKey) {
176
+ continue;
177
+ }
178
+ let path = "";
179
+ for (let i = 0; i < keys.length - 1; i++) {
180
+ const key = keys[i];
181
+ if (key.endsWith("[]")) {
182
+ break;
183
+ }
184
+ path = path ? `${path}.${key}` : key;
185
+ if (!arrayCandidates.has(path)) {
186
+ arrayCandidates.set(path, true);
187
+ }
188
+ if (!isArrayIndexKey(keys[i + 1])) {
189
+ arrayCandidates.set(path, false);
211
190
  }
212
191
  }
213
192
  }
214
- return cookie;
193
+ return new Set(Array.from(arrayCandidates.entries()).filter(([, isArrayPath]) => isArrayPath).map(([path]) => path));
215
194
  };
216
- function resolveSameSite(sameSite) {
217
- if (sameSite === true) {
218
- return "Strict";
219
- }
220
- if (sameSite === false) {
221
- return "None";
195
+ const formToObj = (formData) => {
196
+ const values = /* @__PURE__ */ Object.create(null);
197
+ const arrayPaths = getArrayPaths(formData);
198
+ for (const [name, value] of formData) {
199
+ const keys = name.split(".");
200
+ let hasDangerousKey = false;
201
+ for (let i = 0; i < keys.length; i++) {
202
+ if (isDangerousKey(keys[i])) {
203
+ hasDangerousKey = true;
204
+ break;
205
+ }
206
+ }
207
+ if (hasDangerousKey) {
208
+ continue;
209
+ }
210
+ let object = values;
211
+ let path = "";
212
+ for (let i = 0; i < keys.length; i++) {
213
+ const key = keys[i];
214
+ if (key.endsWith("[]")) {
215
+ const arrayKey = key.slice(0, -2);
216
+ if (isDangerousKey(arrayKey)) {
217
+ break;
218
+ }
219
+ const existingValue = object[arrayKey];
220
+ if (existingValue !== void 0 && !Array.isArray(existingValue)) {
221
+ break;
222
+ }
223
+ object[arrayKey] = existingValue || [];
224
+ object[arrayKey].push(value);
225
+ break;
226
+ }
227
+ if (Array.isArray(object) && !isArrayIndexKey(key)) {
228
+ break;
229
+ }
230
+ if (i < keys.length - 1) {
231
+ path = path ? `${path}.${key}` : key;
232
+ const nextValue = object[key];
233
+ if (nextValue !== void 0) {
234
+ object = nextValue;
235
+ continue;
236
+ }
237
+ object = object[key] = arrayPaths.has(path) ? [] : /* @__PURE__ */ Object.create(null);
238
+ } else {
239
+ object[key] = value;
240
+ }
241
+ }
222
242
  }
223
- if (sameSite) {
224
- return SAMESITE[sameSite];
243
+ return values;
244
+ };
245
+ const parseRequest = async (deps, { request, method, query }, sharedMap) => {
246
+ const type = deps.getContentType(request.headers);
247
+ if (type === "application/x-www-form-urlencoded" || type === "multipart/form-data") {
248
+ const formData = await request.formData();
249
+ sharedMap.set(RequestEvSharedActionFormData, formData);
250
+ return formToObj(formData);
251
+ } else if (type === "application/json") {
252
+ const data = await request.json();
253
+ return data;
254
+ } else if (type === "application/qwik-json") {
255
+ if (method === "GET" && query.has(deps.QDATA_KEY)) {
256
+ const data = query.get(deps.QDATA_KEY);
257
+ if (data) {
258
+ try {
259
+ return _deserialize(decodeURIComponent(data));
260
+ } catch {
261
+ }
262
+ }
263
+ }
264
+ return _deserialize(await request.text());
225
265
  }
226
266
  return void 0;
227
- }
228
- const createSetCookieValue = (cookieName, cookieValue, options) => {
229
- const c = [
230
- `${cookieName}=${cookieValue}`
231
- ];
232
- if (typeof options.domain === "string") {
233
- c.push(`Domain=${options.domain}`);
234
- }
235
- if (typeof options.maxAge === "number") {
236
- c.push(`Max-Age=${options.maxAge}`);
237
- } else if (Array.isArray(options.maxAge)) {
238
- c.push(`Max-Age=${options.maxAge[0] * UNIT[options.maxAge[1]]}`);
239
- } else if (typeof options.expires === "number" || typeof options.expires == "string") {
240
- c.push(`Expires=${options.expires}`);
241
- } else if (options.expires instanceof Date) {
242
- c.push(`Expires=${options.expires.toUTCString()}`);
243
- }
244
- if (options.httpOnly) {
245
- c.push("HttpOnly");
246
- }
247
- if (typeof options.path === "string") {
248
- c.push(`Path=${options.path}`);
249
- }
250
- const sameSite = resolveSameSite(options.sameSite);
251
- if (sameSite) {
252
- c.push(`SameSite=${sameSite}`);
253
- }
254
- if (options.secure) {
255
- c.push("Secure");
256
- }
257
- return c.join("; ");
258
- };
259
- const REQ_COOKIE = /* @__PURE__ */ Symbol("request-cookies");
260
- const RES_COOKIE = /* @__PURE__ */ Symbol("response-cookies");
261
- const LIVE_COOKIE = /* @__PURE__ */ Symbol("live-cookies");
262
- class Cookie {
263
- [REQ_COOKIE];
264
- [RES_COOKIE] = {};
265
- [LIVE_COOKIE] = {};
266
- appendCounter = 0;
267
- constructor(cookieString) {
268
- this[REQ_COOKIE] = parseCookieString(cookieString);
269
- this[LIVE_COOKIE] = {
270
- ...this[REQ_COOKIE]
271
- };
272
- }
273
- get(cookieName, live = true) {
274
- const value = this[live ? LIVE_COOKIE : REQ_COOKIE][cookieName];
275
- if (!value) {
276
- return null;
277
- }
278
- return {
279
- value,
280
- json() {
281
- return JSON.parse(value);
282
- },
283
- number() {
284
- return Number(value);
285
- }
286
- };
287
- }
288
- getAll(live = true) {
289
- return Object.keys(this[live ? LIVE_COOKIE : REQ_COOKIE]).reduce((cookies, cookieName) => {
290
- cookies[cookieName] = this.get(cookieName);
291
- return cookies;
292
- }, {});
293
- }
294
- has(cookieName, live = true) {
295
- return !!this[live ? LIVE_COOKIE : REQ_COOKIE][cookieName];
296
- }
297
- set(cookieName, cookieValue, options = {}) {
298
- this[LIVE_COOKIE][cookieName] = typeof cookieValue === "string" ? cookieValue : JSON.stringify(cookieValue);
299
- const resolvedValue = typeof cookieValue === "string" ? cookieValue : encodeURIComponent(JSON.stringify(cookieValue));
300
- this[RES_COOKIE][cookieName] = createSetCookieValue(cookieName, resolvedValue, options);
301
- }
302
- append(cookieName, cookieValue, options = {}) {
303
- this[LIVE_COOKIE][cookieName] = typeof cookieValue === "string" ? cookieValue : JSON.stringify(cookieValue);
304
- const resolvedValue = typeof cookieValue === "string" ? cookieValue : encodeURIComponent(JSON.stringify(cookieValue));
305
- this[RES_COOKIE][++this.appendCounter] = createSetCookieValue(cookieName, resolvedValue, options);
306
- }
307
- delete(name, options) {
308
- this.set(name, "deleted", {
309
- ...options,
310
- maxAge: 0
311
- });
312
- this[LIVE_COOKIE][name] = null;
313
- }
314
- headers() {
315
- return Object.values(this[RES_COOKIE]);
316
- }
317
- }
318
- const mergeHeadersCookies = (headers, cookies) => {
319
- const cookieHeaders = cookies.headers();
320
- if (cookieHeaders.length > 0) {
321
- const newHeaders = new Headers(headers);
322
- for (const cookie of cookieHeaders) {
323
- newHeaders.append("Set-Cookie", cookie);
324
- }
325
- return newHeaders;
326
- }
327
- return headers;
328
- };
329
-
330
- async function runNext(requestEv, rebuildRouteInfo, resolve) {
331
- try {
332
- const isValidURL = (url) => new URL(url.pathname + url.search, url);
333
- isValidURL(requestEv.originalUrl);
334
- } catch {
335
- const status = 404;
336
- const message = "Resource Not Found";
337
- requestEv.status(status);
338
- const html = getErrorHtml(status, message);
339
- requestEv.html(status, html);
340
- return new ServerError$1(status, message);
341
- }
342
- let rewriteAttempt = 1;
343
- async function _runNext() {
344
- try {
345
- await requestEv.next();
346
- } catch (e) {
347
- if (e instanceof RedirectMessage$1) {
348
- const stream = requestEv.getWritableStream();
349
- await stream.close();
350
- return e;
351
- } else if (e instanceof RewriteMessage$1) {
352
- if (rewriteAttempt > 50) {
353
- return new Error(`Infinite rewrite loop`);
354
- }
355
- rewriteAttempt += 1;
356
- const url = new URL(requestEv.url);
357
- url.pathname = e.pathname;
358
- const { loadedRoute, requestHandlers } = await rebuildRouteInfo(url);
359
- requestEv.resetRoute(loadedRoute, requestHandlers, url);
360
- return await _runNext();
361
- } else if (e instanceof AbortMessage$1) {
362
- return;
363
- } else if (e instanceof ServerError$1 && !requestEv.headersSent) {
364
- const status = e.status;
365
- const accept = requestEv.request.headers.get("Accept");
366
- if (accept && !accept.includes("text/html")) {
367
- requestEv.headers.set("Content-Type", "application/qwik-json");
368
- requestEv.send(status, await _serialize(e.data));
369
- } else {
370
- requestEv.html(status, getErrorHtml(status, e.data));
371
- }
372
- return e;
373
- }
374
- if (!isDev) {
375
- try {
376
- if (!requestEv.headersSent) {
377
- requestEv.headers.set("content-type", "text/html; charset=utf-8");
378
- requestEv.cacheControl({
379
- noCache: true
380
- });
381
- requestEv.status(500);
382
- }
383
- const stream = requestEv.getWritableStream();
384
- if (!stream.locked) {
385
- const writer = stream.getWriter();
386
- await writer.write(encoder.encode(getErrorHtml(500, "Internal Server Error")));
387
- await writer.close();
388
- }
389
- } catch {
390
- console.error("Unable to render error page");
391
- }
392
- }
393
- return e;
394
- }
395
- }
396
- try {
397
- return await _runNext();
398
- } finally {
399
- if (!requestEv.isDirty()) {
400
- resolve(null);
401
- }
402
- }
403
- }
404
- function runQwikRouter(serverRequestEv, loadedRoute, requestHandlers, rebuildRouteInfo, basePathname = "/") {
405
- let resolve;
406
- const responsePromise = new Promise((r) => resolve = r);
407
- const requestEv = createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, basePathname, resolve);
408
- return {
409
- response: responsePromise,
410
- requestEv,
411
- completion: _asyncRequestStore$1 ? _asyncRequestStore$1.run(requestEv, runNext, requestEv, rebuildRouteInfo, resolve) : runNext(requestEv, rebuildRouteInfo, resolve)
412
- };
413
- }
414
- const IsQData = "@isQData";
415
- const QDATA_JSON = "/q-data.json";
416
- function getRouteMatchPathname(pathname) {
417
- const isInternal = pathname.endsWith(QDATA_JSON);
418
- if (isInternal) {
419
- const trimEnd = pathname.length - QDATA_JSON.length + (globalThis.__NO_TRAILING_SLASH__ ? 0 : 1);
420
- pathname = pathname.slice(0, trimEnd);
421
- if (pathname === "") {
422
- pathname = "/";
423
- }
424
- }
425
- return {
426
- pathname,
427
- isInternal
428
- };
429
- }
430
-
431
- const RequestEvLoaders = /* @__PURE__ */ Symbol("RequestEvLoaders");
432
- const RequestEvMode = /* @__PURE__ */ Symbol("RequestEvMode");
433
- const RequestEvRoute = /* @__PURE__ */ Symbol("RequestEvRoute");
434
- const RequestEvLoaderSerializationStrategyMap = /* @__PURE__ */ Symbol("RequestEvLoaderSerializationStrategyMap");
435
- const RequestRouteName = "@routeName";
436
- const RequestEvSharedActionId = "@actionId";
437
- const RequestEvSharedActionFormData = "@actionFormData";
438
- const RequestEvSharedNonce = "@nonce";
439
- const RequestEvIsRewrite = "@rewrite";
440
- const RequestEvShareServerTiming = "@serverTiming";
441
- const RequestEvETagCacheKey = "@eTagCacheKey";
442
- const RequestEvHttpStatusMessage = "@httpStatusMessage";
443
- const RequestEvShareQData = "qData";
444
- function getRequestLoaders(requestEv) {
445
- return requestEv[RequestEvLoaders];
446
- }
447
- function getRequestLoaderSerializationStrategyMap(requestEv) {
448
- return requestEv[RequestEvLoaderSerializationStrategyMap];
449
- }
450
- function getRequestRoute(requestEv) {
451
- return requestEv[RequestEvRoute];
452
- }
453
- function getRequestMode(requestEv) {
454
- return requestEv[RequestEvMode];
455
- }
456
- const ABORT_INDEX = Number.MAX_SAFE_INTEGER;
457
- const isDangerousKey = (k) => k === "__proto__" || k === "constructor" || k === "prototype";
458
- const isArrayIndexKey = (k) => /^(0|[1-9]\d*)$/.test(k);
459
- const getArrayPaths = (formData) => {
460
- const arrayCandidates = /* @__PURE__ */ new Map();
461
- for (const [name] of formData) {
462
- const keys = name.split(".");
463
- let hasDangerousKey = false;
464
- for (const key of keys) {
465
- if (isDangerousKey(key)) {
466
- hasDangerousKey = true;
467
- break;
468
- }
469
- }
470
- if (hasDangerousKey) {
471
- continue;
472
- }
473
- let path = "";
474
- for (let i = 0; i < keys.length - 1; i++) {
475
- const key = keys[i];
476
- if (key.endsWith("[]")) {
477
- break;
478
- }
479
- path = path ? `${path}.${key}` : key;
480
- if (!arrayCandidates.has(path)) {
481
- arrayCandidates.set(path, true);
482
- }
483
- if (!isArrayIndexKey(keys[i + 1])) {
484
- arrayCandidates.set(path, false);
485
- }
486
- }
487
- }
488
- return new Set(Array.from(arrayCandidates.entries()).filter(([, isArrayPath]) => isArrayPath).map(([path]) => path));
489
- };
490
- const formToObj = (formData) => {
491
- const values = /* @__PURE__ */ Object.create(null);
492
- const arrayPaths = getArrayPaths(formData);
493
- for (const [name, value] of formData) {
494
- const keys = name.split(".");
495
- let hasDangerousKey = false;
496
- for (let i = 0; i < keys.length; i++) {
497
- if (isDangerousKey(keys[i])) {
498
- hasDangerousKey = true;
499
- break;
500
- }
501
- }
502
- if (hasDangerousKey) {
503
- continue;
504
- }
505
- let object = values;
506
- let path = "";
507
- for (let i = 0; i < keys.length; i++) {
508
- const key = keys[i];
509
- if (key.endsWith("[]")) {
510
- const arrayKey = key.slice(0, -2);
511
- if (isDangerousKey(arrayKey)) {
512
- break;
513
- }
514
- const existingValue = object[arrayKey];
515
- if (existingValue !== void 0 && !Array.isArray(existingValue)) {
516
- break;
517
- }
518
- object[arrayKey] = existingValue || [];
519
- object[arrayKey].push(value);
520
- break;
521
- }
522
- if (Array.isArray(object) && !isArrayIndexKey(key)) {
523
- break;
524
- }
525
- if (i < keys.length - 1) {
526
- path = path ? `${path}.${key}` : key;
527
- const nextValue = object[key];
528
- if (nextValue !== void 0) {
529
- object = nextValue;
530
- continue;
531
- }
532
- object = object[key] = arrayPaths.has(path) ? [] : /* @__PURE__ */ Object.create(null);
533
- } else {
534
- object[key] = value;
535
- }
536
- }
537
- }
538
- return values;
539
- };
540
- const parseRequest = async ({ request, method, query }, sharedMap) => {
541
- const type = getContentType(request.headers);
542
- if (type === "application/x-www-form-urlencoded" || type === "multipart/form-data") {
543
- const formData = await request.formData();
544
- sharedMap.set(RequestEvSharedActionFormData, formData);
545
- return formToObj(formData);
546
- } else if (type === "application/json") {
547
- const data = await request.json();
548
- return data;
549
- } else if (type === "application/qwik-json") {
550
- if (method === "GET" && query.has(QDATA_KEY)) {
551
- const data = query.get(QDATA_KEY);
552
- if (data) {
553
- try {
554
- return _deserialize(decodeURIComponent(data));
555
- } catch {
556
- }
557
- }
558
- }
559
- return _deserialize(await request.text());
560
- }
561
- return void 0;
562
- };
563
- function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, basePathname, resolved) {
564
- const { request, platform, env } = serverRequestEv;
565
- const sharedMap = /* @__PURE__ */ new Map();
566
- const cookie = new Cookie(request.headers.get("cookie"));
567
- const headers = new Headers();
568
- const url = new URL(request.url);
569
- const { pathname, isInternal } = getRouteMatchPathname(url.pathname);
570
- if (isInternal) {
571
- url.pathname = pathname;
572
- sharedMap.set(IsQData, true);
267
+ };
268
+ function createRequestEventWithDeps(deps, serverRequestEv, loadedRoute, requestHandlers, basePathname, resolved) {
269
+ const { request, platform, env } = serverRequestEv;
270
+ const sharedMap = /* @__PURE__ */ new Map();
271
+ const cookie = new deps.Cookie(request.headers.get("cookie"));
272
+ const headers = new Headers();
273
+ const url = new URL(request.url);
274
+ const { pathname, isInternal } = deps.getRouteMatchPathname(url.pathname);
275
+ if (isInternal) {
276
+ url.pathname = pathname;
277
+ sharedMap.set(deps.IsQData, true);
573
278
  }
574
279
  let routeModuleIndex = -1;
575
280
  let writableStream = null;
@@ -581,7 +286,7 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, baseP
581
286
  while (routeModuleIndex < requestHandlers.length) {
582
287
  const moduleRequestHandler = requestHandlers[routeModuleIndex];
583
288
  const result = moduleRequestHandler(requestEv);
584
- if (isPromise(result)) {
289
+ if (deps.isPromise(result)) {
585
290
  await result;
586
291
  }
587
292
  routeModuleIndex++;
@@ -606,7 +311,7 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, baseP
606
311
  status = statusOrResponse;
607
312
  const writableStream2 = requestEv.getWritableStream();
608
313
  const writer = writableStream2.getWriter();
609
- writer.write(typeof body === "string" ? encoder.encode(body) : body);
314
+ writer.write(typeof body === "string" ? deps.encoder.encode(body) : body);
610
315
  writer.close();
611
316
  } else {
612
317
  status = statusOrResponse.status;
@@ -634,7 +339,7 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, baseP
634
339
  }
635
340
  return exit();
636
341
  };
637
- const exit = (message = new AbortMessage$1()) => {
342
+ const exit = (message = new deps.AbortMessage()) => {
638
343
  routeModuleIndex = ABORT_INDEX;
639
344
  return message;
640
345
  };
@@ -680,7 +385,7 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, baseP
680
385
  exit,
681
386
  cacheControl: (cacheControl, target = "Cache-Control") => {
682
387
  check();
683
- headers.set(target, createCacheControl(cacheControl));
388
+ headers.set(target, deps.createCacheControl(cacheControl));
684
389
  },
685
390
  resolveValue: async (loaderOrAction) => {
686
391
  const id = loaderOrAction.__id;
@@ -689,7 +394,7 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, baseP
689
394
  throw new Error("You can not get the returned data of a loader that has not been executed for this request.");
690
395
  }
691
396
  if (loaders[id] === _UNINITIALIZED) {
692
- await getRouteLoaderPromise(loaderOrAction, loaders, requestEv);
397
+ await deps.getRouteLoaderPromise(loaderOrAction, loaders, requestEv[RequestEvLoaderSerializationStrategyMap], requestEv);
693
398
  }
694
399
  }
695
400
  return loaders[id];
@@ -711,7 +416,7 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, baseP
711
416
  error: (statusCode, message) => {
712
417
  status = statusCode;
713
418
  headers.delete("Cache-Control");
714
- return new ServerError$1(statusCode, message);
419
+ return new deps.ServerError(statusCode, message);
715
420
  },
716
421
  redirect: (statusCode, url2) => {
717
422
  check();
@@ -732,15 +437,15 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, baseP
732
437
  if (statusCode > 301) {
733
438
  headers.set("Cache-Control", "no-store");
734
439
  }
735
- return exit(new RedirectMessage$1());
440
+ return exit(new deps.RedirectMessage());
736
441
  },
737
442
  rewrite: (pathname2) => {
738
443
  check();
739
444
  if (pathname2.startsWith("http")) {
740
- throw new ServerError$1(400, isDev ? "Rewrite does not support absolute urls" : "Bad Request");
445
+ throw new deps.ServerError(400, isDev ? "Rewrite does not support absolute urls" : "Bad Request");
741
446
  }
742
447
  sharedMap.set(RequestEvIsRewrite, true);
743
- return exit(new RewriteMessage$1(pathname2.replace(/\/+/g, "/")));
448
+ return exit(new deps.RewriteMessage(pathname2.replace(/\/+/g, "/")));
744
449
  },
745
450
  defer: (returnData) => {
746
451
  return typeof returnData === "function" ? returnData : () => returnData;
@@ -766,7 +471,7 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, baseP
766
471
  if (requestData !== void 0) {
767
472
  return requestData;
768
473
  }
769
- return requestData = parseRequest(requestEv, sharedMap);
474
+ return requestData = parseRequest(deps, requestEv, sharedMap);
770
475
  },
771
476
  json: (statusCode, data) => {
772
477
  headers.set("Content-Type", "application/json; charset=utf-8");
@@ -792,14 +497,96 @@ function createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, baseP
792
497
  return requestEv;
793
498
  }
794
499
 
795
- function getQwikRouterServerData(requestEv) {
500
+ function verifySerializable(data, qrl) {
501
+ try {
502
+ _verifySerializable(data, void 0);
503
+ } catch (e) {
504
+ if (e instanceof Error && qrl.dev) {
505
+ e.loc = qrl.dev;
506
+ }
507
+ throw e;
508
+ }
509
+ }
510
+ function now() {
511
+ return typeof performance !== "undefined" ? performance.now() : 0;
512
+ }
513
+ async function measure(requestEv, name, fn) {
514
+ const start = now();
515
+ try {
516
+ return await fn();
517
+ } finally {
518
+ const duration = now() - start;
519
+ let measurements = requestEv.sharedMap.get("@serverTiming");
520
+ if (!measurements) {
521
+ requestEv.sharedMap.set("@serverTiming", measurements = []);
522
+ }
523
+ measurements.push([
524
+ name,
525
+ duration
526
+ ]);
527
+ }
528
+ }
529
+ async function runValidators(requestEv, validators, data) {
530
+ let lastResult = {
531
+ success: true,
532
+ data
533
+ };
534
+ if (validators) {
535
+ for (const validator of validators) {
536
+ if (isDev) {
537
+ lastResult = await measure(requestEv, `validator$`, () => validator.validate(requestEv, data));
538
+ } else {
539
+ lastResult = await validator.validate(requestEv, data);
540
+ }
541
+ if (!lastResult.success) {
542
+ return lastResult;
543
+ } else {
544
+ data = lastResult.data;
545
+ }
546
+ }
547
+ }
548
+ return lastResult;
549
+ }
550
+ async function getRouteLoaderPromise(loader, loaders, loadersSerializationStrategy, requestEv) {
551
+ const loaderId = loader.__id;
552
+ loaders[loaderId] = runValidators(
553
+ requestEv,
554
+ loader.__validators,
555
+ void 0
556
+ // data
557
+ ).then((res) => {
558
+ if (res.success) {
559
+ if (isDev) {
560
+ return measure(requestEv, loader.__qrl.getHash(), () => loader.__qrl.call(requestEv, requestEv));
561
+ } else {
562
+ return loader.__qrl.call(requestEv, requestEv);
563
+ }
564
+ } else {
565
+ return requestEv.fail(res.status ?? 500, res.error);
566
+ }
567
+ }).then((resolvedLoader) => {
568
+ if (typeof resolvedLoader === "function") {
569
+ loaders[loaderId] = resolvedLoader();
570
+ } else {
571
+ if (isDev) {
572
+ verifySerializable(resolvedLoader, loader.__qrl);
573
+ }
574
+ loaders[loaderId] = resolvedLoader;
575
+ }
576
+ return resolvedLoader;
577
+ });
578
+ loadersSerializationStrategy.set(loaderId, loader.__serializationStrategy);
579
+ return loaders[loaderId];
580
+ }
581
+
582
+ function getQwikRouterServerDataWithDeps(deps, requestEv) {
796
583
  const { params, request, status, locale, originalUrl } = requestEv;
797
584
  const requestHeaders = {};
798
585
  request.headers.forEach((value, key) => requestHeaders[key] = value);
799
- const action = requestEv.sharedMap.get(RequestEvSharedActionId);
800
- const formData = requestEv.sharedMap.get(RequestEvSharedActionFormData);
801
- const routeName = requestEv.sharedMap.get(RequestRouteName);
802
- const nonce = requestEv.sharedMap.get(RequestEvSharedNonce);
586
+ const action = requestEv.sharedMap.get(deps.RequestEvSharedActionId);
587
+ const formData = requestEv.sharedMap.get(deps.RequestEvSharedActionFormData);
588
+ const routeName = requestEv.sharedMap.get(deps.RequestRouteName);
589
+ const nonce = requestEv.sharedMap.get(deps.RequestEvSharedNonce);
803
590
  const headers = requestEv.request.headers;
804
591
  const reconstructedUrl = new URL(originalUrl.pathname + originalUrl.search, originalUrl);
805
592
  const host = headers.get("X-Forwarded-Host");
@@ -811,15 +598,15 @@ function getQwikRouterServerData(requestEv) {
811
598
  if (protocol) {
812
599
  reconstructedUrl.protocol = protocol;
813
600
  }
814
- const loaders = getRequestLoaders(requestEv);
815
- const loadersSerializationStrategy = getRequestLoaderSerializationStrategyMap(requestEv);
601
+ const loaders = deps.getRequestLoaders(requestEv);
602
+ const loadersSerializationStrategy = deps.getRequestLoaderSerializationStrategyMap(requestEv);
816
603
  return {
817
604
  url: reconstructedUrl.href,
818
605
  requestHeaders,
819
606
  locale: locale(),
820
607
  nonce,
821
608
  containerAttributes: {
822
- [Q_ROUTE]: routeName
609
+ [deps.Q_ROUTE]: routeName
823
610
  },
824
611
  qwikrouter: {
825
612
  routeName,
@@ -827,10 +614,10 @@ function getQwikRouterServerData(requestEv) {
827
614
  params: {
828
615
  ...params
829
616
  },
830
- loadedRoute: getRequestRoute(requestEv),
617
+ loadedRoute: deps.getRequestRoute(requestEv),
831
618
  response: {
832
619
  status: status(),
833
- statusMessage: requestEv.sharedMap.get(RequestEvHttpStatusMessage),
620
+ statusMessage: requestEv.sharedMap.get(deps.RequestEvHttpStatusMessage),
834
621
  loaders,
835
622
  loadersSerializationStrategy,
836
623
  action,
@@ -840,637 +627,1011 @@ function getQwikRouterServerData(requestEv) {
840
627
  };
841
628
  }
842
629
 
843
- const _resolveRequestHandlers = (routeLoaders, routeActions, requestHandlers, routeModules, collectActions, method) => {
844
- for (const routeModule of routeModules) {
845
- if (typeof routeModule.onRequest === "function") {
846
- requestHandlers.push(routeModule.onRequest);
847
- } else if (Array.isArray(routeModule.onRequest)) {
848
- requestHandlers.push(...routeModule.onRequest);
630
+ const responsePageDeps = {
631
+ Q_ROUTE,
632
+ RequestEvHttpStatusMessage,
633
+ RequestEvSharedActionFormData,
634
+ RequestEvSharedActionId,
635
+ RequestEvSharedNonce,
636
+ RequestRouteName,
637
+ getRequestLoaders,
638
+ getRequestLoaderSerializationStrategyMap,
639
+ getRequestRoute
640
+ };
641
+ function getQwikRouterServerData(...args) {
642
+ return getQwikRouterServerDataWithDeps(responsePageDeps, ...args);
643
+ }
644
+
645
+ function createResolveRequestHandlers(deps) {
646
+ const resolveRequestHandlers = (serverPlugins, route, method, checkOrigin, renderHandler, isInternal) => {
647
+ const routeLoaders = [];
648
+ const routeActions = [];
649
+ const requestHandlers = [];
650
+ const isPageRoute = !!(route && isLastModulePageRoute(route.$mods$));
651
+ if (isInternal) {
652
+ requestHandlers.push(handleQDataRedirect);
849
653
  }
850
- let methodReqHandler;
851
- switch (method) {
852
- case "GET": {
853
- methodReqHandler = routeModule.onGet;
854
- break;
654
+ if (isPageRoute) {
655
+ requestHandlers.push(serverErrorMiddleware(route, renderHandler));
656
+ }
657
+ if (serverPlugins) {
658
+ _resolveRequestHandlers(routeLoaders, routeActions, requestHandlers, serverPlugins, isPageRoute, method);
659
+ }
660
+ if (route) {
661
+ const routeModules = route.$mods$;
662
+ _resolveRequestHandlers(routeLoaders, routeActions, requestHandlers, routeModules, isPageRoute, method);
663
+ const routeName = route.$routeName$;
664
+ if (checkOrigin && (method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE")) {
665
+ if (checkOrigin === "lax-proto") {
666
+ requestHandlers.unshift(csrfLaxProtoCheckMiddleware);
667
+ } else {
668
+ requestHandlers.unshift(csrfCheckMiddleware);
669
+ }
855
670
  }
856
- case "POST": {
857
- methodReqHandler = routeModule.onPost;
858
- break;
671
+ if (isPageRoute) {
672
+ if (method === "POST" || method === "GET") {
673
+ requestHandlers.push(runServerFunction);
674
+ }
675
+ requestHandlers.push(fixTrailingSlash);
676
+ if (isInternal) {
677
+ requestHandlers.push(renderQData);
678
+ }
859
679
  }
860
- case "PUT": {
861
- methodReqHandler = routeModule.onPut;
862
- break;
680
+ if (isPageRoute) {
681
+ requestHandlers.push((ev) => {
682
+ ev.sharedMap.set(deps.RequestRouteName, routeName);
683
+ });
684
+ requestHandlers.push(actionsMiddleware(routeActions));
685
+ requestHandlers.push(loadersMiddleware(routeLoaders));
686
+ requestHandlers.push(eTagMiddleware(route));
687
+ requestHandlers.push(renderHandler);
863
688
  }
864
- case "PATCH": {
865
- methodReqHandler = routeModule.onPatch;
866
- break;
689
+ }
690
+ return requestHandlers;
691
+ };
692
+ const _resolveRequestHandlers = (routeLoaders, routeActions, requestHandlers, routeModules, collectActions, method) => {
693
+ for (const routeModule of routeModules) {
694
+ if (typeof routeModule.onRequest === "function") {
695
+ requestHandlers.push(routeModule.onRequest);
696
+ } else if (Array.isArray(routeModule.onRequest)) {
697
+ requestHandlers.push(...routeModule.onRequest);
867
698
  }
868
- case "DELETE": {
869
- methodReqHandler = routeModule.onDelete;
870
- break;
699
+ let methodReqHandler;
700
+ switch (method) {
701
+ case "GET": {
702
+ methodReqHandler = routeModule.onGet;
703
+ break;
704
+ }
705
+ case "POST": {
706
+ methodReqHandler = routeModule.onPost;
707
+ break;
708
+ }
709
+ case "PUT": {
710
+ methodReqHandler = routeModule.onPut;
711
+ break;
712
+ }
713
+ case "PATCH": {
714
+ methodReqHandler = routeModule.onPatch;
715
+ break;
716
+ }
717
+ case "DELETE": {
718
+ methodReqHandler = routeModule.onDelete;
719
+ break;
720
+ }
721
+ case "OPTIONS": {
722
+ methodReqHandler = routeModule.onOptions;
723
+ break;
724
+ }
725
+ case "HEAD": {
726
+ methodReqHandler = routeModule.onHead;
727
+ break;
728
+ }
871
729
  }
872
- case "OPTIONS": {
873
- methodReqHandler = routeModule.onOptions;
874
- break;
730
+ if (typeof methodReqHandler === "function") {
731
+ requestHandlers.push(methodReqHandler);
732
+ } else if (Array.isArray(methodReqHandler)) {
733
+ requestHandlers.push(...methodReqHandler);
875
734
  }
876
- case "HEAD": {
877
- methodReqHandler = routeModule.onHead;
878
- break;
735
+ if (collectActions) {
736
+ for (const module of Object.values(routeModule)) {
737
+ if (typeof module === "function") {
738
+ if (module.__brand === "server_loader") {
739
+ routeLoaders.push(module);
740
+ } else if (module.__brand === "server_action") {
741
+ routeActions.push(module);
742
+ }
743
+ }
744
+ }
879
745
  }
880
746
  }
881
- if (typeof methodReqHandler === "function") {
882
- requestHandlers.push(methodReqHandler);
883
- } else if (Array.isArray(methodReqHandler)) {
884
- requestHandlers.push(...methodReqHandler);
885
- }
886
- if (collectActions) {
887
- for (const module of Object.values(routeModule)) {
888
- if (typeof module === "function") {
889
- if (module.__brand === "server_loader") {
890
- routeLoaders.push(module);
891
- } else if (module.__brand === "server_action") {
892
- routeActions.push(module);
747
+ };
748
+ const checkBrand = (obj, brand) => {
749
+ return obj && typeof obj === "function" && obj.__brand === brand;
750
+ };
751
+ function actionsMiddleware(routeActions) {
752
+ return async (requestEvent) => {
753
+ const requestEv = requestEvent;
754
+ if (requestEv.headersSent) {
755
+ requestEv.exit();
756
+ return;
757
+ }
758
+ const { method } = requestEv;
759
+ const loaders = deps.getRequestLoaders(requestEv);
760
+ if (isDev && method === "GET") {
761
+ if (requestEv.query.has(deps.QACTION_KEY)) {
762
+ console.warn('Seems like you are submitting a Qwik Action via GET request. Qwik Actions should be submitted via POST request.\nMake sure your <form> has method="POST" attribute, like this: <form method="POST">');
763
+ }
764
+ }
765
+ if (method === "POST") {
766
+ const selectedActionId = requestEv.query.get(deps.QACTION_KEY);
767
+ if (selectedActionId) {
768
+ const serverActionsMap = globalThis._qwikActionsMap;
769
+ const action = routeActions.find((action2) => action2.__id === selectedActionId) ?? serverActionsMap?.get(selectedActionId);
770
+ if (action) {
771
+ requestEv.sharedMap.set(deps.RequestEvSharedActionId, selectedActionId);
772
+ const data = await requestEv.parseBody();
773
+ if (!data || typeof data !== "object") {
774
+ throw new Error(`Expected request data for the action id ${selectedActionId} to be an object`);
775
+ }
776
+ const result = await runValidators(requestEv, action.__validators, data);
777
+ if (!result.success) {
778
+ loaders[selectedActionId] = requestEv.fail(result.status ?? 500, result.error);
779
+ } else {
780
+ const actionResolved = isDev ? await measure(requestEv, action.__qrl.getHash(), () => action.__qrl.call(requestEv, result.data, requestEv)) : await action.__qrl.call(requestEv, result.data, requestEv);
781
+ if (isDev) {
782
+ verifySerializable(actionResolved, action.__qrl);
783
+ }
784
+ loaders[selectedActionId] = actionResolved;
785
+ }
893
786
  }
894
787
  }
895
788
  }
789
+ };
790
+ }
791
+ function loadersMiddleware(routeLoaders) {
792
+ return async (requestEvent) => {
793
+ const requestEv = requestEvent;
794
+ if (requestEv.headersSent) {
795
+ requestEv.exit();
796
+ return;
797
+ }
798
+ const loaders = deps.getRequestLoaders(requestEv);
799
+ const loadersSerializationStrategy = deps.getRequestLoaderSerializationStrategyMap(requestEv);
800
+ if (routeLoaders.length > 0) {
801
+ const resolvedLoadersPromises = routeLoaders.map((loader) => deps.getRouteLoaderPromise(loader, loaders, loadersSerializationStrategy, requestEv));
802
+ await Promise.all(resolvedLoadersPromises);
803
+ }
804
+ };
805
+ }
806
+ function eTagMiddleware(route) {
807
+ return (requestEv) => {
808
+ if (requestEv.headersSent) {
809
+ return;
810
+ }
811
+ if (requestEv.method !== "GET" || requestEv.sharedMap.has(deps.IsQData)) {
812
+ return;
813
+ }
814
+ const mods = route.$mods$;
815
+ const hasETag = mods.some((m) => {
816
+ if (!m) {
817
+ return false;
818
+ }
819
+ if (m.routeConfig) {
820
+ return typeof m.routeConfig === "function" || m.routeConfig.eTag !== void 0;
821
+ }
822
+ return m.eTag !== void 0;
823
+ });
824
+ if (!hasETag) {
825
+ return;
826
+ }
827
+ const loaders = deps.getRequestLoaders(requestEv);
828
+ const getData = (loaderOrAction) => {
829
+ const id = loaderOrAction.__id;
830
+ if (loaderOrAction.__brand === "server_loader" && !(id in loaders)) {
831
+ throw new Error("Loader not executed for this request.");
832
+ }
833
+ if (loaders[id] instanceof Promise) {
834
+ throw new Error("Loaders returning a promise cannot be resolved for the eTag function.");
835
+ }
836
+ return loaders[id];
837
+ };
838
+ const routeLocation = {
839
+ params: requestEv.params,
840
+ url: requestEv.url,
841
+ isNavigating: false,
842
+ prevUrl: void 0
843
+ };
844
+ const status = requestEv.status();
845
+ const config = deps.resolveRouteConfig(getData, routeLocation, mods, "", status);
846
+ const headProps = {
847
+ head: config.head,
848
+ status,
849
+ withLocale: (fn) => fn(),
850
+ resolveValue: getData,
851
+ ...routeLocation
852
+ };
853
+ const eTag = deps.resolveETag(config.eTag, headProps);
854
+ if (!eTag) {
855
+ return;
856
+ }
857
+ requestEv.headers.set("ETag", eTag);
858
+ const ifNoneMatch = requestEv.request.headers.get("If-None-Match");
859
+ if (ifNoneMatch && (ifNoneMatch === eTag || ifNoneMatch === `W/${eTag}` || `W/${ifNoneMatch}` === eTag)) {
860
+ requestEv.status(304);
861
+ requestEv.send(304, "");
862
+ return;
863
+ }
864
+ if (deps.MAX_CACHE_SIZE <= 0) {
865
+ return;
866
+ }
867
+ const cacheKey = deps.resolveCacheKey(config.cacheKey, status, eTag, requestEv.url.pathname);
868
+ if (!cacheKey) {
869
+ return;
870
+ }
871
+ const cachedHtml = deps.getCachedHtml(cacheKey);
872
+ if (cachedHtml) {
873
+ requestEv.headers.set("Content-Type", "text/html; charset=utf-8");
874
+ requestEv.headers.set("X-SSR-Cache", "HIT");
875
+ requestEv.send(status, cachedHtml);
876
+ return;
877
+ }
878
+ requestEv.sharedMap.set(deps.RequestEvETagCacheKey, cacheKey);
879
+ };
880
+ }
881
+ function serverErrorMiddleware(route, renderHandler) {
882
+ return async (requestEv) => {
883
+ try {
884
+ await requestEv.next();
885
+ } catch (e) {
886
+ if (!(e instanceof deps.ServerError) || requestEv.headersSent) {
887
+ throw e;
888
+ }
889
+ if (requestEv.sharedMap.has(deps.IsQData)) {
890
+ throw e;
891
+ }
892
+ const accept = requestEv.request.headers.get("Accept");
893
+ if (accept && !accept.includes("text/html")) {
894
+ throw e;
895
+ }
896
+ const status = e.status;
897
+ requestEv.status(status);
898
+ const errorLoader = route.$errorLoader$;
899
+ const errorModule = errorLoader ? await errorLoader() : await deps.loadHttpError();
900
+ route.$mods$ = [
901
+ errorModule
902
+ ];
903
+ requestEv.sharedMap.set(deps.RequestEvHttpStatusMessage, typeof e.data === "string" ? e.data : "Server Error");
904
+ await renderHandler(requestEv);
905
+ }
906
+ };
907
+ }
908
+ async function runValidators(requestEv, validators, data) {
909
+ let lastResult = {
910
+ success: true,
911
+ data
912
+ };
913
+ if (validators) {
914
+ for (const validator of validators) {
915
+ if (isDev) {
916
+ lastResult = await measure(requestEv, `validator$`, () => validator.validate(requestEv, data));
917
+ } else {
918
+ lastResult = await validator.validate(requestEv, data);
919
+ }
920
+ if (!lastResult.success) {
921
+ return lastResult;
922
+ } else {
923
+ data = lastResult.data;
924
+ }
925
+ }
896
926
  }
927
+ return lastResult;
897
928
  }
898
- };
899
- function eTagMiddleware(route) {
900
- return (requestEv) => {
901
- if (requestEv.headersSent) {
929
+ function isAsyncIterator(obj) {
930
+ return obj ? typeof obj === "object" && Symbol.asyncIterator in obj : false;
931
+ }
932
+ async function runServerFunction(ev) {
933
+ const serverFnHash = ev.query.get(deps.QFN_KEY);
934
+ if (serverFnHash && ev.request.headers.get("X-QRL") === serverFnHash && ev.request.headers.get("Content-Type") === "application/qwik-json") {
935
+ ev.exit();
936
+ const data = await ev.parseBody();
937
+ if (!Array.isArray(data) || !(Array.isArray(data[0]) || data[0] === void 0)) {
938
+ throw ev.error(500, "Invalid request");
939
+ }
940
+ const qrl = inlinedQrl(null, serverFnHash, data.slice(1));
941
+ let result;
942
+ try {
943
+ if (isDev) {
944
+ result = await measure(ev, `server_${serverFnHash}`, () => qrl.apply(ev, data[0]));
945
+ } else {
946
+ result = await qrl.apply(ev, data[0]);
947
+ }
948
+ } catch (err) {
949
+ if (err instanceof deps.ServerError) {
950
+ throw ev.error(err.status, err.data);
951
+ }
952
+ console.error(`Server function ${serverFnHash} failed:`, err);
953
+ throw ev.error(500, "Invalid request");
954
+ }
955
+ if (isAsyncIterator(result)) {
956
+ ev.headers.set("Content-Type", "text/qwik-json-stream");
957
+ const writable = ev.getWritableStream();
958
+ const stream = writable.getWriter();
959
+ for await (const item of result) {
960
+ if (isDev) {
961
+ verifySerializable(item, qrl);
962
+ }
963
+ const message = await _serialize(item);
964
+ if (ev.signal.aborted) {
965
+ break;
966
+ }
967
+ await stream.write(deps.encoder.encode(`${message}
968
+ `));
969
+ }
970
+ stream.close();
971
+ } else {
972
+ verifySerializable(result, qrl);
973
+ ev.headers.set("Content-Type", "application/qwik-json");
974
+ const message = await _serialize(result);
975
+ ev.send(200, message);
976
+ }
902
977
  return;
903
978
  }
904
- if (requestEv.method !== "GET" || requestEv.sharedMap.has(IsQData)) {
979
+ }
980
+ function fixTrailingSlash(ev) {
981
+ const { basePathname, originalUrl, sharedMap } = ev;
982
+ const { pathname, search } = originalUrl;
983
+ const isQData = sharedMap.has(deps.IsQData);
984
+ if (!pathname.startsWith("/") || pathname.startsWith("//")) {
905
985
  return;
906
986
  }
907
- const mods = route.$mods$;
908
- const hasETag = mods.some((m) => {
909
- if (!m) {
910
- return false;
987
+ if (!isQData && pathname !== basePathname && !pathname.endsWith(".html")) {
988
+ if (!globalThis.__NO_TRAILING_SLASH__) {
989
+ if (!pathname.endsWith("/")) {
990
+ throw ev.redirect(deps.HttpStatus.MovedPermanently, pathname + "/" + search);
991
+ }
992
+ } else {
993
+ if (pathname.endsWith("/")) {
994
+ throw ev.redirect(deps.HttpStatus.MovedPermanently, pathname.slice(0, pathname.length - 1) + search);
995
+ }
911
996
  }
912
- if (m.routeConfig) {
913
- return typeof m.routeConfig === "function" || m.routeConfig.eTag !== void 0;
997
+ }
998
+ }
999
+ function verifySerializable(data, qrl) {
1000
+ try {
1001
+ _verifySerializable(data, void 0);
1002
+ } catch (e) {
1003
+ if (e instanceof Error && qrl.dev) {
1004
+ e.loc = qrl.dev;
914
1005
  }
915
- return m.eTag !== void 0;
916
- });
917
- if (!hasETag) {
918
- return;
1006
+ throw e;
919
1007
  }
920
- const loaders = getRequestLoaders(requestEv);
921
- const getData = (loaderOrAction) => {
922
- const id = loaderOrAction.__id;
923
- if (loaderOrAction.__brand === "server_loader" && !(id in loaders)) {
924
- throw new Error("Loader not executed for this request.");
1008
+ }
1009
+ const isQrl = (value) => {
1010
+ return typeof value === "function" && typeof value.getSymbol === "function";
1011
+ };
1012
+ function isLastModulePageRoute(routeModules) {
1013
+ const lastRouteModule = routeModules[routeModules.length - 1];
1014
+ return lastRouteModule && typeof lastRouteModule.default === "function";
1015
+ }
1016
+ function getPathname(url) {
1017
+ url = new URL(url);
1018
+ if (url.pathname.endsWith(deps.QDATA_JSON)) {
1019
+ url.pathname = url.pathname.slice(0, -deps.QDATA_JSON.length);
1020
+ }
1021
+ if (!globalThis.__NO_TRAILING_SLASH__) {
1022
+ if (!url.pathname.endsWith("/")) {
1023
+ url.pathname += "/";
925
1024
  }
926
- const data = loaders[id];
927
- if (data instanceof Promise) {
928
- throw new Error("Loaders returning a promise cannot be resolved for the eTag function.");
1025
+ } else {
1026
+ if (url.pathname.endsWith("/")) {
1027
+ url.pathname = url.pathname.slice(0, -1);
929
1028
  }
930
- return data;
931
- };
932
- const routeLocation = {
933
- params: requestEv.params,
934
- url: requestEv.url,
935
- isNavigating: false,
936
- prevUrl: void 0
937
- };
938
- const status = requestEv.status();
939
- const config = resolveRouteConfig(getData, routeLocation, mods, "", status);
940
- const headProps = {
941
- head: config.head,
942
- status,
943
- withLocale: (fn) => fn(),
944
- resolveValue: getData,
945
- ...routeLocation
1029
+ }
1030
+ const search = url.search.slice(1).replaceAll(/&?q(action|data|func|loaders)=[^&]+/g, "");
1031
+ return `${url.pathname}${search ? `?${search}` : ""}${url.hash}`;
1032
+ }
1033
+ function csrfLaxProtoCheckMiddleware(requestEv) {
1034
+ checkCSRF(requestEv, "lax-proto");
1035
+ }
1036
+ function csrfCheckMiddleware(requestEv) {
1037
+ checkCSRF(requestEv);
1038
+ }
1039
+ function checkCSRF(requestEv, laxProto) {
1040
+ const contentType = requestEv.request.headers.get("content-type");
1041
+ const isSimpleRequest = !contentType || deps.isContentType(requestEv.request.headers, "application/x-www-form-urlencoded", "multipart/form-data", "text/plain");
1042
+ if (isSimpleRequest) {
1043
+ const inputOrigin = requestEv.request.headers.get("origin");
1044
+ const origin = requestEv.url.origin;
1045
+ let forbidden = inputOrigin !== origin;
1046
+ if (forbidden && laxProto && inputOrigin?.replace(/^http(s)?/g, "") === origin.replace(/^http(s)?/g, "")) {
1047
+ forbidden = false;
1048
+ }
1049
+ if (forbidden) {
1050
+ throw requestEv.error(403, `CSRF check failed. Cross-site ${requestEv.method} form submissions are forbidden.
1051
+ The request origin "${inputOrigin}" does not match the server origin "${origin}".`);
1052
+ }
1053
+ }
1054
+ }
1055
+ function renderQwikMiddleware(render) {
1056
+ return async (requestEv) => {
1057
+ if (requestEv.headersSent) {
1058
+ return;
1059
+ }
1060
+ if (requestEv.sharedMap.has(deps.IsQData)) {
1061
+ return;
1062
+ }
1063
+ const responseHeaders = requestEv.headers;
1064
+ if (!responseHeaders.has("Content-Type")) {
1065
+ responseHeaders.set("Content-Type", "text/html; charset=utf-8");
1066
+ }
1067
+ const eTagCacheKey = requestEv.sharedMap.get(deps.RequestEvETagCacheKey);
1068
+ const { readable, writable } = new TextEncoderStream();
1069
+ const writableStream = requestEv.getWritableStream();
1070
+ let cacheChunks;
1071
+ let pipeSource = readable;
1072
+ if (eTagCacheKey) {
1073
+ cacheChunks = [];
1074
+ const capture = new TransformStream({
1075
+ transform(chunk, controller) {
1076
+ cacheChunks.push(chunk);
1077
+ controller.enqueue(chunk);
1078
+ }
1079
+ });
1080
+ pipeSource = readable.pipeThrough(capture);
1081
+ }
1082
+ const pipe = pipeSource.pipeTo(writableStream, {
1083
+ preventClose: true
1084
+ });
1085
+ const stream = writable.getWriter();
1086
+ const status = requestEv.status();
1087
+ try {
1088
+ const isStatic = deps.getRequestMode(requestEv) === "static";
1089
+ const serverData = deps.getQwikRouterServerData(requestEv);
1090
+ const result = await render({
1091
+ base: requestEv.basePathname + "build/",
1092
+ stream,
1093
+ serverData,
1094
+ containerAttributes: {
1095
+ ["q:render"]: isStatic ? "static" : "",
1096
+ ...serverData.containerAttributes
1097
+ }
1098
+ });
1099
+ const qData = {
1100
+ loaders: deps.getRequestLoaders(requestEv),
1101
+ action: requestEv.sharedMap.get(deps.RequestEvSharedActionId),
1102
+ status: status !== 200 ? status : 200,
1103
+ href: getPathname(requestEv.url)
1104
+ };
1105
+ if (typeof result.html === "string") {
1106
+ await stream.write(result.html);
1107
+ }
1108
+ requestEv.sharedMap.set(deps.RequestEvShareQData, qData);
1109
+ } finally {
1110
+ await stream.ready;
1111
+ await stream.close();
1112
+ await pipe;
1113
+ }
1114
+ if (eTagCacheKey && cacheChunks && cacheChunks.length > 0) {
1115
+ const totalLength = cacheChunks.reduce((sum, chunk) => sum + chunk.length, 0);
1116
+ const combined = new Uint8Array(totalLength);
1117
+ let offset = 0;
1118
+ for (const chunk of cacheChunks) {
1119
+ combined.set(chunk, offset);
1120
+ offset += chunk.length;
1121
+ }
1122
+ deps.setCachedHtml(eTagCacheKey, new TextDecoder().decode(combined));
1123
+ }
1124
+ await writableStream.close();
946
1125
  };
947
- const eTag = resolveETag(config.eTag, headProps);
948
- if (!eTag) {
949
- return;
1126
+ }
1127
+ async function handleQDataRedirect(requestEv) {
1128
+ try {
1129
+ await requestEv.next();
1130
+ } catch (err) {
1131
+ if (!(err instanceof deps.RedirectMessage)) {
1132
+ throw err;
1133
+ }
950
1134
  }
951
- requestEv.headers.set("ETag", eTag);
952
- const ifNoneMatch = requestEv.request.headers.get("If-None-Match");
953
- if (ifNoneMatch && (ifNoneMatch === eTag || ifNoneMatch === `W/${eTag}` || `W/${ifNoneMatch}` === eTag)) {
954
- requestEv.status(304);
955
- requestEv.send(304, "");
1135
+ if (requestEv.headersSent) {
956
1136
  return;
957
1137
  }
958
- if (MAX_CACHE_SIZE <= 0) {
1138
+ const status = requestEv.status();
1139
+ const location = requestEv.headers.get("Location");
1140
+ const isRedirect = status >= 301 && status <= 308 && location;
1141
+ if (isRedirect) {
1142
+ const adaptedLocation = makeQDataPath(location);
1143
+ if (adaptedLocation) {
1144
+ requestEv.headers.set("Location", adaptedLocation);
1145
+ requestEv.getWritableStream().close();
1146
+ return;
1147
+ } else {
1148
+ requestEv.status(200);
1149
+ requestEv.headers.delete("Location");
1150
+ }
1151
+ }
1152
+ }
1153
+ async function renderQData(requestEv) {
1154
+ await requestEv.next();
1155
+ if (requestEv.headersSent || requestEv.exited) {
959
1156
  return;
960
1157
  }
961
- const cacheKey = resolveCacheKey(config.cacheKey, status, eTag, requestEv.url.pathname);
962
- if (!cacheKey) {
963
- return;
1158
+ const status = requestEv.status();
1159
+ const redirectLocation = requestEv.headers.get("Location");
1160
+ requestEv.headers.set("Content-Type", "application/json; charset=utf-8");
1161
+ let loaders = deps.getRequestLoaders(requestEv);
1162
+ const selectedLoaderIds = requestEv.query.getAll(deps.QLOADER_KEY);
1163
+ const hasCustomLoaders = selectedLoaderIds.length > 0;
1164
+ if (hasCustomLoaders) {
1165
+ const selectedLoaders = {};
1166
+ for (const loaderId of selectedLoaderIds) {
1167
+ const loader = loaders[loaderId];
1168
+ selectedLoaders[loaderId] = loader;
1169
+ }
1170
+ loaders = selectedLoaders;
1171
+ }
1172
+ const qData = hasCustomLoaders ? {
1173
+ loaders,
1174
+ status: status !== 200 ? status : 200,
1175
+ href: getPathname(requestEv.url)
1176
+ } : {
1177
+ loaders,
1178
+ action: requestEv.sharedMap.get(deps.RequestEvSharedActionId),
1179
+ status: status !== 200 ? status : 200,
1180
+ href: getPathname(requestEv.url),
1181
+ redirect: redirectLocation ?? void 0,
1182
+ isRewrite: requestEv.sharedMap.get(deps.RequestEvIsRewrite)
1183
+ };
1184
+ const writer = requestEv.getWritableStream().getWriter();
1185
+ const data = await _serialize(qData);
1186
+ writer.write(deps.encoder.encode(data));
1187
+ requestEv.sharedMap.set(deps.RequestEvShareQData, qData);
1188
+ writer.close();
1189
+ }
1190
+ function makeQDataPath(href) {
1191
+ if (href.startsWith("/")) {
1192
+ if (!href.includes(deps.QDATA_JSON)) {
1193
+ const url = new URL(href, "http://localhost");
1194
+ const pathname = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
1195
+ return pathname + deps.QDATA_JSON + url.search;
1196
+ }
1197
+ return href;
1198
+ } else {
1199
+ return void 0;
964
1200
  }
965
- const cachedHtml = getCachedHtml(cacheKey);
966
- if (cachedHtml) {
967
- requestEv.headers.set("Content-Type", "text/html; charset=utf-8");
968
- requestEv.headers.set("X-SSR-Cache", "HIT");
969
- requestEv.send(status, cachedHtml);
970
- return;
1201
+ }
1202
+ function now() {
1203
+ return typeof performance !== "undefined" ? performance.now() : 0;
1204
+ }
1205
+ async function measure(requestEv, name, fn) {
1206
+ const start = now();
1207
+ try {
1208
+ return await fn();
1209
+ } finally {
1210
+ const duration = now() - start;
1211
+ let measurements = requestEv.sharedMap.get(deps.RequestEvShareServerTiming);
1212
+ if (!measurements) {
1213
+ requestEv.sharedMap.set(deps.RequestEvShareServerTiming, measurements = []);
1214
+ }
1215
+ measurements.push([
1216
+ name,
1217
+ duration
1218
+ ]);
971
1219
  }
972
- requestEv.sharedMap.set(RequestEvETagCacheKey, cacheKey);
1220
+ }
1221
+ return {
1222
+ actionsMiddleware,
1223
+ checkBrand,
1224
+ checkCSRF,
1225
+ fixTrailingSlash,
1226
+ getPathname,
1227
+ isLastModulePageRoute,
1228
+ isQrl,
1229
+ loadersMiddleware,
1230
+ measure,
1231
+ renderQwikMiddleware,
1232
+ resolveRequestHandlers,
1233
+ verifySerializable
973
1234
  };
974
1235
  }
975
- function serverErrorMiddleware(route, renderHandler) {
976
- return async (requestEv) => {
977
- try {
978
- await requestEv.next();
979
- } catch (e) {
980
- if (!(e instanceof ServerError$1) || requestEv.headersSent) {
981
- throw e;
982
- }
983
- if (requestEv.sharedMap.has(IsQData)) {
984
- throw e;
985
- }
986
- const accept = requestEv.request.headers.get("Accept");
987
- if (accept && !accept.includes("text/html")) {
988
- throw e;
989
- }
990
- const status = e.status;
991
- requestEv.status(status);
992
- const errorLoader = route.$errorLoader$;
993
- const errorModule = errorLoader ? await errorLoader() : await import('../../chunks/http-error.qwik.mjs');
994
- route.$mods$ = [
995
- errorModule
996
- ];
997
- requestEv.sharedMap.set(RequestEvHttpStatusMessage, typeof e.data === "string" ? e.data : "Server Error");
998
- await renderHandler(requestEv);
1236
+
1237
+ const encoder = /* @__PURE__ */ new TextEncoder();
1238
+ function getContentType(headers) {
1239
+ return (headers.get("content-type")?.split(/[;,]/, 1)[0].trim() ?? "").toLowerCase();
1240
+ }
1241
+ function isContentType(headers, ...types) {
1242
+ const type = getContentType(headers);
1243
+ for (let i = 0; i < types.length; i++) {
1244
+ if (types[i].toLowerCase() === type) {
1245
+ return true;
999
1246
  }
1000
- };
1247
+ }
1248
+ return false;
1001
1249
  }
1002
- function isAsyncIterator(obj) {
1003
- return obj ? typeof obj === "object" && Symbol.asyncIterator in obj : false;
1250
+
1251
+ class ServerError extends Error {
1252
+ status;
1253
+ data;
1254
+ constructor(status, data) {
1255
+ super(typeof data === "string" ? data : void 0), this.status = status, this.data = data;
1256
+ }
1004
1257
  }
1005
- function fixTrailingSlash(ev) {
1006
- const { basePathname, originalUrl, sharedMap } = ev;
1007
- const { pathname, search } = originalUrl;
1008
- const isQData = sharedMap.has(IsQData);
1009
- if (
1010
- // all valid pathnames must start with a single slash
1011
- !pathname.startsWith("/") || // protocol-relative URLs are not allowed like: //test.com, ///bad.com
1012
- pathname.startsWith("//")
1013
- ) {
1014
- return;
1258
+
1259
+ const requestHandlers = createResolveRequestHandlers({
1260
+ QACTION_KEY,
1261
+ QFN_KEY,
1262
+ QLOADER_KEY,
1263
+ QDATA_JSON,
1264
+ IsQData,
1265
+ RequestEvETagCacheKey,
1266
+ RequestEvHttpStatusMessage,
1267
+ RequestEvIsRewrite,
1268
+ RequestEvShareQData,
1269
+ RequestEvShareServerTiming,
1270
+ RequestEvSharedActionId,
1271
+ RequestRouteName,
1272
+ RedirectMessage,
1273
+ ServerError,
1274
+ HttpStatus,
1275
+ encoder,
1276
+ isContentType,
1277
+ getCachedHtml,
1278
+ getQwikRouterServerData,
1279
+ getRequestLoaderSerializationStrategyMap,
1280
+ getRequestLoaders,
1281
+ getRequestMode,
1282
+ getRouteLoaderPromise,
1283
+ loadHttpError: () => import('../../chunks/http-error.qwik.mjs'),
1284
+ MAX_CACHE_SIZE,
1285
+ resolveCacheKey,
1286
+ resolveETag,
1287
+ resolveRouteConfig,
1288
+ setCachedHtml
1289
+ });
1290
+ const { renderQwikMiddleware, resolveRequestHandlers} = requestHandlers;
1291
+
1292
+ let _asyncRequestStore;
1293
+ 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
+ });
1299
+ }
1300
+
1301
+ function createCacheControl(cacheControl) {
1302
+ const controls = [];
1303
+ if (cacheControl === "day") {
1304
+ cacheControl = 60 * 60 * 24;
1305
+ } else if (cacheControl === "week") {
1306
+ cacheControl = 60 * 60 * 24 * 7;
1307
+ } else if (cacheControl === "month") {
1308
+ cacheControl = 60 * 60 * 24 * 30;
1309
+ } else if (cacheControl === "year") {
1310
+ cacheControl = 60 * 60 * 24 * 365;
1311
+ } else if (cacheControl === "private") {
1312
+ cacheControl = {
1313
+ private: true,
1314
+ noCache: true
1315
+ };
1316
+ } else if (cacheControl === "immutable") {
1317
+ cacheControl = {
1318
+ public: true,
1319
+ immutable: true,
1320
+ maxAge: 60 * 60 * 24 * 365
1321
+ };
1322
+ } else if (cacheControl === "no-cache") {
1323
+ cacheControl = {
1324
+ noCache: true
1325
+ };
1015
1326
  }
1016
- if (!isQData && pathname !== basePathname && !pathname.endsWith(".html")) {
1017
- if (!globalThis.__NO_TRAILING_SLASH__) {
1018
- if (!pathname.endsWith("/")) {
1019
- throw ev.redirect(HttpStatus.MovedPermanently, pathname + "/" + search);
1020
- }
1021
- } else {
1022
- if (pathname.endsWith("/")) {
1023
- throw ev.redirect(HttpStatus.MovedPermanently, pathname.slice(0, pathname.length - 1) + search);
1024
- }
1025
- }
1327
+ if (typeof cacheControl === "number") {
1328
+ cacheControl = {
1329
+ maxAge: cacheControl,
1330
+ sMaxAge: cacheControl
1331
+ };
1332
+ }
1333
+ if (cacheControl.immutable) {
1334
+ controls.push("immutable");
1335
+ }
1336
+ if (cacheControl.maxAge) {
1337
+ controls.push(`max-age=${cacheControl.maxAge}`);
1338
+ }
1339
+ if (cacheControl.sMaxAge) {
1340
+ controls.push(`s-maxage=${cacheControl.sMaxAge}`);
1341
+ }
1342
+ if (cacheControl.noStore) {
1343
+ controls.push("no-store");
1344
+ }
1345
+ if (cacheControl.noCache) {
1346
+ controls.push("no-cache");
1347
+ }
1348
+ if (cacheControl.private) {
1349
+ controls.push("private");
1350
+ }
1351
+ if (cacheControl.public) {
1352
+ controls.push("public");
1353
+ }
1354
+ if (cacheControl.staleWhileRevalidate) {
1355
+ controls.push(`stale-while-revalidate=${cacheControl.staleWhileRevalidate}`);
1356
+ }
1357
+ if (cacheControl.staleIfError) {
1358
+ controls.push(`stale-if-error=${cacheControl.staleIfError}`);
1026
1359
  }
1360
+ return controls.join(", ");
1027
1361
  }
1028
- function verifySerializable(data, qrl) {
1362
+
1363
+ const SAMESITE = {
1364
+ lax: "Lax",
1365
+ Lax: "Lax",
1366
+ None: "None",
1367
+ none: "None",
1368
+ strict: "Strict",
1369
+ Strict: "Strict"
1370
+ };
1371
+ const UNIT = {
1372
+ seconds: 1,
1373
+ minutes: 1 * 60,
1374
+ hours: 1 * 60 * 60,
1375
+ days: 1 * 60 * 60 * 24,
1376
+ weeks: 1 * 60 * 60 * 24 * 7
1377
+ };
1378
+ function tryDecodeUriComponent(str) {
1029
1379
  try {
1030
- _verifySerializable(data, void 0);
1031
- } catch (e) {
1032
- if (e instanceof Error && qrl.dev) {
1033
- e.loc = qrl.dev;
1034
- }
1035
- throw e;
1380
+ return decodeURIComponent(str);
1381
+ } catch {
1382
+ return str;
1036
1383
  }
1037
1384
  }
1038
- function isLastModulePageRoute(routeModules) {
1039
- const lastRouteModule = routeModules[routeModules.length - 1];
1040
- return lastRouteModule && typeof lastRouteModule.default === "function";
1041
- }
1042
- function getPathname(url) {
1043
- url = new URL(url);
1044
- if (url.pathname.endsWith(QDATA_JSON)) {
1045
- url.pathname = url.pathname.slice(0, -QDATA_JSON.length);
1046
- }
1047
- if (!globalThis.__NO_TRAILING_SLASH__) {
1048
- if (!url.pathname.endsWith("/")) {
1049
- url.pathname += "/";
1050
- }
1051
- } else {
1052
- if (url.pathname.endsWith("/")) {
1053
- url.pathname = url.pathname.slice(0, -1);
1385
+ const parseCookieString = (cookieString) => {
1386
+ const cookie = {};
1387
+ if (typeof cookieString === "string" && cookieString !== "") {
1388
+ const cookieSegments = cookieString.split(";");
1389
+ for (const cookieSegment of cookieSegments) {
1390
+ const separatorIndex = cookieSegment.indexOf("=");
1391
+ if (separatorIndex !== -1) {
1392
+ cookie[tryDecodeUriComponent(cookieSegment.slice(0, separatorIndex).trim())] = tryDecodeUriComponent(cookieSegment.slice(separatorIndex + 1).trim());
1393
+ }
1054
1394
  }
1055
1395
  }
1056
- const search = url.search.slice(1).replaceAll(/&?q(action|data|func|loaders)=[^&]+/g, "");
1057
- return `${url.pathname}${search ? `?${search}` : ""}${url.hash}`;
1396
+ return cookie;
1397
+ };
1398
+ function resolveSameSite(sameSite) {
1399
+ if (sameSite === true) {
1400
+ return "Strict";
1401
+ }
1402
+ if (sameSite === false) {
1403
+ return "None";
1404
+ }
1405
+ if (sameSite) {
1406
+ return SAMESITE[sameSite];
1407
+ }
1408
+ return void 0;
1058
1409
  }
1059
- const encoder = /* @__PURE__ */ new TextEncoder();
1060
- function renderQwikMiddleware(render) {
1061
- return async (requestEv) => {
1062
- if (requestEv.headersSent) {
1063
- return;
1064
- }
1065
- const isPageDataReq = requestEv.sharedMap.has(IsQData);
1066
- if (isPageDataReq) {
1067
- return;
1068
- }
1069
- requestEv.request.headers.forEach((value, key) => value);
1070
- const responseHeaders = requestEv.headers;
1071
- if (!responseHeaders.has("Content-Type")) {
1072
- responseHeaders.set("Content-Type", "text/html; charset=utf-8");
1073
- }
1074
- const eTagCacheKey = requestEv.sharedMap.get(RequestEvETagCacheKey);
1075
- const { readable, writable } = new TextEncoderStream();
1076
- const writableStream = requestEv.getWritableStream();
1077
- let cacheChunks;
1078
- let pipeSource = readable;
1079
- if (eTagCacheKey) {
1080
- cacheChunks = [];
1081
- const capture = new TransformStream({
1082
- transform(chunk, controller) {
1083
- cacheChunks.push(chunk);
1084
- controller.enqueue(chunk);
1085
- }
1086
- });
1087
- pipeSource = readable.pipeThrough(capture);
1410
+ const createSetCookieValue = (cookieName, cookieValue, options) => {
1411
+ const c = [
1412
+ `${cookieName}=${cookieValue}`
1413
+ ];
1414
+ if (typeof options.domain === "string") {
1415
+ c.push(`Domain=${options.domain}`);
1416
+ }
1417
+ if (typeof options.maxAge === "number") {
1418
+ c.push(`Max-Age=${options.maxAge}`);
1419
+ } else if (Array.isArray(options.maxAge)) {
1420
+ c.push(`Max-Age=${options.maxAge[0] * UNIT[options.maxAge[1]]}`);
1421
+ } else if (typeof options.expires === "number" || typeof options.expires == "string") {
1422
+ c.push(`Expires=${options.expires}`);
1423
+ } else if (options.expires instanceof Date) {
1424
+ c.push(`Expires=${options.expires.toUTCString()}`);
1425
+ }
1426
+ if (options.httpOnly) {
1427
+ c.push("HttpOnly");
1428
+ }
1429
+ if (typeof options.path === "string") {
1430
+ c.push(`Path=${options.path}`);
1431
+ }
1432
+ const sameSite = resolveSameSite(options.sameSite);
1433
+ if (sameSite) {
1434
+ c.push(`SameSite=${sameSite}`);
1435
+ }
1436
+ if (options.secure) {
1437
+ c.push("Secure");
1438
+ }
1439
+ return c.join("; ");
1440
+ };
1441
+ const REQ_COOKIE = /* @__PURE__ */ Symbol("request-cookies");
1442
+ const RES_COOKIE = /* @__PURE__ */ Symbol("response-cookies");
1443
+ const LIVE_COOKIE = /* @__PURE__ */ Symbol("live-cookies");
1444
+ class Cookie {
1445
+ [REQ_COOKIE];
1446
+ [RES_COOKIE] = {};
1447
+ [LIVE_COOKIE] = {};
1448
+ appendCounter = 0;
1449
+ constructor(cookieString) {
1450
+ this[REQ_COOKIE] = parseCookieString(cookieString);
1451
+ this[LIVE_COOKIE] = {
1452
+ ...this[REQ_COOKIE]
1453
+ };
1454
+ }
1455
+ get(cookieName, live = true) {
1456
+ const value = this[live ? LIVE_COOKIE : REQ_COOKIE][cookieName];
1457
+ if (!value) {
1458
+ return null;
1088
1459
  }
1089
- const pipe = pipeSource.pipeTo(writableStream, {
1090
- preventClose: true
1460
+ return {
1461
+ value,
1462
+ json() {
1463
+ return JSON.parse(value);
1464
+ },
1465
+ number() {
1466
+ return Number(value);
1467
+ }
1468
+ };
1469
+ }
1470
+ getAll(live = true) {
1471
+ return Object.keys(this[live ? LIVE_COOKIE : REQ_COOKIE]).reduce((cookies, cookieName) => {
1472
+ cookies[cookieName] = this.get(cookieName);
1473
+ return cookies;
1474
+ }, {});
1475
+ }
1476
+ has(cookieName, live = true) {
1477
+ return !!this[live ? LIVE_COOKIE : REQ_COOKIE][cookieName];
1478
+ }
1479
+ set(cookieName, cookieValue, options = {}) {
1480
+ this[LIVE_COOKIE][cookieName] = typeof cookieValue === "string" ? cookieValue : JSON.stringify(cookieValue);
1481
+ const resolvedValue = typeof cookieValue === "string" ? cookieValue : encodeURIComponent(JSON.stringify(cookieValue));
1482
+ this[RES_COOKIE][cookieName] = createSetCookieValue(cookieName, resolvedValue, options);
1483
+ }
1484
+ append(cookieName, cookieValue, options = {}) {
1485
+ this[LIVE_COOKIE][cookieName] = typeof cookieValue === "string" ? cookieValue : JSON.stringify(cookieValue);
1486
+ const resolvedValue = typeof cookieValue === "string" ? cookieValue : encodeURIComponent(JSON.stringify(cookieValue));
1487
+ this[RES_COOKIE][++this.appendCounter] = createSetCookieValue(cookieName, resolvedValue, options);
1488
+ }
1489
+ delete(name, options) {
1490
+ this.set(name, "deleted", {
1491
+ ...options,
1492
+ maxAge: 0
1091
1493
  });
1092
- const stream = writable.getWriter();
1093
- const status = requestEv.status();
1094
- try {
1095
- const isStatic = getRequestMode(requestEv) === "static";
1096
- const serverData = getQwikRouterServerData(requestEv);
1097
- const result = await render({
1098
- base: requestEv.basePathname + "build/",
1099
- stream,
1100
- serverData,
1101
- containerAttributes: {
1102
- ["q:render"]: isStatic ? "static" : "",
1103
- ...serverData.containerAttributes
1104
- }
1105
- });
1106
- const qData = {
1107
- loaders: getRequestLoaders(requestEv),
1108
- action: requestEv.sharedMap.get(RequestEvSharedActionId),
1109
- status: status !== 200 ? status : 200,
1110
- href: getPathname(requestEv.url)
1111
- };
1112
- if (typeof result.html === "string") {
1113
- await stream.write(result.html);
1114
- }
1115
- requestEv.sharedMap.set(RequestEvShareQData, qData);
1116
- } finally {
1117
- await stream.ready;
1118
- await stream.close();
1119
- await pipe;
1120
- }
1121
- if (eTagCacheKey && cacheChunks && cacheChunks.length > 0) {
1122
- const totalLength = cacheChunks.reduce((sum, chunk) => sum + chunk.length, 0);
1123
- const combined = new Uint8Array(totalLength);
1124
- let offset = 0;
1125
- for (const chunk of cacheChunks) {
1126
- combined.set(chunk, offset);
1127
- offset += chunk.length;
1128
- }
1129
- setCachedHtml(eTagCacheKey, new TextDecoder().decode(combined));
1130
- }
1131
- await writableStream.close();
1132
- };
1133
- }
1134
- async function renderQData(requestEv) {
1135
- await requestEv.next();
1136
- if (requestEv.headersSent || requestEv.exited) {
1137
- return;
1494
+ this[LIVE_COOKIE][name] = null;
1138
1495
  }
1139
- const status = requestEv.status();
1140
- const redirectLocation = requestEv.headers.get("Location");
1141
- requestEv.request.headers.forEach((value, key) => value);
1142
- requestEv.headers.set("Content-Type", "application/json; charset=utf-8");
1143
- let loaders = getRequestLoaders(requestEv);
1144
- const selectedLoaderIds = requestEv.query.getAll(QLOADER_KEY);
1145
- const hasCustomLoaders = selectedLoaderIds.length > 0;
1146
- if (hasCustomLoaders) {
1147
- const selectedLoaders = {};
1148
- for (const loaderId of selectedLoaderIds) {
1149
- const loader = loaders[loaderId];
1150
- selectedLoaders[loaderId] = loader;
1151
- }
1152
- loaders = selectedLoaders;
1153
- }
1154
- const qData = hasCustomLoaders ? {
1155
- // send minimal data to the client
1156
- loaders,
1157
- status: status !== 200 ? status : 200,
1158
- href: getPathname(requestEv.url)
1159
- } : {
1160
- loaders,
1161
- action: requestEv.sharedMap.get(RequestEvSharedActionId),
1162
- status: status !== 200 ? status : 200,
1163
- href: getPathname(requestEv.url),
1164
- redirect: redirectLocation ?? void 0,
1165
- isRewrite: requestEv.sharedMap.get(RequestEvIsRewrite)
1166
- };
1167
- const writer = requestEv.getWritableStream().getWriter();
1168
- const data = await _serialize(qData);
1169
- writer.write(encoder.encode(data));
1170
- requestEv.sharedMap.set(RequestEvShareQData, qData);
1171
- writer.close();
1172
- }
1173
- function makeQDataPath(href) {
1174
- if (href.startsWith("/")) {
1175
- if (!href.includes(QDATA_JSON)) {
1176
- const url = new URL(href, "http://localhost");
1177
- const pathname = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
1178
- return pathname + QDATA_JSON + url.search;
1179
- }
1180
- return href;
1181
- } else {
1182
- return void 0;
1496
+ headers() {
1497
+ return Object.values(this[RES_COOKIE]);
1183
1498
  }
1184
1499
  }
1185
- async function handleQDataRedirect(requestEv) {
1186
- try {
1187
- await requestEv.next();
1188
- } catch (err) {
1189
- if (!(err instanceof RedirectMessage$1)) {
1190
- throw err;
1500
+ const mergeHeadersCookies = (headers, cookies) => {
1501
+ const cookieHeaders = cookies.headers();
1502
+ if (cookieHeaders.length > 0) {
1503
+ const newHeaders = new Headers(headers);
1504
+ for (const cookie of cookieHeaders) {
1505
+ newHeaders.append("Set-Cookie", cookie);
1191
1506
  }
1507
+ return newHeaders;
1192
1508
  }
1193
- if (requestEv.headersSent) {
1194
- return;
1195
- }
1196
- const status = requestEv.status();
1197
- const location = requestEv.headers.get("Location");
1198
- const isRedirect = status >= 301 && status <= 308 && location;
1199
- if (isRedirect) {
1200
- const adaptedLocation = makeQDataPath(location);
1201
- if (adaptedLocation) {
1202
- requestEv.headers.set("Location", adaptedLocation);
1203
- requestEv.getWritableStream().close();
1204
- return;
1205
- } else {
1206
- requestEv.status(200);
1207
- requestEv.headers.delete("Location");
1208
- }
1509
+ return headers;
1510
+ };
1511
+
1512
+ class RewriteMessage extends AbortMessage {
1513
+ pathname;
1514
+ constructor(pathname) {
1515
+ super(), this.pathname = pathname;
1209
1516
  }
1210
1517
  }
1211
- function now() {
1212
- return typeof performance !== "undefined" ? performance.now() : 0;
1518
+
1519
+ const requestEventDeps = {
1520
+ QDATA_KEY,
1521
+ isPromise,
1522
+ createCacheControl,
1523
+ Cookie,
1524
+ AbortMessage,
1525
+ RedirectMessage,
1526
+ RewriteMessage,
1527
+ ServerError,
1528
+ getRouteLoaderPromise,
1529
+ getRouteMatchPathname,
1530
+ IsQData,
1531
+ encoder,
1532
+ getContentType
1533
+ };
1534
+ function createRequestEvent(...args) {
1535
+ return createRequestEventWithDeps(requestEventDeps, ...args);
1213
1536
  }
1214
- async function measure(requestEv, name, fn) {
1215
- const start = now();
1537
+
1538
+ async function runNextWithDeps(requestEv, rebuildRouteInfo, resolve, deps) {
1216
1539
  try {
1217
- return await fn();
1218
- } finally {
1219
- const duration = now() - start;
1220
- let measurements = requestEv.sharedMap.get(RequestEvShareServerTiming);
1221
- if (!measurements) {
1222
- requestEv.sharedMap.set(RequestEvShareServerTiming, measurements = []);
1223
- }
1224
- measurements.push([
1225
- name,
1226
- duration
1227
- ]);
1228
- }
1229
- }
1230
- async function runValidators(requestEv, validators, data) {
1231
- let lastResult = {
1232
- success: true,
1233
- data
1234
- };
1235
- if (validators) {
1236
- for (const validator of validators) {
1237
- if (isDev) {
1238
- lastResult = await measure(requestEv, `validator$`, () => validator.validate(requestEv, data));
1239
- } else {
1240
- lastResult = await validator.validate(requestEv, data);
1241
- }
1242
- if (!lastResult.success) {
1243
- return lastResult;
1244
- } else {
1245
- data = lastResult.data;
1246
- }
1247
- }
1540
+ const isValidURL = (url) => new URL(url.pathname + url.search, url);
1541
+ isValidURL(requestEv.originalUrl);
1542
+ } catch {
1543
+ const status = 404;
1544
+ const message = "Resource Not Found";
1545
+ requestEv.status(status);
1546
+ requestEv.html(status, deps.getErrorHtml(status, message));
1547
+ return new deps.ServerError(status, message);
1248
1548
  }
1249
- return lastResult;
1250
- }
1251
- function actionsMiddleware(routeActions) {
1252
- return async (requestEvent) => {
1253
- const requestEv = requestEvent;
1254
- if (requestEv.headersSent) {
1255
- requestEv.exit();
1256
- return;
1257
- }
1258
- const { method } = requestEv;
1259
- const loaders = getRequestLoaders(requestEv);
1260
- if (isDev && method === "GET") {
1261
- if (requestEv.query.has(QACTION_KEY)) {
1262
- console.warn('Seems like you are submitting a Qwik Action via GET request. Qwik Actions should be submitted via POST request.\nMake sure your <form> has method="POST" attribute, like this: <form method="POST">');
1549
+ let rewriteAttempt = 1;
1550
+ async function runOnce() {
1551
+ try {
1552
+ await requestEv.next();
1553
+ } catch (e) {
1554
+ if (e instanceof deps.RedirectMessage) {
1555
+ const stream = requestEv.getWritableStream();
1556
+ await stream.close();
1557
+ return e;
1558
+ } else if (e instanceof deps.RewriteMessage) {
1559
+ if (rewriteAttempt > 50) {
1560
+ return new Error(`Infinite rewrite loop`);
1561
+ }
1562
+ rewriteAttempt += 1;
1563
+ const url = new URL(requestEv.url);
1564
+ url.pathname = e.pathname;
1565
+ const { loadedRoute, requestHandlers } = await rebuildRouteInfo(url);
1566
+ requestEv.resetRoute(loadedRoute, requestHandlers, url);
1567
+ return await runOnce();
1568
+ } else if (e instanceof deps.AbortMessage) {
1569
+ return;
1570
+ } else if (e instanceof deps.ServerError && !requestEv.headersSent) {
1571
+ const status = e.status;
1572
+ const accept = requestEv.request.headers.get("Accept");
1573
+ if (accept && !accept.includes("text/html")) {
1574
+ requestEv.headers.set("Content-Type", "application/qwik-json");
1575
+ requestEv.send(status, await _serialize(e.data));
1576
+ } else {
1577
+ requestEv.html(status, deps.getErrorHtml(status, e.data));
1578
+ }
1579
+ return e;
1263
1580
  }
1264
- }
1265
- if (method === "POST") {
1266
- const selectedActionId = requestEv.query.get(QACTION_KEY);
1267
- if (selectedActionId) {
1268
- const serverActionsMap = globalThis._qwikActionsMap;
1269
- const action = routeActions.find((action2) => action2.__id === selectedActionId) ?? serverActionsMap?.get(selectedActionId);
1270
- if (action) {
1271
- requestEv.sharedMap.set(RequestEvSharedActionId, selectedActionId);
1272
- const data = await requestEv.parseBody();
1273
- if (!data || typeof data !== "object") {
1274
- throw new Error(`Expected request data for the action id ${selectedActionId} to be an object`);
1581
+ if (!isDev) {
1582
+ try {
1583
+ if (!requestEv.headersSent) {
1584
+ requestEv.headers.set("content-type", "text/html; charset=utf-8");
1585
+ requestEv.cacheControl({
1586
+ noCache: true
1587
+ });
1588
+ requestEv.status(500);
1275
1589
  }
1276
- const result = await runValidators(requestEv, action.__validators, data);
1277
- if (!result.success) {
1278
- loaders[selectedActionId] = requestEv.fail(result.status ?? 500, result.error);
1279
- } else {
1280
- const actionResolved = isDev ? await measure(requestEv, action.__qrl.getHash(), () => action.__qrl.call(requestEv, result.data, requestEv)) : await action.__qrl.call(requestEv, result.data, requestEv);
1281
- if (isDev) {
1282
- verifySerializable(actionResolved, action.__qrl);
1283
- }
1284
- loaders[selectedActionId] = actionResolved;
1590
+ const stream = requestEv.getWritableStream();
1591
+ if (!stream.locked) {
1592
+ const writer = stream.getWriter();
1593
+ await writer.write(deps.encoder.encode(deps.getErrorHtml(500, "Internal Server Error")));
1594
+ await writer.close();
1285
1595
  }
1596
+ } catch {
1597
+ console.error("Unable to render error page");
1286
1598
  }
1287
1599
  }
1288
- }
1289
- };
1290
- }
1291
- async function getRouteLoaderPromise(loader, loaders, requestEv) {
1292
- const loaderId = loader.__id;
1293
- loaders[loaderId] = runValidators(
1294
- requestEv,
1295
- loader.__validators,
1296
- void 0
1297
- // data
1298
- ).then((res) => {
1299
- if (res.success) {
1300
- if (isDev) {
1301
- return measure(requestEv, loader.__qrl.getHash(), () => loader.__qrl.call(requestEv, requestEv));
1302
- } else {
1303
- return loader.__qrl.call(requestEv, requestEv);
1304
- }
1305
- } else {
1306
- return requestEv.fail(res.status ?? 500, res.error);
1307
- }
1308
- }).then((resolvedLoader) => {
1309
- if (typeof resolvedLoader === "function") {
1310
- loaders[loaderId] = resolvedLoader();
1311
- } else {
1312
- if (isDev) {
1313
- verifySerializable(resolvedLoader, loader.__qrl);
1314
- }
1315
- loaders[loaderId] = resolvedLoader;
1316
- }
1317
- return resolvedLoader;
1318
- });
1319
- const loadersSerializationStrategy = getRequestLoaderSerializationStrategyMap(requestEv);
1320
- loadersSerializationStrategy.set(loaderId, loader.__serializationStrategy);
1321
- return loaders[loaderId];
1322
- }
1323
- function loadersMiddleware(routeLoaders) {
1324
- return async (requestEvent) => {
1325
- const requestEv = requestEvent;
1326
- if (requestEv.headersSent) {
1327
- requestEv.exit();
1328
- return;
1329
- }
1330
- const loaders = getRequestLoaders(requestEv);
1331
- if (routeLoaders.length > 0) {
1332
- const resolvedLoadersPromises = routeLoaders.map((loader) => getRouteLoaderPromise(loader, loaders, requestEv));
1333
- await Promise.all(resolvedLoadersPromises);
1334
- }
1335
- };
1336
- }
1337
- async function runServerFunction(ev) {
1338
- const serverFnHash = ev.query.get(QFN_KEY);
1339
- if (serverFnHash && ev.request.headers.get("X-QRL") === serverFnHash && ev.request.headers.get("Content-Type") === "application/qwik-json") {
1340
- ev.exit();
1341
- const data = await ev.parseBody();
1342
- if (!Array.isArray(data) || !(Array.isArray(data[0]) || data[0] === void 0)) {
1343
- throw ev.error(500, "Invalid request");
1344
- }
1345
- const qrl = inlinedQrl(null, serverFnHash, data.slice(1));
1346
- let result;
1347
- try {
1348
- if (isDev) {
1349
- result = await measure(ev, `server_${serverFnHash}`, () => qrl.apply(ev, data[0]));
1350
- } else {
1351
- result = await qrl.apply(ev, data[0]);
1352
- }
1353
- } catch (err) {
1354
- if (err instanceof ServerError$1) {
1355
- throw ev.error(err.status, err.data);
1356
- }
1357
- console.error(`Server function ${serverFnHash} failed:`, err);
1358
- throw ev.error(500, "Invalid request");
1359
- }
1360
- if (isAsyncIterator(result)) {
1361
- ev.headers.set("Content-Type", "text/qwik-json-stream");
1362
- const writable = ev.getWritableStream();
1363
- const stream = writable.getWriter();
1364
- for await (const item of result) {
1365
- if (isDev) {
1366
- verifySerializable(item, qrl);
1367
- }
1368
- const message = await _serialize(item);
1369
- if (ev.signal.aborted) {
1370
- break;
1371
- }
1372
- await stream.write(encoder.encode(`${message}
1373
- `));
1374
- }
1375
- stream.close();
1376
- } else {
1377
- verifySerializable(result, qrl);
1378
- ev.headers.set("Content-Type", "application/qwik-json");
1379
- const message = await _serialize(result);
1380
- ev.send(200, message);
1381
- }
1382
- return;
1383
- }
1384
- }
1385
- function getContentType(headers) {
1386
- return (headers.get("content-type")?.split(/[;,]/, 1)[0].trim() ?? "").toLowerCase();
1387
- }
1388
- function isContentType(headers, ...types) {
1389
- const type = getContentType(headers);
1390
- for (let i = 0; i < types.length; i++) {
1391
- if (types[i].toLowerCase() === type) {
1392
- return true;
1600
+ return e instanceof Error || typeof e === "object" && e !== null ? e : new Error(String(e));
1393
1601
  }
1394
1602
  }
1395
- return false;
1396
- }
1397
- function checkCSRF(requestEv, laxProto) {
1398
- const contentType = requestEv.request.headers.get("content-type");
1399
- const isSimpleRequest = !contentType || isContentType(requestEv.request.headers, "application/x-www-form-urlencoded", "multipart/form-data", "text/plain");
1400
- if (isSimpleRequest) {
1401
- const inputOrigin = requestEv.request.headers.get("origin");
1402
- const origin = requestEv.url.origin;
1403
- let forbidden = inputOrigin !== origin;
1404
- if (forbidden && laxProto && inputOrigin?.replace(/^http(s)?/g, "") === origin.replace(/^http(s)?/g, "")) {
1405
- forbidden = false;
1406
- }
1407
- if (forbidden) {
1408
- throw requestEv.error(403, `CSRF check failed. Cross-site ${requestEv.method} form submissions are forbidden.
1409
- The request origin "${inputOrigin}" does not match the server origin "${origin}".`);
1603
+ try {
1604
+ return await runOnce();
1605
+ } finally {
1606
+ if (!requestEv.isDirty()) {
1607
+ resolve(null);
1410
1608
  }
1411
1609
  }
1412
1610
  }
1413
- function csrfLaxProtoCheckMiddleware(requestEv) {
1414
- checkCSRF(requestEv, "lax-proto");
1415
- }
1416
- function csrfCheckMiddleware(requestEv) {
1417
- checkCSRF(requestEv);
1611
+ function runQwikRouterWithDeps(serverRequestEv, loadedRoute, requestHandlers, rebuildRouteInfo, basePathname, deps) {
1612
+ let resolve;
1613
+ const responsePromise = new Promise((r) => resolve = r);
1614
+ const requestEv = deps.createRequestEvent(serverRequestEv, loadedRoute, requestHandlers, basePathname, resolve);
1615
+ return {
1616
+ response: responsePromise,
1617
+ requestEv,
1618
+ completion: deps.asyncRequestStore ? deps.asyncRequestStore.run(requestEv, runNextWithDeps, requestEv, rebuildRouteInfo, resolve, deps) : runNextWithDeps(requestEv, rebuildRouteInfo, resolve, deps)
1619
+ };
1418
1620
  }
1419
- const resolveRequestHandlers = (serverPlugins, route, method, checkOrigin, renderHandler, isInternal) => {
1420
- const routeLoaders = [];
1421
- const routeActions = [];
1422
- const requestHandlers = [];
1423
- const isPageRoute = !!(route && isLastModulePageRoute(route.$mods$));
1424
- if (isInternal) {
1425
- requestHandlers.push(handleQDataRedirect);
1426
- }
1427
- if (isPageRoute) {
1428
- requestHandlers.push(serverErrorMiddleware(route, renderHandler));
1429
- }
1430
- if (serverPlugins) {
1431
- _resolveRequestHandlers(routeLoaders, routeActions, requestHandlers, serverPlugins, isPageRoute, method);
1432
- }
1433
- if (route) {
1434
- const routeModules = route.$mods$;
1435
- _resolveRequestHandlers(routeLoaders, routeActions, requestHandlers, routeModules, isPageRoute, method);
1436
- const routeName = route.$routeName$;
1437
- if (checkOrigin && (method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE")) {
1438
- if (checkOrigin === "lax-proto") {
1439
- requestHandlers.unshift(csrfLaxProtoCheckMiddleware);
1440
- } else {
1441
- requestHandlers.unshift(csrfCheckMiddleware);
1442
- }
1443
- }
1444
- if (isPageRoute) {
1445
- if (method === "POST" || method === "GET") {
1446
- requestHandlers.push(runServerFunction);
1447
- }
1448
- requestHandlers.push(fixTrailingSlash);
1449
- if (isInternal) {
1450
- requestHandlers.push(renderQData);
1451
- }
1452
- }
1453
- if (isPageRoute) {
1454
- requestHandlers.push((ev) => {
1455
- ev.sharedMap.set(RequestRouteName, routeName);
1456
- });
1457
- requestHandlers.push(actionsMiddleware(routeActions));
1458
- requestHandlers.push(loadersMiddleware(routeLoaders));
1459
- requestHandlers.push(eTagMiddleware(route));
1460
- requestHandlers.push(renderHandler);
1461
- }
1462
- }
1463
- return requestHandlers;
1464
- };
1465
1621
 
1466
- let _asyncRequestStore;
1467
- if (isServer) {
1468
- import('node:async_hooks').then((module) => {
1469
- _asyncRequestStore = new module.AsyncLocalStorage();
1470
- }).catch((err) => {
1471
- 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);
1622
+ function runQwikRouter(serverRequestEv, loadedRoute, requestHandlers, rebuildRouteInfo, basePathname = "/") {
1623
+ return runQwikRouterWithDeps(serverRequestEv, loadedRoute, requestHandlers, rebuildRouteInfo, basePathname, {
1624
+ AbortMessage,
1625
+ RedirectMessage,
1626
+ RewriteMessage,
1627
+ ServerError,
1628
+ asyncRequestStore: _asyncRequestStore,
1629
+ createRequestEvent,
1630
+ encoder,
1631
+ getErrorHtml
1472
1632
  });
1473
1633
  }
1634
+
1474
1635
  let qwikRouterConfigActual;
1475
1636
  async function loadRequestHandlers(qwikRouterConfig, pathname, method, checkOrigin, renderFn, isInternal) {
1476
1637
  const { routes, serverPlugins, cacheModules } = qwikRouterConfig;
@@ -1550,26 +1711,6 @@ function isStaticPath(method, url) {
1550
1711
  return false;
1551
1712
  }
1552
1713
 
1553
- class ServerError extends Error {
1554
- status;
1555
- data;
1556
- constructor(status, data) {
1557
- super(typeof data === "string" ? data : void 0), this.status = status, this.data = data;
1558
- }
1559
- }
1560
-
1561
- class AbortMessage {
1562
- }
1563
- class RedirectMessage extends AbortMessage {
1564
- }
1565
-
1566
- class RewriteMessage extends AbortMessage {
1567
- pathname;
1568
- constructor(pathname) {
1569
- super(), this.pathname = pathname;
1570
- }
1571
- }
1572
-
1573
1714
  class _TextEncoderStream_polyfill {
1574
1715
  #pendingHighSurrogate = null;
1575
1716
  #handle = new TextEncoder();