@qwik.dev/router 2.0.0-beta.35 → 2.0.0-beta.37
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/cloudflare-pages/vite/index.mjs +2 -4
- package/lib/adapters/shared/vite/index.mjs +26 -23
- package/lib/chunks/action-handler.mjs +782 -0
- package/lib/chunks/deepFreeze.qwik.mjs +591 -1
- package/lib/chunks/format.mjs +31 -0
- package/lib/chunks/fs.mjs +4 -6
- package/lib/chunks/{routing.qwik.mjs → head.qwik.mjs} +299 -416
- package/lib/chunks/http-error.qwik.mjs +2 -2
- package/lib/chunks/not-found-wrapper.qwik.mjs +2 -2
- package/lib/chunks/pathname.mjs +3 -32
- package/lib/chunks/request-path.mjs +60 -0
- package/lib/chunks/system.mjs +6 -7
- package/lib/chunks/use-functions.qwik.mjs +3 -15
- package/lib/chunks/worker-thread.qwik.mjs +436 -425
- package/lib/index.d.ts +180 -13
- package/lib/index.qwik.mjs +640 -447
- package/lib/middleware/aws-lambda/index.mjs +2 -1
- package/lib/middleware/request-handler/index.d.ts +56 -16
- package/lib/middleware/request-handler/index.mjs +198 -754
- package/lib/ssg/index.d.ts +3 -3
- package/lib/ssg/index.mjs +4 -3
- package/lib/vite/index.d.ts +15 -0
- package/lib/vite/index.mjs +145 -23
- package/package.json +3 -3
- package/lib/chunks/redirect-handler.mjs +0 -6
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
import { isDev as isDev$1 } from '@qwik.dev/core';
|
|
2
|
+
import { o as getRouteLoaderValues, p as loadRouteLoader, x as getLoaderRequestEvent, y as resolveRouteLoaderByHash, z as getRouteLoaderResponse, n as getRouteLoaderCtx, q as setRouteLoaders, F as FULLPATH_HEADER, t as RedirectMessage, S as ServerError } from './deepFreeze.qwik.mjs';
|
|
3
|
+
import { Q as QLoaderId, I as IsQLoader, c as resolveValidInternalFullPathname, b as IsQAction, a as QActionId } from './request-path.mjs';
|
|
4
|
+
import { isDev, _deserialize, _serialize, _verifySerializable } from '@qwik.dev/core/internal';
|
|
5
|
+
|
|
6
|
+
const MAX_CACHE_SIZE = typeof globalThis.__SSR_CACHE_SIZE__ === "number" ? globalThis.__SSR_CACHE_SIZE__ : 50;
|
|
7
|
+
function createLruCache() {
|
|
8
|
+
const cache = /* @__PURE__ */ new Map();
|
|
9
|
+
return {
|
|
10
|
+
get(key) {
|
|
11
|
+
const entry = cache.get(key);
|
|
12
|
+
if (entry !== void 0) {
|
|
13
|
+
cache.delete(key);
|
|
14
|
+
cache.set(key, entry);
|
|
15
|
+
}
|
|
16
|
+
return entry;
|
|
17
|
+
},
|
|
18
|
+
set(key, entry) {
|
|
19
|
+
if (MAX_CACHE_SIZE <= 0) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (cache.size >= MAX_CACHE_SIZE) {
|
|
23
|
+
const firstKey = cache.keys().next().value;
|
|
24
|
+
cache.delete(firstKey);
|
|
25
|
+
}
|
|
26
|
+
cache.set(key, entry);
|
|
27
|
+
},
|
|
28
|
+
clear(key) {
|
|
29
|
+
if (key != null) {
|
|
30
|
+
cache.delete(key);
|
|
31
|
+
} else {
|
|
32
|
+
cache.clear();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const ssrCache = createLruCache();
|
|
38
|
+
const loaderCache = createLruCache();
|
|
39
|
+
function resolveETag(eTagExport, headProps) {
|
|
40
|
+
if (eTagExport === void 0) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
if (typeof eTagExport === "function") {
|
|
44
|
+
return eTagExport(headProps);
|
|
45
|
+
}
|
|
46
|
+
return eTagExport;
|
|
47
|
+
}
|
|
48
|
+
function resolveCacheKey(cacheKeyExport, defaultKey, requestEv, eTag) {
|
|
49
|
+
if (cacheKeyExport === true) {
|
|
50
|
+
return defaultKey(requestEv, eTag);
|
|
51
|
+
}
|
|
52
|
+
if (typeof cacheKeyExport !== "function") {
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
return cacheKeyExport(requestEv, eTag) || "";
|
|
56
|
+
}
|
|
57
|
+
function defaultSsrCacheKey(requestEv, eTag) {
|
|
58
|
+
const status = requestEv.status();
|
|
59
|
+
const pathname = requestEv.url.pathname;
|
|
60
|
+
return eTag ? `${status}|${eTag}|${pathname}` : `${status}|${pathname}`;
|
|
61
|
+
}
|
|
62
|
+
function defaultLoaderCacheKey(requestEv, eTag) {
|
|
63
|
+
const loaderId = requestEv.sharedMap.get(QLoaderId);
|
|
64
|
+
const base = `${requestEv.url.pathname}|${requestEv.url.search}|${loaderId}`;
|
|
65
|
+
return eTag ? `${base}|${eTag}` : base;
|
|
66
|
+
}
|
|
67
|
+
function getCachedSsr(key) {
|
|
68
|
+
return ssrCache.get(key);
|
|
69
|
+
}
|
|
70
|
+
function setCachedSsr(key, entry) {
|
|
71
|
+
ssrCache.set(key, entry);
|
|
72
|
+
}
|
|
73
|
+
function getCachedLoader(key) {
|
|
74
|
+
return loaderCache.get(key);
|
|
75
|
+
}
|
|
76
|
+
function setCachedLoader(key, entry) {
|
|
77
|
+
loaderCache.set(key, entry);
|
|
78
|
+
}
|
|
79
|
+
function clearSsrCache(cacheKey) {
|
|
80
|
+
ssrCache.clear(cacheKey);
|
|
81
|
+
}
|
|
82
|
+
function clearLoaderCache(cacheKey) {
|
|
83
|
+
loaderCache.clear(cacheKey);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const RequestEvMode = /* @__PURE__ */ Symbol("RequestEvMode");
|
|
87
|
+
const RequestEvRoute = /* @__PURE__ */ Symbol("RequestEvRoute");
|
|
88
|
+
const RequestRouteName = "@routeName";
|
|
89
|
+
const RequestEvSharedActionId = "@actionId";
|
|
90
|
+
const RequestEvSharedActionFormData = "@actionFormData";
|
|
91
|
+
const RequestEvSharedNonce = "@nonce";
|
|
92
|
+
const RequestEvIsRewrite = "@rewrite";
|
|
93
|
+
const RequestEvShareServerTiming = "@serverTiming";
|
|
94
|
+
const RequestEvETagCacheKey = "@eTagCacheKey";
|
|
95
|
+
const RequestEvHttpStatusMessage = "@httpStatusMessage";
|
|
96
|
+
function getRequestRoute(requestEv) {
|
|
97
|
+
return requestEv[RequestEvRoute];
|
|
98
|
+
}
|
|
99
|
+
function getRequestMode(requestEv) {
|
|
100
|
+
return requestEv[RequestEvMode];
|
|
101
|
+
}
|
|
102
|
+
const ABORT_INDEX = Number.MAX_SAFE_INTEGER;
|
|
103
|
+
const isDangerousKey = (k) => k === "__proto__" || k === "constructor" || k === "prototype";
|
|
104
|
+
const isArrayIndexKey = (k) => /^(0|[1-9]\d*)$/.test(k);
|
|
105
|
+
const getArrayPaths = (formData) => {
|
|
106
|
+
const arrayCandidates = /* @__PURE__ */ new Map();
|
|
107
|
+
for (const [name] of formData) {
|
|
108
|
+
const keys = name.split(".");
|
|
109
|
+
let hasDangerousKey = false;
|
|
110
|
+
for (const key of keys) {
|
|
111
|
+
if (isDangerousKey(key)) {
|
|
112
|
+
hasDangerousKey = true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (hasDangerousKey) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
let path = "";
|
|
120
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
121
|
+
const key = keys[i];
|
|
122
|
+
if (key.endsWith("[]")) {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
path = path ? `${path}.${key}` : key;
|
|
126
|
+
if (!arrayCandidates.has(path)) {
|
|
127
|
+
arrayCandidates.set(path, true);
|
|
128
|
+
}
|
|
129
|
+
if (!isArrayIndexKey(keys[i + 1])) {
|
|
130
|
+
arrayCandidates.set(path, false);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return new Set(Array.from(arrayCandidates.entries()).filter(([, isArrayPath]) => isArrayPath).map(([path]) => path));
|
|
135
|
+
};
|
|
136
|
+
const formToObj = (formData) => {
|
|
137
|
+
const values = /* @__PURE__ */ Object.create(null);
|
|
138
|
+
const arrayPaths = getArrayPaths(formData);
|
|
139
|
+
for (const [name, value] of formData) {
|
|
140
|
+
const keys = name.split(".");
|
|
141
|
+
let hasDangerousKey = false;
|
|
142
|
+
for (let i = 0; i < keys.length; i++) {
|
|
143
|
+
if (isDangerousKey(keys[i])) {
|
|
144
|
+
hasDangerousKey = true;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (hasDangerousKey) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
let object = values;
|
|
152
|
+
let path = "";
|
|
153
|
+
for (let i = 0; i < keys.length; i++) {
|
|
154
|
+
const key = keys[i];
|
|
155
|
+
if (key.endsWith("[]")) {
|
|
156
|
+
const arrayKey = key.slice(0, -2);
|
|
157
|
+
if (isDangerousKey(arrayKey)) {
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
const existingValue = object[arrayKey];
|
|
161
|
+
if (existingValue !== void 0 && !Array.isArray(existingValue)) {
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
object[arrayKey] = existingValue || [];
|
|
165
|
+
object[arrayKey].push(value);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
if (Array.isArray(object) && !isArrayIndexKey(key)) {
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
if (i < keys.length - 1) {
|
|
172
|
+
path = path ? `${path}.${key}` : key;
|
|
173
|
+
const nextValue = object[key];
|
|
174
|
+
if (nextValue !== void 0) {
|
|
175
|
+
object = nextValue;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
object = object[key] = arrayPaths.has(path) ? [] : /* @__PURE__ */ Object.create(null);
|
|
179
|
+
} else {
|
|
180
|
+
object[key] = value;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return values;
|
|
185
|
+
};
|
|
186
|
+
const parseRequest = async (deps, { request, method, query }, sharedMap) => {
|
|
187
|
+
const type = deps.getContentType(request.headers);
|
|
188
|
+
if (type === "application/x-www-form-urlencoded" || type === "multipart/form-data") {
|
|
189
|
+
const formData = await request.formData();
|
|
190
|
+
sharedMap.set(RequestEvSharedActionFormData, formData);
|
|
191
|
+
return formToObj(formData);
|
|
192
|
+
} else if (type === "application/json") {
|
|
193
|
+
const data = await request.json();
|
|
194
|
+
return data;
|
|
195
|
+
} else if (type === "application/qwik-json") {
|
|
196
|
+
if (method === "GET" && query.has(deps.QDATA_KEY)) {
|
|
197
|
+
const data = query.get(deps.QDATA_KEY);
|
|
198
|
+
if (data) {
|
|
199
|
+
try {
|
|
200
|
+
return _deserialize(decodeURIComponent(data));
|
|
201
|
+
} catch {
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return _deserialize(await request.text());
|
|
206
|
+
}
|
|
207
|
+
return void 0;
|
|
208
|
+
};
|
|
209
|
+
function createRequestEventWithDeps(deps, serverRequestEv, loadedRoute, requestHandlers, basePathname, resolved) {
|
|
210
|
+
const { request, platform, env } = serverRequestEv;
|
|
211
|
+
const sharedMap = /* @__PURE__ */ new Map();
|
|
212
|
+
const cookie = new deps.Cookie(request.headers.get("cookie"));
|
|
213
|
+
const headers = new Headers();
|
|
214
|
+
const url = new URL(request.url);
|
|
215
|
+
let internalRequest = false;
|
|
216
|
+
const recognized = deps.recognizeRequest(url.pathname);
|
|
217
|
+
if (recognized) {
|
|
218
|
+
url.pathname = deps.trimRecognizedInternalPathname(url.pathname, recognized);
|
|
219
|
+
sharedMap.set(recognized.type, true);
|
|
220
|
+
internalRequest = "loader";
|
|
221
|
+
if (recognized.data?.loaderId) {
|
|
222
|
+
sharedMap.set(deps.QLoaderId, recognized.data.loaderId);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const actionId = url.searchParams.get(deps.QACTION_KEY);
|
|
226
|
+
if (actionId) {
|
|
227
|
+
sharedMap.set(deps.IsQAction, true);
|
|
228
|
+
sharedMap.set(deps.QActionId, actionId);
|
|
229
|
+
if (request.method === "POST" && request.headers.get("accept")?.includes("application/json")) {
|
|
230
|
+
internalRequest = "action";
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
let routeModuleIndex = -1;
|
|
234
|
+
let writableStream = null;
|
|
235
|
+
let requestData = void 0;
|
|
236
|
+
let locale = serverRequestEv.locale;
|
|
237
|
+
let status = loadedRoute?.$notFound$ ? 404 : 200;
|
|
238
|
+
const next = async () => {
|
|
239
|
+
routeModuleIndex++;
|
|
240
|
+
while (routeModuleIndex < requestHandlers.length) {
|
|
241
|
+
const moduleRequestHandler = requestHandlers[routeModuleIndex];
|
|
242
|
+
const result = moduleRequestHandler(requestEv);
|
|
243
|
+
if (deps.isPromise(result)) {
|
|
244
|
+
await result;
|
|
245
|
+
}
|
|
246
|
+
routeModuleIndex++;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
const resetRoute = (_loadedRoute, _requestHandlers, _url = url) => {
|
|
250
|
+
loadedRoute = _loadedRoute;
|
|
251
|
+
status = loadedRoute?.$notFound$ ? 404 : 200;
|
|
252
|
+
requestHandlers = _requestHandlers;
|
|
253
|
+
url.pathname = _url.pathname;
|
|
254
|
+
url.search = _url.search;
|
|
255
|
+
routeModuleIndex = -1;
|
|
256
|
+
};
|
|
257
|
+
const check = () => {
|
|
258
|
+
if (writableStream !== null) {
|
|
259
|
+
throw new Error("Response already sent");
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
const send = (statusOrResponse, body) => {
|
|
263
|
+
check();
|
|
264
|
+
if (typeof statusOrResponse === "number") {
|
|
265
|
+
status = statusOrResponse;
|
|
266
|
+
const writableStream2 = requestEv.getWritableStream();
|
|
267
|
+
const writer = writableStream2.getWriter();
|
|
268
|
+
writer.write(typeof body === "string" ? deps.encoder.encode(body) : body);
|
|
269
|
+
writer.close();
|
|
270
|
+
} else {
|
|
271
|
+
status = statusOrResponse.status;
|
|
272
|
+
statusOrResponse.headers.forEach((value, key) => {
|
|
273
|
+
if (key.toLowerCase() === "set-cookie") {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
headers.append(key, value);
|
|
277
|
+
});
|
|
278
|
+
statusOrResponse.headers.getSetCookie().forEach((ck) => {
|
|
279
|
+
const index = ck.indexOf("=");
|
|
280
|
+
if (index === -1) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const key = ck.slice(0, index).trim();
|
|
284
|
+
const value = ck.slice(index + 1).trim();
|
|
285
|
+
cookie.set(key, value);
|
|
286
|
+
});
|
|
287
|
+
if (statusOrResponse.body) {
|
|
288
|
+
const writableStream2 = requestEv.getWritableStream();
|
|
289
|
+
statusOrResponse.body.pipeTo(writableStream2);
|
|
290
|
+
} else {
|
|
291
|
+
requestEv.getWritableStream().getWriter().close();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return exit();
|
|
295
|
+
};
|
|
296
|
+
const exit = (message = new deps.AbortMessage()) => {
|
|
297
|
+
routeModuleIndex = ABORT_INDEX;
|
|
298
|
+
return message;
|
|
299
|
+
};
|
|
300
|
+
const requestEv = {
|
|
301
|
+
[RequestEvMode]: serverRequestEv.mode,
|
|
302
|
+
get [RequestEvRoute]() {
|
|
303
|
+
return loadedRoute;
|
|
304
|
+
},
|
|
305
|
+
cookie,
|
|
306
|
+
headers,
|
|
307
|
+
env,
|
|
308
|
+
method: request.method,
|
|
309
|
+
signal: request.signal,
|
|
310
|
+
originalUrl: new URL(url),
|
|
311
|
+
get params() {
|
|
312
|
+
return loadedRoute?.$params$ ?? {};
|
|
313
|
+
},
|
|
314
|
+
get pathname() {
|
|
315
|
+
return url.pathname;
|
|
316
|
+
},
|
|
317
|
+
platform,
|
|
318
|
+
get query() {
|
|
319
|
+
return url.searchParams;
|
|
320
|
+
},
|
|
321
|
+
request,
|
|
322
|
+
url,
|
|
323
|
+
internalRequest,
|
|
324
|
+
basePathname,
|
|
325
|
+
sharedMap,
|
|
326
|
+
get headersSent() {
|
|
327
|
+
return writableStream !== null;
|
|
328
|
+
},
|
|
329
|
+
get exited() {
|
|
330
|
+
return routeModuleIndex >= ABORT_INDEX;
|
|
331
|
+
},
|
|
332
|
+
get clientConn() {
|
|
333
|
+
return serverRequestEv.getClientConn();
|
|
334
|
+
},
|
|
335
|
+
next,
|
|
336
|
+
resetRoute,
|
|
337
|
+
exit,
|
|
338
|
+
cacheControl: (cacheControl, target = "Cache-Control") => {
|
|
339
|
+
check();
|
|
340
|
+
headers.set(target, deps.createCacheControl(cacheControl));
|
|
341
|
+
},
|
|
342
|
+
resolveValue: async (loaderOrAction) => {
|
|
343
|
+
if (loaderOrAction.__brand === "server_loader") {
|
|
344
|
+
const loaderValues = getRouteLoaderValues(requestEv);
|
|
345
|
+
if (loaderOrAction.__id in loaderValues) {
|
|
346
|
+
return loaderValues[loaderOrAction.__id];
|
|
347
|
+
}
|
|
348
|
+
return loadRouteLoader(loaderOrAction, requestEv);
|
|
349
|
+
}
|
|
350
|
+
return void 0;
|
|
351
|
+
},
|
|
352
|
+
status: (statusCode) => {
|
|
353
|
+
if (typeof statusCode === "number") {
|
|
354
|
+
check();
|
|
355
|
+
status = statusCode;
|
|
356
|
+
return statusCode;
|
|
357
|
+
}
|
|
358
|
+
return status;
|
|
359
|
+
},
|
|
360
|
+
locale: (_locale) => {
|
|
361
|
+
if (typeof _locale === "string") {
|
|
362
|
+
locale = _locale;
|
|
363
|
+
}
|
|
364
|
+
return locale || "";
|
|
365
|
+
},
|
|
366
|
+
error: (statusCode, message) => {
|
|
367
|
+
status = statusCode;
|
|
368
|
+
headers.delete("Cache-Control");
|
|
369
|
+
return new deps.ServerError(statusCode, message);
|
|
370
|
+
},
|
|
371
|
+
redirect: (statusCode, url2) => {
|
|
372
|
+
check();
|
|
373
|
+
status = statusCode;
|
|
374
|
+
if (url2) {
|
|
375
|
+
if (
|
|
376
|
+
// //test.com
|
|
377
|
+
/^\/\//.test(url2) || // /test//path
|
|
378
|
+
/([^:])\/\/+/.test(url2)
|
|
379
|
+
) {
|
|
380
|
+
const fixedURL = url2.replace(/^\/\/+/, "/").replace(/([^:])\/\/+/g, "$1/");
|
|
381
|
+
console.warn(`Redirect URL ${url2} is invalid, fixing to ${fixedURL}`);
|
|
382
|
+
url2 = fixedURL;
|
|
383
|
+
}
|
|
384
|
+
headers.set("Location", url2);
|
|
385
|
+
}
|
|
386
|
+
headers.delete("Cache-Control");
|
|
387
|
+
if (statusCode > 301) {
|
|
388
|
+
headers.set("Cache-Control", "no-store");
|
|
389
|
+
}
|
|
390
|
+
if (internalRequest === "loader") {
|
|
391
|
+
return new deps.RedirectMessage();
|
|
392
|
+
}
|
|
393
|
+
return exit(new deps.RedirectMessage());
|
|
394
|
+
},
|
|
395
|
+
rewrite: (pathname) => {
|
|
396
|
+
check();
|
|
397
|
+
if (pathname.startsWith("http")) {
|
|
398
|
+
throw new deps.ServerError(400, isDev ? "Rewrite does not support absolute urls" : "Bad Request");
|
|
399
|
+
}
|
|
400
|
+
sharedMap.set(RequestEvIsRewrite, true);
|
|
401
|
+
return exit(new deps.RewriteMessage(pathname.replace(/\/+/g, "/")));
|
|
402
|
+
},
|
|
403
|
+
defer: (returnData) => {
|
|
404
|
+
return typeof returnData === "function" ? returnData : () => returnData;
|
|
405
|
+
},
|
|
406
|
+
fail: (statusCode, data) => {
|
|
407
|
+
check();
|
|
408
|
+
status = statusCode;
|
|
409
|
+
headers.delete("Cache-Control");
|
|
410
|
+
return {
|
|
411
|
+
failed: true,
|
|
412
|
+
...data
|
|
413
|
+
};
|
|
414
|
+
},
|
|
415
|
+
text: (statusCode, text) => {
|
|
416
|
+
headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
417
|
+
return send(statusCode, text);
|
|
418
|
+
},
|
|
419
|
+
html: (statusCode, html) => {
|
|
420
|
+
headers.set("Content-Type", "text/html; charset=utf-8");
|
|
421
|
+
return send(statusCode, html);
|
|
422
|
+
},
|
|
423
|
+
parseBody: async () => {
|
|
424
|
+
if (requestData !== void 0) {
|
|
425
|
+
return requestData;
|
|
426
|
+
}
|
|
427
|
+
return requestData = parseRequest(deps, requestEv, sharedMap);
|
|
428
|
+
},
|
|
429
|
+
json: (statusCode, data) => {
|
|
430
|
+
headers.set("Content-Type", "application/json; charset=utf-8");
|
|
431
|
+
return send(statusCode, JSON.stringify(data));
|
|
432
|
+
},
|
|
433
|
+
send,
|
|
434
|
+
isDirty: () => {
|
|
435
|
+
return writableStream !== null;
|
|
436
|
+
},
|
|
437
|
+
getWritableStream: () => {
|
|
438
|
+
if (writableStream === null) {
|
|
439
|
+
if (isDev) {
|
|
440
|
+
const serverTiming = sharedMap.get(RequestEvShareServerTiming);
|
|
441
|
+
if (serverTiming) {
|
|
442
|
+
headers.set("Server-Timing", serverTiming.map(([name, duration]) => `${name};dur=${duration}`).join(","));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
writableStream = serverRequestEv.getWritableStream(status, headers, cookie, resolved, requestEv);
|
|
446
|
+
}
|
|
447
|
+
return writableStream;
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
return requestEv;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function hash(str) {
|
|
454
|
+
let hash2 = 2166136261;
|
|
455
|
+
for (let i = 0; i < str.length; i++) {
|
|
456
|
+
hash2 ^= str.charCodeAt(i);
|
|
457
|
+
hash2 = hash2 * 16777619 | 0;
|
|
458
|
+
}
|
|
459
|
+
return (hash2 >>> 0).toString(36);
|
|
460
|
+
}
|
|
461
|
+
function normalizeETag(raw) {
|
|
462
|
+
let tag = raw.trim();
|
|
463
|
+
if (tag.endsWith('"') && tag.startsWith('W/"')) {
|
|
464
|
+
tag = tag.slice(3);
|
|
465
|
+
}
|
|
466
|
+
tag = tag.replace(/[^!#-~]/g, "");
|
|
467
|
+
return tag;
|
|
468
|
+
}
|
|
469
|
+
function setETagHeader(requestEv, normalizedETag) {
|
|
470
|
+
const headerETag = `"${normalizedETag}"`;
|
|
471
|
+
requestEv.headers.set("ETag", headerETag);
|
|
472
|
+
}
|
|
473
|
+
function performETagMatch(requestEv, normalizedETag) {
|
|
474
|
+
setETagHeader(requestEv, normalizedETag);
|
|
475
|
+
const ifNoneMatch = requestEv.request.headers.get("If-None-Match");
|
|
476
|
+
if (ifNoneMatch) {
|
|
477
|
+
let found = ifNoneMatch.trim() === "*";
|
|
478
|
+
if (!found) {
|
|
479
|
+
for (const entry of ifNoneMatch.split(",")) {
|
|
480
|
+
const normalizedEntry = normalizeETag(entry);
|
|
481
|
+
if (normalizedEntry === normalizedETag) {
|
|
482
|
+
found = true;
|
|
483
|
+
break;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (found) {
|
|
488
|
+
requestEv.status(304);
|
|
489
|
+
requestEv.send(304, "");
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function createLoaderRequestEventFactory(requestEv) {
|
|
497
|
+
return (loader) => getLoaderRequestEvent(loader, requestEv);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function setLoaderData(requestEv, routeLoaders, loaderPaths) {
|
|
501
|
+
if (loaderPaths) {
|
|
502
|
+
Object.assign(getRouteLoaderCtx(requestEv).loaderPaths, loaderPaths);
|
|
503
|
+
}
|
|
504
|
+
setRouteLoaders(requestEv, routeLoaders);
|
|
505
|
+
}
|
|
506
|
+
function resolvePreETag(eTagOption, requestEv) {
|
|
507
|
+
if (typeof eTagOption === "string") {
|
|
508
|
+
return normalizeETag(eTagOption);
|
|
509
|
+
}
|
|
510
|
+
const result = eTagOption(requestEv);
|
|
511
|
+
return result ? normalizeETag(result) : "";
|
|
512
|
+
}
|
|
513
|
+
function addVaryHeader(requestEv, value) {
|
|
514
|
+
const vary = requestEv.headers.get("Vary");
|
|
515
|
+
if (!vary) {
|
|
516
|
+
requestEv.headers.set("Vary", value);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const existing = vary.split(",").map((item) => item.trim().toLowerCase());
|
|
520
|
+
if (!existing.includes(value.toLowerCase())) {
|
|
521
|
+
requestEv.headers.set("Vary", `${vary}, ${value}`);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
async function sendLoaderResponse(requestEv, data, loader) {
|
|
525
|
+
requestEv.headers.set("Content-Type", "application/json; charset=utf-8");
|
|
526
|
+
addVaryHeader(requestEv, FULLPATH_HEADER);
|
|
527
|
+
if (loader?.__expires && loader.__expires > 0) {
|
|
528
|
+
requestEv.cacheControl({
|
|
529
|
+
maxAge: Math.ceil(loader.__expires / 1e3)
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
requestEv.send(200, data);
|
|
533
|
+
}
|
|
534
|
+
function loaderHandler(routeLoaders, loaderPaths) {
|
|
535
|
+
return async (requestEvent) => {
|
|
536
|
+
const requestEv = requestEvent;
|
|
537
|
+
if (!requestEv.sharedMap.has(IsQLoader)) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
if (requestEv.headersSent || requestEv.exited) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const loaderId = requestEv.sharedMap.get(QLoaderId);
|
|
544
|
+
const loader = resolveRouteLoaderByHash(routeLoaders, loaderId);
|
|
545
|
+
if (!loader) {
|
|
546
|
+
requestEv.json(404, {
|
|
547
|
+
error: "Loader not found"
|
|
548
|
+
});
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
setLoaderData(requestEv, routeLoaders, loaderPaths);
|
|
552
|
+
const loaderRequestEv = createLoaderRequestEventFactory(requestEv)(loader);
|
|
553
|
+
const normalizedETag = loader.__eTag !== void 0 ? resolvePreETag(loader.__eTag, loaderRequestEv) : "";
|
|
554
|
+
if (normalizedETag && performETagMatch(loaderRequestEv, normalizedETag)) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
let cacheKey = "";
|
|
558
|
+
if (loader.__cacheKey) {
|
|
559
|
+
cacheKey = resolveCacheKey(loader.__cacheKey, defaultLoaderCacheKey, loaderRequestEv, normalizedETag);
|
|
560
|
+
}
|
|
561
|
+
if (cacheKey) {
|
|
562
|
+
const cached = getCachedLoader(cacheKey);
|
|
563
|
+
if (cached) {
|
|
564
|
+
if (!normalizedETag) {
|
|
565
|
+
if (performETagMatch(loaderRequestEv, cached.eTag)) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
} else {
|
|
569
|
+
setETagHeader(loaderRequestEv, cached.eTag);
|
|
570
|
+
}
|
|
571
|
+
await sendLoaderResponse(requestEv, cached.body, loader);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
const responseData = await getRouteLoaderResponse(loader.__qrl, loader.__validators, loaderRequestEv);
|
|
576
|
+
const data = await _serialize(responseData);
|
|
577
|
+
const cacheable = cacheKey && !responseData.r && !responseData.e;
|
|
578
|
+
const finalETag = normalizedETag || (cacheable ? hash(data) : "");
|
|
579
|
+
if (cacheable && finalETag) {
|
|
580
|
+
setCachedLoader(cacheKey, {
|
|
581
|
+
eTag: finalETag,
|
|
582
|
+
body: data
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
if (!normalizedETag && finalETag && performETagMatch(loaderRequestEv, finalETag)) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
await sendLoaderResponse(requestEv, data, loader);
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
async function sendJsonResponse(requestEv, responseData, status = 200) {
|
|
592
|
+
const data = await _serialize(responseData);
|
|
593
|
+
requestEv.headers.set("Content-Type", "application/json; charset=utf-8");
|
|
594
|
+
requestEv.send(status, data);
|
|
595
|
+
}
|
|
596
|
+
async function sendActionResponse(requestEv, responseData) {
|
|
597
|
+
const data = await _serialize(responseData);
|
|
598
|
+
requestEv.headers.set("Content-Type", "application/json; charset=utf-8");
|
|
599
|
+
requestEv.send(responseData.s || 200, data);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function jsonRequestWrapper() {
|
|
603
|
+
return async (requestEvent) => {
|
|
604
|
+
const requestEv = requestEvent;
|
|
605
|
+
const isLoader = requestEv.internalRequest === "loader";
|
|
606
|
+
const isActionJson = requestEv.internalRequest === "action";
|
|
607
|
+
if (!isLoader && !isActionJson) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
if (isLoader) {
|
|
611
|
+
addVaryHeader(requestEv, FULLPATH_HEADER);
|
|
612
|
+
const pagePath = requestEv.request.headers.get(FULLPATH_HEADER);
|
|
613
|
+
const pagePathname = resolveValidInternalFullPathname(requestEv.url.pathname, pagePath);
|
|
614
|
+
if (pagePathname) {
|
|
615
|
+
requestEv.url.pathname = pagePathname;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
try {
|
|
619
|
+
await requestEv.next();
|
|
620
|
+
} catch (err) {
|
|
621
|
+
if (requestEv.headersSent) {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
if (err instanceof RedirectMessage) {
|
|
625
|
+
if (isLoader) {
|
|
626
|
+
const location = requestEv.headers.get("Location") || "/";
|
|
627
|
+
requestEv.headers.delete("Location");
|
|
628
|
+
await sendJsonResponse(requestEv, {
|
|
629
|
+
r: location
|
|
630
|
+
});
|
|
631
|
+
} else {
|
|
632
|
+
throw err;
|
|
633
|
+
}
|
|
634
|
+
} else if (err instanceof ServerError) {
|
|
635
|
+
if (isLoader) {
|
|
636
|
+
await sendJsonResponse(requestEv, {
|
|
637
|
+
e: err
|
|
638
|
+
});
|
|
639
|
+
} else {
|
|
640
|
+
await sendActionResponse(requestEv, {
|
|
641
|
+
e: err,
|
|
642
|
+
s: err.status
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
} else if (err instanceof Error) {
|
|
646
|
+
console.error("JSON request error:", err);
|
|
647
|
+
const message = isDev$1 ? `${err.message}
|
|
648
|
+
(this is only visible in dev mode)` : "Internal Server Error";
|
|
649
|
+
const se = new ServerError(500, message);
|
|
650
|
+
if (isLoader) {
|
|
651
|
+
await sendJsonResponse(requestEv, {
|
|
652
|
+
e: se
|
|
653
|
+
});
|
|
654
|
+
} else {
|
|
655
|
+
await sendActionResponse(requestEv, {
|
|
656
|
+
e: se,
|
|
657
|
+
s: 500
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
} else {
|
|
661
|
+
throw err;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function verifySerializable(data, qrl) {
|
|
668
|
+
try {
|
|
669
|
+
_verifySerializable(data, void 0);
|
|
670
|
+
} catch (e) {
|
|
671
|
+
if (e instanceof Error && qrl.dev) {
|
|
672
|
+
e.loc = qrl.dev;
|
|
673
|
+
}
|
|
674
|
+
throw e;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
async function measure(requestEv, name, fn) {
|
|
678
|
+
const start = performance.now();
|
|
679
|
+
try {
|
|
680
|
+
return await fn();
|
|
681
|
+
} finally {
|
|
682
|
+
const duration = performance.now() - start;
|
|
683
|
+
let measurements = requestEv.sharedMap.get("@serverTiming");
|
|
684
|
+
if (!measurements) {
|
|
685
|
+
requestEv.sharedMap.set("@serverTiming", measurements = []);
|
|
686
|
+
}
|
|
687
|
+
measurements.push([
|
|
688
|
+
name,
|
|
689
|
+
duration
|
|
690
|
+
]);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
async function runValidators(requestEv, validators, data, devMode) {
|
|
694
|
+
let lastResult = {
|
|
695
|
+
success: true,
|
|
696
|
+
data
|
|
697
|
+
};
|
|
698
|
+
if (validators) {
|
|
699
|
+
for (const validator of validators) {
|
|
700
|
+
if (devMode) {
|
|
701
|
+
lastResult = await measure(requestEv, `validator$`, () => validator.validate(requestEv, data));
|
|
702
|
+
} else {
|
|
703
|
+
lastResult = await validator.validate(requestEv, data);
|
|
704
|
+
}
|
|
705
|
+
if (!lastResult.success) {
|
|
706
|
+
return lastResult;
|
|
707
|
+
} else {
|
|
708
|
+
data = lastResult.data;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return lastResult;
|
|
713
|
+
}
|
|
714
|
+
function actionHandler(routeActions) {
|
|
715
|
+
return async (requestEvent) => {
|
|
716
|
+
const requestEv = requestEvent;
|
|
717
|
+
if (!requestEv.sharedMap.has(IsQAction)) {
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
if (!requestEv.request.headers.get("accept")?.includes("application/json")) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
if (requestEv.headersSent || requestEv.exited) {
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const actionId = requestEv.sharedMap.get(QActionId);
|
|
727
|
+
const method = requestEv.method;
|
|
728
|
+
const devMode = isDev;
|
|
729
|
+
if (devMode && method === "GET") {
|
|
730
|
+
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">');
|
|
731
|
+
}
|
|
732
|
+
if (method !== "POST") {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
let action;
|
|
736
|
+
for (const routeAction of routeActions) {
|
|
737
|
+
if (routeAction.__id === actionId) {
|
|
738
|
+
action = routeAction;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
if (!action) {
|
|
742
|
+
const serverActionsMap = globalThis._qwikActionsMap;
|
|
743
|
+
action = serverActionsMap?.get(actionId);
|
|
744
|
+
}
|
|
745
|
+
if (!action) {
|
|
746
|
+
requestEv.json(404, {
|
|
747
|
+
error: "Action not found"
|
|
748
|
+
});
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
const data = await requestEv.parseBody();
|
|
752
|
+
if (!data || typeof data !== "object") {
|
|
753
|
+
throw new Error(`Expected request data for the action id ${actionId} to be an object`);
|
|
754
|
+
}
|
|
755
|
+
let actionResult;
|
|
756
|
+
const result = await runValidators(requestEv, action.__validators, data, devMode);
|
|
757
|
+
if (!result.success) {
|
|
758
|
+
actionResult = requestEv.fail(result.status ?? 500, result.error);
|
|
759
|
+
} else {
|
|
760
|
+
const actionResolved = devMode ? await measure(requestEv, action.__qrl.getHash(), () => action.__qrl.call(requestEv, result.data, requestEv)) : await action.__qrl.call(requestEv, result.data, requestEv);
|
|
761
|
+
if (devMode) {
|
|
762
|
+
verifySerializable(actionResolved, action.__qrl);
|
|
763
|
+
}
|
|
764
|
+
actionResult = actionResolved;
|
|
765
|
+
}
|
|
766
|
+
const responseData = {
|
|
767
|
+
result: actionResult
|
|
768
|
+
};
|
|
769
|
+
requestEv.sharedMap.set(RequestEvSharedActionId, actionId);
|
|
770
|
+
requestEv.sharedMap.set("@actionResult", actionResult);
|
|
771
|
+
if (action.__invalidate) {
|
|
772
|
+
responseData.loaderHashes = action.__invalidate;
|
|
773
|
+
} else if (globalThis.__STRICT_LOADERS__) {
|
|
774
|
+
responseData.loaderHashes = [];
|
|
775
|
+
}
|
|
776
|
+
const serialized = await _serialize(responseData);
|
|
777
|
+
requestEv.headers.set("Content-Type", "application/json; charset=utf-8");
|
|
778
|
+
requestEv.send(requestEv.status(), serialized);
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
export { MAX_CACHE_SIZE as M, RequestRouteName as R, getRequestMode as a, RequestEvSharedNonce as b, RequestEvSharedActionId as c, RequestEvSharedActionFormData as d, RequestEvHttpStatusMessage as e, actionHandler as f, getRequestRoute as g, hash as h, setCachedLoader as i, jsonRequestWrapper as j, setCachedSsr as k, loaderHandler as l, resolveCacheKey as m, normalizeETag as n, getCachedLoader as o, performETagMatch as p, getCachedSsr as q, resolveETag as r, setETagHeader as s, defaultLoaderCacheKey as t, defaultSsrCacheKey as u, RequestEvShareServerTiming as v, RequestEvETagCacheKey as w, createRequestEventWithDeps as x, clearLoaderCache as y, clearSsrCache as z };
|