@kasperpeulen/vitest-plugin-nextjs 0.0.0-alpha.0 → 0.1.0-alpha.0
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/README.md +249 -2
- package/dist/_virtual/_rolldown/runtime.js +14 -0
- package/dist/action-ids.d.ts +18 -0
- package/dist/action-ids.js +44 -0
- package/dist/adapter.d.ts +8 -0
- package/dist/adapter.js +12 -0
- package/dist/app-requests.d.ts +63 -0
- package/dist/app-requests.js +253 -0
- package/dist/browser-context.d.ts +24 -0
- package/dist/browser-context.js +157 -0
- package/dist/build-output.d.ts +68 -0
- package/dist/build-output.js +216 -0
- package/dist/build.d.ts +26 -0
- package/dist/build.js +39 -0
- package/dist/built-files.d.ts +9 -0
- package/dist/built-files.js +22 -0
- package/dist/cache.d.ts +44 -0
- package/dist/cache.js +99 -0
- package/dist/client-components.d.ts +3 -0
- package/dist/client-components.js +4 -0
- package/dist/client-file-key.d.ts +5 -0
- package/dist/client-file-key.js +5 -0
- package/dist/client-files.d.ts +83 -0
- package/dist/client-files.js +186 -0
- package/dist/client-graph.d.ts +94 -0
- package/dist/client-graph.js +266 -0
- package/dist/client-modules.d.ts +10 -0
- package/dist/client-modules.js +43 -0
- package/dist/client.d.ts +42 -0
- package/dist/client.js +263 -0
- package/dist/commonjs.d.ts +44 -0
- package/dist/commonjs.js +111 -0
- package/dist/compat/browser/client.d.ts +19 -0
- package/dist/compat/browser/client.js +13 -0
- package/dist/compat/build.d.ts +145 -0
- package/dist/compat/build.js +149 -0
- package/dist/compat/checks.d.ts +16 -0
- package/dist/compat/checks.js +148 -0
- package/dist/compat/patches.d.ts +13 -0
- package/dist/compat/patches.js +30 -0
- package/dist/compat/paths.d.ts +74 -0
- package/dist/compat/paths.js +160 -0
- package/dist/compat/rsc/handlers.d.ts +2 -0
- package/dist/compat/rsc/handlers.js +2 -0
- package/dist/compat/rsc/prerender.d.ts +10 -0
- package/dist/compat/rsc/prerender.js +9 -0
- package/dist/compat/server/headers.d.ts +3 -0
- package/dist/compat/server/headers.js +3 -0
- package/dist/compat/ssr/build-output.d.ts +4 -0
- package/dist/compat/ssr/build-output.js +3 -0
- package/dist/compat/ssr/cache.d.ts +6 -0
- package/dist/compat/ssr/cache.js +6 -0
- package/dist/compat/ssr/handler.d.ts +8 -0
- package/dist/compat/ssr/handler.js +8 -0
- package/dist/compat/ssr/node-server.d.ts +5 -0
- package/dist/compat/ssr/node-server.js +4 -0
- package/dist/compat/ssr/render-errors.d.ts +4 -0
- package/dist/compat/ssr/render-errors.js +4 -0
- package/dist/compat/ssr/senders.d.ts +6 -0
- package/dist/compat/ssr/senders.js +6 -0
- package/dist/compat/types.d.ts +8 -0
- package/dist/compat/types.js +1 -0
- package/dist/compile.d.ts +31 -0
- package/dist/compile.js +140 -0
- package/dist/disk.d.ts +16 -0
- package/dist/disk.js +13 -0
- package/dist/document-loads.d.ts +15 -0
- package/dist/document-loads.js +53 -0
- package/dist/document.d.ts +79 -0
- package/dist/document.js +328 -0
- package/dist/downloads.d.ts +15 -0
- package/dist/downloads.js +71 -0
- package/dist/esm-require.d.ts +11 -0
- package/dist/esm-require.js +32 -0
- package/dist/fast-crypto/index.js +1093 -0
- package/dist/fast-crypto.d.ts +20 -0
- package/dist/fast-crypto.js +151 -0
- package/dist/flight.d.ts +19 -0
- package/dist/flight.js +42 -0
- package/dist/form-submission.d.ts +15 -0
- package/dist/form-submission.js +27 -0
- package/dist/from-page.d.ts +5 -0
- package/dist/from-page.js +44 -0
- package/dist/globals.d.ts +1 -0
- package/dist/globals.js +197 -0
- package/dist/host-document.d.ts +26 -0
- package/dist/host-document.js +34 -0
- package/dist/index.d.ts +280 -0
- package/dist/index.js +281 -0
- package/dist/internal.d.ts +2 -0
- package/dist/internal.js +3 -0
- package/dist/leftovers.d.ts +11 -0
- package/dist/leftovers.js +55 -0
- package/dist/mode.d.ts +169 -0
- package/dist/mode.js +31 -0
- package/dist/node/LICENSES.md +3540 -0
- package/dist/node/assert/strict.js +29 -0
- package/dist/node/assert.js +3044 -0
- package/dist/node/buffer.js +1499 -0
- package/dist/node/child_process.js +45 -0
- package/dist/node/cluster.js +85 -0
- package/dist/node/console.js +96 -0
- package/dist/node/constants.js +495 -0
- package/dist/node/crypto.js +1388 -0
- package/dist/node/dgram.js +78 -0
- package/dist/node/diagnostics_channel.js +122 -0
- package/dist/node/dns/promises.js +129 -0
- package/dist/node/dns.js +137 -0
- package/dist/node/domain.js +43 -0
- package/dist/node/events.js +1155 -0
- package/dist/node/fs/promises.js +40 -0
- package/dist/node/fs.js +10375 -0
- package/dist/node/http.js +326 -0
- package/dist/node/http2.js +305 -0
- package/dist/node/https.js +57 -0
- package/dist/node/inspector/promises.js +66 -0
- package/dist/node/inspector.js +76 -0
- package/dist/node/module.js +205 -0
- package/dist/node/net.js +392 -0
- package/dist/node/os.js +259 -0
- package/dist/node/path/posix.js +25 -0
- package/dist/node/path/win32.js +25 -0
- package/dist/node/path.js +384 -0
- package/dist/node/perf_hooks.js +313 -0
- package/dist/node/process.js +452 -0
- package/dist/node/punycode.js +178 -0
- package/dist/node/querystring.js +934 -0
- package/dist/node/readline/promises.js +71 -0
- package/dist/node/readline.js +56 -0
- package/dist/node/repl.js +44 -0
- package/dist/node/sqlite.js +22 -0
- package/dist/node/stream/consumers.js +47 -0
- package/dist/node/stream/promises.js +6 -0
- package/dist/node/stream/web.js +52 -0
- package/dist/node/stream.js +4323 -0
- package/dist/node/string_decoder.js +276 -0
- package/dist/node/sys.js +6 -0
- package/dist/node/timers/promises.js +71 -0
- package/dist/node/timers.js +133 -0
- package/dist/node/tls.js +122 -0
- package/dist/node/trace_events.js +23 -0
- package/dist/node/tty.js +69 -0
- package/dist/node/url.js +1426 -0
- package/dist/node/util/types.js +1456 -0
- package/dist/node/util.js +4807 -0
- package/dist/node/v8.js +160 -0
- package/dist/node/vm.js +88 -0
- package/dist/node/wasi.js +16 -0
- package/dist/node/worker_threads.js +123 -0
- package/dist/node/zlib.js +1396 -0
- package/dist/node-builtins.d.ts +40 -0
- package/dist/node-builtins.js +74 -0
- package/dist/node-on-page.d.ts +46 -0
- package/dist/node-on-page.js +101 -0
- package/dist/node-platform.d.ts +36 -0
- package/dist/node-platform.js +152 -0
- package/dist/node-rendered.d.ts +21 -0
- package/dist/node-rendered.js +26 -0
- package/dist/node-server.d.ts +46 -0
- package/dist/node-server.js +193 -0
- package/dist/optimizer-cache-key.d.ts +26 -0
- package/dist/optimizer-cache-key.js +73 -0
- package/dist/own-package.d.ts +1 -0
- package/dist/own-package.js +6 -0
- package/dist/page-store.d.ts +33 -0
- package/dist/page-store.js +80 -0
- package/dist/page-timers.d.ts +22 -0
- package/dist/page-timers.js +47 -0
- package/dist/page.d.ts +112 -0
- package/dist/page.js +549 -0
- package/dist/paths.d.ts +13 -0
- package/dist/paths.js +47 -0
- package/dist/plugin.d.ts +22 -0
- package/dist/plugin.js +995 -0
- package/dist/prerender-verdict.d.ts +8 -0
- package/dist/prerender-verdict.js +67 -0
- package/dist/project/context.d.ts +546 -0
- package/dist/project/context.js +111 -0
- package/dist/project/entries.d.ts +42 -0
- package/dist/project/entries.js +189 -0
- package/dist/project/env-worker.d.ts +1 -0
- package/dist/project/env-worker.js +9 -0
- package/dist/project/env.d.ts +36 -0
- package/dist/project/env.js +82 -0
- package/dist/project/layers.d.ts +31 -0
- package/dist/project/layers.js +150 -0
- package/dist/project/loaders.d.ts +71 -0
- package/dist/project/loaders.js +300 -0
- package/dist/project/metadata.d.ts +8 -0
- package/dist/project/metadata.js +34 -0
- package/dist/project/module-exports.d.ts +29 -0
- package/dist/project/module-exports.js +77 -0
- package/dist/project/routes.d.ts +19 -0
- package/dist/project/routes.js +92 -0
- package/dist/project/routing.d.ts +46 -0
- package/dist/project/routing.js +280 -0
- package/dist/project/transform.d.ts +8 -0
- package/dist/project/transform.js +110 -0
- package/dist/project/webpack.d.ts +46 -0
- package/dist/project/webpack.js +160 -0
- package/dist/project.d.ts +264 -0
- package/dist/project.js +58 -0
- package/dist/registry-global.d.ts +5 -0
- package/dist/registry-global.js +5 -0
- package/dist/registry.d.ts +241 -0
- package/dist/registry.js +30 -0
- package/dist/route-chunks.d.ts +13 -0
- package/dist/route-chunks.js +32 -0
- package/dist/route-lookup.d.ts +34 -0
- package/dist/route-lookup.js +207 -0
- package/dist/routes-ahead-shared.d.ts +17 -0
- package/dist/routes-ahead-shared.js +9 -0
- package/dist/routes-ahead.d.ts +10 -0
- package/dist/routes-ahead.js +54 -0
- package/dist/rsc.d.ts +19 -0
- package/dist/rsc.js +171 -0
- package/dist/server-action.d.ts +9 -0
- package/dist/server-action.js +15 -0
- package/dist/server-code.d.ts +176 -0
- package/dist/server-code.js +357 -0
- package/dist/server-in-front.d.ts +37 -0
- package/dist/server-in-front.js +90 -0
- package/dist/server-layers.d.ts +22 -0
- package/dist/server-layers.js +33 -0
- package/dist/setup-browser.d.ts +1 -0
- package/dist/setup-browser.js +8 -0
- package/dist/setup-node.d.ts +1 -0
- package/dist/setup-node.js +6 -0
- package/dist/setup.d.ts +8 -0
- package/dist/setup.js +62 -0
- package/dist/speed-features.d.ts +13 -0
- package/dist/speed-features.js +95 -0
- package/dist/ssr.d.ts +70 -0
- package/dist/ssr.js +480 -0
- package/dist/static-build.d.ts +36 -0
- package/dist/static-build.js +92 -0
- package/dist/storage.d.ts +31 -0
- package/dist/storage.js +94 -0
- package/dist/styles-shared.d.ts +137 -0
- package/dist/styles-shared.js +124 -0
- package/dist/styles.d.ts +102 -0
- package/dist/styles.js +730 -0
- package/dist/stylesheet-versions.d.ts +17 -0
- package/dist/stylesheet-versions.js +43 -0
- package/dist/tester.html +10 -0
- package/dist/use-cache.d.ts +15 -0
- package/dist/use-cache.js +93 -0
- package/dist/vitest-version.d.ts +6 -0
- package/dist/vitest-version.js +12 -0
- package/package.json +125 -4
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { registry } from "./registry.js";
|
|
2
|
+
import "./globals.js";
|
|
3
|
+
import { speedFeature } from "./speed-features.js";
|
|
4
|
+
import { PageStore } from "./page-store.js";
|
|
5
|
+
import { takesRequest } from "./server-in-front.js";
|
|
6
|
+
import { loadedSsrLayer, ssrLayer } from "./server-layers.js";
|
|
7
|
+
//#region src/app-requests.ts
|
|
8
|
+
const { Headers, Request: ServerNativeRequest } = globalThis;
|
|
9
|
+
const isRequest = (value) => value instanceof ServerNativeRequest;
|
|
10
|
+
async function handledByServer(...[request, options]) {
|
|
11
|
+
const layer = loadedSsrLayer() ?? await ssrLayer();
|
|
12
|
+
request.signal?.throwIfAborted();
|
|
13
|
+
return layer.handleRequest(request, options);
|
|
14
|
+
}
|
|
15
|
+
const nativeFetch = registry.network;
|
|
16
|
+
async function networkFetch(input, init) {
|
|
17
|
+
if (!init?.body && !(isRequest(input) && input.body)) return nativeFetch(input, init);
|
|
18
|
+
const request = new ServerNativeRequest(input, init);
|
|
19
|
+
return nativeFetch(new ServerNativeRequest(request, { body: await request.arrayBuffer() }));
|
|
20
|
+
}
|
|
21
|
+
function sameOriginRequest(input, init) {
|
|
22
|
+
const request = isRequest(input) ? input : void 0;
|
|
23
|
+
const url = new URL(request ? request.url : String(input), window.location.href);
|
|
24
|
+
if (url.origin !== window.location.origin || url.protocol === "blob:") return;
|
|
25
|
+
const headers = new Headers(init?.headers ?? request?.headers);
|
|
26
|
+
return {
|
|
27
|
+
url,
|
|
28
|
+
method: (init?.method ?? request?.method ?? "GET").toUpperCase(),
|
|
29
|
+
headers,
|
|
30
|
+
marked: headers.has("rsc") || headers.has("next-action")
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
let page;
|
|
34
|
+
let routerOutlivesPage = false;
|
|
35
|
+
/** Says which page of the app is open, by its signal, which aborts as it is left. */
|
|
36
|
+
function setOpenPage(signal) {
|
|
37
|
+
page = signal;
|
|
38
|
+
}
|
|
39
|
+
/** Says whether the router of the pages outlives them, from now on. */
|
|
40
|
+
function setRouterOutlivesPage(outlives) {
|
|
41
|
+
routerOutlivesPage = outlives;
|
|
42
|
+
}
|
|
43
|
+
const pageLeft = () => new DOMException("The page was left.", "AbortError");
|
|
44
|
+
const failsAsPageLeft = (action) => routerOutlivesPage && !action;
|
|
45
|
+
function ofLeftPage(action) {
|
|
46
|
+
return failsAsPageLeft(action) ? Promise.reject(pageLeft()) : never();
|
|
47
|
+
}
|
|
48
|
+
function browserHeaders(headers, url, method, from, credentials = true) {
|
|
49
|
+
headers.set("host", url.host);
|
|
50
|
+
from.headers.forEach((value, name) => {
|
|
51
|
+
if (!headers.has(name)) headers.set(name, value);
|
|
52
|
+
});
|
|
53
|
+
if (!headers.has("user-agent")) headers.set("user-agent", navigator.userAgent);
|
|
54
|
+
if (method !== "GET" && method !== "HEAD" && !headers.has("origin")) headers.set("origin", url.origin);
|
|
55
|
+
if (credentials && !headers.has("cookie")) {
|
|
56
|
+
const cookie = from.cookieHeader();
|
|
57
|
+
if (cookie) headers.set("cookie", cookie);
|
|
58
|
+
}
|
|
59
|
+
return headers;
|
|
60
|
+
}
|
|
61
|
+
function leftTheApp(url) {
|
|
62
|
+
return /* @__PURE__ */ new Error(`vitest-plugin-rsc: the app navigated to another origin: ${url.href}. A browser would leave the app for it, which a test cannot do.`);
|
|
63
|
+
}
|
|
64
|
+
function unlessAborted(answer, signal, abandon) {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const abort = () => {
|
|
67
|
+
reject(signal.reason);
|
|
68
|
+
answer.catch(() => {});
|
|
69
|
+
abandon(answer);
|
|
70
|
+
};
|
|
71
|
+
if (signal.aborted) return abort();
|
|
72
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
73
|
+
answer.then((response) => {
|
|
74
|
+
signal.removeEventListener("abort", abort);
|
|
75
|
+
resolve(response);
|
|
76
|
+
}, (error) => {
|
|
77
|
+
signal.removeEventListener("abort", abort);
|
|
78
|
+
reject(error);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async function sendRequest(request, { from, credentials = true, navigation, network, opened }) {
|
|
83
|
+
let url = new URL(request.url);
|
|
84
|
+
let method = request.method;
|
|
85
|
+
let sent = request.headers;
|
|
86
|
+
const own = from && credentials && sent.has("cookie") ? new PageStore() : void 0;
|
|
87
|
+
own?.seed(sent.get("cookie"));
|
|
88
|
+
let body = request.body ? new Uint8Array(await request.arrayBuffer()) : null;
|
|
89
|
+
let redirected = false;
|
|
90
|
+
let redirects = 0;
|
|
91
|
+
for (;;) {
|
|
92
|
+
const headers = from ? browserHeaders(new Headers(sent), url, method, from, credentials) : new Headers(sent);
|
|
93
|
+
const appOpened = opened ? {
|
|
94
|
+
...opened,
|
|
95
|
+
pathname: url.pathname
|
|
96
|
+
} : null;
|
|
97
|
+
const response = await unlessAborted(handledByServer({
|
|
98
|
+
url: url.href,
|
|
99
|
+
method,
|
|
100
|
+
headers,
|
|
101
|
+
body,
|
|
102
|
+
signal: request.signal
|
|
103
|
+
}, {
|
|
104
|
+
nested: !from,
|
|
105
|
+
unrouted: network ? "pass" : "not-found",
|
|
106
|
+
opened: navigation === "app" ? appOpened : redirected ? void 0 : opened,
|
|
107
|
+
pageLoad: navigation === "test"
|
|
108
|
+
}), request.signal, (answer) => void answer.then((late) => late?.body?.cancel()).catch(() => {}));
|
|
109
|
+
if (!response) return redirected ? nativeFetch(url, {
|
|
110
|
+
method,
|
|
111
|
+
headers: sent,
|
|
112
|
+
body,
|
|
113
|
+
signal: request.signal
|
|
114
|
+
}) : network();
|
|
115
|
+
if (credentials) from?.receive(response.headers.getSetCookie());
|
|
116
|
+
const location = response.headers.get("location");
|
|
117
|
+
if (response.status >= 300 && response.status < 400 && location) {
|
|
118
|
+
if (request.redirect === "manual") {
|
|
119
|
+
Object.defineProperties(response, { url: { value: url.href } });
|
|
120
|
+
return response;
|
|
121
|
+
}
|
|
122
|
+
if (request.redirect === "error") throw new TypeError("fetch failed", { cause: /* @__PURE__ */ new Error(`unexpected redirect of ${url.href} to ${location}`) });
|
|
123
|
+
if (!redirected) sent = new Headers(sent);
|
|
124
|
+
url = new URL(location, url);
|
|
125
|
+
const { status } = response;
|
|
126
|
+
if ((status === 301 || status === 302) && method === "POST" || status === 303 && method !== "GET" && method !== "HEAD") {
|
|
127
|
+
method = "GET";
|
|
128
|
+
body = null;
|
|
129
|
+
for (const name of [
|
|
130
|
+
"encoding",
|
|
131
|
+
"language",
|
|
132
|
+
"location",
|
|
133
|
+
"type"
|
|
134
|
+
]) sent.delete(`content-${name}`);
|
|
135
|
+
}
|
|
136
|
+
redirected = true;
|
|
137
|
+
if (++redirects > 20) throw new Error(`vitest-plugin-rsc: too many redirects for ${request.url}. The last one was to ${url.href}.`);
|
|
138
|
+
if (url.origin !== window.location.origin) {
|
|
139
|
+
if (navigation) throw leftTheApp(url);
|
|
140
|
+
sent.delete("authorization");
|
|
141
|
+
return nativeFetch(url, {
|
|
142
|
+
method,
|
|
143
|
+
headers: sent,
|
|
144
|
+
body,
|
|
145
|
+
signal: request.signal
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
if (own) {
|
|
149
|
+
own.receive(response.headers.getSetCookie());
|
|
150
|
+
sent.set("cookie", own.cookieHeader());
|
|
151
|
+
}
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
Object.defineProperties(response, {
|
|
155
|
+
url: { value: url.href },
|
|
156
|
+
redirected: { value: redirected }
|
|
157
|
+
});
|
|
158
|
+
return response;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function pageResponse(response) {
|
|
162
|
+
const { Response: PageResponse } = window;
|
|
163
|
+
if (response instanceof PageResponse && !response.headers.has("set-cookie")) return response;
|
|
164
|
+
const headers = [...response.headers].filter(([name]) => name !== "set-cookie");
|
|
165
|
+
const { status, statusText, url, redirected } = response;
|
|
166
|
+
const page = new PageResponse(response.body, {
|
|
167
|
+
status,
|
|
168
|
+
statusText,
|
|
169
|
+
headers
|
|
170
|
+
});
|
|
171
|
+
return Object.defineProperties(page, {
|
|
172
|
+
url: { value: url },
|
|
173
|
+
redirected: { value: redirected }
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
const appFetch = (store, network) => async (input, init, sent) => {
|
|
177
|
+
const server = !store;
|
|
178
|
+
const from = store?.();
|
|
179
|
+
if (!sent) return network()(input, init);
|
|
180
|
+
const credentials = (init?.credentials ?? (isRequest(input) ? input.credentials : void 0)) !== "omit";
|
|
181
|
+
if (!sent.marked) {
|
|
182
|
+
if (from) sent.headers.delete("cookie");
|
|
183
|
+
const headers = from ? browserHeaders(sent.headers, sent.url, sent.method, from, credentials) : sent.headers;
|
|
184
|
+
if (!await takesRequest({
|
|
185
|
+
url: sent.url.href,
|
|
186
|
+
headers
|
|
187
|
+
})) return network()(input, init);
|
|
188
|
+
}
|
|
189
|
+
if (server && sent.method === "GET" && sent.headers.get("rsc") === "1" && sent.headers.has("x-action-redirect") && registry.openedByRequest.getStore()?.rendersRedirect === false) return new registry.Response(null, { status: 204 });
|
|
190
|
+
const spare = isRequest(input) && input.body ? input.clone() : input;
|
|
191
|
+
const response = await sendRequest(server ? new registry.Request(input, init) : new Request(input, init), {
|
|
192
|
+
from,
|
|
193
|
+
credentials,
|
|
194
|
+
network: sent.marked ? void 0 : () => network()(spare, init)
|
|
195
|
+
});
|
|
196
|
+
return from && response ? pageResponse(response) : response;
|
|
197
|
+
};
|
|
198
|
+
const never = () => new Promise(() => {});
|
|
199
|
+
function isPrefetch(sent, init) {
|
|
200
|
+
return sent.headers.has("rsc") && (sent.headers.has("next-router-prefetch") || init?.priority === "low");
|
|
201
|
+
}
|
|
202
|
+
const noPrefetch = () => new Response(null, { status: 204 });
|
|
203
|
+
async function heardBy(sender, response, action) {
|
|
204
|
+
if (failsAsPageLeft(action)) return failsAsLeft(sender, response);
|
|
205
|
+
const answered = await response.catch(() => void 0);
|
|
206
|
+
if (!sender.aborted) return response;
|
|
207
|
+
answered?.body?.cancel().catch(() => {});
|
|
208
|
+
return ofLeftPage(action);
|
|
209
|
+
}
|
|
210
|
+
function failsAsLeft(sender, response) {
|
|
211
|
+
const left = new Promise((_, reject) => sender.addEventListener("abort", () => reject(pageLeft()), { once: true }));
|
|
212
|
+
left.catch(() => {});
|
|
213
|
+
response.then((answered) => {
|
|
214
|
+
if (sender.aborted) answered?.body?.cancel().catch(() => {});
|
|
215
|
+
}, () => {});
|
|
216
|
+
return Promise.race([response, left]);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The browser's `fetch` while a browser context of the app is open: what
|
|
220
|
+
* Next's client router and Client Components call. `store()` is the store of
|
|
221
|
+
* the context, and `network()` the `fetch` the document had before it, which
|
|
222
|
+
* gets every request that is not the app's, or, where the DOM hands over the
|
|
223
|
+
* page's requests, one that answers none, for the DOM to send. Without a
|
|
224
|
+
* context, `network()` gets every request: code that took this `fetch` in a
|
|
225
|
+
* context has the document's own after it.
|
|
226
|
+
*
|
|
227
|
+
* Next marks the requests it sends for a page, with an `rsc` or a
|
|
228
|
+
* `next-action` header. They belong to the page that is open as they are
|
|
229
|
+
* sent. Its router is there only once its page is, so without a page that
|
|
230
|
+
* page was left, and it sends nothing more. A test asks with
|
|
231
|
+
* `handleRequest()`.
|
|
232
|
+
*/
|
|
233
|
+
function pageFetch(store, network) {
|
|
234
|
+
const browserFetch = appFetch(() => store(), network);
|
|
235
|
+
return async (input, init) => {
|
|
236
|
+
if (!store()) return network()(input, init);
|
|
237
|
+
const sent = sameOriginRequest(input, init);
|
|
238
|
+
if (!sent?.marked) return browserFetch(input, init, sent);
|
|
239
|
+
const action = sent.headers.has("next-action");
|
|
240
|
+
const sender = page;
|
|
241
|
+
if (!sender || sender.aborted) return ofLeftPage(action);
|
|
242
|
+
if (speedFeature("skipPrefetch") && isPrefetch(sent, init)) return noPrefetch();
|
|
243
|
+
return heardBy(sender, browserFetch(input, init, sent), action);
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const serverFetch = appFetch(void 0, () => networkFetch);
|
|
247
|
+
registry.fetch = async (input, init) => {
|
|
248
|
+
if (!isRequest(input) && !URL.canParse(String(input))) throw new TypeError(`Failed to parse URL from ${String(input)}`, { cause: /* @__PURE__ */ new TypeError("Invalid URL") });
|
|
249
|
+
return serverFetch(input, init, sameOriginRequest(input, init));
|
|
250
|
+
};
|
|
251
|
+
process.env.__NEXT_PRIVATE_ORIGIN = window.location.origin;
|
|
252
|
+
//#endregion
|
|
253
|
+
export { leftTheApp, pageFetch, sendRequest, setOpenPage, setRouterOutlivesPage, unlessAborted };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PageStore } from "./page-store.js";
|
|
2
|
+
import { PageStorage } from "./storage.js";
|
|
3
|
+
|
|
4
|
+
//#region src/browser-context.d.ts
|
|
5
|
+
/** A browser context of the app: its cookie jar, the headers of its requests, and its storage. */
|
|
6
|
+
declare class BrowserContext {
|
|
7
|
+
readonly store: PageStore;
|
|
8
|
+
readonly storage: PageStorage;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Opens a browser context of the app, empty, unless one is open: the
|
|
12
|
+
* document has its cookies, storage, requests and navigations until
|
|
13
|
+
* `closeContext()`.
|
|
14
|
+
*/
|
|
15
|
+
declare function openContext(): BrowserContext;
|
|
16
|
+
/**
|
|
17
|
+
* Closes the browser context that is open, and puts back what the document
|
|
18
|
+
* had wherever the context's own are in place. Also with none open: a patch
|
|
19
|
+
* over them that has gone since, like a spy that a test restored after
|
|
20
|
+
* `cleanup()`, put them back.
|
|
21
|
+
*/
|
|
22
|
+
declare function closeContext(): void;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { BrowserContext, closeContext, openContext };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { registry } from "./registry.js";
|
|
2
|
+
import { PageStore } from "./page-store.js";
|
|
3
|
+
import { pageFetch } from "./app-requests.js";
|
|
4
|
+
import { streamedContentQueue } from "./document.js";
|
|
5
|
+
import { fromPage } from "./from-page.js";
|
|
6
|
+
import { followNavigation } from "./document-loads.js";
|
|
7
|
+
import { appHasNavigations } from "./host-document.js";
|
|
8
|
+
import { PageStorage, itemsOfStorage, storageOf } from "./storage.js";
|
|
9
|
+
import pageHost from "virtual:vitest-plugin-rsc/page-host";
|
|
10
|
+
//#region src/browser-context.ts
|
|
11
|
+
/** A browser context of the app: its cookie jar, the headers of its requests, and its storage. */
|
|
12
|
+
var BrowserContext = class {
|
|
13
|
+
store = new PageStore();
|
|
14
|
+
storage = new PageStorage();
|
|
15
|
+
};
|
|
16
|
+
let opened;
|
|
17
|
+
const saved = /* @__PURE__ */ new Map();
|
|
18
|
+
const patched = /* @__PURE__ */ new Set();
|
|
19
|
+
const savedOf = (binding) => saved.get(binding) ?? descriptorOf(Object.getPrototypeOf(binding.target), binding.name);
|
|
20
|
+
function savedValue(binding) {
|
|
21
|
+
const descriptor = savedOf(binding);
|
|
22
|
+
return descriptor?.get ? descriptor.get.call(binding.target) : descriptor?.value;
|
|
23
|
+
}
|
|
24
|
+
function descriptorOf(target, name) {
|
|
25
|
+
for (; target; target = Object.getPrototypeOf(target)) {
|
|
26
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, name);
|
|
27
|
+
if (descriptor) return descriptor;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const leftToDocument = /* @__PURE__ */ new WeakSet();
|
|
31
|
+
const interceptedFetch = pageFetch(() => opened?.store, () => async () => void 0);
|
|
32
|
+
const handsOver = pageHost.interceptRequests(async (request) => leftToDocument.has(request) ? void 0 : interceptedFetch(...fromPage(request)));
|
|
33
|
+
const toDocument = (input, init) => () => {
|
|
34
|
+
const documentFetch = savedValue(fetchBinding) ?? registry.network;
|
|
35
|
+
if (!handsOver) return documentFetch;
|
|
36
|
+
return () => {
|
|
37
|
+
const request = new window.Request(input, init);
|
|
38
|
+
leftToDocument.add(request);
|
|
39
|
+
return documentFetch(request);
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
const fetchBinding = {
|
|
43
|
+
target: window,
|
|
44
|
+
name: "fetch",
|
|
45
|
+
own: {
|
|
46
|
+
configurable: true,
|
|
47
|
+
enumerable: true,
|
|
48
|
+
writable: true,
|
|
49
|
+
value: (input, init) => pageFetch(() => opened?.store, toDocument(input, init))(...fromPage(input, init))
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const cookieBinding = {
|
|
53
|
+
target: document,
|
|
54
|
+
name: "cookie",
|
|
55
|
+
own: {
|
|
56
|
+
configurable: true,
|
|
57
|
+
get: () => opened ? opened.store.documentCookie() : savedValue(cookieBinding),
|
|
58
|
+
set(cookie) {
|
|
59
|
+
if (opened) opened.store.write(String(cookie));
|
|
60
|
+
else savedOf(cookieBinding)?.set?.call(document, cookie);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const storageBindings = ["localStorage", "sessionStorage"].map((name) => {
|
|
65
|
+
const binding = {
|
|
66
|
+
target: window,
|
|
67
|
+
name,
|
|
68
|
+
own: void 0
|
|
69
|
+
};
|
|
70
|
+
const items = () => opened ? opened.storage[name] : itemsOfStorage(savedValue(binding));
|
|
71
|
+
const storage = storageOf(items);
|
|
72
|
+
binding.own = {
|
|
73
|
+
configurable: true,
|
|
74
|
+
enumerable: true,
|
|
75
|
+
get: () => storage
|
|
76
|
+
};
|
|
77
|
+
return binding;
|
|
78
|
+
});
|
|
79
|
+
const bindings = [
|
|
80
|
+
fetchBinding,
|
|
81
|
+
cookieBinding,
|
|
82
|
+
...[
|
|
83
|
+
"cookieStore",
|
|
84
|
+
"CookieStore",
|
|
85
|
+
"CookieChangeEvent"
|
|
86
|
+
].map((name) => ({
|
|
87
|
+
target: window,
|
|
88
|
+
name,
|
|
89
|
+
own: void 0
|
|
90
|
+
})),
|
|
91
|
+
...storageBindings,
|
|
92
|
+
{
|
|
93
|
+
target: self,
|
|
94
|
+
name: "$RC",
|
|
95
|
+
own: streamedContentQueue
|
|
96
|
+
}
|
|
97
|
+
];
|
|
98
|
+
function same(one, other) {
|
|
99
|
+
if (!one || !other) return one === other;
|
|
100
|
+
return one.get === other.get && one.set === other.set && one.value === other.value;
|
|
101
|
+
}
|
|
102
|
+
const ownOf = ({ target, name }) => Object.getOwnPropertyDescriptor(target, name);
|
|
103
|
+
const original = new Map(bindings.map((binding) => [binding, ownOf(binding)]));
|
|
104
|
+
const isDocuments = (binding, descriptor) => saved.has(binding) && same(descriptor, saved.get(binding)) || same(descriptor, original.get(binding));
|
|
105
|
+
const { navigation } = pageHost;
|
|
106
|
+
const followed = (event) => {
|
|
107
|
+
if (opened) followNavigation(event, opened.store);
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Opens a browser context of the app, empty, unless one is open: the
|
|
111
|
+
* document has its cookies, storage, requests and navigations until
|
|
112
|
+
* `closeContext()`.
|
|
113
|
+
*/
|
|
114
|
+
function openContext() {
|
|
115
|
+
if (opened) return opened;
|
|
116
|
+
for (const binding of bindings) {
|
|
117
|
+
const { target, name, own } = binding;
|
|
118
|
+
const descriptor = ownOf(binding);
|
|
119
|
+
if (same(descriptor, own)) continue;
|
|
120
|
+
if (patched.has(binding) && !isDocuments(binding, descriptor)) continue;
|
|
121
|
+
patched.delete(binding);
|
|
122
|
+
saved.set(binding, descriptor);
|
|
123
|
+
if (own) Object.defineProperty(target, name, own);
|
|
124
|
+
else Reflect.deleteProperty(target, name);
|
|
125
|
+
}
|
|
126
|
+
navigation?.addEventListener("navigate", followed);
|
|
127
|
+
appHasNavigations(true);
|
|
128
|
+
return opened = new BrowserContext();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Closes the browser context that is open, and puts back what the document
|
|
132
|
+
* had wherever the context's own are in place. Also with none open: a patch
|
|
133
|
+
* over them that has gone since, like a spy that a test restored after
|
|
134
|
+
* `cleanup()`, put them back.
|
|
135
|
+
*/
|
|
136
|
+
function closeContext() {
|
|
137
|
+
const closing = opened !== void 0;
|
|
138
|
+
opened = void 0;
|
|
139
|
+
navigation?.removeEventListener("navigate", followed);
|
|
140
|
+
appHasNavigations(false);
|
|
141
|
+
for (const binding of bindings) {
|
|
142
|
+
const { target, name, own } = binding;
|
|
143
|
+
const descriptor = ownOf(binding);
|
|
144
|
+
if (!same(descriptor, own)) {
|
|
145
|
+
if (isDocuments(binding, descriptor)) patched.delete(binding);
|
|
146
|
+
else if (closing) patched.add(binding);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
patched.delete(binding);
|
|
150
|
+
if (!saved.has(binding)) continue;
|
|
151
|
+
const put = saved.get(binding);
|
|
152
|
+
if (put) Object.defineProperty(target, name, put);
|
|
153
|
+
else Reflect.deleteProperty(target, name);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//#endregion
|
|
157
|
+
export { BrowserContext, closeContext, openContext };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Stylesheets } from "./styles-shared.js";
|
|
2
|
+
import { PrerenderVerdict } from "./registry.js";
|
|
3
|
+
import { PrerenderManifest } from "./compat/ssr/build-output.js";
|
|
4
|
+
|
|
5
|
+
//#region src/build-output.d.ts
|
|
6
|
+
declare function setStylesheets(of: Stylesheets): void;
|
|
7
|
+
declare function setServerActions(ids: Iterable<string>): void;
|
|
8
|
+
declare let prerenderManifest: PrerenderManifest;
|
|
9
|
+
type Route = {
|
|
10
|
+
kind: "page" | "route";
|
|
11
|
+
page: string;
|
|
12
|
+
pathname: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Lists what Next's build prerenders of a route, by its verdict: the entries
|
|
16
|
+
* of the prerender manifest that Next's handler reads. With them the handler
|
|
17
|
+
* prerenders a pathname at its first request, keeps it, and serves it from
|
|
18
|
+
* then on, as `next start` serves what the build prerendered. Without the
|
|
19
|
+
* `revalidate` the build writes: Next's cache reads it for a page the build
|
|
20
|
+
* prerendered, and here every page is first prerendered by the handler,
|
|
21
|
+
* which keeps the `revalidate` of the render with it.
|
|
22
|
+
*/
|
|
23
|
+
declare function recordRoute(route: Route, verdict: PrerenderVerdict | undefined): void;
|
|
24
|
+
/** Forgets what the build kept, as the server's cache forgets its entries. */
|
|
25
|
+
declare function forgetBuilt(): void;
|
|
26
|
+
/**
|
|
27
|
+
* A page as the build keeps it, which Next's handler is about to keep for
|
|
28
|
+
* `pathname`, at the first prerender of that pathname in this `generation`
|
|
29
|
+
* of the cache. The build keeps a page of a parallel route's slot without a
|
|
30
|
+
* status (`export/routes/app-page.js:126-141`), which `next start` serves
|
|
31
|
+
* with 200: a page whose other slots have no page for its path renders the
|
|
32
|
+
* not-found page there, which a page load shows, and a navigation, which
|
|
33
|
+
* keeps the other slots as they are, does not. Next's handler would keep
|
|
34
|
+
* the status of its render, 404, and Next's router takes a navigation that
|
|
35
|
+
* answers 404 for a page load. So the entry gets 200, in place: the handler
|
|
36
|
+
* answers the request of its prerender with the status of the entry it
|
|
37
|
+
* keeps, and otherwise with the one its render set. `route` is the source
|
|
38
|
+
* of the entry, the page, as Next's response cache hands it to the cache.
|
|
39
|
+
*/
|
|
40
|
+
declare function keptAsBuilt(pathname: string, value: {
|
|
41
|
+
kind: string;
|
|
42
|
+
status?: number;
|
|
43
|
+
} | null, {
|
|
44
|
+
route
|
|
45
|
+
}: {
|
|
46
|
+
route?: {
|
|
47
|
+
sourceRoute?: string;
|
|
48
|
+
};
|
|
49
|
+
}, generation: number): void;
|
|
50
|
+
/**
|
|
51
|
+
* Hands Next's handler a manifest without `route` for `respond()`, the
|
|
52
|
+
* response of a prefetch: Next reads the manifest as it starts a request.
|
|
53
|
+
*/
|
|
54
|
+
declare function withoutRoute<T>(route: Route, respond: () => Promise<T>): Promise<T>;
|
|
55
|
+
/** Whether the build prerenders a route, or a pathname of it. */
|
|
56
|
+
declare function isPrerendered(route: Route): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Takes a route out of what the build prerenders: for a render that read the
|
|
59
|
+
* request, which the build would have found as it prerendered the route.
|
|
60
|
+
* All of its pathnames: of a route with `generateStaticParams`, the build
|
|
61
|
+
* keeps those that did not read it, and here the others are not rendered yet.
|
|
62
|
+
*/
|
|
63
|
+
declare function demoteRoute(route: Route): void;
|
|
64
|
+
/** A file of `.next/`, by its path, as Next's `loadManifest()` reads it. */
|
|
65
|
+
declare function loadManifest(file: string): unknown;
|
|
66
|
+
declare const nodeRouteDistDir = ".next/vitest-plugin-rsc-node";
|
|
67
|
+
//#endregion
|
|
68
|
+
export { demoteRoute, forgetBuilt, isPrerendered, keptAsBuilt, loadManifest, nodeRouteDistDir, prerenderManifest, recordRoute, setServerActions, setStylesheets, withoutRoute };
|