@thomas.f/prnext 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/build/action-exports.mjs +107 -0
- package/build/actions.mjs +214 -0
- package/build/app-config.mjs +138 -0
- package/build/app-routing.mjs +119 -0
- package/build/app-static.mjs +158 -0
- package/build/app.mjs +536 -0
- package/build/cache-components.mjs +91 -0
- package/build/cache-handlers.mjs +54 -0
- package/build/check.mjs +40 -0
- package/build/compiler.mjs +222 -0
- package/build/config.mjs +193 -0
- package/build/css-modules.mjs +114 -0
- package/build/custom-routes.mjs +363 -0
- package/build/dev-worker.mjs +18 -0
- package/build/dev.mjs +76 -0
- package/build/directory-cache.mjs +29 -0
- package/build/dynamic.mjs +125 -0
- package/build/edge.mjs +141 -0
- package/build/font-data.LICENSE.txt +25 -0
- package/build/font-data.json +1 -0
- package/build/font-files.mjs +221 -0
- package/build/fonts.mjs +131 -0
- package/build/framework-imports.mjs +17 -0
- package/build/i18n.mjs +39 -0
- package/build/images-config.mjs +62 -0
- package/build/images.mjs +86 -0
- package/build/incremental.mjs +76 -0
- package/build/index.mjs +356 -0
- package/build/loader-cache.mjs +69 -0
- package/build/metadata-images.mjs +72 -0
- package/build/metadata-routes.mjs +77 -0
- package/build/middleware.mjs +231 -0
- package/build/migrate.mjs +289 -0
- package/build/module-resolution.mjs +78 -0
- package/build/native-discovery.mjs +134 -0
- package/build/native-packages.mjs +105 -0
- package/build/npm-packages.mjs +110 -0
- package/build/og.mjs +14 -0
- package/build/page-extensions.mjs +12 -0
- package/build/passthrough-loader.cjs +4 -0
- package/build/precompress.mjs +122 -0
- package/build/scan.mjs +178 -0
- package/build/script-workers.mjs +25 -0
- package/build/server-actions-config.mjs +27 -0
- package/build/source-maps.mjs +34 -0
- package/build/standalone-config.mjs +19 -0
- package/build/standalone-runner.mjs +21 -0
- package/build/standalone-worker.mjs +5 -0
- package/build/standalone.mjs +240 -0
- package/build/static-export.mjs +93 -0
- package/build/styles.mjs +174 -0
- package/build/transform-cache.mjs +50 -0
- package/build/transform.mjs +155 -0
- package/build/turbopack-conditions.mjs +59 -0
- package/build/webpack-bridge.cjs +3 -0
- package/build/webpack-graph.mjs +256 -0
- package/build/webpack-loader.cjs +9 -0
- package/cli.mjs +210 -0
- package/compat/app-client-page.cjs +32 -0
- package/compat/app-context.cjs +40 -0
- package/compat/app-context.d.cts +20 -0
- package/compat/app-dynamic.cjs +34 -0
- package/compat/app-error-boundary.cjs +53 -0
- package/compat/app-global-error-boundary.cjs +34 -0
- package/compat/app-layout-context.cjs +27 -0
- package/compat/app-navigation-boundary.cjs +47 -0
- package/compat/app.cjs +33 -0
- package/compat/app.d.cts +9 -0
- package/compat/cache-handlers.cjs +223 -0
- package/compat/cache-life.cjs +34 -0
- package/compat/cache.cjs +99 -0
- package/compat/cache.d.cts +36 -0
- package/compat/compat/router.d.cts +2 -0
- package/compat/compat-router.cjs +7 -0
- package/compat/constants.cjs +8 -0
- package/compat/constants.d.cts +7 -0
- package/compat/cookies.cjs +129 -0
- package/compat/data-cache.cjs +389 -0
- package/compat/dist/shared/lib/app-router-context.shared-runtime.d.cts +3 -0
- package/compat/dist/shared/lib/router-context.d.cts +1 -0
- package/compat/dist/shared/lib/router-context.shared-runtime.d.cts +3 -0
- package/compat/document.cjs +73 -0
- package/compat/document.d.cts +41 -0
- package/compat/draft.cjs +28 -0
- package/compat/dynamic-bailout.cjs +13 -0
- package/compat/dynamic.cjs +119 -0
- package/compat/dynamic.d.cts +32 -0
- package/compat/error.cjs +36 -0
- package/compat/error.d.cts +15 -0
- package/compat/fetch.d.cts +10 -0
- package/compat/font/google.d.cts +1 -0
- package/compat/font/local.d.cts +2 -0
- package/compat/font-google.cjs +2 -0
- package/compat/font-google.d.cts +1944 -0
- package/compat/font-local.cjs +5 -0
- package/compat/font-local.d.cts +3 -0
- package/compat/font-types.d.cts +25 -0
- package/compat/head.cjs +180 -0
- package/compat/head.d.cts +3 -0
- package/compat/headers.cjs +89 -0
- package/compat/headers.d.cts +26 -0
- package/compat/image-config.cjs +3 -0
- package/compat/image-imports.d.ts +12 -0
- package/compat/image-shared.cjs +62 -0
- package/compat/image.cjs +64 -0
- package/compat/image.d.cts +35 -0
- package/compat/incremental-cache.cjs +186 -0
- package/compat/index.d.ts +159 -0
- package/compat/instant-samples.cjs +39 -0
- package/compat/instant.cjs +13 -0
- package/compat/link.cjs +68 -0
- package/compat/link.d.cts +17 -0
- package/compat/locale.cjs +30 -0
- package/compat/metadata.d.cts +77 -0
- package/compat/navigation-controls.cjs +27 -0
- package/compat/navigation-server.cjs +25 -0
- package/compat/navigation.cjs +52 -0
- package/compat/navigation.d.cts +28 -0
- package/compat/next-app-router-context.cjs +6 -0
- package/compat/next-router-context.cjs +3 -0
- package/compat/og.cjs +32 -0
- package/compat/og.d.cts +1 -0
- package/compat/paths.cjs +33 -0
- package/compat/preview.cjs +41 -0
- package/compat/rewrite.cjs +41 -0
- package/compat/route-request.cjs +64 -0
- package/compat/router.cjs +137 -0
- package/compat/router.d.cts +58 -0
- package/compat/script-context.cjs +5 -0
- package/compat/script-loader.cjs +135 -0
- package/compat/script.cjs +69 -0
- package/compat/script.d.cts +17 -0
- package/compat/server-primitives.cjs +109 -0
- package/compat/server.cjs +13 -0
- package/compat/server.d.cts +37 -0
- package/compat/static-generation.cjs +108 -0
- package/compat/use-cache.cjs +129 -0
- package/index.mjs +1 -0
- package/native/platforms.json +6 -0
- package/native/resolve.mjs +81 -0
- package/package.json +127 -0
- package/release/verify.mjs +59 -0
- package/runtime/action-client.mjs +19 -0
- package/runtime/action-crypto.mjs +138 -0
- package/runtime/action-reference.mjs +2 -0
- package/runtime/action-server.mjs +161 -0
- package/runtime/action-ssr.mjs +5 -0
- package/runtime/api.mjs +326 -0
- package/runtime/app-client.mjs +539 -0
- package/runtime/app-content.mjs +8 -0
- package/runtime/app-errors.mjs +12 -0
- package/runtime/app-instant.mjs +30 -0
- package/runtime/app-metadata.mjs +410 -0
- package/runtime/app-partial-model.mjs +228 -0
- package/runtime/app-partial.mjs +130 -0
- package/runtime/app-prefetch.mjs +34 -0
- package/runtime/app-recovery.mjs +87 -0
- package/runtime/app-render.mjs +544 -0
- package/runtime/app-routing-access.mjs +143 -0
- package/runtime/app-routing-context.mjs +35 -0
- package/runtime/app-routing-state.mjs +61 -0
- package/runtime/app-routing.mjs +323 -0
- package/runtime/app-segments.mjs +22 -0
- package/runtime/app-static-params.mjs +120 -0
- package/runtime/app-static.mjs +86 -0
- package/runtime/app-stream-channel.mjs +92 -0
- package/runtime/app-stream-html.mjs +279 -0
- package/runtime/build-directory.mjs +48 -0
- package/runtime/cache-arguments.mjs +33 -0
- package/runtime/cache-components-rsc.mjs +64 -0
- package/runtime/cache-request.mjs +23 -0
- package/runtime/client-navigation.mjs +40 -0
- package/runtime/dev-client.mjs +129 -0
- package/runtime/dev-file-changes.mjs +110 -0
- package/runtime/document.mjs +96 -0
- package/runtime/edge.mjs +105 -0
- package/runtime/env.mjs +68 -0
- package/runtime/fetch-cache.mjs +379 -0
- package/runtime/font-preload.mjs +4 -0
- package/runtime/http.mjs +190 -0
- package/runtime/incremental-cache.mjs +83 -0
- package/runtime/metadata-route.mjs +83 -0
- package/runtime/middleware.mjs +186 -0
- package/runtime/module-loader.mjs +21 -0
- package/runtime/navigation.mjs +16 -0
- package/runtime/og-loader.mjs +17 -0
- package/runtime/pages-client.mjs +197 -0
- package/runtime/pages-error-props.mjs +6 -0
- package/runtime/pages-errors.mjs +85 -0
- package/runtime/pages-initial-props.mjs +43 -0
- package/runtime/pages-navigation.mjs +565 -0
- package/runtime/pages-paths.mjs +80 -0
- package/runtime/pages-revalidate.mjs +33 -0
- package/runtime/partial-artifact-cache.mjs +37 -0
- package/runtime/partial-template.mjs +174 -0
- package/runtime/profile.mjs +9 -0
- package/runtime/profiles.json +47 -0
- package/runtime/render.mjs +304 -0
- package/runtime/request-transport.mjs +41 -0
- package/runtime/request-work.mjs +7 -0
- package/runtime/route-static.mjs +117 -0
- package/runtime/rsc-worker.mjs +426 -0
- package/runtime/script-html.mjs +61 -0
- package/runtime/stream-select.mjs +25 -0
- package/runtime/stream-utils.mjs +127 -0
- package/runtime/transport.mjs +108 -0
- package/runtime/watch-project.mjs +130 -0
- package/runtime/worker-artifacts.mjs +22 -0
- package/runtime/worker-metadata.mjs +31 -0
- package/runtime/worker.mjs +197 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const React = require('react');
|
|
5
|
+
const { AppRouterContext } = require('./app-context.cjs');
|
|
6
|
+
|
|
7
|
+
function initialError(value) {
|
|
8
|
+
if (!value) return null;
|
|
9
|
+
if (value instanceof Error) return value;
|
|
10
|
+
const error = new Error(value.message || 'An error occurred while rendering this page.');
|
|
11
|
+
if (value.digest !== undefined) error.digest = value.digest;
|
|
12
|
+
return error;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function ThrowInitialError({ error }) { throw initialError(error); }
|
|
16
|
+
|
|
17
|
+
class AppErrorBoundary extends React.Component {
|
|
18
|
+
static contextType = AppRouterContext;
|
|
19
|
+
state = { children: this.props.children, resetKey: this.props.resetKey, error: initialError(this.props.initialError), hasError: !!this.props.initialError };
|
|
20
|
+
|
|
21
|
+
static getDerivedStateFromError(error) {
|
|
22
|
+
if (error?.digest === 'NEXT_HTTP_ERROR_FALLBACK;404' || String(error?.digest || '').startsWith('NEXT_REDIRECT;')) throw error;
|
|
23
|
+
return { error, hasError: true };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static getDerivedStateFromProps(props, state) {
|
|
27
|
+
if ((props.resetOnChildrenChange !== false && props.children !== state.children) || props.resetKey !== state.resetKey) {
|
|
28
|
+
return { children: props.children, resetKey: props.resetKey, error: initialError(props.initialError), hasError: !!props.initialError };
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
reset = () => { this.setState({ error: null, hasError: false }); };
|
|
34
|
+
|
|
35
|
+
retry = () => {
|
|
36
|
+
React.startTransition(() => {
|
|
37
|
+
(this.props.refresh || this.context?.router.refresh)?.();
|
|
38
|
+
this.reset();
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
render() {
|
|
43
|
+
if (this.state.hasError) return React.createElement(React.Fragment, null, this.props.errorStyles,
|
|
44
|
+
React.createElement(this.props.errorComponent, { error: this.state.error, reset: this.reset, retry: this.retry }));
|
|
45
|
+
// A server fallback contains no successful subtree to reveal. Reset retries
|
|
46
|
+
// that same encoded failure; only retry/refresh obtains new server work.
|
|
47
|
+
if (this.props.initialError) return React.createElement(ThrowInitialError, { error: this.props.initialError });
|
|
48
|
+
return this.props.children;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = AppErrorBoundary;
|
|
53
|
+
module.exports.default = AppErrorBoundary;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const React = require('react');
|
|
5
|
+
const AppErrorBoundary = require('./app-error-boundary.cjs');
|
|
6
|
+
|
|
7
|
+
function DefaultGlobalError({ error }) {
|
|
8
|
+
const digest = typeof error?.digest === 'string' ? error.digest : undefined;
|
|
9
|
+
return React.createElement('html', { id: '__prnext_error__' },
|
|
10
|
+
React.createElement('head'),
|
|
11
|
+
React.createElement('body', { style: { margin: 0, fontFamily: 'system-ui, sans-serif' } },
|
|
12
|
+
React.createElement('main', { style: { minHeight: '100vh', display: 'grid', placeContent: 'center', padding: 24 } },
|
|
13
|
+
React.createElement('h1', null, 'This page couldn’t load'),
|
|
14
|
+
React.createElement('p', null, digest ? 'A server error occurred. Reload to try again.' : 'Reload to try again, or go back.'),
|
|
15
|
+
React.createElement('form', null, React.createElement('button', { type: 'submit' }, 'Reload')),
|
|
16
|
+
digest ? React.createElement('p', null, 'ERROR ', digest) : null)));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function GlobalErrorTrigger({ error }) {
|
|
20
|
+
if (error instanceof Error) throw error;
|
|
21
|
+
const failure = new Error(error.message || 'An error occurred while rendering this page.');
|
|
22
|
+
if (typeof error.digest === 'string') failure.digest = error.digest;
|
|
23
|
+
throw failure;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function GlobalErrorBoundary({ component = DefaultGlobalError, css = [], children, ...props }) {
|
|
27
|
+
const styles = css.map(href => React.createElement('link', { key: href, rel: 'stylesheet', href, precedence: 'prnext-global-error' }));
|
|
28
|
+
return React.createElement(AppErrorBoundary, { ...props, resetOnChildrenChange: false, errorComponent: component, errorStyles: styles }, children);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = GlobalErrorBoundary;
|
|
32
|
+
module.exports.default = GlobalErrorBoundary;
|
|
33
|
+
module.exports.DefaultGlobalError = DefaultGlobalError;
|
|
34
|
+
module.exports.GlobalErrorTrigger = GlobalErrorTrigger;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
const React = require('react');
|
|
4
|
+
const LayoutContext = React.createContext({ children: [] });
|
|
5
|
+
const LayoutCacheContext = React.createContext(null);
|
|
6
|
+
|
|
7
|
+
function LayoutProvider({ id, segments, children }) {
|
|
8
|
+
const cache = React.useContext(LayoutCacheContext);
|
|
9
|
+
const previous = React.useRef({});
|
|
10
|
+
const current = {};
|
|
11
|
+
for (const [slot, value] of Object.entries(segments || {})) current[slot] = value === null ? cache?.segments[id]?.[slot] || previous.current[slot] || [] : value;
|
|
12
|
+
React.useLayoutEffect(() => { previous.current = current; if (cache && id) cache.segments[id] = current; });
|
|
13
|
+
return React.createElement(LayoutContext.Provider, { value: current }, children);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function LayoutSlot({ id, preserve, children }) {
|
|
17
|
+
const cache = React.useContext(LayoutCacheContext);
|
|
18
|
+
const previous = React.useRef(children);
|
|
19
|
+
const current = preserve && cache && Object.hasOwn(cache.slots, id) ? cache.slots[id] : preserve ? previous.current : children;
|
|
20
|
+
React.useLayoutEffect(() => { previous.current = current; if (cache && id) cache.slots[id] = current; }, [cache, id, current]);
|
|
21
|
+
return current ?? null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports.LayoutContext = LayoutContext;
|
|
25
|
+
module.exports.LayoutCacheContext = LayoutCacheContext;
|
|
26
|
+
module.exports.LayoutProvider = LayoutProvider;
|
|
27
|
+
module.exports.LayoutSlot = LayoutSlot;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const React = require('react');
|
|
5
|
+
const { AppRouterContext } = require('./app-context.cjs');
|
|
6
|
+
|
|
7
|
+
function navigation(error) {
|
|
8
|
+
const digest = error?.digest;
|
|
9
|
+
if (digest === 'NEXT_HTTP_ERROR_FALLBACK;404') return { notFound: true };
|
|
10
|
+
if (typeof digest !== 'string' || !digest.startsWith('NEXT_REDIRECT;')) return null;
|
|
11
|
+
const parts = digest.split(';');
|
|
12
|
+
if (parts.at(-1) === '') parts.pop();
|
|
13
|
+
const status = Number(parts.pop());
|
|
14
|
+
const href = parts.slice(2).join(';');
|
|
15
|
+
if (![307, 308].includes(status) || !href || /[\r\n]/.test(href)) return null;
|
|
16
|
+
let url;
|
|
17
|
+
try { url = new URL(href, 'http://prnext.invalid'); } catch { return null; }
|
|
18
|
+
if (!['http:', 'https:'].includes(url.protocol)) return null;
|
|
19
|
+
return { href, replace: parts[1] === 'replace' };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class AppNavigationBoundary extends React.Component {
|
|
23
|
+
static contextType = AppRouterContext;
|
|
24
|
+
state = { control: null, resetKey: this.props.resetKey };
|
|
25
|
+
static getDerivedStateFromError(error) {
|
|
26
|
+
const control = navigation(error);
|
|
27
|
+
if (!control) throw error;
|
|
28
|
+
return { control };
|
|
29
|
+
}
|
|
30
|
+
static getDerivedStateFromProps(props, state) {
|
|
31
|
+
return props.resetKey !== state.resetKey ? { control: null, resetKey: props.resetKey } : null;
|
|
32
|
+
}
|
|
33
|
+
componentDidCatch(error) {
|
|
34
|
+
const control = navigation(error);
|
|
35
|
+
if (control?.href) this.context.router[control.replace ? 'replace' : 'push'](control.href);
|
|
36
|
+
}
|
|
37
|
+
render() {
|
|
38
|
+
if (this.state.control?.notFound) return React.createElement(React.Fragment, null,
|
|
39
|
+
React.createElement('meta', { name: 'robots', content: 'noindex' }), this.props.notFound);
|
|
40
|
+
if (this.state.control) return null;
|
|
41
|
+
return this.props.children;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = AppNavigationBoundary;
|
|
46
|
+
module.exports.default = AppNavigationBoundary;
|
|
47
|
+
module.exports.getNavigationControl = navigation;
|
package/compat/app.cjs
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const React = require('react');
|
|
3
|
+
|
|
4
|
+
// Shared by the server and browser: an App without a hook still delegates to
|
|
5
|
+
// its Page, while a custom App hook owns that delegation itself.
|
|
6
|
+
async function loadGetInitialProps(Component, context) {
|
|
7
|
+
if (process.env.NODE_ENV !== 'production' && Component.prototype?.getInitialProps) {
|
|
8
|
+
throw new Error(`${Component.displayName || Component.name || 'Component'}.getInitialProps must be a static method`);
|
|
9
|
+
}
|
|
10
|
+
const response = context.res || context.ctx?.res;
|
|
11
|
+
if (!Component.getInitialProps) {
|
|
12
|
+
if (context.ctx && context.Component) return { pageProps: await loadGetInitialProps(context.Component, context.ctx) };
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
const props = await Component.getInitialProps(context);
|
|
16
|
+
if (response?.finished || response?.headersSent || response?.writableEnded) return props;
|
|
17
|
+
if (!props) throw new Error(`${Component.displayName || Component.name || 'Component'}.getInitialProps must return an object; received ${props}`);
|
|
18
|
+
return props;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function appGetInitialProps({ Component, ctx }) {
|
|
22
|
+
return { pageProps: await loadGetInitialProps(Component, ctx) };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class App extends React.Component {
|
|
26
|
+
static origGetInitialProps = appGetInitialProps;
|
|
27
|
+
static getInitialProps = appGetInitialProps;
|
|
28
|
+
render() { return React.createElement(this.props.Component, this.props.pageProps); }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = App;
|
|
32
|
+
module.exports.default = App;
|
|
33
|
+
module.exports.loadGetInitialProps = loadGetInitialProps;
|
package/compat/app.d.cts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Component } from 'react';
|
|
2
|
+
import type { AppContext, AppInitialProps, AppProps } from './index.d.ts';
|
|
3
|
+
export type { AppContext, AppInitialProps, AppProps } from './index.d.ts';
|
|
4
|
+
|
|
5
|
+
export default class App<P = {}, CP = {}, S = {}> extends Component<P & AppProps<CP>, S> {
|
|
6
|
+
static origGetInitialProps(context: AppContext): Promise<AppInitialProps>;
|
|
7
|
+
static getInitialProps(context: AppContext): Promise<AppInitialProps>;
|
|
8
|
+
render(): import('react').ReactElement;
|
|
9
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { pathToFileURL } = require('node:url');
|
|
4
|
+
const { validateTags, cacheState, getCachePaths } = require('./data-cache.cjs');
|
|
5
|
+
const MAX_BYTES = 2 * 1024 * 1024;
|
|
6
|
+
const modules = new Map();
|
|
7
|
+
const requests = new WeakMap();
|
|
8
|
+
const revisions = new WeakMap();
|
|
9
|
+
const owners = new WeakMap();
|
|
10
|
+
let pendingCount = 0;
|
|
11
|
+
|
|
12
|
+
function configured(context) { return context?.cacheHandlers || context?.manifest?.config?.cacheHandlers || {}; }
|
|
13
|
+
function outsideRequest(callback) { return require('./headers.cjs').runWithoutRequestContext(callback); }
|
|
14
|
+
function wait(promise, signal, timeoutMs = 5000) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const timer = setTimeout(() => finish(reject, new Error('Cache handler operation timed out')), Math.max(1, timeoutMs));
|
|
17
|
+
timer.unref();
|
|
18
|
+
function finish(callback, value) { clearTimeout(timer); signal?.removeEventListener('abort', abort); callback(value); }
|
|
19
|
+
function abort() { finish(reject, signal.reason); }
|
|
20
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
21
|
+
if (signal?.aborted) abort();
|
|
22
|
+
Promise.resolve(promise).then(value => finish(resolve, value), error => finish(reject, error));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async function call(handler, method, args, context) {
|
|
26
|
+
return wait(outsideRequest(() => Promise.resolve().then(() => handler[method](...args))), context?.signal);
|
|
27
|
+
}
|
|
28
|
+
function softTags(context) {
|
|
29
|
+
// Next's implicit tags use /layout and /page suffixes and the _N_T_ prefix.
|
|
30
|
+
return [...new Set(getCachePaths(context).map(value => {
|
|
31
|
+
const separator = value.indexOf(':');
|
|
32
|
+
return '_N_T_' + value.slice(separator + 1).replace(/\/$/, '') + '/' + value.slice(0, separator);
|
|
33
|
+
}).concat(context?.url ? ['_N_T_' + (new URL(context.url).pathname.replace(/\/$/, '') || '/')] : []))];
|
|
34
|
+
}
|
|
35
|
+
async function load(context, kind) {
|
|
36
|
+
const source = configured(context)[kind];
|
|
37
|
+
if (!source) return undefined;
|
|
38
|
+
if (!context.distDir) throw new Error('Custom cache handlers require a build directory');
|
|
39
|
+
const root = path.resolve(context.distDir);
|
|
40
|
+
const file = path.resolve(root, source);
|
|
41
|
+
if (!source.startsWith('server/') || !file.startsWith(root + path.sep)) throw new Error('Invalid cache handler module path');
|
|
42
|
+
let pending = modules.get(file);
|
|
43
|
+
if (!pending) {
|
|
44
|
+
pending = outsideRequest(() => import(pathToFileURL(file).href)).then(module => {
|
|
45
|
+
const handler = module.default;
|
|
46
|
+
if (!handler || ['get', 'set', 'refreshTags', 'getExpiration', 'updateTags'].some(name => typeof handler[name] !== 'function')) throw new TypeError(`cacheHandlers.${kind} must export get, set, refreshTags, getExpiration and updateTags methods`);
|
|
47
|
+
return handler;
|
|
48
|
+
});
|
|
49
|
+
// Production workers have a single build. Bound this lookup for direct runtime callers.
|
|
50
|
+
if (modules.size >= 32) modules.delete(modules.keys().next().value);
|
|
51
|
+
modules.set(file, pending);
|
|
52
|
+
}
|
|
53
|
+
return wait(pending, context.signal);
|
|
54
|
+
}
|
|
55
|
+
async function refresh(handler, context) {
|
|
56
|
+
let refreshed = requests.get(context);
|
|
57
|
+
if (!refreshed) requests.set(context, refreshed = new Map());
|
|
58
|
+
if (!refreshed.has(handler)) refreshed.set(handler, call(handler, 'refreshTags', [], context));
|
|
59
|
+
return refreshed.get(handler);
|
|
60
|
+
}
|
|
61
|
+
function metadata(entry) {
|
|
62
|
+
if (!entry || !entry.value || typeof entry.value.getReader !== 'function' || typeof entry.value.cancel !== 'function') throw new TypeError('Invalid CacheEntry stream');
|
|
63
|
+
const tags = validateTags(entry.tags);
|
|
64
|
+
for (const field of ['timestamp', 'stale', 'revalidate', 'expire']) if (typeof entry[field] !== 'number' || !Number.isFinite(entry[field]) || entry[field] < 0) throw new TypeError(`Invalid CacheEntry ${field}`);
|
|
65
|
+
return { tags, timestamp: entry.timestamp, stale: entry.stale, revalidate: entry.revalidate, expire: entry.expire };
|
|
66
|
+
}
|
|
67
|
+
async function read(entry, context) {
|
|
68
|
+
const reader = entry.value.getReader();
|
|
69
|
+
const chunks = [];
|
|
70
|
+
let size = 0, complete = false;
|
|
71
|
+
const deadline = Date.now() + 5000;
|
|
72
|
+
let chunksRead = 0;
|
|
73
|
+
try {
|
|
74
|
+
while (true) {
|
|
75
|
+
if (Date.now() >= deadline || ++chunksRead > 10_000) throw new Error('CacheEntry stream exceeded its read budget');
|
|
76
|
+
const { value, done } = await wait(reader.read(), context.signal, deadline - Date.now());
|
|
77
|
+
if (done) { complete = true; break; }
|
|
78
|
+
if (!(value instanceof Uint8Array)) throw new TypeError('CacheEntry streams must contain bytes');
|
|
79
|
+
size += value.byteLength;
|
|
80
|
+
if (size > MAX_BYTES) throw new Error('CacheEntry exceeds 2 MiB');
|
|
81
|
+
chunks.push(Buffer.from(value));
|
|
82
|
+
}
|
|
83
|
+
return Buffer.concat(chunks, size);
|
|
84
|
+
} finally {
|
|
85
|
+
if (!complete) void reader.cancel().catch(() => {});
|
|
86
|
+
reader.releaseLock();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function stream(bytes) {
|
|
90
|
+
let offset = 0;
|
|
91
|
+
return new ReadableStream({ pull(controller) {
|
|
92
|
+
if (offset === bytes.length) { controller.close(); return; }
|
|
93
|
+
const end = Math.min(bytes.length, offset + 64 * 1024);
|
|
94
|
+
controller.enqueue(new Uint8Array(bytes.subarray(offset, end))); offset = end;
|
|
95
|
+
} });
|
|
96
|
+
}
|
|
97
|
+
async function expiration(handler, tags, context) {
|
|
98
|
+
const value = await call(handler, 'getExpiration', [tags], context);
|
|
99
|
+
if (typeof value !== 'number' || value < 0 || Number.isNaN(value)) throw new TypeError('Cache handler getExpiration must return a nonnegative timestamp');
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
async function customCachedValue(key, kind, producer, options) {
|
|
103
|
+
const { context, resolveMetadata } = options;
|
|
104
|
+
const handler = await load(context, kind);
|
|
105
|
+
if (!handler) return undefined;
|
|
106
|
+
const state = cacheState(context);
|
|
107
|
+
await state.invalidations;
|
|
108
|
+
context.signal?.throwIfAborted();
|
|
109
|
+
const tags = softTags(context);
|
|
110
|
+
let entry, info, bytes;
|
|
111
|
+
try {
|
|
112
|
+
await refresh(handler, context);
|
|
113
|
+
const tagTime = await expiration(handler, tags, context);
|
|
114
|
+
entry = await call(handler, 'get', [key, tags], context);
|
|
115
|
+
if (entry) {
|
|
116
|
+
info = metadata(entry);
|
|
117
|
+
const age = Date.now() - info.timestamp;
|
|
118
|
+
if ((tagTime === Infinity || tagTime < info.timestamp) && age < info.expire * 1000 && (!context.staticState || age < info.revalidate * 1000)) bytes = await read(entry, context);
|
|
119
|
+
else void entry.value.cancel().catch(() => {});
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
context.signal?.throwIfAborted();
|
|
123
|
+
if (typeof entry?.value?.cancel === 'function' && !entry.value.locked) void entry.value.cancel().catch(() => {});
|
|
124
|
+
}
|
|
125
|
+
const stale = bytes && Date.now() - info.timestamp >= info.revalidate * 1000;
|
|
126
|
+
if (bytes) options.onReadMetadata?.(info);
|
|
127
|
+
if (bytes && !stale) return bytes;
|
|
128
|
+
let pending = owners.get(handler);
|
|
129
|
+
if (!pending) owners.set(handler, pending = new Map());
|
|
130
|
+
const existing = pending.get(key);
|
|
131
|
+
// A post-mutation reader must not join a producer started before invalidation.
|
|
132
|
+
// Preventing that producer's write alone would still return its old value.
|
|
133
|
+
const joined = options.allowJoin !== false && existing?.revision === (revisions.get(handler) || 0) ? existing : undefined;
|
|
134
|
+
let work = joined?.work;
|
|
135
|
+
if (!work) {
|
|
136
|
+
const revision = revisions.get(handler) || 0;
|
|
137
|
+
const timestamp = performance.timeOrigin + performance.now();
|
|
138
|
+
const record = { revision, timestamp, work: undefined, metadata: undefined };
|
|
139
|
+
work = (async () => {
|
|
140
|
+
const result = await producer();
|
|
141
|
+
const info = resolveMetadata();
|
|
142
|
+
record.metadata = info;
|
|
143
|
+
if (result.byteLength <= MAX_BYTES && revision === (revisions.get(handler) || 0)) {
|
|
144
|
+
try {
|
|
145
|
+
const changed = await expiration(handler, [...new Set([...tags, ...info.tags])], context);
|
|
146
|
+
// Many backends record Date.now() millisecond timestamps. Treat the
|
|
147
|
+
// same millisecond conservatively so fractional producer times cannot
|
|
148
|
+
// hide an invalidation which actually happened after the read started.
|
|
149
|
+
if ((changed === Infinity || changed < Math.floor(timestamp)) && revision === (revisions.get(handler) || 0)) {
|
|
150
|
+
const value = stream(result);
|
|
151
|
+
try { await call(handler, 'set', [key, Promise.resolve({ value, tags: info.tags, stale: info.stale, revalidate: info.revalidate, expire: info.expire, timestamp })], context); }
|
|
152
|
+
catch { if (!value.locked) void value.cancel().catch(() => {}); }
|
|
153
|
+
}
|
|
154
|
+
} catch { context.signal?.throwIfAborted(); }
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
})();
|
|
158
|
+
record.work = work;
|
|
159
|
+
if (pendingCount < 64) {
|
|
160
|
+
pendingCount++; pending.set(key, record);
|
|
161
|
+
const cleanup = () => { if (pending.get(key) === record) pending.delete(key); pendingCount--; };
|
|
162
|
+
void work.then(cleanup, cleanup);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (stale) {
|
|
166
|
+
const background = work.catch(() => {});
|
|
167
|
+
state.pending.add(background);
|
|
168
|
+
void background.finally(() => state.pending.delete(background));
|
|
169
|
+
return bytes;
|
|
170
|
+
}
|
|
171
|
+
const result = await work;
|
|
172
|
+
if (joined) {
|
|
173
|
+
let invalidated = joined.revision !== (revisions.get(handler) || 0);
|
|
174
|
+
let confirmed;
|
|
175
|
+
try {
|
|
176
|
+
const changed = await expiration(handler, [...new Set([...tags, ...(joined.metadata?.tags || [])])], context);
|
|
177
|
+
// Infinity delegates expiration to handler.get; it is not an invalidation
|
|
178
|
+
// timestamp. A finite timestamp can expose a different instance's update.
|
|
179
|
+
invalidated ||= changed !== Infinity && changed >= Math.floor(joined.timestamp);
|
|
180
|
+
if (changed === Infinity && !invalidated) {
|
|
181
|
+
// The backend may know about an invalidation that this worker cannot
|
|
182
|
+
// observe through getExpiration. Re-read its published value instead of
|
|
183
|
+
// trusting the result returned directly by the older producer.
|
|
184
|
+
confirmed = await call(handler, 'get', [key, tags], context);
|
|
185
|
+
if (confirmed) {
|
|
186
|
+
const confirmedInfo = metadata(confirmed);
|
|
187
|
+
const age = Date.now() - confirmedInfo.timestamp;
|
|
188
|
+
if (age < Math.min(confirmedInfo.expire, confirmedInfo.revalidate) * 1000) {
|
|
189
|
+
const confirmedBytes = await read(confirmed, context);
|
|
190
|
+
options.onReadMetadata?.(confirmedInfo);
|
|
191
|
+
return confirmedBytes;
|
|
192
|
+
}
|
|
193
|
+
void confirmed.value.cancel().catch(() => {});
|
|
194
|
+
}
|
|
195
|
+
invalidated = true;
|
|
196
|
+
}
|
|
197
|
+
} catch {
|
|
198
|
+
context.signal?.throwIfAborted(); invalidated = true;
|
|
199
|
+
if (typeof confirmed?.value?.cancel === 'function' && !confirmed.value.locked) void confirmed.value.cancel().catch(() => {});
|
|
200
|
+
}
|
|
201
|
+
// Only a joined reader retries, at most once, and cannot join another old
|
|
202
|
+
// producer on that attempt. The original caller's producer is never replayed.
|
|
203
|
+
if (invalidated) return customCachedValue(key, kind, producer, { ...options, allowJoin: false });
|
|
204
|
+
}
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
async function invalidateHandlers(context, operation) {
|
|
208
|
+
const tags = [...(operation.tags || []), ...(operation.paths || []).map(value => {
|
|
209
|
+
const separator = value.indexOf(':');
|
|
210
|
+
return '_N_T_' + value.slice(separator + 1).replace(/\/$/, '') + '/' + value.slice(0, separator);
|
|
211
|
+
})];
|
|
212
|
+
const durations = operation.mode === 'expire' ? { expire: 0 } : operation.expire === undefined ? undefined : { expire: operation.expire };
|
|
213
|
+
const seen = new Set();
|
|
214
|
+
for (const kind of Object.keys(configured(context))) {
|
|
215
|
+
const handler = await load(context, kind);
|
|
216
|
+
if (seen.has(handler)) continue;
|
|
217
|
+
seen.add(handler);
|
|
218
|
+
revisions.set(handler, (revisions.get(handler) || 0) + 1);
|
|
219
|
+
await call(handler, 'updateTags', [tags, durations], context);
|
|
220
|
+
requests.get(context)?.delete(handler);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
module.exports = { configured, customCachedValue, invalidateHandlers, softTags };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const defaults = Object.freeze({
|
|
3
|
+
default: { stale: 300, revalidate: 900, expire: 4294967294 },
|
|
4
|
+
seconds: { stale: 30, revalidate: 1, expire: 60 },
|
|
5
|
+
minutes: { stale: 300, revalidate: 60, expire: 3600 },
|
|
6
|
+
hours: { stale: 300, revalidate: 3600, expire: 86400 },
|
|
7
|
+
days: { stale: 300, revalidate: 86400, expire: 604800 },
|
|
8
|
+
weeks: { stale: 300, revalidate: 604800, expire: 2592000 },
|
|
9
|
+
max: { stale: 300, revalidate: 2592000, expire: 31536000 },
|
|
10
|
+
});
|
|
11
|
+
function validateLife(input, { partial = true } = {}) {
|
|
12
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new TypeError('cacheLife requires a profile name or { stale, revalidate, expire }');
|
|
13
|
+
const result = {};
|
|
14
|
+
for (const key of Object.keys(input)) {
|
|
15
|
+
if (!['stale', 'revalidate', 'expire'].includes(key)) throw new TypeError(`Unknown cacheLife option: ${key}`);
|
|
16
|
+
const value = input[key];
|
|
17
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0 || value > 4294967294) throw new TypeError(`cacheLife ${key} must be a nonnegative finite number of seconds`);
|
|
18
|
+
result[key] = value;
|
|
19
|
+
}
|
|
20
|
+
if (!Object.keys(result).length || !partial && Object.keys(result).length !== 3) throw new TypeError('cacheLife profiles must specify their lifetime');
|
|
21
|
+
if (result.expire !== undefined && result.revalidate !== undefined && result.expire < result.revalidate) throw new TypeError('cacheLife expire must be at least revalidate');
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
function profiles(input = {}) {
|
|
25
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new TypeError('cacheLife configuration must be an object of profiles');
|
|
26
|
+
const result = Object.fromEntries(Object.entries(defaults).map(([key, value]) => [key, { ...value }]));
|
|
27
|
+
for (const [key, value] of Object.entries(input)) {
|
|
28
|
+
if (!key || ['__proto__', 'constructor', 'prototype'].includes(key)) throw new TypeError('Invalid cacheLife profile name');
|
|
29
|
+
result[key] = { ...(result[key] || defaults.default), ...validateLife(value) };
|
|
30
|
+
validateLife(result[key], { partial: false });
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
module.exports = { defaults, validateLife, profiles };
|
package/compat/cache.cjs
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { createHash } = require('node:crypto');
|
|
3
|
+
const { dynamicUsage, staticBailout, trackStaticDependency } = require('./static-generation.cjs');
|
|
4
|
+
const { cacheTag, cacheLife, recordCacheDependency } = require('./use-cache.cjs');
|
|
5
|
+
const {
|
|
6
|
+
cachedValue, cacheState, runCacheScope, inCacheScope, optionalRequestContext,
|
|
7
|
+
getCachePaths, normalizeCachePath, validateTags, validateRevalidate, queueInvalidation,
|
|
8
|
+
} = require('./data-cache.cjs');
|
|
9
|
+
|
|
10
|
+
function unstable_cache(callback, keyParts = [], options = {}) {
|
|
11
|
+
if (typeof callback !== 'function') throw new TypeError('unstable_cache requires a function');
|
|
12
|
+
if (!Array.isArray(keyParts) || keyParts.some(part => typeof part !== 'string')) throw new TypeError('unstable_cache keyParts must be an array of strings');
|
|
13
|
+
const tags = validateTags(options.tags);
|
|
14
|
+
const revalidate = validateRevalidate(options.revalidate === Infinity ? false : options.revalidate);
|
|
15
|
+
const fixedKey = JSON.stringify([callback.toString(), keyParts]);
|
|
16
|
+
return async function (...args) {
|
|
17
|
+
const context = optionalRequestContext();
|
|
18
|
+
const paths = getCachePaths(context);
|
|
19
|
+
if (context?.staticState) trackStaticDependency({ tags, revalidate, paths }, context);
|
|
20
|
+
recordCacheDependency({ tags, revalidate });
|
|
21
|
+
// Nested calls must not wait on a lease already owned by their outer call.
|
|
22
|
+
// A route's explicit force-no-store setting also prevents cached reads.
|
|
23
|
+
if (inCacheScope() || context?.cacheConfig?.forceNoStore || context?.draftMode) {
|
|
24
|
+
return runCacheScope(() => callback.apply(this, args));
|
|
25
|
+
}
|
|
26
|
+
const key = createHash('sha256').update(JSON.stringify(['unstable_cache', fixedKey, args])).digest('hex');
|
|
27
|
+
let produced = false;
|
|
28
|
+
let result;
|
|
29
|
+
const bytes = await cachedValue(key, async ({ background }) => runCacheScope(async () => {
|
|
30
|
+
const value = await callback.apply(this, args);
|
|
31
|
+
const serialized = Buffer.from(JSON.stringify({ value }));
|
|
32
|
+
if (!background) { produced = true; result = value; }
|
|
33
|
+
return serialized;
|
|
34
|
+
}), { tags, paths, revalidate, context,
|
|
35
|
+
incremental: {
|
|
36
|
+
encode: bytes => { const { value } = JSON.parse(bytes.toString('utf8')); return { headers: {}, status: 200, url: '', body: JSON.stringify(value) ?? 'null', ...(value === undefined ? { _prnextUndefined: true } : {}) }; },
|
|
37
|
+
decode: entry => {
|
|
38
|
+
if (typeof entry.data?.body !== 'string' || Buffer.byteLength(entry.data.body) > 2 * 1024 * 1024) throw new Error('Invalid incremental cached function data');
|
|
39
|
+
return Buffer.from(JSON.stringify(entry.data._prnextUndefined ? {} : { value: JSON.parse(entry.data.body) }));
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
forceFresh: !context || context.phase === 'pages' || Boolean(context.staticState) });
|
|
43
|
+
return produced ? result : JSON.parse(bytes.toString('utf8')).value;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function mutationContext(name, actionOnly = false) {
|
|
48
|
+
if (inCacheScope()) throw new Error(`${name} cannot be called inside unstable_cache`);
|
|
49
|
+
const context = optionalRequestContext();
|
|
50
|
+
if (!context || (actionOnly ? context.phase !== 'action' : !['action', 'route'].includes(context.phase))) {
|
|
51
|
+
throw new Error(`${name} can only be called in ${actionOnly ? 'a Server Action' : 'a Server Action or Route Handler'}, outside rendering`);
|
|
52
|
+
}
|
|
53
|
+
if (context.cacheState?.closed) throw new Error(`${name} cannot be called after the response headers have been sent`);
|
|
54
|
+
if (context.staticState) staticBailout(`${name}()`, context);
|
|
55
|
+
return context;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Only the expiration window is relevant to tag invalidation. Values match
|
|
59
|
+
// Next's built-in cacheLife profiles; custom next.config profiles are not yet read.
|
|
60
|
+
const expirations = Object.freeze({ default: null, seconds: 60, minutes: 3600, hours: 86400, days: 604800, weeks: 2592000, max: 31536000 });
|
|
61
|
+
function revalidateTag(tag, profile) {
|
|
62
|
+
const context = mutationContext('revalidateTag');
|
|
63
|
+
validateTags([tag]);
|
|
64
|
+
let expire;
|
|
65
|
+
if (profile === undefined) expire = 0;
|
|
66
|
+
else if (typeof profile === 'string') {
|
|
67
|
+
const configured = context.cacheLife || context.manifest?.config?.cacheLife;
|
|
68
|
+
if (configured && Object.hasOwn(configured, profile)) expire = configured[profile].expire;
|
|
69
|
+
else {
|
|
70
|
+
if (!Object.hasOwn(expirations, profile)) throw new TypeError(`Unknown cache lifetime profile: ${profile}`);
|
|
71
|
+
expire = expirations[profile];
|
|
72
|
+
}
|
|
73
|
+
} else if (profile && typeof profile === 'object' && !Array.isArray(profile)) {
|
|
74
|
+
expire = profile.expire ?? null;
|
|
75
|
+
if (expire !== null && (typeof expire !== 'number' || !Number.isFinite(expire) || expire < 0)) throw new TypeError('revalidateTag expire must be a nonnegative number of seconds');
|
|
76
|
+
} else throw new TypeError('revalidateTag requires a cache profile name or an expire option');
|
|
77
|
+
queueInvalidation(context, { tags: [tag], mode: expire === 0 ? 'expire' : 'stale', ...(expire === null ? {} : { expire }) });
|
|
78
|
+
}
|
|
79
|
+
function updateTag(tag) {
|
|
80
|
+
const context = mutationContext('updateTag', true);
|
|
81
|
+
validateTags([tag]);
|
|
82
|
+
queueInvalidation(context, { tags: [tag], mode: 'expire' });
|
|
83
|
+
}
|
|
84
|
+
function revalidatePath(path, type) {
|
|
85
|
+
const context = mutationContext('revalidatePath');
|
|
86
|
+
if (type !== undefined && type !== 'page' && type !== 'layout') throw new TypeError('revalidatePath type must be page or layout');
|
|
87
|
+
if (typeof path === 'string' && /\[[^/]+\]/.test(path) && !type) throw new TypeError('revalidatePath requires a page or layout type for dynamic paths');
|
|
88
|
+
const normalized = normalizeCachePath(path, { groups: true });
|
|
89
|
+
queueInvalidation(context, { paths: [`${type || 'page'}:${normalized}`], mode: 'expire' });
|
|
90
|
+
}
|
|
91
|
+
function unstable_noStore() {
|
|
92
|
+
if (inCacheScope()) return;
|
|
93
|
+
if (!dynamicUsage('unstable_noStore()')) return;
|
|
94
|
+
const state = cacheState();
|
|
95
|
+
if (state) state.noStore = true;
|
|
96
|
+
}
|
|
97
|
+
function refresh() { cacheState(mutationContext('refresh', true)).refresh = true; }
|
|
98
|
+
|
|
99
|
+
module.exports = { unstable_cache, revalidateTag, updateTag, revalidatePath, unstable_noStore, refresh, cacheTag, cacheLife };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/// <reference path="./fetch.d.cts" />
|
|
2
|
+
export interface CacheLife { stale?: number; revalidate?: number; expire?: number }
|
|
3
|
+
export function cacheLife(profile: string | CacheLife): void;
|
|
4
|
+
|
|
5
|
+
/** Storage interface accepted by the cacheHandlers project option. */
|
|
6
|
+
export interface CacheEntry {
|
|
7
|
+
value: ReadableStream<Uint8Array>;
|
|
8
|
+
tags: string[];
|
|
9
|
+
stale: number;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
expire: number;
|
|
12
|
+
revalidate: number;
|
|
13
|
+
}
|
|
14
|
+
export interface CacheHandler {
|
|
15
|
+
get(cacheKey: string, softTags: string[]): Promise<CacheEntry | undefined>;
|
|
16
|
+
set(cacheKey: string, pendingEntry: Promise<CacheEntry>): Promise<void>;
|
|
17
|
+
refreshTags(): Promise<void>;
|
|
18
|
+
getExpiration(tags: string[]): Promise<number>;
|
|
19
|
+
updateTags(tags: string[], durations?: { expire?: number }): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export function cacheTag(...tags: string[]): void;
|
|
22
|
+
|
|
23
|
+
export interface UnstableCacheOptions {
|
|
24
|
+
tags?: string[];
|
|
25
|
+
revalidate?: number | false;
|
|
26
|
+
}
|
|
27
|
+
export function unstable_cache<T extends (...args: any[]) => Promise<any>>(
|
|
28
|
+
callback: T,
|
|
29
|
+
keyParts?: string[],
|
|
30
|
+
options?: UnstableCacheOptions,
|
|
31
|
+
): T;
|
|
32
|
+
export function revalidateTag(tag: string, profile?: string | { expire?: number }): void;
|
|
33
|
+
export function updateTag(tag: string): void;
|
|
34
|
+
export function revalidatePath(path: string, type?: 'page' | 'layout'): void;
|
|
35
|
+
export function unstable_noStore(): void;
|
|
36
|
+
export function refresh(): void;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const React = require('react');
|
|
3
|
+
const {RouterContext} = require('./router.cjs');
|
|
4
|
+
|
|
5
|
+
// Shared migration components may render outside the Pages Router provider.
|
|
6
|
+
function useRouter() { return React.useContext(RouterContext); }
|
|
7
|
+
module.exports = {useRouter};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
exports.PHASE_EXPORT = 'phase-export';
|
|
3
|
+
exports.PHASE_ANALYZE = 'phase-analyze';
|
|
4
|
+
exports.PHASE_PRODUCTION_BUILD = 'phase-production-build';
|
|
5
|
+
exports.PHASE_PRODUCTION_SERVER = 'phase-production-server';
|
|
6
|
+
exports.PHASE_DEVELOPMENT_SERVER = 'phase-development-server';
|
|
7
|
+
exports.PHASE_TEST = 'phase-test';
|
|
8
|
+
exports.PHASE_INFO = 'phase-info';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const PHASE_EXPORT: 'phase-export';
|
|
2
|
+
export const PHASE_ANALYZE: 'phase-analyze';
|
|
3
|
+
export const PHASE_PRODUCTION_BUILD: 'phase-production-build';
|
|
4
|
+
export const PHASE_PRODUCTION_SERVER: 'phase-production-server';
|
|
5
|
+
export const PHASE_DEVELOPMENT_SERVER: 'phase-development-server';
|
|
6
|
+
export const PHASE_TEST: 'phase-test';
|
|
7
|
+
export const PHASE_INFO: 'phase-info';
|