vinext 0.0.27 → 0.0.28
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/dist/build/static-export.d.ts +1 -1
- package/dist/build/static-export.d.ts.map +1 -1
- package/dist/build/static-export.js +2 -1
- package/dist/build/static-export.js.map +1 -1
- package/dist/cloudflare/kv-cache-handler.d.ts +28 -17
- package/dist/cloudflare/kv-cache-handler.d.ts.map +1 -1
- package/dist/cloudflare/kv-cache-handler.js +95 -30
- package/dist/cloudflare/kv-cache-handler.js.map +1 -1
- package/dist/config/config-matchers.d.ts +1 -0
- package/dist/config/config-matchers.d.ts.map +1 -1
- package/dist/config/config-matchers.js +51 -23
- package/dist/config/config-matchers.js.map +1 -1
- package/dist/deploy.d.ts +1 -1
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +48 -32
- package/dist/deploy.js.map +1 -1
- package/dist/entries/app-rsc-entry.d.ts +3 -1
- package/dist/entries/app-rsc-entry.d.ts.map +1 -1
- package/dist/entries/app-rsc-entry.js +495 -75
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.d.ts.map +1 -1
- package/dist/entries/pages-server-entry.js +68 -22
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/index.d.ts +23 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +128 -41
- package/dist/index.js.map +1 -1
- package/dist/plugins/client-reference-dedup.d.ts +19 -0
- package/dist/plugins/client-reference-dedup.d.ts.map +1 -0
- package/dist/plugins/client-reference-dedup.js +96 -0
- package/dist/plugins/client-reference-dedup.js.map +1 -0
- package/dist/routing/app-router.d.ts.map +1 -1
- package/dist/routing/app-router.js +60 -89
- package/dist/routing/app-router.js.map +1 -1
- package/dist/routing/pages-router.d.ts +1 -1
- package/dist/routing/pages-router.d.ts.map +1 -1
- package/dist/routing/pages-router.js +25 -13
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/routing/route-validation.d.ts +8 -0
- package/dist/routing/route-validation.d.ts.map +1 -0
- package/dist/routing/route-validation.js +124 -0
- package/dist/routing/route-validation.js.map +1 -0
- package/dist/server/api-handler.d.ts.map +1 -1
- package/dist/server/api-handler.js +24 -7
- package/dist/server/api-handler.js.map +1 -1
- package/dist/server/dev-server.d.ts.map +1 -1
- package/dist/server/dev-server.js +9 -3
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/isr-cache.d.ts +5 -13
- package/dist/server/isr-cache.d.ts.map +1 -1
- package/dist/server/isr-cache.js +13 -12
- package/dist/server/isr-cache.js.map +1 -1
- package/dist/server/metadata-routes.d.ts +8 -2
- package/dist/server/metadata-routes.d.ts.map +1 -1
- package/dist/server/metadata-routes.js +73 -28
- package/dist/server/metadata-routes.js.map +1 -1
- package/dist/server/middleware-codegen.d.ts +1 -1
- package/dist/server/middleware-codegen.d.ts.map +1 -1
- package/dist/server/middleware-codegen.js +165 -12
- package/dist/server/middleware-codegen.js.map +1 -1
- package/dist/server/middleware.d.ts +9 -8
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +74 -13
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/prod-server.d.ts.map +1 -1
- package/dist/server/prod-server.js +84 -54
- package/dist/server/prod-server.js.map +1 -1
- package/dist/shims/cache.d.ts +2 -0
- package/dist/shims/cache.d.ts.map +1 -1
- package/dist/shims/cache.js +20 -8
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/fetch-cache.d.ts.map +1 -1
- package/dist/shims/fetch-cache.js +5 -2
- package/dist/shims/fetch-cache.js.map +1 -1
- package/dist/shims/form.d.ts.map +1 -1
- package/dist/shims/form.js +103 -8
- package/dist/shims/form.js.map +1 -1
- package/dist/shims/headers.d.ts +11 -3
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/headers.js +180 -25
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/internal/parse-cookie-header.d.ts +12 -0
- package/dist/shims/internal/parse-cookie-header.d.ts.map +1 -0
- package/dist/shims/internal/parse-cookie-header.js +32 -0
- package/dist/shims/internal/parse-cookie-header.js.map +1 -0
- package/dist/shims/link.d.ts +2 -1
- package/dist/shims/link.d.ts.map +1 -1
- package/dist/shims/link.js +8 -2
- package/dist/shims/link.js.map +1 -1
- package/dist/shims/navigation.d.ts +3 -7
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/shims/navigation.js +20 -10
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/readonly-url-search-params.d.ts +11 -0
- package/dist/shims/readonly-url-search-params.d.ts.map +1 -0
- package/dist/shims/readonly-url-search-params.js +24 -0
- package/dist/shims/readonly-url-search-params.js.map +1 -0
- package/dist/shims/router.d.ts +4 -3
- package/dist/shims/router.d.ts.map +1 -1
- package/dist/shims/router.js +42 -29
- package/dist/shims/router.js.map +1 -1
- package/dist/shims/server.d.ts +1 -1
- package/dist/shims/server.d.ts.map +1 -1
- package/dist/shims/server.js +7 -13
- package/dist/shims/server.js.map +1 -1
- package/dist/utils/manifest-paths.d.ts +4 -0
- package/dist/utils/manifest-paths.d.ts.map +1 -0
- package/dist/utils/manifest-paths.js +20 -0
- package/dist/utils/manifest-paths.js.map +1 -0
- package/dist/utils/query.d.ts +9 -0
- package/dist/utils/query.d.ts.map +1 -1
- package/dist/utils/query.js +59 -9
- package/dist/utils/query.js.map +1 -1
- package/package.json +1 -1
package/dist/shims/form.js
CHANGED
|
@@ -19,8 +19,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
19
19
|
*/
|
|
20
20
|
import { forwardRef, useActionState } from "react";
|
|
21
21
|
import { isDangerousScheme } from "./url-safety.js";
|
|
22
|
+
import { toSameOriginPath } from "./url-utils.js";
|
|
22
23
|
// Re-export useActionState from React 19 to match Next.js's next/form module
|
|
23
24
|
export { useActionState };
|
|
25
|
+
const SUPPORTED_FORM_ENCTYPE = "application/x-www-form-urlencoded";
|
|
26
|
+
const SUPPORTED_FORM_METHOD = "GET";
|
|
27
|
+
const SUPPORTED_FORM_TARGET = "_self";
|
|
24
28
|
function isSafeAction(action) {
|
|
25
29
|
// Block dangerous URI schemes
|
|
26
30
|
if (isDangerousScheme(action))
|
|
@@ -44,6 +48,86 @@ function isSafeAction(action) {
|
|
|
44
48
|
}
|
|
45
49
|
return true;
|
|
46
50
|
}
|
|
51
|
+
function getSubmitter(nativeEvent) {
|
|
52
|
+
const submitter = nativeEvent &&
|
|
53
|
+
typeof nativeEvent === "object" &&
|
|
54
|
+
"submitter" in nativeEvent &&
|
|
55
|
+
nativeEvent.submitter instanceof Element
|
|
56
|
+
? nativeEvent.submitter
|
|
57
|
+
: null;
|
|
58
|
+
if (submitter instanceof HTMLButtonElement || submitter instanceof HTMLInputElement) {
|
|
59
|
+
return submitter;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function getEffectiveMethod(submitter, formMethod) {
|
|
64
|
+
const override = submitter?.getAttribute("formmethod");
|
|
65
|
+
return (override ?? formMethod ?? "GET").toUpperCase();
|
|
66
|
+
}
|
|
67
|
+
function getEffectiveAction(submitter, formAction) {
|
|
68
|
+
return submitter?.getAttribute("formaction") ?? formAction;
|
|
69
|
+
}
|
|
70
|
+
function checkFormActionUrl(action, source) {
|
|
71
|
+
const aPropName = source === "action" ? "an `action`" : "a `formAction`";
|
|
72
|
+
let testUrl;
|
|
73
|
+
try {
|
|
74
|
+
testUrl = new URL(action, "http://n");
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
console.error(`<Form> received ${aPropName} that cannot be parsed as a URL: "${action}".`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (testUrl.searchParams.size) {
|
|
81
|
+
console.warn(`<Form> received ${aPropName} that contains search params: "${action}". This is not supported, and they will be ignored. ` +
|
|
82
|
+
`If you need to pass in additional search params, use an \`<input type="hidden" />\` instead.`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function hasUnsupportedSubmitterAttributes(submitter) {
|
|
86
|
+
const formEncType = submitter.getAttribute("formenctype");
|
|
87
|
+
if (formEncType !== null && formEncType !== SUPPORTED_FORM_ENCTYPE) {
|
|
88
|
+
console.error(`<Form>'s \`encType\` was set to an unsupported value via \`formEncType="${formEncType}"\`. ` +
|
|
89
|
+
`This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
const formMethod = submitter.getAttribute("formmethod");
|
|
93
|
+
if (formMethod !== null && formMethod.toUpperCase() !== SUPPORTED_FORM_METHOD) {
|
|
94
|
+
console.error(`<Form>'s \`method\` was set to an unsupported value via \`formMethod="${formMethod}"\`. ` +
|
|
95
|
+
`This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
const formTarget = submitter.getAttribute("formtarget");
|
|
99
|
+
if (formTarget !== null && formTarget !== SUPPORTED_FORM_TARGET) {
|
|
100
|
+
console.error(`<Form>'s \`target\` was set to an unsupported value via \`formTarget="${formTarget}"\`. ` +
|
|
101
|
+
`This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
function createFormSubmitDestinationUrl(action, form, submitter) {
|
|
107
|
+
const targetUrl = new URL(action, window.location.href);
|
|
108
|
+
if (targetUrl.searchParams.size) {
|
|
109
|
+
targetUrl.search = "";
|
|
110
|
+
}
|
|
111
|
+
const formData = buildFormData(form, submitter);
|
|
112
|
+
for (const [name, value] of formData) {
|
|
113
|
+
targetUrl.searchParams.append(name, typeof value === "string" ? value : value.name);
|
|
114
|
+
}
|
|
115
|
+
return toSameOriginPath(targetUrl.href) ?? targetUrl.href;
|
|
116
|
+
}
|
|
117
|
+
function buildFormData(form, submitter) {
|
|
118
|
+
if (!submitter)
|
|
119
|
+
return new FormData(form);
|
|
120
|
+
try {
|
|
121
|
+
return new FormData(form, submitter);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
const formData = new FormData(form);
|
|
125
|
+
if (!submitter.disabled && submitter.name) {
|
|
126
|
+
formData.append(submitter.name, submitter.value);
|
|
127
|
+
}
|
|
128
|
+
return formData;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
47
131
|
const Form = forwardRef(function Form(props, ref) {
|
|
48
132
|
const { action, replace = false, scroll = true, onSubmit, ...rest } = props;
|
|
49
133
|
// If action is a function (server action), pass it directly to React
|
|
@@ -52,6 +136,9 @@ const Form = forwardRef(function Form(props, ref) {
|
|
|
52
136
|
}
|
|
53
137
|
// Block dangerous action URLs. Render <form> without action attribute
|
|
54
138
|
// so it submits to the current page (safe default).
|
|
139
|
+
if (process.env.NODE_ENV !== "production") {
|
|
140
|
+
checkFormActionUrl(action, "action");
|
|
141
|
+
}
|
|
55
142
|
if (!isSafeAction(action)) {
|
|
56
143
|
if (process.env.NODE_ENV !== "production") {
|
|
57
144
|
console.warn(`<Form> blocked unsafe action: ${action}`);
|
|
@@ -65,19 +152,27 @@ const Form = forwardRef(function Form(props, ref) {
|
|
|
65
152
|
if (e.defaultPrevented)
|
|
66
153
|
return;
|
|
67
154
|
}
|
|
155
|
+
const submitter = getSubmitter(e.nativeEvent);
|
|
156
|
+
if (submitter && hasUnsupportedSubmitterAttributes(submitter)) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
68
159
|
// Only intercept GET forms for client-side navigation
|
|
69
|
-
const method = (rest.method
|
|
160
|
+
const method = getEffectiveMethod(submitter, rest.method);
|
|
70
161
|
if (method !== "GET")
|
|
71
162
|
return;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
163
|
+
const effectiveAction = getEffectiveAction(submitter, action);
|
|
164
|
+
if (process.env.NODE_ENV !== "production" && submitter?.getAttribute("formaction") !== null) {
|
|
165
|
+
checkFormActionUrl(effectiveAction, "formAction");
|
|
166
|
+
}
|
|
167
|
+
if (!isSafeAction(effectiveAction)) {
|
|
168
|
+
if (process.env.NODE_ENV !== "production") {
|
|
169
|
+
console.warn(`<Form> blocked unsafe action: ${effectiveAction}`);
|
|
78
170
|
}
|
|
171
|
+
e.preventDefault();
|
|
172
|
+
return;
|
|
79
173
|
}
|
|
80
|
-
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
const url = createFormSubmitDestinationUrl(effectiveAction, e.currentTarget, submitter);
|
|
81
176
|
// Navigate client-side
|
|
82
177
|
if (typeof window.__VINEXT_RSC_NAVIGATE__ === "function") {
|
|
83
178
|
// App Router: RSC navigation. Await so scroll happens after new content renders.
|
package/dist/shims/form.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.js","sourceRoot":"","sources":["../../src/shims/form.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * next/form shim\n *\n * Progressive enhancement form component. In Next.js, this replaces\n * the standard <form> element with one that intercepts submissions\n * and performs client-side navigation for GET forms (search forms).\n *\n * For POST forms with server actions, it delegates to React's built-in\n * form action handling.\n *\n * Usage:\n * import Form from 'next/form';\n * <Form action=\"/search\">\n * <input name=\"q\" />\n * <button type=\"submit\">Search</button>\n * </Form>\n */\n\nimport { forwardRef, useActionState, type FormHTMLAttributes, type ForwardedRef } from \"react\";\nimport { isDangerousScheme } from \"./url-safety.js\";\n\n// Re-export useActionState from React 19 to match Next.js's next/form module\nexport { useActionState };\n\nfunction isSafeAction(action: string): boolean {\n // Block dangerous URI schemes\n if (isDangerousScheme(action)) return false;\n // Block protocol-relative URLs (//evil.com/...)\n if (action.startsWith(\"//\")) return false;\n // Block absolute URLs to external origins (client-side: compare origins)\n if (/^https?:\\/\\//i.test(action)) {\n if (typeof window !== \"undefined\") {\n try {\n const actionUrl = new URL(action);\n return actionUrl.origin === window.location.origin;\n } catch {\n return false;\n }\n }\n // Server-side: block all absolute URLs (can't compare origins)\n return false;\n }\n return true;\n}\n\ninterface FormProps extends FormHTMLAttributes<HTMLFormElement> {\n /** Target URL for GET forms, or server action for POST forms */\n action: string | ((formData: FormData) => void | Promise<void>);\n /** Replace instead of push in history (default: false) */\n replace?: boolean;\n /** Scroll to top after navigation (default: true) */\n scroll?: boolean;\n}\n\nconst Form = forwardRef(function Form(props: FormProps, ref: ForwardedRef<HTMLFormElement>) {\n const { action, replace = false, scroll = true, onSubmit, ...rest } = props;\n\n // If action is a function (server action), pass it directly to React\n if (typeof action === \"function\") {\n return <form ref={ref} action={action as any} onSubmit={onSubmit as any} {...rest} />;\n }\n\n // Block dangerous action URLs. Render <form> without action attribute\n // so it submits to the current page (safe default).\n if (!isSafeAction(action)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(`<Form> blocked unsafe action: ${action}`);\n }\n return <form ref={ref} onSubmit={onSubmit as any} {...rest} />;\n }\n\n async function handleSubmit(e: any) {\n // Call user's onSubmit first\n if (onSubmit) {\n (onSubmit as any)(e);\n if (e.defaultPrevented) return;\n }\n\n // Only intercept GET forms for client-side navigation\n const method = (rest.method ?? \"GET\").toUpperCase();\n if (method !== \"GET\") return;\n\n e.preventDefault();\n\n const formData = new FormData(e.currentTarget);\n const params = new URLSearchParams();\n for (const [key, value] of formData) {\n if (typeof value === \"string\") {\n params.append(key, value);\n }\n }\n\n const url = `${action as string}?${params.toString()}`;\n\n // Navigate client-side\n if (typeof window.__VINEXT_RSC_NAVIGATE__ === \"function\") {\n // App Router: RSC navigation. Await so scroll happens after new content renders.\n if (replace) {\n window.history.replaceState(null, \"\", url);\n } else {\n window.history.pushState(null, \"\", url);\n }\n await window.__VINEXT_RSC_NAVIGATE__(url);\n } else {\n // Pages Router: use router or fallback\n if (replace) {\n window.history.replaceState({}, \"\", url);\n } else {\n window.history.pushState({}, \"\", url);\n }\n window.dispatchEvent(new PopStateEvent(\"popstate\"));\n }\n\n if (scroll) {\n window.scrollTo(0, 0);\n }\n }\n\n return <form ref={ref} action={action} onSubmit={handleSubmit} {...rest} />;\n});\n\nexport default Form;\n"]}
|
|
1
|
+
{"version":3,"file":"form.js","sourceRoot":"","sources":["../../src/shims/form.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * next/form shim\n *\n * Progressive enhancement form component. In Next.js, this replaces\n * the standard <form> element with one that intercepts submissions\n * and performs client-side navigation for GET forms (search forms).\n *\n * For POST forms with server actions, it delegates to React's built-in\n * form action handling.\n *\n * Usage:\n * import Form from 'next/form';\n * <Form action=\"/search\">\n * <input name=\"q\" />\n * <button type=\"submit\">Search</button>\n * </Form>\n */\n\nimport { forwardRef, useActionState, type FormHTMLAttributes, type ForwardedRef } from \"react\";\nimport { isDangerousScheme } from \"./url-safety.js\";\nimport { toSameOriginPath } from \"./url-utils.js\";\n\n// Re-export useActionState from React 19 to match Next.js's next/form module\nexport { useActionState };\n\ntype FormSubmitter = HTMLButtonElement | HTMLInputElement;\nconst SUPPORTED_FORM_ENCTYPE = \"application/x-www-form-urlencoded\";\nconst SUPPORTED_FORM_METHOD = \"GET\";\nconst SUPPORTED_FORM_TARGET = \"_self\";\n\nfunction isSafeAction(action: string): boolean {\n // Block dangerous URI schemes\n if (isDangerousScheme(action)) return false;\n // Block protocol-relative URLs (//evil.com/...)\n if (action.startsWith(\"//\")) return false;\n // Block absolute URLs to external origins (client-side: compare origins)\n if (/^https?:\\/\\//i.test(action)) {\n if (typeof window !== \"undefined\") {\n try {\n const actionUrl = new URL(action);\n return actionUrl.origin === window.location.origin;\n } catch {\n return false;\n }\n }\n // Server-side: block all absolute URLs (can't compare origins)\n return false;\n }\n return true;\n}\n\nfunction getSubmitter(nativeEvent: unknown): FormSubmitter | null {\n const submitter =\n nativeEvent &&\n typeof nativeEvent === \"object\" &&\n \"submitter\" in nativeEvent &&\n nativeEvent.submitter instanceof Element\n ? nativeEvent.submitter\n : null;\n\n if (submitter instanceof HTMLButtonElement || submitter instanceof HTMLInputElement) {\n return submitter;\n }\n return null;\n}\n\nfunction getEffectiveMethod(\n submitter: FormSubmitter | null,\n formMethod: FormHTMLAttributes<HTMLFormElement>[\"method\"],\n): string {\n const override = submitter?.getAttribute(\"formmethod\");\n return (override ?? formMethod ?? \"GET\").toUpperCase();\n}\n\nfunction getEffectiveAction(submitter: FormSubmitter | null, formAction: string): string {\n return submitter?.getAttribute(\"formaction\") ?? formAction;\n}\n\nfunction checkFormActionUrl(action: string, source: \"action\" | \"formAction\"): void {\n const aPropName = source === \"action\" ? \"an `action`\" : \"a `formAction`\";\n\n let testUrl: URL;\n try {\n testUrl = new URL(action, \"http://n\");\n } catch {\n console.error(`<Form> received ${aPropName} that cannot be parsed as a URL: \"${action}\".`);\n return;\n }\n\n if (testUrl.searchParams.size) {\n console.warn(\n `<Form> received ${aPropName} that contains search params: \"${action}\". This is not supported, and they will be ignored. ` +\n `If you need to pass in additional search params, use an \\`<input type=\"hidden\" />\\` instead.`,\n );\n }\n}\n\nfunction hasUnsupportedSubmitterAttributes(submitter: FormSubmitter): boolean {\n const formEncType = submitter.getAttribute(\"formenctype\");\n if (formEncType !== null && formEncType !== SUPPORTED_FORM_ENCTYPE) {\n console.error(\n `<Form>'s \\`encType\\` was set to an unsupported value via \\`formEncType=\"${formEncType}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n const formMethod = submitter.getAttribute(\"formmethod\");\n if (formMethod !== null && formMethod.toUpperCase() !== SUPPORTED_FORM_METHOD) {\n console.error(\n `<Form>'s \\`method\\` was set to an unsupported value via \\`formMethod=\"${formMethod}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n const formTarget = submitter.getAttribute(\"formtarget\");\n if (formTarget !== null && formTarget !== SUPPORTED_FORM_TARGET) {\n console.error(\n `<Form>'s \\`target\\` was set to an unsupported value via \\`formTarget=\"${formTarget}\"\\`. ` +\n `This will disable <Form>'s navigation functionality. If you need this, use a native <form> element instead.`,\n );\n return true;\n }\n\n return false;\n}\n\nfunction createFormSubmitDestinationUrl(\n action: string,\n form: HTMLFormElement,\n submitter: FormSubmitter | null,\n): string {\n const targetUrl = new URL(action, window.location.href);\n if (targetUrl.searchParams.size) {\n targetUrl.search = \"\";\n }\n\n const formData = buildFormData(form, submitter);\n for (const [name, value] of formData) {\n targetUrl.searchParams.append(name, typeof value === \"string\" ? value : value.name);\n }\n\n return toSameOriginPath(targetUrl.href) ?? targetUrl.href;\n}\n\nfunction buildFormData(form: HTMLFormElement, submitter: FormSubmitter | null): FormData {\n if (!submitter) return new FormData(form);\n\n try {\n return new FormData(form, submitter);\n } catch {\n const formData = new FormData(form);\n if (!submitter.disabled && submitter.name) {\n formData.append(submitter.name, submitter.value);\n }\n return formData;\n }\n}\n\ninterface FormProps extends FormHTMLAttributes<HTMLFormElement> {\n /** Target URL for GET forms, or server action for POST forms */\n action: string | ((formData: FormData) => void | Promise<void>);\n /** Replace instead of push in history (default: false) */\n replace?: boolean;\n /** Scroll to top after navigation (default: true) */\n scroll?: boolean;\n}\n\nconst Form = forwardRef(function Form(props: FormProps, ref: ForwardedRef<HTMLFormElement>) {\n const { action, replace = false, scroll = true, onSubmit, ...rest } = props;\n\n // If action is a function (server action), pass it directly to React\n if (typeof action === \"function\") {\n return <form ref={ref} action={action as any} onSubmit={onSubmit as any} {...rest} />;\n }\n\n // Block dangerous action URLs. Render <form> without action attribute\n // so it submits to the current page (safe default).\n if (process.env.NODE_ENV !== \"production\") {\n checkFormActionUrl(action, \"action\");\n }\n\n if (!isSafeAction(action)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(`<Form> blocked unsafe action: ${action}`);\n }\n return <form ref={ref} onSubmit={onSubmit as any} {...rest} />;\n }\n\n async function handleSubmit(e: any) {\n // Call user's onSubmit first\n if (onSubmit) {\n (onSubmit as any)(e);\n if (e.defaultPrevented) return;\n }\n\n const submitter = getSubmitter(e.nativeEvent);\n if (submitter && hasUnsupportedSubmitterAttributes(submitter)) {\n return;\n }\n\n // Only intercept GET forms for client-side navigation\n const method = getEffectiveMethod(submitter, rest.method);\n if (method !== \"GET\") return;\n\n const effectiveAction = getEffectiveAction(submitter, action as string);\n if (process.env.NODE_ENV !== \"production\" && submitter?.getAttribute(\"formaction\") !== null) {\n checkFormActionUrl(effectiveAction, \"formAction\");\n }\n if (!isSafeAction(effectiveAction)) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(`<Form> blocked unsafe action: ${effectiveAction}`);\n }\n e.preventDefault();\n return;\n }\n\n e.preventDefault();\n const url = createFormSubmitDestinationUrl(effectiveAction, e.currentTarget, submitter);\n\n // Navigate client-side\n if (typeof window.__VINEXT_RSC_NAVIGATE__ === \"function\") {\n // App Router: RSC navigation. Await so scroll happens after new content renders.\n if (replace) {\n window.history.replaceState(null, \"\", url);\n } else {\n window.history.pushState(null, \"\", url);\n }\n await window.__VINEXT_RSC_NAVIGATE__(url);\n } else {\n // Pages Router: use router or fallback\n if (replace) {\n window.history.replaceState({}, \"\", url);\n } else {\n window.history.pushState({}, \"\", url);\n }\n window.dispatchEvent(new PopStateEvent(\"popstate\"));\n }\n\n if (scroll) {\n window.scrollTo(0, 0);\n }\n }\n\n return <form ref={ref} action={action} onSubmit={handleSubmit} {...rest} />;\n});\n\nexport default Form;\n"]}
|
package/dist/shims/headers.d.ts
CHANGED
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
interface HeadersContext {
|
|
11
11
|
headers: Headers;
|
|
12
12
|
cookies: Map<string, string>;
|
|
13
|
+
accessError?: Error;
|
|
14
|
+
mutableCookies?: RequestCookies;
|
|
15
|
+
readonlyCookies?: RequestCookies;
|
|
16
|
+
readonlyHeaders?: Headers;
|
|
13
17
|
}
|
|
18
|
+
export type HeadersAccessPhase = "render" | "action" | "route-handler";
|
|
14
19
|
/**
|
|
15
20
|
* Dynamic usage flag — set when a component calls connection(), cookies(),
|
|
16
21
|
* headers(), or noStore() during rendering. When true, ISR caching is
|
|
@@ -34,6 +39,7 @@ export declare function throwIfInsideCacheScope(apiName: string): void;
|
|
|
34
39
|
* Called by the server after rendering to decide on caching.
|
|
35
40
|
*/
|
|
36
41
|
export declare function consumeDynamicUsage(): boolean;
|
|
42
|
+
export declare function setHeadersAccessPhase(phase: HeadersAccessPhase): HeadersAccessPhase;
|
|
37
43
|
/**
|
|
38
44
|
* Set the headers/cookies context for the current RSC render.
|
|
39
45
|
* Called by the framework's RSC entry before rendering each request.
|
|
@@ -98,12 +104,12 @@ export declare function headersContextFromRequest(request: Request): HeadersCont
|
|
|
98
104
|
* Returns a Promise in Next.js 15+ style (but resolves synchronously since
|
|
99
105
|
* the context is already available).
|
|
100
106
|
*/
|
|
101
|
-
export declare function headers(): Promise<Headers
|
|
107
|
+
export declare function headers(): Promise<Headers> & Headers;
|
|
102
108
|
/**
|
|
103
109
|
* Cookie jar from the incoming request.
|
|
104
110
|
* Returns a ReadonlyRequestCookies-like object.
|
|
105
111
|
*/
|
|
106
|
-
export declare function cookies(): Promise<RequestCookies
|
|
112
|
+
export declare function cookies(): Promise<RequestCookies> & RequestCookies;
|
|
107
113
|
/** Accumulated Set-Cookie headers from cookies().set() / .delete() calls */
|
|
108
114
|
/**
|
|
109
115
|
* Get and clear all pending Set-Cookie headers generated by cookies().set()/delete().
|
|
@@ -135,7 +141,9 @@ declare class RequestCookies {
|
|
|
135
141
|
name: string;
|
|
136
142
|
value: string;
|
|
137
143
|
} | undefined;
|
|
138
|
-
getAll(
|
|
144
|
+
getAll(nameOrOptions?: string | {
|
|
145
|
+
name: string;
|
|
146
|
+
}): Array<{
|
|
139
147
|
name: string;
|
|
140
148
|
value: string;
|
|
141
149
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/shims/headers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../../src/shims/headers.ts"],"names":[],"mappings":"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"}
|
package/dist/shims/headers.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
11
11
|
import { buildRequestHeadersFromMiddlewareResponse } from "../server/middleware-request-headers.js";
|
|
12
|
+
import { parseCookieHeader } from "./internal/parse-cookie-header.js";
|
|
12
13
|
// NOTE:
|
|
13
14
|
// - This shim can be loaded under multiple module specifiers in Vite's
|
|
14
15
|
// multi-environment setup (RSC/SSR). Store the AsyncLocalStorage on
|
|
@@ -26,6 +27,7 @@ const _fallbackState = (_g[_FALLBACK_KEY] ??= {
|
|
|
26
27
|
dynamicUsageDetected: false,
|
|
27
28
|
pendingSetCookies: [],
|
|
28
29
|
draftModeCookieHeader: null,
|
|
30
|
+
phase: "render",
|
|
29
31
|
});
|
|
30
32
|
function _getState() {
|
|
31
33
|
const state = _als.getStore();
|
|
@@ -92,6 +94,18 @@ export function consumeDynamicUsage() {
|
|
|
92
94
|
state.dynamicUsageDetected = false;
|
|
93
95
|
return used;
|
|
94
96
|
}
|
|
97
|
+
function _setStatePhase(state, phase) {
|
|
98
|
+
const previous = state.phase;
|
|
99
|
+
state.phase = phase;
|
|
100
|
+
return previous;
|
|
101
|
+
}
|
|
102
|
+
function _areCookiesMutableInCurrentPhase() {
|
|
103
|
+
const phase = _getState().phase;
|
|
104
|
+
return phase === "action" || phase === "route-handler";
|
|
105
|
+
}
|
|
106
|
+
export function setHeadersAccessPhase(phase) {
|
|
107
|
+
return _setStatePhase(_getState(), phase);
|
|
108
|
+
}
|
|
95
109
|
/**
|
|
96
110
|
* Set the headers/cookies context for the current RSC render.
|
|
97
111
|
* Called by the framework's RSC entry before rendering each request.
|
|
@@ -120,12 +134,14 @@ export function setHeadersContext(ctx) {
|
|
|
120
134
|
existing.dynamicUsageDetected = false;
|
|
121
135
|
existing.pendingSetCookies = [];
|
|
122
136
|
existing.draftModeCookieHeader = null;
|
|
137
|
+
existing.phase = "render";
|
|
123
138
|
}
|
|
124
139
|
else {
|
|
125
140
|
_fallbackState.headersContext = ctx;
|
|
126
141
|
_fallbackState.dynamicUsageDetected = false;
|
|
127
142
|
_fallbackState.pendingSetCookies = [];
|
|
128
143
|
_fallbackState.draftModeCookieHeader = null;
|
|
144
|
+
_fallbackState.phase = "render";
|
|
129
145
|
}
|
|
130
146
|
return;
|
|
131
147
|
}
|
|
@@ -134,9 +150,11 @@ export function setHeadersContext(ctx) {
|
|
|
134
150
|
const state = _als.getStore();
|
|
135
151
|
if (state) {
|
|
136
152
|
state.headersContext = null;
|
|
153
|
+
state.phase = "render";
|
|
137
154
|
}
|
|
138
155
|
else {
|
|
139
156
|
_fallbackState.headersContext = null;
|
|
157
|
+
_fallbackState.phase = "render";
|
|
140
158
|
}
|
|
141
159
|
}
|
|
142
160
|
/**
|
|
@@ -155,6 +173,7 @@ export function runWithHeadersContext(ctx, fn) {
|
|
|
155
173
|
dynamicUsageDetected: false,
|
|
156
174
|
pendingSetCookies: [],
|
|
157
175
|
draftModeCookieHeader: null,
|
|
176
|
+
phase: "render",
|
|
158
177
|
};
|
|
159
178
|
return _als.run(state, fn);
|
|
160
179
|
}
|
|
@@ -183,16 +202,126 @@ export function applyMiddlewareRequestHeaders(middlewareResponseHeaders) {
|
|
|
183
202
|
// If middleware modified the cookie header, rebuild the cookies map.
|
|
184
203
|
ctx.cookies.clear();
|
|
185
204
|
if (nextCookieHeader !== null) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
ctx.cookies.set(k.trim(), rest.join("=").trim());
|
|
190
|
-
}
|
|
205
|
+
const nextCookies = parseCookieHeader(nextCookieHeader);
|
|
206
|
+
for (const [name, value] of nextCookies) {
|
|
207
|
+
ctx.cookies.set(name, value);
|
|
191
208
|
}
|
|
192
209
|
}
|
|
193
210
|
}
|
|
194
211
|
/** Methods on `Headers` that mutate state. Hoisted to module scope — static. */
|
|
195
212
|
const _HEADERS_MUTATING_METHODS = new Set(["set", "delete", "append"]);
|
|
213
|
+
class ReadonlyHeadersError extends Error {
|
|
214
|
+
constructor() {
|
|
215
|
+
super("Headers cannot be modified. Read more: https://nextjs.org/docs/app/api-reference/functions/headers");
|
|
216
|
+
}
|
|
217
|
+
static callable() {
|
|
218
|
+
throw new ReadonlyHeadersError();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
class ReadonlyRequestCookiesError extends Error {
|
|
222
|
+
constructor() {
|
|
223
|
+
super("Cookies can only be modified in a Server Action or Route Handler. Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#options");
|
|
224
|
+
}
|
|
225
|
+
static callable() {
|
|
226
|
+
throw new ReadonlyRequestCookiesError();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function _decorateRequestApiPromise(promise, target) {
|
|
230
|
+
return new Proxy(promise, {
|
|
231
|
+
get(promiseTarget, prop) {
|
|
232
|
+
if (prop in promiseTarget) {
|
|
233
|
+
const value = Reflect.get(promiseTarget, prop, promiseTarget);
|
|
234
|
+
return typeof value === "function" ? value.bind(promiseTarget) : value;
|
|
235
|
+
}
|
|
236
|
+
const value = Reflect.get(target, prop, target);
|
|
237
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
238
|
+
},
|
|
239
|
+
has(promiseTarget, prop) {
|
|
240
|
+
return prop in promiseTarget || prop in target;
|
|
241
|
+
},
|
|
242
|
+
ownKeys(promiseTarget) {
|
|
243
|
+
return Array.from(new Set([...Reflect.ownKeys(promiseTarget), ...Reflect.ownKeys(target)]));
|
|
244
|
+
},
|
|
245
|
+
getOwnPropertyDescriptor(promiseTarget, prop) {
|
|
246
|
+
return (Reflect.getOwnPropertyDescriptor(promiseTarget, prop) ??
|
|
247
|
+
Reflect.getOwnPropertyDescriptor(target, prop));
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
function _decorateRejectedRequestApiPromise(error) {
|
|
252
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
253
|
+
const promise = Promise.reject(normalizedError);
|
|
254
|
+
// Mark the rejection as handled so legacy sync access does not trigger
|
|
255
|
+
// spurious unhandled rejection noise before callers await/catch it.
|
|
256
|
+
promise.catch(() => { });
|
|
257
|
+
const throwingTarget = new Proxy({}, {
|
|
258
|
+
get(_target, prop) {
|
|
259
|
+
if (prop === "then" || prop === "catch" || prop === "finally") {
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
throw normalizedError;
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
return _decorateRequestApiPromise(promise, throwingTarget);
|
|
266
|
+
}
|
|
267
|
+
function _sealHeaders(headers) {
|
|
268
|
+
return new Proxy(headers, {
|
|
269
|
+
get(target, prop) {
|
|
270
|
+
if (typeof prop === "string" && _HEADERS_MUTATING_METHODS.has(prop)) {
|
|
271
|
+
throw new ReadonlyHeadersError();
|
|
272
|
+
}
|
|
273
|
+
const value = Reflect.get(target, prop, target);
|
|
274
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function _wrapMutableCookies(cookies) {
|
|
279
|
+
return new Proxy(cookies, {
|
|
280
|
+
get(target, prop) {
|
|
281
|
+
if (prop === "set" || prop === "delete") {
|
|
282
|
+
return (...args) => {
|
|
283
|
+
if (!_areCookiesMutableInCurrentPhase()) {
|
|
284
|
+
throw new ReadonlyRequestCookiesError();
|
|
285
|
+
}
|
|
286
|
+
return Reflect.get(target, prop, target).apply(target, args);
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const value = Reflect.get(target, prop, target);
|
|
290
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
function _sealCookies(cookies) {
|
|
295
|
+
return new Proxy(cookies, {
|
|
296
|
+
get(target, prop) {
|
|
297
|
+
if (prop === "set" || prop === "delete") {
|
|
298
|
+
throw new ReadonlyRequestCookiesError();
|
|
299
|
+
}
|
|
300
|
+
const value = Reflect.get(target, prop, target);
|
|
301
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
function _getMutableCookies(ctx) {
|
|
306
|
+
if (!ctx.mutableCookies) {
|
|
307
|
+
ctx.mutableCookies = _wrapMutableCookies(new RequestCookies(ctx.cookies));
|
|
308
|
+
}
|
|
309
|
+
return ctx.mutableCookies;
|
|
310
|
+
}
|
|
311
|
+
function _getReadonlyCookies(ctx) {
|
|
312
|
+
if (!ctx.readonlyCookies) {
|
|
313
|
+
// Keep a separate readonly wrapper so render-path reads avoid the
|
|
314
|
+
// mutable phase-checking proxy while still reflecting the shared cookie map.
|
|
315
|
+
ctx.readonlyCookies = _sealCookies(new RequestCookies(ctx.cookies));
|
|
316
|
+
}
|
|
317
|
+
return ctx.readonlyCookies;
|
|
318
|
+
}
|
|
319
|
+
function _getReadonlyHeaders(ctx) {
|
|
320
|
+
if (!ctx.readonlyHeaders) {
|
|
321
|
+
ctx.readonlyHeaders = _sealHeaders(ctx.headers);
|
|
322
|
+
}
|
|
323
|
+
return ctx.readonlyHeaders;
|
|
324
|
+
}
|
|
196
325
|
/**
|
|
197
326
|
* Create a HeadersContext from a standard Request object.
|
|
198
327
|
*
|
|
@@ -250,15 +379,9 @@ export function headersContextFromRequest(request) {
|
|
|
250
379
|
function getCookies() {
|
|
251
380
|
if (_cookies)
|
|
252
381
|
return _cookies;
|
|
253
|
-
_cookies = new Map();
|
|
254
382
|
// Read from the proxy so middleware-modified cookie headers are respected.
|
|
255
383
|
const cookieHeader = headersProxy.get("cookie") || "";
|
|
256
|
-
|
|
257
|
-
const [key, ...rest] = part.split("=");
|
|
258
|
-
if (key) {
|
|
259
|
-
_cookies.set(key.trim(), rest.join("=").trim());
|
|
260
|
-
}
|
|
261
|
-
}
|
|
384
|
+
_cookies = parseCookieHeader(cookieHeader);
|
|
262
385
|
return _cookies;
|
|
263
386
|
}
|
|
264
387
|
// Expose cookies as a lazy getter that memoises on first access.
|
|
@@ -278,28 +401,48 @@ export function headersContextFromRequest(request) {
|
|
|
278
401
|
* Returns a Promise in Next.js 15+ style (but resolves synchronously since
|
|
279
402
|
* the context is already available).
|
|
280
403
|
*/
|
|
281
|
-
export
|
|
282
|
-
|
|
404
|
+
export function headers() {
|
|
405
|
+
try {
|
|
406
|
+
throwIfInsideCacheScope("headers()");
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
return _decorateRejectedRequestApiPromise(error);
|
|
410
|
+
}
|
|
283
411
|
const state = _getState();
|
|
284
412
|
if (!state.headersContext) {
|
|
285
|
-
|
|
286
|
-
"or Server Action. Make sure you're not calling it from a Client Component.");
|
|
413
|
+
return _decorateRejectedRequestApiPromise(new Error("headers() can only be called from a Server Component, Route Handler, " +
|
|
414
|
+
"or Server Action. Make sure you're not calling it from a Client Component."));
|
|
415
|
+
}
|
|
416
|
+
if (state.headersContext.accessError) {
|
|
417
|
+
return _decorateRejectedRequestApiPromise(state.headersContext.accessError);
|
|
287
418
|
}
|
|
288
419
|
markDynamicUsage();
|
|
289
|
-
|
|
420
|
+
const readonlyHeaders = _getReadonlyHeaders(state.headersContext);
|
|
421
|
+
return _decorateRequestApiPromise(Promise.resolve(readonlyHeaders), readonlyHeaders);
|
|
290
422
|
}
|
|
291
423
|
/**
|
|
292
424
|
* Cookie jar from the incoming request.
|
|
293
425
|
* Returns a ReadonlyRequestCookies-like object.
|
|
294
426
|
*/
|
|
295
|
-
export
|
|
296
|
-
|
|
427
|
+
export function cookies() {
|
|
428
|
+
try {
|
|
429
|
+
throwIfInsideCacheScope("cookies()");
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
return _decorateRejectedRequestApiPromise(error);
|
|
433
|
+
}
|
|
297
434
|
const state = _getState();
|
|
298
435
|
if (!state.headersContext) {
|
|
299
|
-
|
|
436
|
+
return _decorateRejectedRequestApiPromise(new Error("cookies() can only be called from a Server Component, Route Handler, or Server Action."));
|
|
437
|
+
}
|
|
438
|
+
if (state.headersContext.accessError) {
|
|
439
|
+
return _decorateRejectedRequestApiPromise(state.headersContext.accessError);
|
|
300
440
|
}
|
|
301
441
|
markDynamicUsage();
|
|
302
|
-
|
|
442
|
+
const cookieStore = _areCookiesMutableInCurrentPhase()
|
|
443
|
+
? _getMutableCookies(state.headersContext)
|
|
444
|
+
: _getReadonlyCookies(state.headersContext);
|
|
445
|
+
return _decorateRequestApiPromise(Promise.resolve(cookieStore), cookieStore);
|
|
303
446
|
}
|
|
304
447
|
// ---------------------------------------------------------------------------
|
|
305
448
|
// Writable cookie accumulator for Route Handlers / Server Actions
|
|
@@ -350,8 +493,11 @@ export function getDraftModeCookieHeader() {
|
|
|
350
493
|
*/
|
|
351
494
|
export async function draftMode() {
|
|
352
495
|
throwIfInsideCacheScope("draftMode()");
|
|
353
|
-
markDynamicUsage();
|
|
354
496
|
const state = _getState();
|
|
497
|
+
if (state.headersContext?.accessError) {
|
|
498
|
+
throw state.headersContext.accessError;
|
|
499
|
+
}
|
|
500
|
+
markDynamicUsage();
|
|
355
501
|
const secret = getDraftSecret();
|
|
356
502
|
const isEnabled = state.headersContext
|
|
357
503
|
? state.headersContext.cookies.get(DRAFT_MODE_COOKIE) === secret
|
|
@@ -359,6 +505,9 @@ export async function draftMode() {
|
|
|
359
505
|
return {
|
|
360
506
|
isEnabled,
|
|
361
507
|
enable() {
|
|
508
|
+
if (state.headersContext?.accessError) {
|
|
509
|
+
throw state.headersContext.accessError;
|
|
510
|
+
}
|
|
362
511
|
if (state.headersContext) {
|
|
363
512
|
state.headersContext.cookies.set(DRAFT_MODE_COOKIE, secret);
|
|
364
513
|
}
|
|
@@ -366,6 +515,9 @@ export async function draftMode() {
|
|
|
366
515
|
state.draftModeCookieHeader = `${DRAFT_MODE_COOKIE}=${secret}; Path=/; HttpOnly; SameSite=Lax${secure}`;
|
|
367
516
|
},
|
|
368
517
|
disable() {
|
|
518
|
+
if (state.headersContext?.accessError) {
|
|
519
|
+
throw state.headersContext.accessError;
|
|
520
|
+
}
|
|
369
521
|
if (state.headersContext) {
|
|
370
522
|
state.headersContext.cookies.delete(DRAFT_MODE_COOKIE);
|
|
371
523
|
}
|
|
@@ -413,10 +565,13 @@ class RequestCookies {
|
|
|
413
565
|
return undefined;
|
|
414
566
|
return { name, value };
|
|
415
567
|
}
|
|
416
|
-
getAll() {
|
|
568
|
+
getAll(nameOrOptions) {
|
|
569
|
+
const name = typeof nameOrOptions === "string" ? nameOrOptions : nameOrOptions?.name;
|
|
417
570
|
const result = [];
|
|
418
|
-
for (const [
|
|
419
|
-
|
|
571
|
+
for (const [cookieName, value] of this._cookies) {
|
|
572
|
+
if (name === undefined || cookieName === name) {
|
|
573
|
+
result.push({ name: cookieName, value });
|
|
574
|
+
}
|
|
420
575
|
}
|
|
421
576
|
return result;
|
|
422
577
|
}
|