vinext 0.0.0 → 0.0.2
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/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/build/static-export.d.ts +78 -0
- package/dist/build/static-export.d.ts.map +1 -0
- package/dist/build/static-export.js +553 -0
- package/dist/build/static-export.js.map +1 -0
- package/dist/check.d.ts +52 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +483 -0
- package/dist/check.js.map +1 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +565 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/entry.d.ts +2 -0
- package/dist/client/entry.d.ts.map +1 -0
- package/dist/client/entry.js +85 -0
- package/dist/client/entry.js.map +1 -0
- package/dist/cloudflare/index.d.ts +8 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +8 -0
- package/dist/cloudflare/index.js.map +1 -0
- package/dist/cloudflare/kv-cache-handler.d.ts +68 -0
- package/dist/cloudflare/kv-cache-handler.d.ts.map +1 -0
- package/dist/cloudflare/kv-cache-handler.js +304 -0
- package/dist/cloudflare/kv-cache-handler.js.map +1 -0
- package/dist/cloudflare/tpr.d.ts +78 -0
- package/dist/cloudflare/tpr.d.ts.map +1 -0
- package/dist/cloudflare/tpr.js +672 -0
- package/dist/cloudflare/tpr.js.map +1 -0
- package/dist/config/config-matchers.d.ts +106 -0
- package/dist/config/config-matchers.d.ts.map +1 -0
- package/dist/config/config-matchers.js +499 -0
- package/dist/config/config-matchers.js.map +1 -0
- package/dist/config/next-config.d.ts +153 -0
- package/dist/config/next-config.d.ts.map +1 -0
- package/dist/config/next-config.js +274 -0
- package/dist/config/next-config.js.map +1 -0
- package/dist/deploy.d.ts +87 -0
- package/dist/deploy.d.ts.map +1 -0
- package/dist/deploy.js +644 -0
- package/dist/deploy.js.map +1 -0
- package/dist/index.d.ts +156 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3296 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +55 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +201 -0
- package/dist/init.js.map +1 -0
- package/dist/routing/app-router.d.ts +96 -0
- package/dist/routing/app-router.d.ts.map +1 -0
- package/dist/routing/app-router.js +815 -0
- package/dist/routing/app-router.js.map +1 -0
- package/dist/routing/pages-router.d.ts +52 -0
- package/dist/routing/pages-router.d.ts.map +1 -0
- package/dist/routing/pages-router.js +239 -0
- package/dist/routing/pages-router.js.map +1 -0
- package/dist/server/api-handler.d.ts +18 -0
- package/dist/server/api-handler.d.ts.map +1 -0
- package/dist/server/api-handler.js +169 -0
- package/dist/server/api-handler.js.map +1 -0
- package/dist/server/app-dev-server.d.ts +42 -0
- package/dist/server/app-dev-server.d.ts.map +1 -0
- package/dist/server/app-dev-server.js +2718 -0
- package/dist/server/app-dev-server.js.map +1 -0
- package/dist/server/app-router-entry.d.ts +18 -0
- package/dist/server/app-router-entry.d.ts.map +1 -0
- package/dist/server/app-router-entry.js +34 -0
- package/dist/server/app-router-entry.js.map +1 -0
- package/dist/server/dev-server.d.ts +40 -0
- package/dist/server/dev-server.d.ts.map +1 -0
- package/dist/server/dev-server.js +758 -0
- package/dist/server/dev-server.js.map +1 -0
- package/dist/server/html.d.ts +22 -0
- package/dist/server/html.d.ts.map +1 -0
- package/dist/server/html.js +29 -0
- package/dist/server/html.js.map +1 -0
- package/dist/server/image-optimization.d.ts +56 -0
- package/dist/server/image-optimization.d.ts.map +1 -0
- package/dist/server/image-optimization.js +103 -0
- package/dist/server/image-optimization.js.map +1 -0
- package/dist/server/instrumentation.d.ts +68 -0
- package/dist/server/instrumentation.d.ts.map +1 -0
- package/dist/server/instrumentation.js +90 -0
- package/dist/server/instrumentation.js.map +1 -0
- package/dist/server/isr-cache.d.ts +61 -0
- package/dist/server/isr-cache.d.ts.map +1 -0
- package/dist/server/isr-cache.js +134 -0
- package/dist/server/isr-cache.js.map +1 -0
- package/dist/server/metadata-routes.d.ts +103 -0
- package/dist/server/metadata-routes.d.ts.map +1 -0
- package/dist/server/metadata-routes.js +270 -0
- package/dist/server/metadata-routes.js.map +1 -0
- package/dist/server/middleware.d.ts +77 -0
- package/dist/server/middleware.d.ts.map +1 -0
- package/dist/server/middleware.js +228 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/prod-server.d.ts +78 -0
- package/dist/server/prod-server.d.ts.map +1 -0
- package/dist/server/prod-server.js +712 -0
- package/dist/server/prod-server.js.map +1 -0
- package/dist/shims/amp.d.ts +17 -0
- package/dist/shims/amp.d.ts.map +1 -0
- package/dist/shims/amp.js +21 -0
- package/dist/shims/amp.js.map +1 -0
- package/dist/shims/app.d.ts +12 -0
- package/dist/shims/app.d.ts.map +1 -0
- package/dist/shims/app.js +2 -0
- package/dist/shims/app.js.map +1 -0
- package/dist/shims/cache-runtime.d.ts +68 -0
- package/dist/shims/cache-runtime.d.ts.map +1 -0
- package/dist/shims/cache-runtime.js +437 -0
- package/dist/shims/cache-runtime.js.map +1 -0
- package/dist/shims/cache.d.ts +243 -0
- package/dist/shims/cache.d.ts.map +1 -0
- package/dist/shims/cache.js +415 -0
- package/dist/shims/cache.js.map +1 -0
- package/dist/shims/client-only.d.ts +18 -0
- package/dist/shims/client-only.d.ts.map +1 -0
- package/dist/shims/client-only.js +18 -0
- package/dist/shims/client-only.js.map +1 -0
- package/dist/shims/config.d.ts +27 -0
- package/dist/shims/config.d.ts.map +1 -0
- package/dist/shims/config.js +30 -0
- package/dist/shims/config.js.map +1 -0
- package/dist/shims/constants.d.ts +13 -0
- package/dist/shims/constants.d.ts.map +1 -0
- package/dist/shims/constants.js +13 -0
- package/dist/shims/constants.js.map +1 -0
- package/dist/shims/document.d.ts +33 -0
- package/dist/shims/document.d.ts.map +1 -0
- package/dist/shims/document.js +32 -0
- package/dist/shims/document.js.map +1 -0
- package/dist/shims/dynamic.d.ts +33 -0
- package/dist/shims/dynamic.d.ts.map +1 -0
- package/dist/shims/dynamic.js +149 -0
- package/dist/shims/dynamic.js.map +1 -0
- package/dist/shims/error-boundary.d.ts +33 -0
- package/dist/shims/error-boundary.d.ts.map +1 -0
- package/dist/shims/error-boundary.js +88 -0
- package/dist/shims/error-boundary.js.map +1 -0
- package/dist/shims/error.d.ts +16 -0
- package/dist/shims/error.d.ts.map +1 -0
- package/dist/shims/error.js +45 -0
- package/dist/shims/error.js.map +1 -0
- package/dist/shims/fetch-cache.d.ts +61 -0
- package/dist/shims/fetch-cache.d.ts.map +1 -0
- package/dist/shims/fetch-cache.js +307 -0
- package/dist/shims/fetch-cache.js.map +1 -0
- package/dist/shims/font-google.d.ts +122 -0
- package/dist/shims/font-google.d.ts.map +1 -0
- package/dist/shims/font-google.js +387 -0
- package/dist/shims/font-google.js.map +1 -0
- package/dist/shims/font-local.d.ts +61 -0
- package/dist/shims/font-local.d.ts.map +1 -0
- package/dist/shims/font-local.js +303 -0
- package/dist/shims/font-local.js.map +1 -0
- package/dist/shims/form.d.ts +30 -0
- package/dist/shims/form.d.ts.map +1 -0
- package/dist/shims/form.js +78 -0
- package/dist/shims/form.js.map +1 -0
- package/dist/shims/head-state.d.ts +11 -0
- package/dist/shims/head-state.d.ts.map +1 -0
- package/dist/shims/head-state.js +47 -0
- package/dist/shims/head-state.js.map +1 -0
- package/dist/shims/head.d.ts +28 -0
- package/dist/shims/head.d.ts.map +1 -0
- package/dist/shims/head.js +148 -0
- package/dist/shims/head.js.map +1 -0
- package/dist/shims/headers.d.ts +150 -0
- package/dist/shims/headers.d.ts.map +1 -0
- package/dist/shims/headers.js +412 -0
- package/dist/shims/headers.js.map +1 -0
- package/dist/shims/image-config.d.ts +30 -0
- package/dist/shims/image-config.d.ts.map +1 -0
- package/dist/shims/image-config.js +91 -0
- package/dist/shims/image-config.js.map +1 -0
- package/dist/shims/image.d.ts +63 -0
- package/dist/shims/image.d.ts.map +1 -0
- package/dist/shims/image.js +284 -0
- package/dist/shims/image.js.map +1 -0
- package/dist/shims/internal/api-utils.d.ts +12 -0
- package/dist/shims/internal/api-utils.d.ts.map +1 -0
- package/dist/shims/internal/api-utils.js +7 -0
- package/dist/shims/internal/api-utils.js.map +1 -0
- package/dist/shims/internal/app-router-context.d.ts +21 -0
- package/dist/shims/internal/app-router-context.d.ts.map +1 -0
- package/dist/shims/internal/app-router-context.js +15 -0
- package/dist/shims/internal/app-router-context.js.map +1 -0
- package/dist/shims/internal/cookies.d.ts +9 -0
- package/dist/shims/internal/cookies.d.ts.map +1 -0
- package/dist/shims/internal/cookies.js +9 -0
- package/dist/shims/internal/cookies.js.map +1 -0
- package/dist/shims/internal/router-context.d.ts +2 -0
- package/dist/shims/internal/router-context.d.ts.map +1 -0
- package/dist/shims/internal/router-context.js +9 -0
- package/dist/shims/internal/router-context.js.map +1 -0
- package/dist/shims/internal/utils.d.ts +48 -0
- package/dist/shims/internal/utils.d.ts.map +1 -0
- package/dist/shims/internal/utils.js +35 -0
- package/dist/shims/internal/utils.js.map +1 -0
- package/dist/shims/internal/work-unit-async-storage.d.ts +12 -0
- package/dist/shims/internal/work-unit-async-storage.d.ts.map +1 -0
- package/dist/shims/internal/work-unit-async-storage.js +13 -0
- package/dist/shims/internal/work-unit-async-storage.js.map +1 -0
- package/dist/shims/layout-segment-context.d.ts +21 -0
- package/dist/shims/layout-segment-context.d.ts.map +1 -0
- package/dist/shims/layout-segment-context.js +27 -0
- package/dist/shims/layout-segment-context.js.map +1 -0
- package/dist/shims/legacy-image.d.ts +52 -0
- package/dist/shims/legacy-image.d.ts.map +1 -0
- package/dist/shims/legacy-image.js +46 -0
- package/dist/shims/legacy-image.js.map +1 -0
- package/dist/shims/link.d.ts +48 -0
- package/dist/shims/link.d.ts.map +1 -0
- package/dist/shims/link.js +395 -0
- package/dist/shims/link.js.map +1 -0
- package/dist/shims/metadata.d.ts +184 -0
- package/dist/shims/metadata.d.ts.map +1 -0
- package/dist/shims/metadata.js +472 -0
- package/dist/shims/metadata.js.map +1 -0
- package/dist/shims/navigation-state.d.ts +14 -0
- package/dist/shims/navigation-state.d.ts.map +1 -0
- package/dist/shims/navigation-state.js +77 -0
- package/dist/shims/navigation-state.js.map +1 -0
- package/dist/shims/navigation.d.ts +201 -0
- package/dist/shims/navigation.d.ts.map +1 -0
- package/dist/shims/navigation.js +672 -0
- package/dist/shims/navigation.js.map +1 -0
- package/dist/shims/og.d.ts +20 -0
- package/dist/shims/og.d.ts.map +1 -0
- package/dist/shims/og.js +19 -0
- package/dist/shims/og.js.map +1 -0
- package/dist/shims/router-state.d.ts +11 -0
- package/dist/shims/router-state.d.ts.map +1 -0
- package/dist/shims/router-state.js +56 -0
- package/dist/shims/router-state.js.map +1 -0
- package/dist/shims/router.d.ts +103 -0
- package/dist/shims/router.d.ts.map +1 -0
- package/dist/shims/router.js +536 -0
- package/dist/shims/router.js.map +1 -0
- package/dist/shims/script.d.ts +58 -0
- package/dist/shims/script.d.ts.map +1 -0
- package/dist/shims/script.js +163 -0
- package/dist/shims/script.js.map +1 -0
- package/dist/shims/server-only.d.ts +19 -0
- package/dist/shims/server-only.d.ts.map +1 -0
- package/dist/shims/server-only.js +19 -0
- package/dist/shims/server-only.js.map +1 -0
- package/dist/shims/server.d.ts +178 -0
- package/dist/shims/server.d.ts.map +1 -0
- package/dist/shims/server.js +377 -0
- package/dist/shims/server.js.map +1 -0
- package/dist/shims/web-vitals.d.ts +24 -0
- package/dist/shims/web-vitals.d.ts.map +1 -0
- package/dist/shims/web-vitals.js +17 -0
- package/dist/shims/web-vitals.js.map +1 -0
- package/dist/utils/hash.d.ts +6 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +20 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/project.d.ts +36 -0
- package/dist/utils/project.d.ts.map +1 -0
- package/dist/utils/project.js +112 -0
- package/dist/utils/project.js.map +1 -0
- package/dist/utils/query.d.ts +10 -0
- package/dist/utils/query.d.ts.map +1 -0
- package/dist/utils/query.js +27 -0
- package/dist/utils/query.js.map +1 -0
- package/package.json +65 -7
- package/index.js +0 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- next/navigation is shimmed
|
|
5
|
+
import { usePathname } from "next/navigation";
|
|
6
|
+
/**
|
|
7
|
+
* Generic ErrorBoundary used to wrap route segments with error.tsx.
|
|
8
|
+
* This must be a client component since error boundaries use
|
|
9
|
+
* componentDidCatch / getDerivedStateFromError.
|
|
10
|
+
*/
|
|
11
|
+
export class ErrorBoundary extends React.Component {
|
|
12
|
+
constructor(props) {
|
|
13
|
+
super(props);
|
|
14
|
+
this.state = { error: null };
|
|
15
|
+
}
|
|
16
|
+
static getDerivedStateFromError(error) {
|
|
17
|
+
// notFound(), forbidden(), unauthorized(), and redirect() must propagate
|
|
18
|
+
// past error boundaries. Re-throw them so they bubble up to the
|
|
19
|
+
// framework's HTTP access fallback / redirect handler.
|
|
20
|
+
if (error && typeof error === "object" && "digest" in error) {
|
|
21
|
+
const digest = String(error.digest);
|
|
22
|
+
if (digest === "NEXT_NOT_FOUND" || // legacy compat
|
|
23
|
+
digest.startsWith("NEXT_HTTP_ERROR_FALLBACK;") ||
|
|
24
|
+
digest.startsWith("NEXT_REDIRECT;")) {
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return { error };
|
|
29
|
+
}
|
|
30
|
+
reset = () => {
|
|
31
|
+
this.setState({ error: null });
|
|
32
|
+
};
|
|
33
|
+
render() {
|
|
34
|
+
if (this.state.error) {
|
|
35
|
+
const FallbackComponent = this.props.fallback;
|
|
36
|
+
return (_jsx(FallbackComponent, { error: this.state.error, reset: this.reset }));
|
|
37
|
+
}
|
|
38
|
+
return this.props.children;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Inner class component that catches notFound() errors and renders the
|
|
43
|
+
* not-found.tsx fallback. Resets when the pathname changes (client navigation)
|
|
44
|
+
* so a previous notFound() doesn't permanently stick.
|
|
45
|
+
*
|
|
46
|
+
* The ErrorBoundary above re-throws notFound errors so they propagate up to this
|
|
47
|
+
* boundary. This must be placed above the ErrorBoundary in the component tree.
|
|
48
|
+
*/
|
|
49
|
+
class NotFoundBoundaryInner extends React.Component {
|
|
50
|
+
constructor(props) {
|
|
51
|
+
super(props);
|
|
52
|
+
this.state = { notFound: false, previousPathname: props.pathname };
|
|
53
|
+
}
|
|
54
|
+
static getDerivedStateFromProps(props, state) {
|
|
55
|
+
// Reset the boundary when the route changes so a previous notFound()
|
|
56
|
+
// doesn't permanently stick after client-side navigation.
|
|
57
|
+
if (props.pathname !== state.previousPathname && state.notFound) {
|
|
58
|
+
return { notFound: false, previousPathname: props.pathname };
|
|
59
|
+
}
|
|
60
|
+
return { notFound: state.notFound, previousPathname: props.pathname };
|
|
61
|
+
}
|
|
62
|
+
static getDerivedStateFromError(error) {
|
|
63
|
+
if (error && typeof error === "object" && "digest" in error) {
|
|
64
|
+
const digest = String(error.digest);
|
|
65
|
+
if (digest === "NEXT_NOT_FOUND" ||
|
|
66
|
+
digest.startsWith("NEXT_HTTP_ERROR_FALLBACK;404")) {
|
|
67
|
+
return { notFound: true };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Not a notFound error — re-throw so it reaches an ErrorBoundary or propagates
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
render() {
|
|
74
|
+
if (this.state.notFound) {
|
|
75
|
+
return this.props.fallback;
|
|
76
|
+
}
|
|
77
|
+
return this.props.children;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Wrapper that reads the current pathname and passes it to the inner class
|
|
82
|
+
* component. This enables automatic reset on client-side navigation.
|
|
83
|
+
*/
|
|
84
|
+
export function NotFoundBoundary({ fallback, children }) {
|
|
85
|
+
const pathname = usePathname();
|
|
86
|
+
return (_jsx(NotFoundBoundaryInner, { pathname: pathname, fallback: fallback, children: children }));
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=error-boundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-boundary.js","sourceRoot":"","sources":["../../src/shims/error-boundary.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport React from \"react\";\n// eslint-disable-next-line @typescript-eslint/no-require-imports -- next/navigation is shimmed\nimport { usePathname } from \"next/navigation\";\n\ninterface ErrorBoundaryProps {\n fallback: React.ComponentType<{ error: Error; reset: () => void }>;\n children: React.ReactNode;\n}\n\ninterface ErrorBoundaryState {\n error: Error | null;\n}\n\n/**\n * Generic ErrorBoundary used to wrap route segments with error.tsx.\n * This must be a client component since error boundaries use\n * componentDidCatch / getDerivedStateFromError.\n */\nexport class ErrorBoundary extends React.Component<\n ErrorBoundaryProps,\n ErrorBoundaryState\n> {\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = { error: null };\n }\n\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n // notFound(), forbidden(), unauthorized(), and redirect() must propagate\n // past error boundaries. Re-throw them so they bubble up to the\n // framework's HTTP access fallback / redirect handler.\n if (error && typeof error === \"object\" && \"digest\" in error) {\n const digest = String((error as any).digest);\n if (\n digest === \"NEXT_NOT_FOUND\" || // legacy compat\n digest.startsWith(\"NEXT_HTTP_ERROR_FALLBACK;\") ||\n digest.startsWith(\"NEXT_REDIRECT;\")\n ) {\n throw error;\n }\n }\n return { error };\n }\n\n reset = () => {\n this.setState({ error: null });\n };\n\n render() {\n if (this.state.error) {\n const FallbackComponent = this.props.fallback;\n return (\n <FallbackComponent error={this.state.error} reset={this.reset} />\n );\n }\n return this.props.children;\n }\n}\n\n// ---------------------------------------------------------------------------\n// NotFoundBoundary — catches notFound() on the client and renders not-found.tsx\n// ---------------------------------------------------------------------------\n\ninterface NotFoundBoundaryProps {\n fallback: React.ReactNode;\n children: React.ReactNode;\n}\n\ninterface NotFoundBoundaryInnerProps extends NotFoundBoundaryProps {\n pathname: string;\n}\n\ninterface NotFoundBoundaryState {\n notFound: boolean;\n previousPathname: string;\n}\n\n/**\n * Inner class component that catches notFound() errors and renders the\n * not-found.tsx fallback. Resets when the pathname changes (client navigation)\n * so a previous notFound() doesn't permanently stick.\n *\n * The ErrorBoundary above re-throws notFound errors so they propagate up to this\n * boundary. This must be placed above the ErrorBoundary in the component tree.\n */\nclass NotFoundBoundaryInner extends React.Component<\n NotFoundBoundaryInnerProps,\n NotFoundBoundaryState\n> {\n constructor(props: NotFoundBoundaryInnerProps) {\n super(props);\n this.state = { notFound: false, previousPathname: props.pathname };\n }\n\n static getDerivedStateFromProps(\n props: NotFoundBoundaryInnerProps,\n state: NotFoundBoundaryState,\n ): NotFoundBoundaryState | null {\n // Reset the boundary when the route changes so a previous notFound()\n // doesn't permanently stick after client-side navigation.\n if (props.pathname !== state.previousPathname && state.notFound) {\n return { notFound: false, previousPathname: props.pathname };\n }\n return { notFound: state.notFound, previousPathname: props.pathname };\n }\n\n static getDerivedStateFromError(error: Error): Partial<NotFoundBoundaryState> {\n if (error && typeof error === \"object\" && \"digest\" in error) {\n const digest = String((error as any).digest);\n if (\n digest === \"NEXT_NOT_FOUND\" ||\n digest.startsWith(\"NEXT_HTTP_ERROR_FALLBACK;404\")\n ) {\n return { notFound: true };\n }\n }\n // Not a notFound error — re-throw so it reaches an ErrorBoundary or propagates\n throw error;\n }\n\n render() {\n if (this.state.notFound) {\n return this.props.fallback;\n }\n return this.props.children;\n }\n}\n\n/**\n * Wrapper that reads the current pathname and passes it to the inner class\n * component. This enables automatic reset on client-side navigation.\n */\nexport function NotFoundBoundary({ fallback, children }: NotFoundBoundaryProps) {\n const pathname = usePathname();\n return (\n <NotFoundBoundaryInner pathname={pathname} fallback={fallback}>\n {children}\n </NotFoundBoundaryInner>\n );\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* next/error shim
|
|
3
|
+
*
|
|
4
|
+
* Provides the default Next.js error page component.
|
|
5
|
+
* Used by apps that import `import Error from 'next/error'` for
|
|
6
|
+
* custom error handling in getServerSideProps or API routes.
|
|
7
|
+
*/
|
|
8
|
+
import React from "react";
|
|
9
|
+
interface ErrorProps {
|
|
10
|
+
statusCode: number;
|
|
11
|
+
title?: string;
|
|
12
|
+
withDarkMode?: boolean;
|
|
13
|
+
}
|
|
14
|
+
declare function ErrorComponent({ statusCode, title }: ErrorProps): React.ReactElement;
|
|
15
|
+
export default ErrorComponent;
|
|
16
|
+
//# sourceMappingURL=error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/shims/error.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,UAAU,UAAU;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,iBAAS,cAAc,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,UAAU,GAAG,KAAK,CAAC,YAAY,CA2D7E;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* next/error shim
|
|
3
|
+
*
|
|
4
|
+
* Provides the default Next.js error page component.
|
|
5
|
+
* Used by apps that import `import Error from 'next/error'` for
|
|
6
|
+
* custom error handling in getServerSideProps or API routes.
|
|
7
|
+
*/
|
|
8
|
+
import React from "react";
|
|
9
|
+
function ErrorComponent({ statusCode, title }) {
|
|
10
|
+
const defaultTitle = statusCode === 404
|
|
11
|
+
? "This page could not be found"
|
|
12
|
+
: "Internal Server Error";
|
|
13
|
+
const displayTitle = title ?? defaultTitle;
|
|
14
|
+
return React.createElement("div", {
|
|
15
|
+
style: {
|
|
16
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
|
17
|
+
height: "100vh",
|
|
18
|
+
textAlign: "center",
|
|
19
|
+
display: "flex",
|
|
20
|
+
flexDirection: "column",
|
|
21
|
+
alignItems: "center",
|
|
22
|
+
justifyContent: "center",
|
|
23
|
+
},
|
|
24
|
+
}, React.createElement("div", null, React.createElement("h1", {
|
|
25
|
+
style: {
|
|
26
|
+
display: "inline-block",
|
|
27
|
+
margin: "0 20px 0 0",
|
|
28
|
+
padding: "0 23px 0 0",
|
|
29
|
+
fontSize: 24,
|
|
30
|
+
fontWeight: 500,
|
|
31
|
+
verticalAlign: "top",
|
|
32
|
+
lineHeight: "49px",
|
|
33
|
+
borderRight: "1px solid rgba(0, 0, 0, .3)",
|
|
34
|
+
},
|
|
35
|
+
}, statusCode), React.createElement("div", { style: { display: "inline-block" } }, React.createElement("h2", {
|
|
36
|
+
style: {
|
|
37
|
+
fontSize: 14,
|
|
38
|
+
fontWeight: 400,
|
|
39
|
+
lineHeight: "49px",
|
|
40
|
+
margin: 0,
|
|
41
|
+
},
|
|
42
|
+
}, displayTitle + "."))));
|
|
43
|
+
}
|
|
44
|
+
export default ErrorComponent;
|
|
45
|
+
//# sourceMappingURL=error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/shims/error.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,SAAS,cAAc,CAAC,EAAE,UAAU,EAAE,KAAK,EAAc;IACvD,MAAM,YAAY,GAChB,UAAU,KAAK,GAAG;QAChB,CAAC,CAAC,8BAA8B;QAChC,CAAC,CAAC,uBAAuB,CAAC;IAE9B,MAAM,YAAY,GAAG,KAAK,IAAI,YAAY,CAAC;IAE3C,OAAO,KAAK,CAAC,aAAa,CACxB,KAAK,EACL;QACE,KAAK,EAAE;YACL,UAAU,EACR,4FAA4F;YAC9F,MAAM,EAAE,OAAO;YACf,SAAS,EAAE,QAAiB;YAC5B,OAAO,EAAE,MAAM;YACf,aAAa,EAAE,QAAiB;YAChC,UAAU,EAAE,QAAQ;YACpB,cAAc,EAAE,QAAQ;SACzB;KACF,EACD,KAAK,CAAC,aAAa,CACjB,KAAK,EACL,IAAI,EACJ,KAAK,CAAC,aAAa,CACjB,IAAI,EACJ;QACE,KAAK,EAAE;YACL,OAAO,EAAE,cAAc;YACvB,MAAM,EAAE,YAAY;YACpB,OAAO,EAAE,YAAY;YACrB,QAAQ,EAAE,EAAE;YACZ,UAAU,EAAE,GAAG;YACf,aAAa,EAAE,KAAK;YACpB,UAAU,EAAE,MAAM;YAClB,WAAW,EAAE,6BAA6B;SAC3C;KACF,EACD,UAAU,CACX,EACD,KAAK,CAAC,aAAa,CACjB,KAAK,EACL,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,EACtC,KAAK,CAAC,aAAa,CACjB,IAAI,EACJ;QACE,KAAK,EAAE;YACL,QAAQ,EAAE,EAAE;YACZ,UAAU,EAAE,GAAG;YACf,UAAU,EAAE,MAAM;YAClB,MAAM,EAAE,CAAC;SACV;KACF,EACD,YAAY,GAAG,GAAG,CACnB,CACF,CACF,CACF,CAAC;AACJ,CAAC;AAED,eAAe,cAAc,CAAC","sourcesContent":["/**\n * next/error shim\n *\n * Provides the default Next.js error page component.\n * Used by apps that import `import Error from 'next/error'` for\n * custom error handling in getServerSideProps or API routes.\n */\nimport React from \"react\";\n\ninterface ErrorProps {\n statusCode: number;\n title?: string;\n withDarkMode?: boolean;\n}\n\nfunction ErrorComponent({ statusCode, title }: ErrorProps): React.ReactElement {\n const defaultTitle =\n statusCode === 404\n ? \"This page could not be found\"\n : \"Internal Server Error\";\n\n const displayTitle = title ?? defaultTitle;\n\n return React.createElement(\n \"div\",\n {\n style: {\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n height: \"100vh\",\n textAlign: \"center\" as const,\n display: \"flex\",\n flexDirection: \"column\" as const,\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n },\n React.createElement(\n \"div\",\n null,\n React.createElement(\n \"h1\",\n {\n style: {\n display: \"inline-block\",\n margin: \"0 20px 0 0\",\n padding: \"0 23px 0 0\",\n fontSize: 24,\n fontWeight: 500,\n verticalAlign: \"top\",\n lineHeight: \"49px\",\n borderRight: \"1px solid rgba(0, 0, 0, .3)\",\n },\n },\n statusCode,\n ),\n React.createElement(\n \"div\",\n { style: { display: \"inline-block\" } },\n React.createElement(\n \"h2\",\n {\n style: {\n fontSize: 14,\n fontWeight: 400,\n lineHeight: \"49px\",\n margin: 0,\n },\n },\n displayTitle + \".\",\n ),\n ),\n ),\n );\n}\n\nexport default ErrorComponent;\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended fetch() with Next.js caching semantics.
|
|
3
|
+
*
|
|
4
|
+
* Patches `globalThis.fetch` during server rendering to support:
|
|
5
|
+
*
|
|
6
|
+
* fetch(url, { next: { revalidate: 60, tags: ['posts'] } })
|
|
7
|
+
* fetch(url, { cache: 'force-cache' })
|
|
8
|
+
* fetch(url, { cache: 'no-store' })
|
|
9
|
+
*
|
|
10
|
+
* Cached responses are stored via the pluggable CacheHandler, so
|
|
11
|
+
* revalidateTag() and revalidatePath() invalidate fetch-level caches.
|
|
12
|
+
*
|
|
13
|
+
* Usage (in server entry):
|
|
14
|
+
* import { withFetchCache, cleanupFetchCache } from './fetch-cache';
|
|
15
|
+
* const cleanup = withFetchCache();
|
|
16
|
+
* try { ... render ... } finally { cleanup(); }
|
|
17
|
+
*
|
|
18
|
+
* Or use the async helper:
|
|
19
|
+
* await runWithFetchCache(async () => { ... render ... });
|
|
20
|
+
*/
|
|
21
|
+
interface NextFetchOptions {
|
|
22
|
+
revalidate?: number | false;
|
|
23
|
+
tags?: string[];
|
|
24
|
+
}
|
|
25
|
+
declare global {
|
|
26
|
+
interface RequestInit {
|
|
27
|
+
next?: NextFetchOptions;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get tags collected during the current render pass.
|
|
32
|
+
* Useful for associating page-level cache entries with all the
|
|
33
|
+
* fetch tags used during rendering.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getCollectedFetchTags(): string[];
|
|
36
|
+
/**
|
|
37
|
+
* Install the patched fetch and reset per-request tag state.
|
|
38
|
+
* Returns a cleanup function that clears tags.
|
|
39
|
+
*
|
|
40
|
+
* In single-threaded contexts (dev server, tests) this is safe because
|
|
41
|
+
* requests are sequential. For concurrent environments, prefer
|
|
42
|
+
* `runWithFetchCache()` which uses `AsyncLocalStorage.run()`.
|
|
43
|
+
*
|
|
44
|
+
* Usage:
|
|
45
|
+
* const cleanup = withFetchCache();
|
|
46
|
+
* try { await render(); } finally { cleanup(); }
|
|
47
|
+
*/
|
|
48
|
+
export declare function withFetchCache(): () => void;
|
|
49
|
+
/**
|
|
50
|
+
* Run an async function with patched fetch caching enabled.
|
|
51
|
+
* Uses `AsyncLocalStorage.run()` for proper per-request isolation
|
|
52
|
+
* of collected fetch tags in concurrent server environments.
|
|
53
|
+
*/
|
|
54
|
+
export declare function runWithFetchCache<T>(fn: () => Promise<T>): Promise<T>;
|
|
55
|
+
/**
|
|
56
|
+
* Get the original (unpatched) fetch function.
|
|
57
|
+
* Useful for internal code that should bypass caching.
|
|
58
|
+
*/
|
|
59
|
+
export declare function getOriginalFetch(): typeof globalThis.fetch;
|
|
60
|
+
export {};
|
|
61
|
+
//# sourceMappingURL=fetch-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-cache.d.ts","sourceRoot":"","sources":["../../src/shims/fetch-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAgDH,UAAU,gBAAgB;IACxB,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAGD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,WAAW;QACnB,IAAI,CAAC,EAAE,gBAAgB,CAAC;KACzB;CACF;AA0CD;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAyMD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,IAAI,MAAM,IAAI,CAO3C;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAG3E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,UAAU,CAAC,KAAK,CAE1D"}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended fetch() with Next.js caching semantics.
|
|
3
|
+
*
|
|
4
|
+
* Patches `globalThis.fetch` during server rendering to support:
|
|
5
|
+
*
|
|
6
|
+
* fetch(url, { next: { revalidate: 60, tags: ['posts'] } })
|
|
7
|
+
* fetch(url, { cache: 'force-cache' })
|
|
8
|
+
* fetch(url, { cache: 'no-store' })
|
|
9
|
+
*
|
|
10
|
+
* Cached responses are stored via the pluggable CacheHandler, so
|
|
11
|
+
* revalidateTag() and revalidatePath() invalidate fetch-level caches.
|
|
12
|
+
*
|
|
13
|
+
* Usage (in server entry):
|
|
14
|
+
* import { withFetchCache, cleanupFetchCache } from './fetch-cache';
|
|
15
|
+
* const cleanup = withFetchCache();
|
|
16
|
+
* try { ... render ... } finally { cleanup(); }
|
|
17
|
+
*
|
|
18
|
+
* Or use the async helper:
|
|
19
|
+
* await runWithFetchCache(async () => { ... render ... });
|
|
20
|
+
*/
|
|
21
|
+
import { getCacheHandler, } from "./cache.js";
|
|
22
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Cache key generation
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* Generate a deterministic cache key from a fetch request.
|
|
28
|
+
*
|
|
29
|
+
* Key = "fetch:" + URL + "|" + sorted relevant options.
|
|
30
|
+
* We exclude headers that are request-specific (cookie, authorization)
|
|
31
|
+
* unless the user explicitly opts in via `next.tags`.
|
|
32
|
+
*/
|
|
33
|
+
function buildFetchCacheKey(input, init) {
|
|
34
|
+
let url;
|
|
35
|
+
let method = "GET";
|
|
36
|
+
let body;
|
|
37
|
+
if (typeof input === "string") {
|
|
38
|
+
url = input;
|
|
39
|
+
}
|
|
40
|
+
else if (input instanceof URL) {
|
|
41
|
+
url = input.toString();
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// Request object
|
|
45
|
+
url = input.url;
|
|
46
|
+
method = input.method || "GET";
|
|
47
|
+
}
|
|
48
|
+
if (init?.method)
|
|
49
|
+
method = init.method;
|
|
50
|
+
if (init?.body && typeof init.body === "string")
|
|
51
|
+
body = init.body;
|
|
52
|
+
// Build a stable key from URL + method + body
|
|
53
|
+
const parts = [`fetch:${method}:${url}`];
|
|
54
|
+
if (body)
|
|
55
|
+
parts.push(body);
|
|
56
|
+
return parts.join("|");
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Patching
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Capture the real (unpatched) fetch once, shared across Vite's
|
|
62
|
+
// multi-environment module instances via Symbol.for().
|
|
63
|
+
const _ORIG_FETCH_KEY = Symbol.for("vinext.fetchCache.originalFetch");
|
|
64
|
+
const _gFetch = globalThis;
|
|
65
|
+
const originalFetch = (_gFetch[_ORIG_FETCH_KEY] ??= globalThis.fetch);
|
|
66
|
+
const _ALS_KEY = Symbol.for("vinext.fetchCache.als");
|
|
67
|
+
const _FALLBACK_KEY = Symbol.for("vinext.fetchCache.fallback");
|
|
68
|
+
const _g = globalThis;
|
|
69
|
+
const _als = (_g[_ALS_KEY] ??= new AsyncLocalStorage());
|
|
70
|
+
const _fallbackState = (_g[_FALLBACK_KEY] ??= {
|
|
71
|
+
currentRequestTags: [],
|
|
72
|
+
});
|
|
73
|
+
function _getState() {
|
|
74
|
+
return _als.getStore() ?? _fallbackState;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Reset the fallback state for a new request. Used by `withFetchCache()`
|
|
78
|
+
* in single-threaded contexts where ALS.run() isn't used.
|
|
79
|
+
*/
|
|
80
|
+
function _resetFallbackState() {
|
|
81
|
+
_fallbackState.currentRequestTags = [];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get tags collected during the current render pass.
|
|
85
|
+
* Useful for associating page-level cache entries with all the
|
|
86
|
+
* fetch tags used during rendering.
|
|
87
|
+
*/
|
|
88
|
+
export function getCollectedFetchTags() {
|
|
89
|
+
return [..._getState().currentRequestTags];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create a patched fetch function with Next.js caching semantics.
|
|
93
|
+
*
|
|
94
|
+
* The patched fetch:
|
|
95
|
+
* 1. Checks `cache` and `next` options to determine caching behavior
|
|
96
|
+
* 2. On cache hit, returns the cached response without hitting the network
|
|
97
|
+
* 3. On cache miss, fetches from network, stores in cache, returns response
|
|
98
|
+
* 4. Respects `next.revalidate` for TTL-based revalidation
|
|
99
|
+
* 5. Respects `next.tags` for tag-based invalidation via revalidateTag()
|
|
100
|
+
*/
|
|
101
|
+
function createPatchedFetch() {
|
|
102
|
+
return async function patchedFetch(input, init) {
|
|
103
|
+
const nextOpts = init?.next;
|
|
104
|
+
const cacheDirective = init?.cache;
|
|
105
|
+
// Determine caching behavior:
|
|
106
|
+
// - cache: 'no-store' → skip cache entirely
|
|
107
|
+
// - cache: 'force-cache' → cache indefinitely (revalidate = Infinity)
|
|
108
|
+
// - next.revalidate: false → same as 'no-store'
|
|
109
|
+
// - next.revalidate: 0 → same as 'no-store'
|
|
110
|
+
// - next.revalidate: N → cache for N seconds
|
|
111
|
+
// - No cache/next options → default behavior (no caching, pass-through)
|
|
112
|
+
// If no caching options at all, just pass through to original fetch
|
|
113
|
+
if (!nextOpts && !cacheDirective) {
|
|
114
|
+
return originalFetch(input, init);
|
|
115
|
+
}
|
|
116
|
+
// Explicit no-store
|
|
117
|
+
if (cacheDirective === "no-store" || nextOpts?.revalidate === false || nextOpts?.revalidate === 0) {
|
|
118
|
+
// Strip the `next` property before passing to real fetch
|
|
119
|
+
const cleanInit = stripNextFromInit(init);
|
|
120
|
+
return originalFetch(input, cleanInit);
|
|
121
|
+
}
|
|
122
|
+
// Determine revalidation period
|
|
123
|
+
let revalidateSeconds;
|
|
124
|
+
if (cacheDirective === "force-cache") {
|
|
125
|
+
// force-cache means cache indefinitely (we use a very large number)
|
|
126
|
+
revalidateSeconds = nextOpts?.revalidate && typeof nextOpts.revalidate === "number"
|
|
127
|
+
? nextOpts.revalidate
|
|
128
|
+
: 31536000; // 1 year
|
|
129
|
+
}
|
|
130
|
+
else if (typeof nextOpts?.revalidate === "number" && nextOpts.revalidate > 0) {
|
|
131
|
+
revalidateSeconds = nextOpts.revalidate;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// Has `next` options but no explicit revalidate — Next.js defaults to
|
|
135
|
+
// caching when `next` is present (force-cache behavior).
|
|
136
|
+
// If only tags are specified, cache indefinitely.
|
|
137
|
+
if (nextOpts?.tags && nextOpts.tags.length > 0) {
|
|
138
|
+
revalidateSeconds = 31536000;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// next: {} with no revalidate or tags — pass through
|
|
142
|
+
const cleanInit = stripNextFromInit(init);
|
|
143
|
+
return originalFetch(input, cleanInit);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const tags = nextOpts?.tags ?? [];
|
|
147
|
+
const cacheKey = buildFetchCacheKey(input, init);
|
|
148
|
+
const handler = getCacheHandler();
|
|
149
|
+
// Collect tags for this render pass
|
|
150
|
+
const reqTags = _getState().currentRequestTags;
|
|
151
|
+
if (tags.length > 0) {
|
|
152
|
+
for (const tag of tags) {
|
|
153
|
+
if (!reqTags.includes(tag)) {
|
|
154
|
+
reqTags.push(tag);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Try cache first
|
|
159
|
+
try {
|
|
160
|
+
const cached = await handler.get(cacheKey, { kind: "FETCH", tags });
|
|
161
|
+
if (cached?.value && cached.value.kind === "FETCH" && cached.cacheState !== "stale") {
|
|
162
|
+
const cachedData = cached.value.data;
|
|
163
|
+
// Reconstruct a Response from the cached data
|
|
164
|
+
return new Response(cachedData.body, {
|
|
165
|
+
status: cachedData.status ?? 200,
|
|
166
|
+
headers: cachedData.headers,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
// Stale entry — we could do stale-while-revalidate here, but for fetch()
|
|
170
|
+
// the simpler approach is to just re-fetch (the page-level ISR handles SWR).
|
|
171
|
+
// However, if we have a stale entry, return it and trigger background refetch.
|
|
172
|
+
if (cached?.value && cached.value.kind === "FETCH" && cached.cacheState === "stale") {
|
|
173
|
+
const staleData = cached.value.data;
|
|
174
|
+
// Background refetch
|
|
175
|
+
const cleanInit = stripNextFromInit(init);
|
|
176
|
+
originalFetch(input, cleanInit).then(async (freshResp) => {
|
|
177
|
+
const freshBody = await freshResp.text();
|
|
178
|
+
const freshHeaders = {};
|
|
179
|
+
freshResp.headers.forEach((v, k) => { freshHeaders[k] = v; });
|
|
180
|
+
const freshValue = {
|
|
181
|
+
kind: "FETCH",
|
|
182
|
+
data: {
|
|
183
|
+
headers: freshHeaders,
|
|
184
|
+
body: freshBody,
|
|
185
|
+
url: typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url,
|
|
186
|
+
status: freshResp.status,
|
|
187
|
+
},
|
|
188
|
+
tags,
|
|
189
|
+
revalidate: revalidateSeconds,
|
|
190
|
+
};
|
|
191
|
+
await handler.set(cacheKey, freshValue, {
|
|
192
|
+
fetchCache: true,
|
|
193
|
+
tags,
|
|
194
|
+
revalidate: revalidateSeconds,
|
|
195
|
+
});
|
|
196
|
+
}).catch((err) => {
|
|
197
|
+
console.error("[vinext] fetch cache background revalidation failed:", err);
|
|
198
|
+
});
|
|
199
|
+
// Return stale data immediately
|
|
200
|
+
return new Response(staleData.body, {
|
|
201
|
+
status: staleData.status ?? 200,
|
|
202
|
+
headers: staleData.headers,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (cacheErr) {
|
|
207
|
+
// Cache read failed — fall through to network
|
|
208
|
+
console.error("[vinext] fetch cache read error:", cacheErr);
|
|
209
|
+
}
|
|
210
|
+
// Cache miss — fetch from network
|
|
211
|
+
const cleanInit = stripNextFromInit(init);
|
|
212
|
+
const response = await originalFetch(input, cleanInit);
|
|
213
|
+
// Only cache successful responses (2xx)
|
|
214
|
+
if (response.ok) {
|
|
215
|
+
// Clone before reading body
|
|
216
|
+
const cloned = response.clone();
|
|
217
|
+
const body = await cloned.text();
|
|
218
|
+
const headers = {};
|
|
219
|
+
cloned.headers.forEach((v, k) => { headers[k] = v; });
|
|
220
|
+
const cacheValue = {
|
|
221
|
+
kind: "FETCH",
|
|
222
|
+
data: {
|
|
223
|
+
headers,
|
|
224
|
+
body,
|
|
225
|
+
url: typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url,
|
|
226
|
+
status: cloned.status,
|
|
227
|
+
},
|
|
228
|
+
tags,
|
|
229
|
+
revalidate: revalidateSeconds,
|
|
230
|
+
};
|
|
231
|
+
// Store in cache (fire-and-forget)
|
|
232
|
+
handler.set(cacheKey, cacheValue, {
|
|
233
|
+
fetchCache: true,
|
|
234
|
+
tags,
|
|
235
|
+
revalidate: revalidateSeconds,
|
|
236
|
+
}).catch((err) => {
|
|
237
|
+
console.error("[vinext] fetch cache write error:", err);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return response;
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Strip the `next` property from RequestInit before passing to real fetch.
|
|
245
|
+
* The `next` property is not a standard fetch option and would cause warnings
|
|
246
|
+
* in some environments.
|
|
247
|
+
*/
|
|
248
|
+
function stripNextFromInit(init) {
|
|
249
|
+
if (!init)
|
|
250
|
+
return init;
|
|
251
|
+
if (!("next" in init))
|
|
252
|
+
return init;
|
|
253
|
+
const { next: _next, ...rest } = init;
|
|
254
|
+
return Object.keys(rest).length > 0 ? rest : undefined;
|
|
255
|
+
}
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
// Public API
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
// ---------------------------------------------------------------------------
|
|
260
|
+
// Fetch patching — install once, not per-request.
|
|
261
|
+
// The patched fetch uses _getState() internally, which reads from ALS
|
|
262
|
+
// (concurrent) or _fallbackState (single-threaded), so per-request
|
|
263
|
+
// isolation is handled at the state level, not by swapping globalThis.fetch.
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
const _PATCH_KEY = Symbol.for("vinext.fetchCache.patchInstalled");
|
|
266
|
+
function _ensurePatchInstalled() {
|
|
267
|
+
if (_g[_PATCH_KEY])
|
|
268
|
+
return;
|
|
269
|
+
_g[_PATCH_KEY] = true;
|
|
270
|
+
globalThis.fetch = createPatchedFetch();
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Install the patched fetch and reset per-request tag state.
|
|
274
|
+
* Returns a cleanup function that clears tags.
|
|
275
|
+
*
|
|
276
|
+
* In single-threaded contexts (dev server, tests) this is safe because
|
|
277
|
+
* requests are sequential. For concurrent environments, prefer
|
|
278
|
+
* `runWithFetchCache()` which uses `AsyncLocalStorage.run()`.
|
|
279
|
+
*
|
|
280
|
+
* Usage:
|
|
281
|
+
* const cleanup = withFetchCache();
|
|
282
|
+
* try { await render(); } finally { cleanup(); }
|
|
283
|
+
*/
|
|
284
|
+
export function withFetchCache() {
|
|
285
|
+
_ensurePatchInstalled();
|
|
286
|
+
_resetFallbackState();
|
|
287
|
+
return () => {
|
|
288
|
+
_resetFallbackState();
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Run an async function with patched fetch caching enabled.
|
|
293
|
+
* Uses `AsyncLocalStorage.run()` for proper per-request isolation
|
|
294
|
+
* of collected fetch tags in concurrent server environments.
|
|
295
|
+
*/
|
|
296
|
+
export async function runWithFetchCache(fn) {
|
|
297
|
+
_ensurePatchInstalled();
|
|
298
|
+
return _als.run({ currentRequestTags: [] }, fn);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Get the original (unpatched) fetch function.
|
|
302
|
+
* Useful for internal code that should bypass caching.
|
|
303
|
+
*/
|
|
304
|
+
export function getOriginalFetch() {
|
|
305
|
+
return originalFetch;
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=fetch-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-cache.js","sourceRoot":"","sources":["../../src/shims/fetch-cache.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Extended fetch() with Next.js caching semantics.\n *\n * Patches `globalThis.fetch` during server rendering to support:\n *\n * fetch(url, { next: { revalidate: 60, tags: ['posts'] } })\n * fetch(url, { cache: 'force-cache' })\n * fetch(url, { cache: 'no-store' })\n *\n * Cached responses are stored via the pluggable CacheHandler, so\n * revalidateTag() and revalidatePath() invalidate fetch-level caches.\n *\n * Usage (in server entry):\n * import { withFetchCache, cleanupFetchCache } from './fetch-cache';\n * const cleanup = withFetchCache();\n * try { ... render ... } finally { cleanup(); }\n *\n * Or use the async helper:\n * await runWithFetchCache(async () => { ... render ... });\n */\n\nimport {\n getCacheHandler,\n type CachedFetchValue,\n} from \"./cache.js\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\n// ---------------------------------------------------------------------------\n// Cache key generation\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a deterministic cache key from a fetch request.\n *\n * Key = \"fetch:\" + URL + \"|\" + sorted relevant options.\n * We exclude headers that are request-specific (cookie, authorization)\n * unless the user explicitly opts in via `next.tags`.\n */\nfunction buildFetchCacheKey(input: string | URL | Request, init?: RequestInit & { next?: NextFetchOptions }): string {\n let url: string;\n let method = \"GET\";\n let body: string | undefined;\n\n if (typeof input === \"string\") {\n url = input;\n } else if (input instanceof URL) {\n url = input.toString();\n } else {\n // Request object\n url = input.url;\n method = input.method || \"GET\";\n }\n\n if (init?.method) method = init.method;\n if (init?.body && typeof init.body === \"string\") body = init.body;\n\n // Build a stable key from URL + method + body\n const parts = [`fetch:${method}:${url}`];\n if (body) parts.push(body);\n\n return parts.join(\"|\");\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface NextFetchOptions {\n revalidate?: number | false;\n tags?: string[];\n}\n\n// Extend the standard RequestInit to include `next`\ndeclare global {\n interface RequestInit {\n next?: NextFetchOptions;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Patching\n// ---------------------------------------------------------------------------\n\n// Capture the real (unpatched) fetch once, shared across Vite's\n// multi-environment module instances via Symbol.for().\nconst _ORIG_FETCH_KEY = Symbol.for(\"vinext.fetchCache.originalFetch\");\nconst _gFetch = globalThis as unknown as Record<PropertyKey, unknown>;\nconst originalFetch: typeof globalThis.fetch = (_gFetch[_ORIG_FETCH_KEY] ??= globalThis.fetch) as typeof globalThis.fetch;\n\n// ---------------------------------------------------------------------------\n// AsyncLocalStorage for request-scoped fetch cache state.\n// Uses Symbol.for() on globalThis so the storage is shared across Vite's\n// multi-environment module instances.\n// ---------------------------------------------------------------------------\ninterface FetchCacheState {\n currentRequestTags: string[];\n}\n\nconst _ALS_KEY = Symbol.for(\"vinext.fetchCache.als\");\nconst _FALLBACK_KEY = Symbol.for(\"vinext.fetchCache.fallback\");\nconst _g = globalThis as unknown as Record<PropertyKey, unknown>;\nconst _als = (_g[_ALS_KEY] ??= new AsyncLocalStorage<FetchCacheState>()) as AsyncLocalStorage<FetchCacheState>;\n\nconst _fallbackState = (_g[_FALLBACK_KEY] ??= {\n currentRequestTags: [],\n} satisfies FetchCacheState) as FetchCacheState;\n\nfunction _getState(): FetchCacheState {\n return _als.getStore() ?? _fallbackState;\n}\n\n/**\n * Reset the fallback state for a new request. Used by `withFetchCache()`\n * in single-threaded contexts where ALS.run() isn't used.\n */\nfunction _resetFallbackState(): void {\n _fallbackState.currentRequestTags = [];\n}\n\n/**\n * Get tags collected during the current render pass.\n * Useful for associating page-level cache entries with all the\n * fetch tags used during rendering.\n */\nexport function getCollectedFetchTags(): string[] {\n return [..._getState().currentRequestTags];\n}\n\n/**\n * Create a patched fetch function with Next.js caching semantics.\n *\n * The patched fetch:\n * 1. Checks `cache` and `next` options to determine caching behavior\n * 2. On cache hit, returns the cached response without hitting the network\n * 3. On cache miss, fetches from network, stores in cache, returns response\n * 4. Respects `next.revalidate` for TTL-based revalidation\n * 5. Respects `next.tags` for tag-based invalidation via revalidateTag()\n */\nfunction createPatchedFetch(): typeof globalThis.fetch {\n return async function patchedFetch(\n input: string | URL | Request,\n init?: RequestInit,\n ): Promise<Response> {\n const nextOpts = init?.next as NextFetchOptions | undefined;\n const cacheDirective = init?.cache;\n\n // Determine caching behavior:\n // - cache: 'no-store' → skip cache entirely\n // - cache: 'force-cache' → cache indefinitely (revalidate = Infinity)\n // - next.revalidate: false → same as 'no-store'\n // - next.revalidate: 0 → same as 'no-store'\n // - next.revalidate: N → cache for N seconds\n // - No cache/next options → default behavior (no caching, pass-through)\n\n // If no caching options at all, just pass through to original fetch\n if (!nextOpts && !cacheDirective) {\n return originalFetch(input, init);\n }\n\n // Explicit no-store\n if (cacheDirective === \"no-store\" || nextOpts?.revalidate === false || nextOpts?.revalidate === 0) {\n // Strip the `next` property before passing to real fetch\n const cleanInit = stripNextFromInit(init);\n return originalFetch(input, cleanInit);\n }\n\n // Determine revalidation period\n let revalidateSeconds: number;\n if (cacheDirective === \"force-cache\") {\n // force-cache means cache indefinitely (we use a very large number)\n revalidateSeconds = nextOpts?.revalidate && typeof nextOpts.revalidate === \"number\"\n ? nextOpts.revalidate\n : 31536000; // 1 year\n } else if (typeof nextOpts?.revalidate === \"number\" && nextOpts.revalidate > 0) {\n revalidateSeconds = nextOpts.revalidate;\n } else {\n // Has `next` options but no explicit revalidate — Next.js defaults to\n // caching when `next` is present (force-cache behavior).\n // If only tags are specified, cache indefinitely.\n if (nextOpts?.tags && nextOpts.tags.length > 0) {\n revalidateSeconds = 31536000;\n } else {\n // next: {} with no revalidate or tags — pass through\n const cleanInit = stripNextFromInit(init);\n return originalFetch(input, cleanInit);\n }\n }\n\n const tags = nextOpts?.tags ?? [];\n const cacheKey = buildFetchCacheKey(input, init);\n const handler = getCacheHandler();\n\n // Collect tags for this render pass\n const reqTags = _getState().currentRequestTags;\n if (tags.length > 0) {\n for (const tag of tags) {\n if (!reqTags.includes(tag)) {\n reqTags.push(tag);\n }\n }\n }\n\n // Try cache first\n try {\n const cached = await handler.get(cacheKey, { kind: \"FETCH\", tags });\n if (cached?.value && cached.value.kind === \"FETCH\" && cached.cacheState !== \"stale\") {\n const cachedData = cached.value.data;\n // Reconstruct a Response from the cached data\n return new Response(cachedData.body, {\n status: cachedData.status ?? 200,\n headers: cachedData.headers,\n });\n }\n\n // Stale entry — we could do stale-while-revalidate here, but for fetch()\n // the simpler approach is to just re-fetch (the page-level ISR handles SWR).\n // However, if we have a stale entry, return it and trigger background refetch.\n if (cached?.value && cached.value.kind === \"FETCH\" && cached.cacheState === \"stale\") {\n const staleData = cached.value.data;\n\n // Background refetch\n const cleanInit = stripNextFromInit(init);\n originalFetch(input, cleanInit).then(async (freshResp) => {\n const freshBody = await freshResp.text();\n const freshHeaders: Record<string, string> = {};\n freshResp.headers.forEach((v, k) => { freshHeaders[k] = v; });\n\n const freshValue: CachedFetchValue = {\n kind: \"FETCH\",\n data: {\n headers: freshHeaders,\n body: freshBody,\n url: typeof input === \"string\" ? input : input instanceof URL ? input.toString() : input.url,\n status: freshResp.status,\n },\n tags,\n revalidate: revalidateSeconds,\n };\n await handler.set(cacheKey, freshValue, {\n fetchCache: true,\n tags,\n revalidate: revalidateSeconds,\n });\n }).catch((err) => {\n console.error(\"[vinext] fetch cache background revalidation failed:\", err);\n });\n\n // Return stale data immediately\n return new Response(staleData.body, {\n status: staleData.status ?? 200,\n headers: staleData.headers,\n });\n }\n } catch (cacheErr) {\n // Cache read failed — fall through to network\n console.error(\"[vinext] fetch cache read error:\", cacheErr);\n }\n\n // Cache miss — fetch from network\n const cleanInit = stripNextFromInit(init);\n const response = await originalFetch(input, cleanInit);\n\n // Only cache successful responses (2xx)\n if (response.ok) {\n // Clone before reading body\n const cloned = response.clone();\n const body = await cloned.text();\n const headers: Record<string, string> = {};\n cloned.headers.forEach((v, k) => { headers[k] = v; });\n\n const cacheValue: CachedFetchValue = {\n kind: \"FETCH\",\n data: {\n headers,\n body,\n url: typeof input === \"string\" ? input : input instanceof URL ? input.toString() : input.url,\n status: cloned.status,\n },\n tags,\n revalidate: revalidateSeconds,\n };\n\n // Store in cache (fire-and-forget)\n handler.set(cacheKey, cacheValue, {\n fetchCache: true,\n tags,\n revalidate: revalidateSeconds,\n }).catch((err) => {\n console.error(\"[vinext] fetch cache write error:\", err);\n });\n }\n\n return response;\n } as typeof globalThis.fetch;\n}\n\n/**\n * Strip the `next` property from RequestInit before passing to real fetch.\n * The `next` property is not a standard fetch option and would cause warnings\n * in some environments.\n */\nfunction stripNextFromInit(init?: RequestInit): RequestInit | undefined {\n if (!init) return init;\n if (!(\"next\" in init)) return init;\n const { next: _next, ...rest } = init as RequestInit & { next?: unknown };\n return Object.keys(rest).length > 0 ? rest : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Fetch patching — install once, not per-request.\n// The patched fetch uses _getState() internally, which reads from ALS\n// (concurrent) or _fallbackState (single-threaded), so per-request\n// isolation is handled at the state level, not by swapping globalThis.fetch.\n// ---------------------------------------------------------------------------\n\nconst _PATCH_KEY = Symbol.for(\"vinext.fetchCache.patchInstalled\");\n\nfunction _ensurePatchInstalled(): void {\n if (_g[_PATCH_KEY]) return;\n _g[_PATCH_KEY] = true;\n globalThis.fetch = createPatchedFetch();\n}\n\n/**\n * Install the patched fetch and reset per-request tag state.\n * Returns a cleanup function that clears tags.\n *\n * In single-threaded contexts (dev server, tests) this is safe because\n * requests are sequential. For concurrent environments, prefer\n * `runWithFetchCache()` which uses `AsyncLocalStorage.run()`.\n *\n * Usage:\n * const cleanup = withFetchCache();\n * try { await render(); } finally { cleanup(); }\n */\nexport function withFetchCache(): () => void {\n _ensurePatchInstalled();\n _resetFallbackState();\n\n return () => {\n _resetFallbackState();\n };\n}\n\n/**\n * Run an async function with patched fetch caching enabled.\n * Uses `AsyncLocalStorage.run()` for proper per-request isolation\n * of collected fetch tags in concurrent server environments.\n */\nexport async function runWithFetchCache<T>(fn: () => Promise<T>): Promise<T> {\n _ensurePatchInstalled();\n return _als.run({ currentRequestTags: [] }, fn);\n}\n\n/**\n * Get the original (unpatched) fetch function.\n * Useful for internal code that should bypass caching.\n */\nexport function getOriginalFetch(): typeof globalThis.fetch {\n return originalFetch;\n}\n"]}
|