@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.
- package/lib/adapters/shared/vite/index.d.ts +1 -1
- package/lib/adapters/shared/vite/index.mjs +32 -3
- package/lib/chunks/deepFreeze.qwik.mjs +18 -0
- package/lib/chunks/http-error.qwik.mjs +14 -6
- package/lib/chunks/redirect-handler.mjs +6 -0
- package/lib/chunks/routing.qwik.mjs +52 -41
- package/lib/chunks/system.mjs +13 -8
- package/lib/chunks/worker-thread.qwik.mjs +2572 -0
- package/lib/index.qwik.mjs +70 -32
- package/lib/middleware/bun/index.mjs +3 -3
- package/lib/middleware/cloudflare-pages/index.mjs +3 -3
- package/lib/middleware/deno/index.mjs +3 -3
- package/lib/middleware/netlify-edge/index.mjs +3 -3
- package/lib/middleware/request-handler/index.d.ts +4 -15
- package/lib/middleware/request-handler/index.mjs +1190 -1049
- package/lib/middleware/vercel-edge/index.mjs +3 -3
- package/lib/ssg/index.d.ts +2 -2
- package/lib/ssg/index.mjs +32 -23
- package/lib/vite/index.mjs +162 -2
- package/package.json +4 -4
- package/lib/chunks/worker-thread.mjs +0 -271
|
@@ -1,10 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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 {
|
|
5
|
-
import {
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
|
193
|
+
return new Set(Array.from(arrayCandidates.entries()).filter(([, isArrayPath]) => isArrayPath).map(([path]) => path));
|
|
215
194
|
};
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
224
|
-
|
|
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
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
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
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
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
|
-
|
|
857
|
-
|
|
858
|
-
|
|
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
|
-
|
|
861
|
-
|
|
862
|
-
|
|
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
|
-
|
|
865
|
-
|
|
866
|
-
|
|
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
|
-
|
|
869
|
-
|
|
870
|
-
|
|
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
|
-
|
|
873
|
-
methodReqHandler
|
|
874
|
-
|
|
730
|
+
if (typeof methodReqHandler === "function") {
|
|
731
|
+
requestHandlers.push(methodReqHandler);
|
|
732
|
+
} else if (Array.isArray(methodReqHandler)) {
|
|
733
|
+
requestHandlers.push(...methodReqHandler);
|
|
875
734
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
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
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
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
|
-
|
|
900
|
-
|
|
901
|
-
|
|
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
|
-
|
|
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
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
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
|
-
|
|
913
|
-
|
|
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
|
-
|
|
916
|
-
});
|
|
917
|
-
if (!hasETag) {
|
|
918
|
-
return;
|
|
1006
|
+
throw e;
|
|
919
1007
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
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
|
-
|
|
927
|
-
if (
|
|
928
|
-
|
|
1025
|
+
} else {
|
|
1026
|
+
if (url.pathname.endsWith("/")) {
|
|
1027
|
+
url.pathname = url.pathname.slice(0, -1);
|
|
929
1028
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
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
|
-
|
|
948
|
-
|
|
949
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
962
|
-
|
|
963
|
-
|
|
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
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
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
|
-
|
|
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
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
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
|
-
|
|
1003
|
-
|
|
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
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
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 (
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1031
|
-
} catch
|
|
1032
|
-
|
|
1033
|
-
e.loc = qrl.dev;
|
|
1034
|
-
}
|
|
1035
|
-
throw e;
|
|
1380
|
+
return decodeURIComponent(str);
|
|
1381
|
+
} catch {
|
|
1382
|
+
return str;
|
|
1036
1383
|
}
|
|
1037
1384
|
}
|
|
1038
|
-
|
|
1039
|
-
const
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
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
|
-
|
|
1057
|
-
|
|
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
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
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
|
-
|
|
1090
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1140
|
-
|
|
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
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
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
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
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
|
-
|
|
1212
|
-
|
|
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
|
-
|
|
1215
|
-
|
|
1537
|
+
|
|
1538
|
+
async function runNextWithDeps(requestEv, rebuildRouteInfo, resolve, deps) {
|
|
1216
1539
|
try {
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
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
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
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
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
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
|
|
1277
|
-
if (!
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
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
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
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
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
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
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
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();
|