From 683f72f72317cbc9541a314605fe22d8a9a345b4 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 29 Mar 2026 21:52:17 +0100 Subject: [PATCH 1/2] chore: turn on lint rule to prefer `type` over `interface` --- packages/vinext/src/build/prerender.ts | 16 ++--- packages/vinext/src/build/report.ts | 4 +- packages/vinext/src/build/run-prerender.ts | 4 +- packages/vinext/src/build/static-export.ts | 12 ++-- packages/vinext/src/check.ts | 8 +-- packages/vinext/src/cli.ts | 8 +-- .../src/client/instrumentation-client.ts | 4 +- .../vinext/src/client/vinext-next-data.ts | 4 +- .../vinext/src/cloudflare/kv-cache-handler.ts | 8 +-- packages/vinext/src/cloudflare/tpr.ts | 24 +++---- packages/vinext/src/config/config-matchers.ts | 4 +- packages/vinext/src/config/dotenv.ts | 8 +-- packages/vinext/src/config/next-config.ts | 32 ++++----- packages/vinext/src/deploy.ts | 20 +++--- packages/vinext/src/entries/app-rsc-entry.ts | 4 +- packages/vinext/src/global.d.ts | 2 + packages/vinext/src/index.ts | 8 +-- packages/vinext/src/init.ts | 8 +-- .../vinext/src/plugins/optimize-imports.ts | 16 ++--- packages/vinext/src/routing/app-router.ts | 12 ++-- packages/vinext/src/routing/file-matcher.ts | 4 +- packages/vinext/src/routing/pages-router.ts | 4 +- packages/vinext/src/routing/route-trie.ts | 4 +- packages/vinext/src/server/api-handler.ts | 8 +-- .../vinext/src/server/app-browser-entry.ts | 4 +- .../vinext/src/server/app-browser-stream.ts | 12 ++-- .../src/server/app-page-boundary-render.ts | 37 +++++----- .../vinext/src/server/app-page-boundary.ts | 22 +++--- packages/vinext/src/server/app-page-cache.ts | 20 +++--- .../vinext/src/server/app-page-execution.ts | 20 +++--- packages/vinext/src/server/app-page-probe.ts | 4 +- packages/vinext/src/server/app-page-render.ts | 8 +-- .../vinext/src/server/app-page-request.ts | 24 +++---- .../vinext/src/server/app-page-response.ts | 36 +++++----- packages/vinext/src/server/app-page-stream.ts | 36 +++++----- .../src/server/app-route-handler-cache.ts | 4 +- .../src/server/app-route-handler-execution.ts | 12 ++-- .../src/server/app-route-handler-policy.ts | 16 ++--- .../src/server/app-route-handler-response.ts | 12 ++-- .../src/server/app-route-handler-runtime.ts | 8 +-- packages/vinext/src/server/app-ssr-entry.ts | 8 +-- packages/vinext/src/server/app-ssr-stream.ts | 4 +- .../vinext/src/server/dev-module-runner.ts | 4 +- .../vinext/src/server/image-optimization.ts | 8 +-- packages/vinext/src/server/instrumentation.ts | 8 +-- packages/vinext/src/server/isr-cache.ts | 4 +- packages/vinext/src/server/metadata-routes.ts | 20 +++--- packages/vinext/src/server/middleware.ts | 4 +- packages/vinext/src/server/pages-api-route.ts | 12 ++-- packages/vinext/src/server/pages-i18n.ts | 8 +-- .../vinext/src/server/pages-node-compat.ts | 20 +++--- packages/vinext/src/server/pages-page-data.ts | 44 ++++++------ .../vinext/src/server/pages-page-response.ts | 20 +++--- packages/vinext/src/server/prod-server.ts | 16 ++--- packages/vinext/src/server/seed-cache.ts | 8 +-- packages/vinext/src/shims/app.ts | 4 +- packages/vinext/src/shims/cache-runtime.ts | 12 ++-- packages/vinext/src/shims/cache.ts | 60 ++++++++-------- packages/vinext/src/shims/config.ts | 4 +- packages/vinext/src/shims/dynamic.ts | 4 +- packages/vinext/src/shims/error-boundary.tsx | 20 +++--- packages/vinext/src/shims/error.tsx | 4 +- packages/vinext/src/shims/fetch-cache.ts | 13 ++-- packages/vinext/src/shims/font-google-base.ts | 8 +-- packages/vinext/src/shims/font-local.ts | 12 ++-- packages/vinext/src/shims/form.tsx | 4 +- packages/vinext/src/shims/head-state.ts | 4 +- packages/vinext/src/shims/head.ts | 4 +- packages/vinext/src/shims/headers.ts | 8 +-- packages/vinext/src/shims/i18n-context.ts | 4 +- packages/vinext/src/shims/i18n-state.ts | 4 +- packages/vinext/src/shims/image-config.ts | 4 +- packages/vinext/src/shims/image.tsx | 8 +-- .../src/shims/internal/app-router-context.ts | 12 ++-- packages/vinext/src/shims/internal/utils.ts | 4 +- packages/vinext/src/shims/legacy-image.tsx | 4 +- packages/vinext/src/shims/link.tsx | 12 ++-- packages/vinext/src/shims/metadata.tsx | 32 ++++----- packages/vinext/src/shims/navigation-state.ts | 4 +- packages/vinext/src/shims/navigation.ts | 12 ++-- packages/vinext/src/shims/next-shims.d.ts | 72 +++++++++---------- packages/vinext/src/shims/request-context.ts | 4 +- packages/vinext/src/shims/router-state.ts | 8 +-- packages/vinext/src/shims/router.ts | 20 +++--- packages/vinext/src/shims/script.tsx | 4 +- packages/vinext/src/shims/server.ts | 20 +++--- .../src/shims/unified-request-context.ts | 20 +++--- packages/vinext/src/shims/web-vitals.ts | 4 +- packages/vinext/src/vite-hmr.d.ts | 12 ++-- packages/vinext/src/vite-plugin-commonjs.d.ts | 4 +- tests/helpers.ts | 8 +-- tests/nitro-route-rules.test.ts | 8 +-- tests/pages-router.test.ts | 4 +- tests/route-trie.test.ts | 4 +- vite.config.ts | 2 +- 95 files changed, 571 insertions(+), 573 deletions(-) diff --git a/packages/vinext/src/build/prerender.ts b/packages/vinext/src/build/prerender.ts index ae508ebf2..5a67a0a0b 100644 --- a/packages/vinext/src/build/prerender.ts +++ b/packages/vinext/src/build/prerender.ts @@ -33,10 +33,10 @@ export { readPrerenderSecret } from "./server-manifest.js"; // ─── Public Types ───────────────────────────────────────────────────────────── -export interface PrerenderResult { +export type PrerenderResult = { /** One entry per route (including skipped/error routes). */ routes: PrerenderRouteResult[]; -} +}; export type PrerenderRouteResult = | { @@ -77,7 +77,7 @@ export type PrerenderProgressCallback = (update: { status: PrerenderRouteResult["status"]; }) => void; -export interface PrerenderOptions { +export type PrerenderOptions = { /** * 'default' — prerender static/ISR routes; skip SSR routes * 'export' — same as default but SSR routes are errors @@ -110,9 +110,9 @@ export interface PrerenderOptions { * multiple phases and write a single unified manifest itself. */ skipManifest?: boolean; -} +}; -export interface PrerenderPagesOptions extends PrerenderOptions { +export type PrerenderPagesOptions = { /** Discovered page routes (non-API). */ routes: Route[]; /** Discovered API routes. */ @@ -127,16 +127,16 @@ export interface PrerenderPagesOptions extends PrerenderOptions { * `runPrerender` passes a shared `_prodServer` instead. */ pagesBundlePath?: string; -} +} & PrerenderOptions; -export interface PrerenderAppOptions extends PrerenderOptions { +export type PrerenderAppOptions = { /** Discovered app routes. */ routes: AppRoute[]; /** * Absolute path to the pre-built RSC handler bundle (e.g. `dist/server/index.js`). */ rscBundlePath: string; -} +} & PrerenderOptions; // ─── Internal option extensions ─────────────────────────────────────────────── // These types extend the public option interfaces with an internal `_prodServer` diff --git a/packages/vinext/src/build/report.ts b/packages/vinext/src/build/report.ts index 4e4cfdfe2..a29b05842 100644 --- a/packages/vinext/src/build/report.ts +++ b/packages/vinext/src/build/report.ts @@ -29,7 +29,7 @@ import type { PrerenderResult } from "./prerender.js"; export type RouteType = "static" | "isr" | "ssr" | "unknown" | "api"; -export interface RouteRow { +export type RouteRow = { pattern: string; type: RouteType; /** Only set for `isr` routes. */ @@ -40,7 +40,7 @@ export interface RouteRow { * Used by `formatBuildReport` to add a note in the legend. */ prerendered?: boolean; -} +}; // ─── Regex-based export detection ──────────────────────────────────────────── diff --git a/packages/vinext/src/build/run-prerender.ts b/packages/vinext/src/build/run-prerender.ts index fdab320ac..23298d390 100644 --- a/packages/vinext/src/build/run-prerender.ts +++ b/packages/vinext/src/build/run-prerender.ts @@ -72,7 +72,7 @@ export class PrerenderProgress { // ─── Shared runner ──────────────────────────────────────────────────────────── -export interface RunPrerenderOptions { +export type RunPrerenderOptions = { /** Project root directory. */ root: string; /** @@ -93,7 +93,7 @@ export interface RunPrerenderOptions { * Intended for tests that build to a custom outDir. */ rscBundlePath?: string; -} +}; /** * Run the prerender phase using pre-built production bundles. diff --git a/packages/vinext/src/build/static-export.ts b/packages/vinext/src/build/static-export.ts index 1fd949fc2..c992d4ce4 100644 --- a/packages/vinext/src/build/static-export.ts +++ b/packages/vinext/src/build/static-export.ts @@ -22,7 +22,7 @@ import type { AppRoute } from "../routing/app-router.js"; import type { ResolvedNextConfig } from "../config/next-config.js"; import { prerenderPages, prerenderApp, type PrerenderRouteResult } from "./prerender.js"; -export interface StaticExportOptions { +export type StaticExportOptions = { /** * Absolute path to the pre-built Pages Router server bundle * (e.g. `dist/server/entry.js`). @@ -38,9 +38,9 @@ export interface StaticExportOptions { outDir: string; /** Resolved next.config.js */ config: ResolvedNextConfig; -} +}; -export interface StaticExportResult { +export type StaticExportResult = { /** Number of HTML files generated */ pageCount: number; /** Generated file paths (relative to outDir) */ @@ -49,7 +49,7 @@ export interface StaticExportResult { warnings: string[]; /** Errors encountered (non-fatal, specific pages) */ errors: Array<{ route: string; error: string }>; -} +}; /** * Convert a `PrerenderResult` into the legacy `StaticExportResult` shape. @@ -103,7 +103,7 @@ export async function staticExportPages(options: StaticExportOptions): Promise void; -} +}; export const clientInstrumentationHooks = setClientInstrumentationHooks( normalizeClientInstrumentationHooks(instrumentationClientHooks as ClientInstrumentationHooks), diff --git a/packages/vinext/src/client/vinext-next-data.ts b/packages/vinext/src/client/vinext-next-data.ts index dea4e6d33..c06e2c676 100644 --- a/packages/vinext/src/client/vinext-next-data.ts +++ b/packages/vinext/src/client/vinext-next-data.ts @@ -7,7 +7,7 @@ */ import type { NEXT_DATA } from "../shims/internal/utils.js"; -export interface VinextNextData extends NEXT_DATA { +export type VinextNextData = { /** vinext-specific additions (not part of Next.js upstream). */ __vinext?: { /** Absolute URL of the page module for dynamic import. */ @@ -19,4 +19,4 @@ export interface VinextNextData extends NEXT_DATA { __pageModule?: string; /** Serialised `_app` module path (legacy — used by `client/entry.ts`). */ __appModule?: string; -} +} & NEXT_DATA; diff --git a/packages/vinext/src/cloudflare/kv-cache-handler.ts b/packages/vinext/src/cloudflare/kv-cache-handler.ts index b4d6ccf1f..b01949c72 100644 --- a/packages/vinext/src/cloudflare/kv-cache-handler.ts +++ b/packages/vinext/src/cloudflare/kv-cache-handler.ts @@ -63,7 +63,7 @@ type SerializedIncrementalCacheValue = | SerializedCachedImageValue; // Cloudflare KV namespace interface (matches Workers types) -interface KVNamespace { +type KVNamespace = { get(key: string, options?: { type?: string }): Promise; get(key: string, options: { type: "arrayBuffer" }): Promise; put( @@ -77,16 +77,16 @@ interface KVNamespace { list_complete: boolean; cursor?: string; }>; -} +}; /** Shape stored in KV for each cache entry. */ -interface KVCacheEntry { +type KVCacheEntry = { value: SerializedIncrementalCacheValue | null; tags: string[]; lastModified: number; /** Absolute timestamp (ms) after which the entry is "stale" (but still served). */ revalidateAt: number | null; -} +}; /** Key prefix for tag invalidation timestamps. */ const TAG_PREFIX = "__tag:"; diff --git a/packages/vinext/src/cloudflare/tpr.ts b/packages/vinext/src/cloudflare/tpr.ts index 019fccd1b..4ca10ed9d 100644 --- a/packages/vinext/src/cloudflare/tpr.ts +++ b/packages/vinext/src/cloudflare/tpr.ts @@ -26,7 +26,7 @@ import { spawn, type ChildProcess } from "node:child_process"; // ─── Types ─────────────────────────────────────────────────────────────────── -export interface TPROptions { +export type TPROptions = { /** Project root directory. */ root: string; /** Traffic coverage percentage (0–100). Default: 90. */ @@ -35,9 +35,9 @@ export interface TPROptions { limit: number; /** Analytics lookback window in hours. Default: 24. */ window: number; -} +}; -export interface TPRResult { +export type TPRResult = { /** Total unique page paths found in analytics. */ totalPaths: number; /** Number of pages successfully pre-rendered and uploaded. */ @@ -48,31 +48,31 @@ export interface TPRResult { durationMs: number; /** If TPR was skipped, the reason. */ skipped?: string; -} +}; -interface TrafficEntry { +type TrafficEntry = { path: string; requests: number; -} +}; -interface SelectedRoutes { +type SelectedRoutes = { routes: TrafficEntry[]; totalRequests: number; coveredRequests: number; coveragePercent: number; -} +}; -interface PrerenderResult { +type PrerenderResult = { html: string; status: number; headers: Record; -} +}; -interface WranglerConfig { +type WranglerConfig = { accountId?: string; kvNamespaceId?: string; customDomain?: string; -} +}; // ─── Wrangler Config Parsing ───────────────────────────────────────────────── diff --git a/packages/vinext/src/config/config-matchers.ts b/packages/vinext/src/config/config-matchers.ts index 3f8605405..d5990e733 100644 --- a/packages/vinext/src/config/config-matchers.ts +++ b/packages/vinext/src/config/config-matchers.ts @@ -421,12 +421,12 @@ export function escapeHeaderSource(source: string): string { * Request context needed for evaluating has/missing conditions. * Callers extract the relevant parts from the incoming Request. */ -export interface RequestContext { +export type RequestContext = { headers: Headers; cookies: Record; query: URLSearchParams; host: string; -} +}; /** * Parse a Cookie header string into a key-value record. diff --git a/packages/vinext/src/config/dotenv.ts b/packages/vinext/src/config/dotenv.ts index d751db369..1d0e0f1a3 100644 --- a/packages/vinext/src/config/dotenv.ts +++ b/packages/vinext/src/config/dotenv.ts @@ -4,17 +4,17 @@ import { parseEnv } from "node:util"; export type VinextEnvMode = "development" | "production" | "test"; -export interface LoadDotenvOptions { +export type LoadDotenvOptions = { root: string; mode: VinextEnvMode; processEnv?: NodeJS.ProcessEnv; -} +}; -export interface LoadDotenvResult { +export type LoadDotenvResult = { mode: VinextEnvMode; loadedFiles: string[]; loadedEnv: Record; -} +}; /** * Next.js-compatible dotenv lookup order (highest priority first). diff --git a/packages/vinext/src/config/next-config.ts b/packages/vinext/src/config/next-config.ts index 15afbcb9c..94b4def9f 100644 --- a/packages/vinext/src/config/next-config.ts +++ b/packages/vinext/src/config/next-config.ts @@ -62,35 +62,35 @@ export function parseBodySizeLimit(value: string | number | undefined | null): n return bytes; } -export interface HasCondition { +export type HasCondition = { type: "header" | "cookie" | "query" | "host"; key: string; value?: string; -} +}; -export interface NextRedirect { +export type NextRedirect = { source: string; destination: string; permanent: boolean; has?: HasCondition[]; missing?: HasCondition[]; -} +}; -export interface NextRewrite { +export type NextRewrite = { source: string; destination: string; has?: HasCondition[]; missing?: HasCondition[]; -} +}; -export interface NextHeader { +export type NextHeader = { source: string; has?: HasCondition[]; missing?: HasCondition[]; headers: Array<{ key: string; value: string }>; -} +}; -export interface NextI18nConfig { +export type NextI18nConfig = { /** List of supported locales */ locales: string[]; /** The default locale (used when no locale prefix is in the URL) */ @@ -109,20 +109,20 @@ export interface NextI18nConfig { locales?: string[]; http?: boolean; }>; -} +}; /** * MDX compilation options extracted from @next/mdx config. * These are passed through to @mdx-js/rollup so that custom * remark/rehype/recma plugins configured in next.config work with Vite. */ -export interface MdxOptions { +export type MdxOptions = { remarkPlugins?: unknown[]; rehypePlugins?: unknown[]; recmaPlugins?: unknown[]; -} +}; -export interface NextConfig { +export type NextConfig = { /** Additional env variables */ env?: Record; /** Base URL path prefix */ @@ -202,7 +202,7 @@ export interface NextConfig { generateBuildId?: () => string | null | Promise; /** Any other options */ [key: string]: unknown; -} +}; export type NextConfigFactory = ( phase: string, @@ -214,7 +214,7 @@ export type NextConfigInput = NextConfig | NextConfigFactory; /** * Resolved configuration with all async values awaited. */ -export interface ResolvedNextConfig { +export type ResolvedNextConfig = { env: Record; basePath: string; trailingSlash: boolean; @@ -250,7 +250,7 @@ export interface ResolvedNextConfig { serverExternalPackages: string[]; /** Resolved build ID (from generateBuildId, or a random UUID if not provided). */ buildId: string; -} +}; const CONFIG_FILES = ["next.config.ts", "next.config.mjs", "next.config.js", "next.config.cjs"]; diff --git a/packages/vinext/src/deploy.ts b/packages/vinext/src/deploy.ts index 01a6eacee..827e7e5ef 100644 --- a/packages/vinext/src/deploy.ts +++ b/packages/vinext/src/deploy.ts @@ -34,7 +34,7 @@ import { loadNextConfig, resolveNextConfig } from "./config/next-config.js"; // ─── Types ─────────────────────────────────────────────────────────────────── -export interface DeployOptions { +export type DeployOptions = { /** Project root directory */ root: string; /** Deploy to preview environment (default: production) */ @@ -57,7 +57,7 @@ export interface DeployOptions { tprLimit?: number; /** TPR: analytics lookback window in hours (default: 24) */ tprWindow?: number; -} +}; // ─── CLI arg parsing (uses Node.js util.parseArgs) ────────────────────────── @@ -106,7 +106,7 @@ export function parseDeployArgs(args: string[]) { // ─── Project Detection ────────────────────────────────────────────────────── -interface ProjectInfo { +type ProjectInfo = { root: string; isAppRouter: boolean; isPagesRouter: boolean; @@ -127,7 +127,7 @@ interface ProjectInfo { hasCodeHike: boolean; /** Native Node modules that need stubbing for Workers */ nativeModulesToStub: string[]; -} +}; // ─── Detection ─────────────────────────────────────────────────────────────── @@ -993,10 +993,10 @@ export default defineConfig({ // ─── Dependency Management ─────────────────────────────────────────────────── -interface MissingDep { +type MissingDep = { name: string; version: string; -} +}; /** * Check if a package is resolvable from a given root directory using @@ -1066,11 +1066,11 @@ const detectPackageManager = _detectPackageManager; // ─── File Writing ──────────────────────────────────────────────────────────── -interface GeneratedFile { +type GeneratedFile = { path: string; content: string; description: string; -} +}; /** * Check whether an existing vite.config file already imports and uses the @@ -1183,10 +1183,10 @@ async function runBuild(info: ProjectInfo): Promise { // ─── Deploy ────────────────────────────────────────────────────────────────── -export interface WranglerDeployArgs { +export type WranglerDeployArgs = { args: string[]; env: string | undefined; -} +}; export function buildWranglerDeployArgs( options: Pick, diff --git a/packages/vinext/src/entries/app-rsc-entry.ts b/packages/vinext/src/entries/app-rsc-entry.ts index 7a14d4b1a..7ad6a457b 100644 --- a/packages/vinext/src/entries/app-rsc-entry.ts +++ b/packages/vinext/src/entries/app-rsc-entry.ts @@ -68,7 +68,7 @@ const metadataRoutesPath = resolveEntryPath("../server/metadata-routes.js", impo * Resolved config options relevant to App Router request handling. * Passed from the Vite plugin where the full next.config.js is loaded. */ -export interface AppRouterConfig { +export type AppRouterConfig = { redirects?: NextRedirect[]; rewrites?: { beforeFiles: NextRewrite[]; @@ -93,7 +93,7 @@ export interface AppRouterConfig { * `virtual:vinext-server-entry` when this flag is set. */ hasPagesDir?: boolean; -} +}; /** * Generate the virtual RSC entry module. diff --git a/packages/vinext/src/global.d.ts b/packages/vinext/src/global.d.ts index 417f935b5..31f14302d 100644 --- a/packages/vinext/src/global.d.ts +++ b/packages/vinext/src/global.d.ts @@ -1,3 +1,5 @@ +// oxlint-disable typescript/consistent-type-definitions + /** * Global ambient type declarations for vinext runtime globals. * diff --git a/packages/vinext/src/index.ts b/packages/vinext/src/index.ts index fc4e9c2f7..dbf41ae2d 100644 --- a/packages/vinext/src/index.ts +++ b/packages/vinext/src/index.ts @@ -840,7 +840,7 @@ function augmentSsrManifestFromBundle( ) as Record; } -export interface VinextOptions { +export type VinextOptions = { /** * Base directory containing the app/ and pages/ directories. * Can be an absolute path or a path relative to the Vite root. @@ -910,9 +910,9 @@ export interface VinextOptions { */ clientReferenceDedup?: boolean; }; -} +}; -interface NitroSetupContext { +type NitroSetupContext = { options: { dev?: boolean; routeRules?: Record; @@ -920,7 +920,7 @@ interface NitroSetupContext { logger?: { warn?: (message: string) => void; }; -} +}; export default function vinext(options: VinextOptions = {}): PluginOption[] { const viteMajorVersion = getViteMajorVersion(); diff --git a/packages/vinext/src/init.ts b/packages/vinext/src/init.ts index 588c73644..5e478efa4 100644 --- a/packages/vinext/src/init.ts +++ b/packages/vinext/src/init.ts @@ -32,7 +32,7 @@ import { // ─── Types ─────────────────────────────────────────────────────────────────── -export interface InitOptions { +export type InitOptions = { /** Project root directory */ root: string; /** Dev server port (default: 3001) */ @@ -43,9 +43,9 @@ export interface InitOptions { force?: boolean; /** @internal — override exec for testing (avoids ESM spy issues) */ _exec?: (cmd: string, opts: { cwd: string; stdio: string }) => void; -} +}; -export interface InitResult { +export type InitResult = { /** Whether dependencies were installed */ installedDeps: string[]; /** Whether "type": "module" was added */ @@ -60,7 +60,7 @@ export interface InitResult { skippedViteConfig: boolean; /** Whether .gitignore was updated to include /dist/ */ updatedGitignore: boolean; -} +}; // ─── Vite Config Generation (minimal, non-Cloudflare) ──────────────────────── diff --git a/packages/vinext/src/plugins/optimize-imports.ts b/packages/vinext/src/plugins/optimize-imports.ts index a68e0cfe8..c6f9cd701 100644 --- a/packages/vinext/src/plugins/optimize-imports.ts +++ b/packages/vinext/src/plugins/optimize-imports.ts @@ -43,18 +43,18 @@ function astName(node: { name?: string; value?: string | boolean | number | null type ExportsValue = string | { [condition: string]: ExportsValue }; /** Minimal package.json shape for entry point resolution. */ -interface PackageJson { +type PackageJson = { name?: string; exports?: Record; module?: string; main?: string; -} +}; -interface BarrelExportEntry { +type BarrelExportEntry = { source: string; isNamespace: boolean; originalName?: string; -} +}; type BarrelExportMap = Map; @@ -65,7 +65,7 @@ type DeclarationNode = { }; /** Caches used by the optimize-imports plugin, scoped to a plugin instance. */ -interface BarrelCaches { +type BarrelCaches = { /** Barrel export maps keyed by resolved entry file path. */ exportMapCache: Map; /** @@ -76,7 +76,7 @@ interface BarrelCaches { * already environment-keyed via the "rsc:"/"ssr:" prefix on its cache keys. */ subpkgOrigin: Map>; -} +}; // Shared with Vite's internal AST node types (not publicly exported) type AstBodyNode = { @@ -222,10 +222,10 @@ function resolveExportsValue(value: ExportsValue, preferReactServer: boolean): s * Result of resolving a package, including the directory and parsed package.json. * Used internally by resolvePackageInfo. */ -interface PackageInfo { +type PackageInfo = { pkgDir: string; pkgJson: PackageJson; -} +}; /** * Resolve a package name to its directory and parsed package.json. diff --git a/packages/vinext/src/routing/app-router.ts b/packages/vinext/src/routing/app-router.ts index 8064861ae..c9142a36a 100644 --- a/packages/vinext/src/routing/app-router.ts +++ b/packages/vinext/src/routing/app-router.ts @@ -24,7 +24,7 @@ import { import { validateRoutePatterns } from "./route-validation.js"; import { buildRouteTrie, trieMatch, type TrieNode } from "./route-trie.js"; -export interface InterceptingRoute { +export type InterceptingRoute = { /** The interception convention: "." | ".." | "../.." | "..." */ convention: string; /** The URL pattern this intercepts (e.g. "/photos/:id") */ @@ -33,9 +33,9 @@ export interface InterceptingRoute { pagePath: string; /** Parameter names for dynamic segments */ params: string[]; -} +}; -export interface ParallelSlot { +export type ParallelSlot = { /** Slot name (e.g. "team" from @team) */ name: string; /** Absolute path to the @slot directory that owns this slot. Internal routing metadata. */ @@ -58,9 +58,9 @@ export interface ParallelSlot { * necessarily the innermost layout. -1 means "innermost" (legacy default). */ layoutIndex: number; -} +}; -export interface AppRoute { +export type AppRoute = { /** URL pattern, e.g. "/" or "/about" or "/blog/:slug" */ pattern: string; /** Absolute file path to the page component */ @@ -118,7 +118,7 @@ export interface AppRoute { params: string[]; /** Pre-split pattern segments (computed once at scan time, reused per request) */ patternParts: string[]; -} +}; // Cache for app routes let cachedRoutes: AppRoute[] | null = null; diff --git a/packages/vinext/src/routing/file-matcher.ts b/packages/vinext/src/routing/file-matcher.ts index 7d03e22bf..075f167b4 100644 --- a/packages/vinext/src/routing/file-matcher.ts +++ b/packages/vinext/src/routing/file-matcher.ts @@ -25,7 +25,7 @@ export function buildExtensionGlob(stem: string, extensions: readonly string[]): return `${stem}.{${extensions.join(",")}}`; } -export interface ValidFileMatcher { +export type ValidFileMatcher = { extensions: string[]; dottedExtensions: string[]; extensionRegex: RegExp; @@ -35,7 +35,7 @@ export interface ValidFileMatcher { isAppLayoutFile(filePath: string): boolean; isAppDefaultFile(filePath: string): boolean; stripExtension(filePath: string): string; -} +}; /** * Ported in spirit from Next.js createValidFileMatcher: diff --git a/packages/vinext/src/routing/pages-router.ts b/packages/vinext/src/routing/pages-router.ts index 91b712da1..739827535 100644 --- a/packages/vinext/src/routing/pages-router.ts +++ b/packages/vinext/src/routing/pages-router.ts @@ -8,7 +8,7 @@ import { import { patternToNextFormat, validateRoutePatterns } from "./route-validation.js"; import { buildRouteTrie, trieMatch, type TrieNode } from "./route-trie.js"; -export interface Route { +export type Route = { /** URL pattern, e.g. "/" or "/about" or "/posts/:id" */ pattern: string; /** Pre-split pattern segments (computed once at scan time, reused per request) */ @@ -19,7 +19,7 @@ export interface Route { isDynamic: boolean; /** Parameter names for dynamic segments */ params: string[]; -} +}; // Route cache — invalidated when pages directory changes const routeCache = new Map }>(); diff --git a/packages/vinext/src/routing/route-trie.ts b/packages/vinext/src/routing/route-trie.ts index 9ef3b0ea2..16d46b64e 100644 --- a/packages/vinext/src/routing/route-trie.ts +++ b/packages/vinext/src/routing/route-trie.ts @@ -12,13 +12,13 @@ * branches fall through to catch-all alternatives. */ -export interface TrieNode { +export type TrieNode = { staticChildren: Map>; dynamicChild: { paramName: string; node: TrieNode } | null; catchAllChild: { paramName: string; route: R } | null; optionalCatchAllChild: { paramName: string; route: R } | null; route: R | null; -} +}; function createNode(): TrieNode { return { diff --git a/packages/vinext/src/server/api-handler.ts b/packages/vinext/src/server/api-handler.ts index 5ebf5e0fe..88307b4f3 100644 --- a/packages/vinext/src/server/api-handler.ts +++ b/packages/vinext/src/server/api-handler.ts @@ -17,21 +17,21 @@ import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-medi /** * Extend the Node.js request with Next.js-style helpers. */ -interface NextApiRequest extends IncomingMessage { +type NextApiRequest = { query: Record; body: unknown; cookies: Record; -} +} & IncomingMessage; /** * Extend the Node.js response with Next.js-style helpers. */ -interface NextApiResponse extends ServerResponse { +type NextApiResponse = { status(code: number): NextApiResponse; json(data: unknown): void; send(data: unknown): void; redirect(statusOrUrl: number | string, url?: string): void; -} +} & ServerResponse; /** * Maximum request body size (1 MB). Matches Next.js default bodyParser sizeLimit. diff --git a/packages/vinext/src/server/app-browser-entry.ts b/packages/vinext/src/server/app-browser-entry.ts index a64a523d8..84dcaf3de 100644 --- a/packages/vinext/src/server/app-browser-entry.ts +++ b/packages/vinext/src/server/app-browser-entry.ts @@ -29,13 +29,13 @@ import { type SearchParamInput = ConstructorParameters[0]; -interface ServerActionResult { +type ServerActionResult = { root: ReactNode; returnValue?: { ok: boolean; data: unknown; }; -} +}; let reactRoot: Root | null = null; diff --git a/packages/vinext/src/server/app-browser-stream.ts b/packages/vinext/src/server/app-browser-stream.ts index 52f89feb5..177032688 100644 --- a/packages/vinext/src/server/app-browser-stream.ts +++ b/packages/vinext/src/server/app-browser-stream.ts @@ -1,21 +1,21 @@ -export interface NavigationSnapshot { +export type NavigationSnapshot = { pathname: string; searchParams: [string, string][]; -} +}; -export interface LegacyRscEmbedData { +export type LegacyRscEmbedData = { rsc: string[]; params?: Record; nav?: NavigationSnapshot; -} +}; -export interface VinextBrowserGlobals { +export type VinextBrowserGlobals = { __VINEXT_RSC__?: LegacyRscEmbedData; __VINEXT_RSC_CHUNKS__?: string[]; __VINEXT_RSC_DONE__?: boolean; __VINEXT_RSC_PARAMS__?: Record; __VINEXT_RSC_NAV__?: NavigationSnapshot; -} +}; export function getVinextBrowserGlobal(): typeof globalThis & VinextBrowserGlobals { return globalThis as typeof globalThis & VinextBrowserGlobals; diff --git a/packages/vinext/src/server/app-page-boundary-render.ts b/packages/vinext/src/server/app-page-boundary-render.ts index f62b4d7e1..3c93c5625 100644 --- a/packages/vinext/src/server/app-page-boundary-render.ts +++ b/packages/vinext/src/server/app-page-boundary-render.ts @@ -36,7 +36,7 @@ type AppPageBoundaryOnError = ( errorContext: unknown, ) => unknown; -export interface AppPageBoundaryRoute { +export type AppPageBoundaryRoute = { error?: TModule | null; errors?: readonly (TModule | null | undefined)[] | null; forbidden?: TModule | null; @@ -47,9 +47,9 @@ export interface AppPageBoundaryRoute { +type AppPageBoundaryRenderCommonOptions = { buildFontLinkHeader: (preloads: readonly AppPageFontPreload[] | null | undefined) => string; clearRequestContext: () => void; createRscOnErrorHandler: (pathname: string, routePath: string) => AppPageBoundaryOnError; @@ -72,29 +72,26 @@ interface AppPageBoundaryRenderCommonOptions string[]; rootLayouts: readonly (TModule | null | undefined)[]; -} +}; -export interface RenderAppPageHttpAccessFallbackOptions< - TModule extends AppPageModule = AppPageModule, -> extends AppPageBoundaryRenderCommonOptions { - boundaryComponent?: AppPageComponent | null; - layoutModules?: readonly (TModule | null | undefined)[] | null; - matchedParams: AppPageParams; - rootForbiddenModule?: TModule | null; - rootNotFoundModule?: TModule | null; - rootUnauthorizedModule?: TModule | null; - route?: AppPageBoundaryRoute | null; - statusCode: number; -} +export type RenderAppPageHttpAccessFallbackOptions = + { + boundaryComponent?: AppPageComponent | null; + layoutModules?: readonly (TModule | null | undefined)[] | null; + matchedParams: AppPageParams; + rootForbiddenModule?: TModule | null; + rootNotFoundModule?: TModule | null; + rootUnauthorizedModule?: TModule | null; + route?: AppPageBoundaryRoute | null; + statusCode: number; + } & AppPageBoundaryRenderCommonOptions; -export interface RenderAppPageErrorBoundaryOptions< - TModule extends AppPageModule = AppPageModule, -> extends AppPageBoundaryRenderCommonOptions { +export type RenderAppPageErrorBoundaryOptions = { error: unknown; matchedParams?: AppPageParams | null; route?: AppPageBoundaryRoute | null; sanitizeErrorForClient: (error: Error) => Error; -} +} & AppPageBoundaryRenderCommonOptions; function getDefaultExport( module: TModule | null | undefined, diff --git a/packages/vinext/src/server/app-page-boundary.ts b/packages/vinext/src/server/app-page-boundary.ts index 14b999fe4..06b5f9b28 100644 --- a/packages/vinext/src/server/app-page-boundary.ts +++ b/packages/vinext/src/server/app-page-boundary.ts @@ -1,6 +1,6 @@ export type AppPageParams = Record; -export interface ResolveAppPageHttpAccessBoundaryComponentOptions { +export type ResolveAppPageHttpAccessBoundaryComponentOptions = { getDefaultExport: (module: TModule | null | undefined) => TComponent | null | undefined; rootForbiddenModule?: TModule | null; rootNotFoundModule?: TModule | null; @@ -9,27 +9,27 @@ export interface ResolveAppPageHttpAccessBoundaryComponentOptions { +export type ResolveAppPageErrorBoundaryOptions = { getDefaultExport: (module: TModule | null | undefined) => TComponent | null | undefined; globalErrorModule?: TModule | null; layoutErrorModules?: readonly (TModule | null | undefined)[] | null; pageErrorModule?: TModule | null; -} +}; -export interface ResolveAppPageErrorBoundaryResult { +export type ResolveAppPageErrorBoundaryResult = { component: TComponent | null; isGlobalError: boolean; -} +}; -export interface WrapAppPageBoundaryElementOptions< +export type WrapAppPageBoundaryElementOptions< TElement, TLayoutModule, TLayoutComponent, TChildSegments, TGlobalErrorComponent, -> { +> = { element: TElement; getDefaultExport: ( module: TLayoutModule | null | undefined, @@ -51,7 +51,7 @@ export interface WrapAppPageBoundaryElementOptions< ) => TChildSegments; routeSegments?: readonly string[]; skipLayoutWrapping?: boolean; -} +}; type AppPageBoundaryOnError = ( error: unknown, @@ -59,7 +59,7 @@ type AppPageBoundaryOnError = ( errorContext: unknown, ) => unknown; -export interface RenderAppPageBoundaryResponseOptions { +export type RenderAppPageBoundaryResponseOptions = { createHtmlResponse: (rscStream: ReadableStream, status: number) => Promise; createRscOnErrorHandler: () => AppPageBoundaryOnError; element: TElement; @@ -69,7 +69,7 @@ export interface RenderAppPageBoundaryResponseOptions { options: { onError: AppPageBoundaryOnError }, ) => ReadableStream; status: number; -} +}; export function resolveAppPageHttpAccessBoundaryComponent( options: ResolveAppPageHttpAccessBoundaryComponentOptions, diff --git a/packages/vinext/src/server/app-page-cache.ts b/packages/vinext/src/server/app-page-cache.ts index c2ae91e44..3520619cb 100644 --- a/packages/vinext/src/server/app-page-cache.ts +++ b/packages/vinext/src/server/app-page-cache.ts @@ -11,19 +11,19 @@ type AppPageCacheSetter = ( ) => Promise; type AppPageBackgroundRegenerator = (key: string, renderFn: () => Promise) => void; -export interface AppPageCacheRenderResult { +export type AppPageCacheRenderResult = { html: string; rscData: ArrayBuffer; tags: string[]; -} +}; -export interface BuildAppPageCachedResponseOptions { +export type BuildAppPageCachedResponseOptions = { cacheState: "HIT" | "STALE"; isRscRequest: boolean; revalidateSeconds: number; -} +}; -export interface ReadAppPageCacheResponseOptions { +export type ReadAppPageCacheResponseOptions = { cleanPathname: string; clearRequestContext: () => void; isRscRequest: boolean; @@ -35,9 +35,9 @@ export interface ReadAppPageCacheResponseOptions { revalidateSeconds: number; renderFreshPageForCache: () => Promise; scheduleBackgroundRegeneration: AppPageBackgroundRegenerator; -} +}; -export interface FinalizeAppPageHtmlCacheResponseOptions { +export type FinalizeAppPageHtmlCacheResponseOptions = { capturedRscDataPromise: Promise | null; cleanPathname: string; getPageTags: () => string[]; @@ -47,9 +47,9 @@ export interface FinalizeAppPageHtmlCacheResponseOptions { isrSet: AppPageCacheSetter; revalidateSeconds: number; waitUntil?: (promise: Promise) => void; -} +}; -export interface ScheduleAppPageRscCacheWriteOptions { +export type ScheduleAppPageRscCacheWriteOptions = { capturedRscDataPromise: Promise | null; cleanPathname: string; consumeDynamicUsage: () => boolean; @@ -60,7 +60,7 @@ export interface ScheduleAppPageRscCacheWriteOptions { isrSet: AppPageCacheSetter; revalidateSeconds: number; waitUntil?: (promise: Promise) => void; -} +}; function buildAppPageCacheControl( cacheState: BuildAppPageCachedResponseOptions["cacheState"], diff --git a/packages/vinext/src/server/app-page-execution.ts b/packages/vinext/src/server/app-page-execution.ts index b8a96a862..806d7214b 100644 --- a/packages/vinext/src/server/app-page-execution.ts +++ b/packages/vinext/src/server/app-page-execution.ts @@ -2,36 +2,36 @@ export type AppPageSpecialError = | { kind: "redirect"; location: string; statusCode: number } | { kind: "http-access-fallback"; statusCode: number }; -export interface AppPageFontPreload { +export type AppPageFontPreload = { href: string; type: string; -} +}; -export interface AppPageRscStreamCapture { +export type AppPageRscStreamCapture = { capturedRscDataPromise: Promise | null; responseStream: ReadableStream; -} +}; -export interface BuildAppPageSpecialErrorResponseOptions { +export type BuildAppPageSpecialErrorResponseOptions = { clearRequestContext: () => void; renderFallbackPage?: (statusCode: number) => Promise; requestUrl: string; specialError: AppPageSpecialError; -} +}; -export interface ProbeAppPageLayoutsOptions { +export type ProbeAppPageLayoutsOptions = { layoutCount: number; onLayoutError: (error: unknown, layoutIndex: number) => Promise; probeLayoutAt: (layoutIndex: number) => unknown; runWithSuppressedHookWarning(probe: () => Promise): Promise; -} +}; -export interface ProbeAppPageComponentOptions { +export type ProbeAppPageComponentOptions = { awaitAsyncResult: boolean; onError: (error: unknown) => Promise; probePage: () => unknown; runWithSuppressedHookWarning(probe: () => Promise): Promise; -} +}; function isPromiseLike(value: unknown): value is PromiseLike { return Boolean( diff --git a/packages/vinext/src/server/app-page-probe.ts b/packages/vinext/src/server/app-page-probe.ts index 09c968a1e..58c8c29e0 100644 --- a/packages/vinext/src/server/app-page-probe.ts +++ b/packages/vinext/src/server/app-page-probe.ts @@ -4,7 +4,7 @@ import { type AppPageSpecialError, } from "./app-page-execution.js"; -export interface ProbeAppPageBeforeRenderOptions { +export type ProbeAppPageBeforeRenderOptions = { hasLoadingBoundary: boolean; layoutCount: number; probeLayoutAt: (layoutIndex: number) => unknown; @@ -16,7 +16,7 @@ export interface ProbeAppPageBeforeRenderOptions { renderPageSpecialError: (specialError: AppPageSpecialError) => Promise; resolveSpecialError: (error: unknown) => AppPageSpecialError | null; runWithSuppressedHookWarning(probe: () => Promise): Promise; -} +}; export async function probeAppPageBeforeRender( options: ProbeAppPageBeforeRenderOptions, diff --git a/packages/vinext/src/server/app-page-render.ts b/packages/vinext/src/server/app-page-render.ts index 283ceb6bf..8591cae79 100644 --- a/packages/vinext/src/server/app-page-render.ts +++ b/packages/vinext/src/server/app-page-render.ts @@ -43,11 +43,11 @@ type AppPageCacheSetter = ( tags: string[], ) => Promise; -interface AppPageRequestCacheLife { +type AppPageRequestCacheLife = { revalidate?: number; -} +}; -export interface RenderAppPageLifecycleOptions { +export type RenderAppPageLifecycleOptions = { cleanPathname: string; clearRequestContext: () => void; consumeDynamicUsage: () => boolean; @@ -92,7 +92,7 @@ export interface RenderAppPageLifecycleOptions { runWithSuppressedHookWarning(probe: () => Promise): Promise; waitUntil?: (promise: Promise) => void; element: ReactNode; -} +}; function buildResponseTiming( options: Pick & { diff --git a/packages/vinext/src/server/app-page-request.ts b/packages/vinext/src/server/app-page-request.ts index 62a31c9dc..2e7c68f55 100644 --- a/packages/vinext/src/server/app-page-request.ts +++ b/packages/vinext/src/server/app-page-request.ts @@ -2,35 +2,35 @@ import type { AppPageSpecialError } from "./app-page-execution.js"; export type AppPageParams = Record; -export interface ValidateAppPageDynamicParamsOptions { +export type ValidateAppPageDynamicParamsOptions = { clearRequestContext: () => void; enforceStaticParamsOnly: boolean; generateStaticParams?: ((args: { params: AppPageParams }) => unknown) | null; isDynamicRoute: boolean; logGenerateStaticParamsError?: (error: unknown) => void; params: AppPageParams; -} +}; -export interface BuildAppPageElementOptions { +export type BuildAppPageElementOptions = { buildPageElement: () => Promise; renderErrorBoundaryPage: (error: unknown) => Promise; renderSpecialError: (specialError: AppPageSpecialError) => Promise; resolveSpecialError: (error: unknown) => AppPageSpecialError | null; -} +}; -export interface BuildAppPageElementResult { +export type BuildAppPageElementResult = { element: TElement | null; response: Response | null; -} +}; -export interface AppPageInterceptMatch { +export type AppPageInterceptMatch = { matchedParams: AppPageParams; page: TPage; slotName: string; sourceRouteIndex: number; -} +}; -export interface ResolveAppPageInterceptOptions { +export type ResolveAppPageInterceptOptions = { buildPageElement: ( route: TRoute, params: AppPageParams, @@ -52,12 +52,12 @@ export interface ResolveAppPageInterceptOptions { searchParams: URLSearchParams; }) => void; toInterceptOpts: (intercept: AppPageInterceptMatch) => TInterceptOpts; -} +}; -export interface ResolveAppPageInterceptResult { +export type ResolveAppPageInterceptResult = { interceptOpts: TInterceptOpts | undefined; response: Response | null; -} +}; function areStaticParamsAllowed( params: AppPageParams, diff --git a/packages/vinext/src/server/app-page-response.ts b/packages/vinext/src/server/app-page-response.ts index 2be834818..c8784764b 100644 --- a/packages/vinext/src/server/app-page-response.ts +++ b/packages/vinext/src/server/app-page-response.ts @@ -1,54 +1,54 @@ -export interface AppPageMiddlewareContext { +export type AppPageMiddlewareContext = { headers: Headers | null; status: number | null; -} +}; -export interface AppPageResponseTiming { +export type AppPageResponseTiming = { compileEnd?: number; handlerStart: number; renderEnd?: number; responseKind: "html" | "rsc"; -} +}; -export interface AppPageResponsePolicy { +export type AppPageResponsePolicy = { cacheControl?: string; cacheState?: "MISS" | "STATIC"; -} +}; -interface ResolveAppPageResponsePolicyBaseOptions { +type ResolveAppPageResponsePolicyBaseOptions = { isDynamicError: boolean; isForceDynamic: boolean; isForceStatic: boolean; isProduction: boolean; revalidateSeconds: number | null; -} +}; -export interface ResolveAppPageRscResponsePolicyOptions extends ResolveAppPageResponsePolicyBaseOptions { +export type ResolveAppPageRscResponsePolicyOptions = { dynamicUsedDuringBuild: boolean; -} +} & ResolveAppPageResponsePolicyBaseOptions; -export interface ResolveAppPageHtmlResponsePolicyOptions extends ResolveAppPageResponsePolicyBaseOptions { +export type ResolveAppPageHtmlResponsePolicyOptions = { dynamicUsedDuringRender: boolean; -} +} & ResolveAppPageResponsePolicyBaseOptions; -export interface AppPageHtmlResponsePolicy extends AppPageResponsePolicy { +export type AppPageHtmlResponsePolicy = { shouldWriteToCache: boolean; -} +} & AppPageResponsePolicy; -export interface BuildAppPageRscResponseOptions { +export type BuildAppPageRscResponseOptions = { middlewareContext: AppPageMiddlewareContext; params?: Record; policy: AppPageResponsePolicy; timing?: AppPageResponseTiming; -} +}; -export interface BuildAppPageHtmlResponseOptions { +export type BuildAppPageHtmlResponseOptions = { draftCookie?: string | null; fontLinkHeader?: string; middlewareContext: AppPageMiddlewareContext; policy: AppPageResponsePolicy; timing?: AppPageResponseTiming; -} +}; const STATIC_CACHE_CONTROL = "s-maxage=31536000, stale-while-revalidate"; const NO_STORE_CACHE_CONTROL = "no-store, must-revalidate"; diff --git a/packages/vinext/src/server/app-page-stream.ts b/packages/vinext/src/server/app-page-stream.ts index c89b1af00..7f2f78365 100644 --- a/packages/vinext/src/server/app-page-stream.ts +++ b/packages/vinext/src/server/app-page-stream.ts @@ -1,60 +1,60 @@ import type { AppPageFontPreload } from "./app-page-execution.js"; -export interface AppPageFontData { +export type AppPageFontData = { links: string[]; preloads: readonly AppPageFontPreload[]; styles: string[]; -} +}; -export interface CreateAppPageFontDataOptions { +export type CreateAppPageFontDataOptions = { getLinks: () => string[]; getPreloads: () => AppPageFontPreload[]; getStyles: () => string[]; -} +}; -export interface AppPageSsrHandler { +export type AppPageSsrHandler = { handleSsr: ( rscStream: ReadableStream, navigationContext: unknown, fontData: AppPageFontData, ) => Promise>; -} +}; -export interface RenderAppPageHtmlStreamOptions { +export type RenderAppPageHtmlStreamOptions = { fontData: AppPageFontData; navigationContext: unknown; rscStream: ReadableStream; ssrHandler: AppPageSsrHandler; -} +}; -export interface RenderAppPageHtmlResponseOptions extends RenderAppPageHtmlStreamOptions { +export type RenderAppPageHtmlResponseOptions = { clearRequestContext: () => void; fontLinkHeader?: string; status: number; -} +} & RenderAppPageHtmlStreamOptions; -export interface AppPageHtmlStreamRecoveryResult { +export type AppPageHtmlStreamRecoveryResult = { htmlStream: ReadableStream | null; response: Response | null; -} +}; -export interface RenderAppPageHtmlStreamWithRecoveryOptions { +export type RenderAppPageHtmlStreamWithRecoveryOptions = { onShellRendered?: () => void; renderErrorBoundaryResponse: (error: unknown) => Promise; renderHtmlStream: () => Promise>; renderSpecialErrorResponse: (specialError: TSpecialError) => Promise; resolveSpecialError: (error: unknown) => TSpecialError | null; -} +}; -export interface AppPageRscErrorTracker { +export type AppPageRscErrorTracker = { getCapturedError: () => unknown; onRenderError: (error: unknown, requestInfo: unknown, errorContext: unknown) => unknown; -} +}; -export interface ShouldRerenderAppPageWithGlobalErrorOptions { +export type ShouldRerenderAppPageWithGlobalErrorOptions = { capturedError: unknown; hasLocalBoundary: boolean; -} +}; export function createAppPageFontData(options: CreateAppPageFontDataOptions): AppPageFontData { return { diff --git a/packages/vinext/src/server/app-route-handler-cache.ts b/packages/vinext/src/server/app-route-handler-cache.ts index 369b28cd1..9df6172d9 100644 --- a/packages/vinext/src/server/app-route-handler-cache.ts +++ b/packages/vinext/src/server/app-route-handler-cache.ts @@ -21,7 +21,7 @@ type RouteHandlerCacheGetter = (key: string) => Promise; type RouteHandlerBackgroundRegenerator = (key: string, renderFn: () => Promise) => void; type RouteHandlerRevalidationContextRunner = (renderFn: () => Promise) => Promise; -export interface ReadAppRouteHandlerCacheOptions { +export type ReadAppRouteHandlerCacheOptions = { basePath?: string; buildPageCacheTags: (pathname: string, extraTags: string[]) => string[]; cleanPathname: string; @@ -51,7 +51,7 @@ export interface ReadAppRouteHandlerCacheOptions { params: AppRouteParams; } | null, ) => void; -} +}; function getCachedAppRouteValue(entry: ISRCacheEntry | null) { return entry?.value.value && entry.value.value.kind === "APP_ROUTE" ? entry.value.value : null; diff --git a/packages/vinext/src/server/app-route-handler-execution.ts b/packages/vinext/src/server/app-route-handler-execution.ts index e61e6d6b8..e4b1f68e4 100644 --- a/packages/vinext/src/server/app-route-handler-execution.ts +++ b/packages/vinext/src/server/app-route-handler-execution.ts @@ -41,7 +41,7 @@ type AppRouteErrorReporter = ( ) => void; export type AppRouteDebugLogger = (event: string, detail: string) => void; -export interface RunAppRouteHandlerOptions { +export type RunAppRouteHandlerOptions = { basePath?: string; consumeDynamicUsage: AppRouteDynamicUsageFn; handlerFn: AppRouteHandlerFunction; @@ -49,14 +49,14 @@ export interface RunAppRouteHandlerOptions { markDynamicUsage: MarkAppRouteDynamicUsageFn; params: AppRouteParams; request: Request; -} +}; -export interface RunAppRouteHandlerResult { +export type RunAppRouteHandlerResult = { dynamicUsedInHandler: boolean; response: Response; -} +}; -export interface ExecuteAppRouteHandlerOptions extends RunAppRouteHandlerOptions { +export type ExecuteAppRouteHandlerOptions = { buildPageCacheTags: (pathname: string, extraTags: string[]) => string[]; clearRequestContext: () => void; cleanPathname: string; @@ -76,7 +76,7 @@ export interface ExecuteAppRouteHandlerOptions extends RunAppRouteHandlerOptions revalidateSeconds: number | null; routePattern: string; setHeadersAccessPhase: (phase: HeadersAccessPhase) => HeadersAccessPhase; -} +} & RunAppRouteHandlerOptions; export async function runAppRouteHandler( options: RunAppRouteHandlerOptions, diff --git a/packages/vinext/src/server/app-route-handler-policy.ts b/packages/vinext/src/server/app-route-handler-policy.ts index 2f3f1fc29..73028c804 100644 --- a/packages/vinext/src/server/app-route-handler-policy.ts +++ b/packages/vinext/src/server/app-route-handler-policy.ts @@ -5,22 +5,22 @@ import { type RouteHandlerModule, } from "./app-route-handler-runtime.js"; -export interface AppRouteHandlerModule extends RouteHandlerModule { +export type AppRouteHandlerModule = { dynamic?: string; revalidate?: unknown; -} +} & RouteHandlerModule; type AppRouteHandlerFunction = (...args: unknown[]) => unknown; -export interface ResolvedAppRouteHandlerMethod { +export type ResolvedAppRouteHandlerMethod = { allowHeaderForOptions: string; exportedMethods: RouteHandlerHttpMethod[]; handlerFn: AppRouteHandlerFunction | undefined; isAutoHead: boolean; shouldAutoRespondToOptions: boolean; -} +}; -export interface AppRouteHandlerCacheReadOptions { +export type AppRouteHandlerCacheReadOptions = { dynamicConfig?: string; handlerFn: unknown; isAutoHead: boolean; @@ -28,9 +28,9 @@ export interface AppRouteHandlerCacheReadOptions { isProduction: boolean; method: string; revalidateSeconds: number | null; -} +}; -export interface AppRouteHandlerResponseCacheOptions { +export type AppRouteHandlerResponseCacheOptions = { dynamicConfig?: string; dynamicUsedInHandler: boolean; handlerSetCacheControl: boolean; @@ -38,7 +38,7 @@ export interface AppRouteHandlerResponseCacheOptions { isProduction: boolean; method: string; revalidateSeconds: number | null; -} +}; export type AppRouteHandlerSpecialError = | { diff --git a/packages/vinext/src/server/app-route-handler-response.ts b/packages/vinext/src/server/app-route-handler-response.ts index 51c30046e..8a84310f7 100644 --- a/packages/vinext/src/server/app-route-handler-response.ts +++ b/packages/vinext/src/server/app-route-handler-response.ts @@ -1,21 +1,21 @@ import type { CachedRouteValue } from "../shims/cache.js"; -export interface RouteHandlerMiddlewareContext { +export type RouteHandlerMiddlewareContext = { headers: Headers | null; status: number | null; -} +}; -export interface BuildRouteHandlerCachedResponseOptions { +export type BuildRouteHandlerCachedResponseOptions = { cacheState: "HIT" | "STALE"; isHead: boolean; revalidateSeconds: number; -} +}; -export interface FinalizeRouteHandlerResponseOptions { +export type FinalizeRouteHandlerResponseOptions = { pendingCookies: string[]; draftCookie?: string | null; isHead: boolean; -} +}; function buildRouteHandlerCacheControl( cacheState: BuildRouteHandlerCachedResponseOptions["cacheState"], diff --git a/packages/vinext/src/server/app-route-handler-runtime.ts b/packages/vinext/src/server/app-route-handler-runtime.ts index 525898221..ea4e5f745 100644 --- a/packages/vinext/src/server/app-route-handler-runtime.ts +++ b/packages/vinext/src/server/app-route-handler-runtime.ts @@ -75,16 +75,16 @@ type NextUrlDynamicAccess = export type AppRouteDynamicRequestAccess = RequestDynamicAccess | NextUrlDynamicAccess; -export interface TrackedAppRouteRequestOptions { +export type TrackedAppRouteRequestOptions = { basePath?: string; i18n?: NextI18nConfig | null; onDynamicAccess?: (access: AppRouteDynamicRequestAccess) => void; -} +}; -export interface TrackedAppRouteRequest { +export type TrackedAppRouteRequest = { request: NextRequest; didAccessDynamicRequest(): boolean; -} +}; function bindMethodIfNeeded(value: T, target: object): T { return typeof value === "function" ? (value.bind(target) as T) : value; diff --git a/packages/vinext/src/server/app-ssr-entry.ts b/packages/vinext/src/server/app-ssr-entry.ts index 3f0b37b3a..32d754c47 100644 --- a/packages/vinext/src/server/app-ssr-entry.ts +++ b/packages/vinext/src/server/app-ssr-entry.ts @@ -17,16 +17,16 @@ import { runWithNavigationContext } from "../shims/navigation-state.js"; import { safeJsonStringify } from "./html.js"; import { createRscEmbedTransform, createTickBufferedTransform } from "./app-ssr-stream.js"; -export interface FontPreload { +export type FontPreload = { href: string; type: string; -} +}; -export interface FontData { +export type FontData = { links?: string[]; styles?: string[]; preloads?: FontPreload[]; -} +}; type ClientRequire = (id: string) => Promise; diff --git a/packages/vinext/src/server/app-ssr-stream.ts b/packages/vinext/src/server/app-ssr-stream.ts index d39431f71..cfd7b53d3 100644 --- a/packages/vinext/src/server/app-ssr-stream.ts +++ b/packages/vinext/src/server/app-ssr-stream.ts @@ -1,9 +1,9 @@ import { safeJsonStringify } from "./html.js"; -export interface RscEmbedTransform { +export type RscEmbedTransform = { flush(): string; finalize(): Promise; -} +}; /** * Fix invalid preload "as" values in RSC Flight hint lines before they reach diff --git a/packages/vinext/src/server/dev-module-runner.ts b/packages/vinext/src/server/dev-module-runner.ts index ae93ac224..07df21cf8 100644 --- a/packages/vinext/src/server/dev-module-runner.ts +++ b/packages/vinext/src/server/dev-module-runner.ts @@ -65,13 +65,13 @@ import type { DevEnvironment } from "vite"; * environment types — including Cloudflare's custom environments that don't * support the hot-channel-based transport. */ -export interface DevEnvironmentLike { +export type DevEnvironmentLike = { fetchModule: ( id: string, importer?: string, options?: { cached?: boolean; startOffset?: number }, ) => Promise>; -} +}; /** * Build a ModuleRunner that calls `environment.fetchModule()` directly, diff --git a/packages/vinext/src/server/image-optimization.ts b/packages/vinext/src/server/image-optimization.ts index 11874c231..2093c910a 100644 --- a/packages/vinext/src/server/image-optimization.ts +++ b/packages/vinext/src/server/image-optimization.ts @@ -24,14 +24,14 @@ export const IMAGE_OPTIMIZATION_PATH = "/_vinext/image"; * Image security configuration from next.config.js `images` section. * Controls SVG handling and security headers for the image endpoint. */ -export interface ImageConfig { +export type ImageConfig = { /** Allow SVG through the image optimization endpoint. Default: false. */ dangerouslyAllowSVG?: boolean; /** Content-Disposition header value. Default: "inline". */ contentDispositionType?: "inline" | "attachment"; /** Content-Security-Policy header value. Default: "script-src 'none'; frame-src 'none'; sandbox;" */ contentSecurityPolicy?: string; -} +}; /** * Next.js default device sizes and image sizes. @@ -189,7 +189,7 @@ function createPassthroughImageResponse(source: Response, config?: ImageConfig): * Handlers for image optimization I/O operations. * Workers provide these callbacks to adapt their specific bindings. */ -export interface ImageHandlers { +export type ImageHandlers = { /** Fetch the source image from storage (e.g., Cloudflare ASSETS binding). */ fetchAsset: (path: string, request: Request) => Promise; /** Optional: Transform the image (resize, format, quality). */ @@ -197,7 +197,7 @@ export interface ImageHandlers { body: ReadableStream, options: { width: number; format: string; quality: number }, ) => Promise; -} +}; /** * Handle image optimization requests. diff --git a/packages/vinext/src/server/instrumentation.ts b/packages/vinext/src/server/instrumentation.ts index 5762a3b0a..d4e2bc9f8 100644 --- a/packages/vinext/src/server/instrumentation.ts +++ b/packages/vinext/src/server/instrumentation.ts @@ -45,9 +45,9 @@ import { ValidFileMatcher } from "../routing/file-matcher.js"; * `runInstrumentation`. Only `.import()` is used — this avoids requiring * callers (including tests) to provide a full `ModuleRunner` instance. */ -export interface ModuleImporter { +export type ModuleImporter = { import(id: string): Promise; -} +}; /** * Import a module via the runner and cast the result to `Record`. @@ -109,7 +109,7 @@ export function findInstrumentationClientFile( * Called when an unhandled error occurs during request handling. * Provides the error, the request info, and an error context. */ -export interface OnRequestErrorContext { +export type OnRequestErrorContext = { /** The route path (e.g., '/blog/[slug]') */ routerKind: "Pages Router" | "App Router"; /** The matched route pattern */ @@ -118,7 +118,7 @@ export interface OnRequestErrorContext { routeType: "render" | "route" | "action" | "middleware"; /** HTTP status code that will be sent */ revalidateReason?: "on-demand" | "stale" | undefined; -} +}; export type OnRequestErrorHandler = ( error: Error, diff --git a/packages/vinext/src/server/isr-cache.ts b/packages/vinext/src/server/isr-cache.ts index 860aa4681..d1fd2d4aa 100644 --- a/packages/vinext/src/server/isr-cache.ts +++ b/packages/vinext/src/server/isr-cache.ts @@ -23,10 +23,10 @@ import { import { fnv1a64 } from "../utils/hash.js"; import { getRequestExecutionContext } from "../shims/request-context.js"; -export interface ISRCacheEntry { +export type ISRCacheEntry = { value: CacheHandlerValue; isStale: boolean; -} +}; /** * Get a cache entry with staleness information. diff --git a/packages/vinext/src/server/metadata-routes.ts b/packages/vinext/src/server/metadata-routes.ts index d226f6d16..47bc72fed 100644 --- a/packages/vinext/src/server/metadata-routes.ts +++ b/packages/vinext/src/server/metadata-routes.ts @@ -22,7 +22,7 @@ import path from "node:path"; // Types matching Next.js MetadataRoute // ------------------------------------------------------------------- -export interface SitemapEntry { +export type SitemapEntry = { url: string; lastModified?: string | Date; changeFrequency?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never"; @@ -53,22 +53,22 @@ export interface SitemapEntry { live?: "yes" | "no"; tag?: string; }>; -} +}; -export interface RobotsRule { +export type RobotsRule = { userAgent?: string | string[]; allow?: string | string[]; disallow?: string | string[]; crawlDelay?: number; -} +}; -export interface RobotsConfig { +export type RobotsConfig = { rules: RobotsRule | RobotsRule[]; sitemap?: string | string[]; host?: string; -} +}; -export interface ManifestConfig { +export type ManifestConfig = { name?: string; short_name?: string; description?: string; @@ -83,7 +83,7 @@ export interface ManifestConfig { purpose?: string; }>; [key: string]: unknown; -} +}; // ------------------------------------------------------------------- // Known metadata file patterns @@ -343,7 +343,7 @@ function serializeDate(value: string | Date): string { // Metadata route discovery // ------------------------------------------------------------------- -export interface MetadataFileRoute { +export type MetadataFileRoute = { /** Type of metadata file */ type: string; /** Whether this is a dynamic (code-generated) route */ @@ -354,7 +354,7 @@ export interface MetadataFileRoute { servedUrl: string; /** Content type for the response */ contentType: string; -} +}; function metadataRouteSuffix(parentSegments: string[], metaType: string): string { if (metaType === "sitemap" || metaType === "robots" || metaType === "manifest") { diff --git a/packages/vinext/src/server/middleware.ts b/packages/vinext/src/server/middleware.ts index 672e171d4..738a86739 100644 --- a/packages/vinext/src/server/middleware.ts +++ b/packages/vinext/src/server/middleware.ts @@ -345,7 +345,7 @@ function compileMatcherPattern(pattern: string): RegExp | null { } /** Result of running middleware. */ -export interface MiddlewareResult { +export type MiddlewareResult = { /** Whether to continue to the route handler. */ continue: boolean; /** If set, redirect to this URL. */ @@ -362,7 +362,7 @@ export interface MiddlewareResult { response?: Response; /** Promises registered via event.waitUntil() during middleware execution */ waitUntilPromises?: Promise[]; -} +}; /** * Load and execute middleware for a given request. diff --git a/packages/vinext/src/server/pages-api-route.ts b/packages/vinext/src/server/pages-api-route.ts index 8579c2e50..2cf395a48 100644 --- a/packages/vinext/src/server/pages-api-route.ts +++ b/packages/vinext/src/server/pages-api-route.ts @@ -9,23 +9,23 @@ import { PagesApiBodyParseError, } from "./pages-node-compat.js"; -interface PagesApiRouteModule { +type PagesApiRouteModule = { default?: (req: PagesReqResRequest, res: PagesReqResResponse) => void | Promise; -} +}; -export interface PagesApiRouteMatch { +export type PagesApiRouteMatch = { params: PagesRequestQuery; route: Pick & { module: PagesApiRouteModule; }; -} +}; -export interface HandlePagesApiRouteOptions { +export type HandlePagesApiRouteOptions = { match: PagesApiRouteMatch | null; reportRequestError?: (error: Error, routePattern: string) => void | Promise; request: Request; url: string; -} +}; function buildPagesApiQuery(url: string, params: PagesRequestQuery): PagesRequestQuery { const query: PagesRequestQuery = { ...params }; diff --git a/packages/vinext/src/server/pages-i18n.ts b/packages/vinext/src/server/pages-i18n.ts index 2222760af..28cf59254 100644 --- a/packages/vinext/src/server/pages-i18n.ts +++ b/packages/vinext/src/server/pages-i18n.ts @@ -8,7 +8,7 @@ import { type HeaderValue = string | string[] | undefined; type HeaderBag = Headers | Record | undefined; -interface LocaleRedirectOptions { +type LocaleRedirectOptions = { headers?: HeaderBag; nextConfig: { basePath?: string; @@ -21,15 +21,15 @@ interface LocaleRedirectOptions { pathname: string; search?: string; }; -} +}; -export interface PagesI18nRequestInfo { +export type PagesI18nRequestInfo = { locale: string; url: string; hadPrefix: boolean; domainLocale?: DomainLocale; redirectUrl?: string; -} +}; function readHeader(headers: HeaderBag, name: string): string | undefined { if (!headers) return undefined; diff --git a/packages/vinext/src/server/pages-node-compat.ts b/packages/vinext/src/server/pages-node-compat.ts index f92bcf481..1b1940179 100644 --- a/packages/vinext/src/server/pages-node-compat.ts +++ b/packages/vinext/src/server/pages-node-compat.ts @@ -12,20 +12,20 @@ export { PagesBodyParseError as PagesApiBodyParseError }; export type PagesRequestQuery = Record; -export interface PagesReqResRequest { +export type PagesReqResRequest = { method: string; url: string; headers: Record; query: PagesRequestQuery; body: unknown; cookies: Record; -} +}; -export interface PagesReqResHeaders { +export type PagesReqResHeaders = { [key: string]: string | number | boolean | string[]; -} +}; -export interface PagesReqResResponse { +export type PagesReqResResponse = { statusCode: number; readonly headersSent: boolean; writeHead: (code: number, headers?: PagesReqResHeaders) => PagesReqResResponse; @@ -37,20 +37,20 @@ export interface PagesReqResResponse { send: (data: unknown) => void; redirect: (statusOrUrl: number | string, url?: string) => void; getHeaders: () => PagesReqResHeaders; -} +}; -export interface CreatePagesReqResOptions { +export type CreatePagesReqResOptions = { body: unknown; query: PagesRequestQuery; request: Request; url: string; -} +}; -export interface CreatePagesReqResResult { +export type CreatePagesReqResResult = { req: PagesReqResRequest; res: PagesReqResResponse; responsePromise: Promise; -} +}; async function readPagesRequestBodyWithLimit(request: Request, maxBytes: number): Promise { if (!request.body) { diff --git a/packages/vinext/src/server/pages-page-data.ts b/packages/vinext/src/server/pages-page-data.ts index 49b22a6b6..7451a15c0 100644 --- a/packages/vinext/src/server/pages-page-data.ts +++ b/packages/vinext/src/server/pages-page-data.ts @@ -8,39 +8,39 @@ import { type PagesI18nRenderContext, } from "./pages-page-response.js"; -interface PagesRedirectResult { +type PagesRedirectResult = { destination: string; permanent?: boolean; statusCode?: number; -} +}; -interface PagesStaticPathsEntry { +type PagesStaticPathsEntry = { params: Record; -} +}; -interface PagesStaticPathsResult { +type PagesStaticPathsResult = { fallback?: boolean | "blocking"; paths?: PagesStaticPathsEntry[]; -} +}; -interface PagesPagePropsResult { +type PagesPagePropsResult = { props?: Record; redirect?: PagesRedirectResult; notFound?: boolean; revalidate?: number; -} +}; -export interface PagesMutableGsspResponse extends PagesGsspResponse { +export type PagesMutableGsspResponse = { headersSent: boolean; -} +} & PagesGsspResponse; -export interface PagesGsspContextResponse { +export type PagesGsspContextResponse = { req: unknown; res: PagesMutableGsspResponse; responsePromise: Promise; -} +}; -export interface PagesPageModule { +export type PagesPageModule = { default?: unknown; getStaticPaths?: (context: { locales: string[]; @@ -62,9 +62,9 @@ export interface PagesPageModule { locales?: string[]; defaultLocale?: string; }) => Promise | PagesPagePropsResult; -} +}; -export interface RenderPagesIsrHtmlOptions { +export type RenderPagesIsrHtmlOptions = { buildId: string | null; cachedHtml: string; createPageElement: (pageProps: Record) => ReactNode; @@ -74,9 +74,9 @@ export interface RenderPagesIsrHtmlOptions { renderIsrPassToStringAsync: (element: ReactNode) => Promise; routePattern: string; safeJsonStringify: (value: unknown) => string; -} +}; -export interface ResolvePagesPageDataOptions { +export type ResolvePagesPageDataOptions = { applyRequestContexts: () => void; buildId: string | null; createGsspReqRes: () => PagesGsspContextResponse; @@ -102,19 +102,19 @@ export interface ResolvePagesPageDataOptions { sanitizeDestination: (destination: string) => string; triggerBackgroundRegeneration: (key: string, renderFn: () => Promise) => void; renderIsrPassToStringAsync: (element: ReactNode) => Promise; -} +}; -export interface ResolvePagesPageDataRenderResult { +export type ResolvePagesPageDataRenderResult = { kind: "render"; gsspRes: PagesGsspResponse | null; isrRevalidateSeconds: number | null; pageProps: Record; -} +}; -export interface ResolvePagesPageDataResponseResult { +export type ResolvePagesPageDataResponseResult = { kind: "response"; response: Response; -} +}; export type ResolvePagesPageDataResult = | ResolvePagesPageDataRenderResult diff --git a/packages/vinext/src/server/pages-page-response.ts b/packages/vinext/src/server/pages-page-response.ts index abfdb7ff7..73c514b51 100644 --- a/packages/vinext/src/server/pages-page-response.ts +++ b/packages/vinext/src/server/pages-page-response.ts @@ -1,27 +1,27 @@ import React, { type ComponentType, type ReactNode } from "react"; -export interface PagesFontPreload { +export type PagesFontPreload = { href: string; type: string; -} +}; -export interface PagesI18nRenderContext { +export type PagesI18nRenderContext = { locale?: string; locales?: string[]; defaultLocale?: string; domainLocales?: unknown; -} +}; -export interface PagesGsspResponse { +export type PagesGsspResponse = { statusCode: number; getHeaders(): Record; -} +}; -interface PagesStreamedHtmlResponse extends Response { +type PagesStreamedHtmlResponse = { __vinextStreamedHtmlResponse?: boolean; -} +} & Response; -export interface RenderPagesPageResponseOptions { +export type RenderPagesPageResponseOptions = { assetTags: string; buildId: string | null; clearSsrContext: () => void; @@ -57,7 +57,7 @@ export interface RenderPagesPageResponseOptions { routePattern: string; routeUrl: string; safeJsonStringify: (value: unknown) => string; -} +}; function escapeAttr(value: string): string { return value.replace(/&/g, "&").replace(/"/g, """); diff --git a/packages/vinext/src/server/prod-server.ts b/packages/vinext/src/server/prod-server.ts index 3db842da4..627449c7c 100644 --- a/packages/vinext/src/server/prod-server.ts +++ b/packages/vinext/src/server/prod-server.ts @@ -63,7 +63,7 @@ function readNodeStream(req: IncomingMessage): ReadableStream { }); } -export interface ProdServerOptions { +export type ProdServerOptions = { /** Port to listen on */ port?: number; /** Host to bind to */ @@ -72,7 +72,7 @@ export interface ProdServerOptions { outDir?: string; /** Disable compression (default: false) */ noCompression?: boolean; -} +}; /** Content types that benefit from compression. */ const COMPRESSIBLE_TYPES = new Set([ @@ -683,17 +683,17 @@ export async function startProdServer(options: ProdServerOptions = {}) { // ─── App Router Production Server ───────────────────────────────────────────── -interface AppRouterServerOptions { +type AppRouterServerOptions = { port: number; host: string; clientDir: string; rscEntryPath: string; compress: boolean; -} +}; -interface WorkerAppRouterEntry { +type WorkerAppRouterEntry = { fetch(request: Request, env?: unknown, ctx?: ExecutionContextLike): Promise | Response; -} +}; function createNodeExecutionContext(): ExecutionContextLike { return { @@ -907,13 +907,13 @@ async function startAppRouterServer(options: AppRouterServerOptions) { // ─── Pages Router Production Server ─────────────────────────────────────────── -interface PagesRouterServerOptions { +type PagesRouterServerOptions = { port: number; host: string; clientDir: string; serverEntryPath: string; compress: boolean; -} +}; /** * Start the Pages Router production server. diff --git a/packages/vinext/src/server/seed-cache.ts b/packages/vinext/src/server/seed-cache.ts index bffa72e90..abbc6f43b 100644 --- a/packages/vinext/src/server/seed-cache.ts +++ b/packages/vinext/src/server/seed-cache.ts @@ -37,19 +37,19 @@ import { getOutputPath, getRscOutputPath } from "../build/prerender.js"; // ─── Manifest types ─────────────────────────────────────────────────────────── -interface PrerenderManifest { +type PrerenderManifest = { buildId: string; trailingSlash?: boolean; routes: PrerenderManifestRoute[]; -} +}; -interface PrerenderManifestRoute { +type PrerenderManifestRoute = { route: string; status: string; revalidate?: number | false; path?: string; router?: "app" | "pages"; -} +}; // ─── Public API ─────────────────────────────────────────────────────────────── diff --git a/packages/vinext/src/shims/app.ts b/packages/vinext/src/shims/app.ts index 65c536adb..53ec6849d 100644 --- a/packages/vinext/src/shims/app.ts +++ b/packages/vinext/src/shims/app.ts @@ -5,10 +5,10 @@ */ import type { ComponentType } from "react"; -export interface AppProps

> { +export type AppProps

> = { Component: ComponentType

; pageProps: P; -} +}; // Re-export for named import compatibility export type { AppProps as default }; diff --git a/packages/vinext/src/shims/cache-runtime.ts b/packages/vinext/src/shims/cache-runtime.ts index bff5f363e..2aaab7404 100644 --- a/packages/vinext/src/shims/cache-runtime.ts +++ b/packages/vinext/src/shims/cache-runtime.ts @@ -45,14 +45,14 @@ import { // Cache execution context — AsyncLocalStorage for cacheLife/cacheTag // --------------------------------------------------------------------------- -export interface CacheContext { +export type CacheContext = { /** Tags collected via cacheTag() during execution */ tags: string[]; /** Cache life configs collected via cacheLife() — minimum-wins rule applies */ lifeConfigs: CacheLifeConfig[]; /** Cache variant: "default" | "remote" | "private" */ variant: string; -} +}; // Store on globalThis via Symbol so headers.ts can detect "use cache" scope // without a direct import (avoiding circular dependencies). @@ -82,14 +82,14 @@ export function getCacheContext(): CacheContext | null { * (they depend on virtual modules set up by @vitejs/plugin-rsc). * In test environments, the import fails and we fall back to JSON. */ -interface RscModule { +type RscModule = { renderToReadableStream: (data: unknown, options?: object) => ReadableStream; createFromReadableStream: (stream: ReadableStream, options?: object) => Promise; encodeReply: (v: unknown[], options?: unknown) => Promise; createTemporaryReferenceSet: () => unknown; createClientTemporaryReferenceSet: () => unknown; decodeReply: (body: string | FormData, options?: unknown) => Promise; -} +}; const NOT_LOADED = Symbol("not-loaded"); let _rscModule: RscModule | null | typeof NOT_LOADED = NOT_LOADED; @@ -229,9 +229,9 @@ function resolveCacheLife(configs: CacheLifeConfig[]): CacheLifeConfig { // Uses AsyncLocalStorage for request isolation so concurrent requests // on Workers don't share private cache entries. // --------------------------------------------------------------------------- -export interface PrivateCacheState { +export type PrivateCacheState = { _privateCache: Map | null; -} +}; const _PRIVATE_ALS_KEY = Symbol.for("vinext.cacheRuntime.privateAls"); const _PRIVATE_FALLBACK_KEY = Symbol.for("vinext.cacheRuntime.privateFallback"); diff --git a/packages/vinext/src/shims/cache.ts b/packages/vinext/src/shims/cache.ts index da63084dd..08f7254e6 100644 --- a/packages/vinext/src/shims/cache.ts +++ b/packages/vinext/src/shims/cache.ts @@ -31,11 +31,11 @@ import { // The cache-runtime module sets this on load. // --------------------------------------------------------------------------- -interface CacheContextLike { +type CacheContextLike = { tags: string[]; lifeConfigs: import("./cache-runtime.js").CacheContext["lifeConfigs"]; variant: string; -} +}; /** @internal Set by cache-runtime.ts on import to avoid circular dependency */ let _getCacheContextFn: (() => CacheContextLike | null) | null = null; @@ -53,12 +53,12 @@ export function _registerCacheContextAccessor(fn: () => CacheContextLike | null) // Implement this to provide a custom cache backend. // --------------------------------------------------------------------------- -export interface CacheHandlerValue { +export type CacheHandlerValue = { lastModified: number; age?: number; cacheState?: string; value: IncrementalCacheValue | null; -} +}; /** Discriminated union of cache value types. */ export type IncrementalCacheValue = @@ -69,7 +69,7 @@ export type IncrementalCacheValue = | CachedRedirectValue | CachedImageValue; -export interface CachedFetchValue { +export type CachedFetchValue = { kind: "FETCH"; data: { headers: Record; @@ -79,53 +79,53 @@ export interface CachedFetchValue { }; tags?: string[]; revalidate: number | false; -} +}; -export interface CachedAppPageValue { +export type CachedAppPageValue = { kind: "APP_PAGE"; html: string; rscData: ArrayBuffer | undefined; headers: Record | undefined; postponed: string | undefined; status: number | undefined; -} +}; -export interface CachedPagesValue { +export type CachedPagesValue = { kind: "PAGES"; html: string; pageData: object; headers: Record | undefined; status: number | undefined; -} +}; -export interface CachedRouteValue { +export type CachedRouteValue = { kind: "APP_ROUTE"; body: ArrayBuffer; status: number; headers: Record; -} +}; -export interface CachedRedirectValue { +export type CachedRedirectValue = { kind: "REDIRECT"; props: object; -} +}; -export interface CachedImageValue { +export type CachedImageValue = { kind: "IMAGE"; etag: string; buffer: ArrayBuffer; extension: string; revalidate?: number; -} +}; -export interface CacheHandlerContext { +export type CacheHandlerContext = { dev?: boolean; maxMemoryCacheSize?: number; revalidatedTags?: string[]; [key: string]: unknown; -} +}; -export interface CacheHandler { +export type CacheHandler = { get(key: string, ctx?: Record): Promise; set( @@ -137,7 +137,7 @@ export interface CacheHandler { revalidateTag(tags: string | string[], durations?: { expire?: number }): Promise; resetRequestCache?(): void; -} +}; // --------------------------------------------------------------------------- // No-op cache handler — used during prerender to skip wasteful isrSet writes. @@ -169,25 +169,25 @@ export class NoOpCacheHandler implements CacheHandler { // single-process production. Not shared across workers/instances. // --------------------------------------------------------------------------- -interface MemoryEntry { +type MemoryEntry = { value: IncrementalCacheValue | null; tags: string[]; lastModified: number; revalidateAt: number | null; -} +}; /** * Shape of the optional `ctx` argument passed to `CacheHandler.set()`. * Covers both the older `{ revalidate: number }` shape and the newer * `{ cacheControl: { revalidate: number } }` shape (Next.js 16). */ -interface SetCtx { +type SetCtx = { tags?: string[]; fetchCache?: boolean; revalidate?: number; cacheControl?: { revalidate?: number }; [key: string]: unknown; -} +}; export class MemoryCacheHandler implements CacheHandler { private store = new Map(); @@ -430,9 +430,9 @@ export { unstable_noStore as noStore }; // // Uses AsyncLocalStorage for request isolation on concurrent workers. // --------------------------------------------------------------------------- -export interface CacheState { +export type CacheState = { requestScopedCacheLife: CacheLifeConfig | null; -} +}; const _ALS_KEY = Symbol.for("vinext.cache.als"); const _FALLBACK_KEY = Symbol.for("vinext.cache.fallback"); @@ -528,14 +528,14 @@ export function _consumeRequestScopedCacheLife(): CacheLifeConfig | null { /** * Cache life configuration. Controls stale-while-revalidate behavior. */ -export interface CacheLifeConfig { +export type CacheLifeConfig = { /** How long (seconds) the client can cache without checking the server */ stale?: number; /** How frequently (seconds) the server cache refreshes */ revalidate?: number; /** Max staleness (seconds) before deoptimizing to dynamic */ expire?: number; -} +}; /** * Built-in cache life profiles matching Next.js 16. @@ -666,10 +666,10 @@ export function isInsideUnstableCacheScope(): boolean { return _unstableCacheAls.getStore() === true; } -interface UnstableCacheOptions { +type UnstableCacheOptions = { revalidate?: number | false; tags?: string[]; -} +}; /** * Wrap an async function with caching. diff --git a/packages/vinext/src/shims/config.ts b/packages/vinext/src/shims/config.ts index a3ade70b0..ecee9ec4b 100644 --- a/packages/vinext/src/shims/config.ts +++ b/packages/vinext/src/shims/config.ts @@ -10,10 +10,10 @@ * const { publicRuntimeConfig, serverRuntimeConfig } = getConfig(); */ -interface RuntimeConfig { +type RuntimeConfig = { serverRuntimeConfig: Record; publicRuntimeConfig: Record; -} +}; let runtimeConfig: RuntimeConfig = { serverRuntimeConfig: {}, diff --git a/packages/vinext/src/shims/dynamic.ts b/packages/vinext/src/shims/dynamic.ts index 949ffedd5..30599ff18 100644 --- a/packages/vinext/src/shims/dynamic.ts +++ b/packages/vinext/src/shims/dynamic.ts @@ -19,10 +19,10 @@ */ import React, { type ComponentType } from "react"; -interface DynamicOptions { +type DynamicOptions = { loading?: ComponentType<{ error?: Error | null; isLoading?: boolean; pastDelay?: boolean }>; ssr?: boolean; -} +}; type Loader

= () => Promise<{ default: ComponentType

} | ComponentType

>; diff --git a/packages/vinext/src/shims/error-boundary.tsx b/packages/vinext/src/shims/error-boundary.tsx index 9c311f890..1f097ba1c 100644 --- a/packages/vinext/src/shims/error-boundary.tsx +++ b/packages/vinext/src/shims/error-boundary.tsx @@ -4,14 +4,14 @@ import React from "react"; // oxlint-disable-next-line @typescript-eslint/no-require-imports -- next/navigation is shimmed import { usePathname } from "next/navigation"; -export interface ErrorBoundaryProps { +export type ErrorBoundaryProps = { fallback: React.ComponentType<{ error: Error; reset: () => void }>; children: React.ReactNode; -} +}; -export interface ErrorBoundaryState { +export type ErrorBoundaryState = { error: Error | null; -} +}; /** * Generic ErrorBoundary used to wrap route segments with error.tsx. @@ -58,19 +58,19 @@ export class ErrorBoundary extends React.Component 0 ? `${normalizedType}; ${keptParams.join("; ")}` : normalizedType; } -interface SerializedBodyResult { +type SerializedBodyResult = { bodyChunks: string[]; canonicalizedContentType?: string; -} +}; async function readRequestBodyChunksWithinLimit(request: Request): Promise<{ chunks: Uint8Array[]; @@ -414,13 +414,14 @@ async function buildFetchCacheKey( // Types // --------------------------------------------------------------------------- -interface NextFetchOptions { +type NextFetchOptions = { revalidate?: number | false; tags?: string[]; -} +}; // Extend the standard RequestInit to include `next` declare global { + // oxlint-disable-next-line typescript/consistent-type-definitions interface RequestInit { next?: NextFetchOptions; } @@ -465,9 +466,9 @@ const originalFetch: typeof globalThis.fetch = (_gFetch[_ORIG_FETCH_KEY] ??= // Uses Symbol.for() on globalThis so the storage is shared across Vite's // multi-environment module instances. // --------------------------------------------------------------------------- -export interface FetchCacheState { +export type FetchCacheState = { currentRequestTags: string[]; -} +}; const _ALS_KEY = Symbol.for("vinext.fetchCache.als"); const _FALLBACK_KEY = Symbol.for("vinext.fetchCache.fallback"); diff --git a/packages/vinext/src/shims/font-google-base.ts b/packages/vinext/src/shims/font-google-base.ts index f308c71a5..77f3028c9 100644 --- a/packages/vinext/src/shims/font-google-base.ts +++ b/packages/vinext/src/shims/font-google-base.ts @@ -81,7 +81,7 @@ let classCounter = 0; // Track which font stylesheets have been injected (SSR + client) const injectedFonts = new Set(); -export interface FontOptions { +export type FontOptions = { weight?: string | string[]; style?: string | string[]; subsets?: string[]; @@ -91,13 +91,13 @@ export interface FontOptions { adjustFontFallback?: boolean | string; variable?: string; axes?: string[]; -} +}; -export interface FontResult { +export type FontResult = { className: string; style: { fontFamily: string }; variable?: string; -} +}; /** * Convert a font family name to a CSS variable name. diff --git a/packages/vinext/src/shims/font-local.ts b/packages/vinext/src/shims/font-local.ts index d689c9adb..0b03ea714 100644 --- a/packages/vinext/src/shims/font-local.ts +++ b/packages/vinext/src/shims/font-local.ts @@ -100,13 +100,13 @@ function sanitizeCSSValue(value: string): string | undefined { let classCounter = 0; const injectedFonts = new Set(); -interface LocalFontSrc { +type LocalFontSrc = { path: string; weight?: string; style?: string; -} +}; -interface LocalFontOptions { +type LocalFontOptions = { src: string | LocalFontSrc | LocalFontSrc[]; display?: string; weight?: string; @@ -116,13 +116,13 @@ interface LocalFontOptions { variable?: string; adjustFontFallback?: boolean | string; declarations?: Array<{ prop: string; value: string }>; -} +}; -interface FontResult { +type FontResult = { className: string; style: { fontFamily: string }; variable?: string; -} +}; function generateFontFaceCSS(family: string, options: LocalFontOptions): string { const sources = normalizeSources(options); diff --git a/packages/vinext/src/shims/form.tsx b/packages/vinext/src/shims/form.tsx index c617f4806..1b8f31e01 100644 --- a/packages/vinext/src/shims/form.tsx +++ b/packages/vinext/src/shims/form.tsx @@ -160,14 +160,14 @@ function buildFormData(form: HTMLFormElement, submitter: FormSubmitter | null): } } -interface FormProps extends FormHTMLAttributes { +type FormProps = { /** Target URL for GET forms, or server action for POST forms */ action: string | ((formData: FormData) => void | Promise); /** Replace instead of push in history (default: false) */ replace?: boolean; /** Scroll to top after navigation (default: true) */ scroll?: boolean; -} +} & FormHTMLAttributes; const Form = forwardRef(function Form(props: FormProps, ref: ForwardedRef) { const { action, replace = false, scroll = true, onSubmit, ...rest } = props; diff --git a/packages/vinext/src/shims/head-state.ts b/packages/vinext/src/shims/head-state.ts index 0ea1e58bc..33900c1f4 100644 --- a/packages/vinext/src/shims/head-state.ts +++ b/packages/vinext/src/shims/head-state.ts @@ -21,9 +21,9 @@ import { // ALS setup // --------------------------------------------------------------------------- -export interface HeadState { +export type HeadState = { ssrHeadChildren: React.ReactNode[]; -} +}; const _ALS_KEY = Symbol.for("vinext.head.als"); const _FALLBACK_KEY = Symbol.for("vinext.head.fallback"); diff --git a/packages/vinext/src/shims/head.ts b/packages/vinext/src/shims/head.ts index 0b42c3057..cc7ecad21 100644 --- a/packages/vinext/src/shims/head.ts +++ b/packages/vinext/src/shims/head.ts @@ -9,9 +9,9 @@ */ import React, { useEffect, useRef, Children, isValidElement } from "react"; -interface HeadProps { +type HeadProps = { children?: React.ReactNode; -} +}; // --- SSR head collection --- // State uses a registration pattern so this module can be bundled for the diff --git a/packages/vinext/src/shims/headers.ts b/packages/vinext/src/shims/headers.ts index 170fcaf6c..d27133016 100644 --- a/packages/vinext/src/shims/headers.ts +++ b/packages/vinext/src/shims/headers.ts @@ -21,14 +21,14 @@ import { // Request context // --------------------------------------------------------------------------- -export interface HeadersContext { +export type HeadersContext = { headers: Headers; cookies: Map; accessError?: Error; mutableCookies?: RequestCookies; readonlyCookies?: RequestCookies; readonlyHeaders?: Headers; -} +}; export type HeadersAccessPhase = "render" | "action" | "route-handler"; @@ -605,11 +605,11 @@ export function getDraftModeCookieHeader(): string | null { return header; } -interface DraftModeResult { +type DraftModeResult = { isEnabled: boolean; enable(): void; disable(): void; -} +}; /** * Draft mode — check/toggle via a `__prerender_bypass` cookie. diff --git a/packages/vinext/src/shims/i18n-context.ts b/packages/vinext/src/shims/i18n-context.ts index 8a578b499..60472c523 100644 --- a/packages/vinext/src/shims/i18n-context.ts +++ b/packages/vinext/src/shims/i18n-context.ts @@ -9,13 +9,13 @@ import type { DomainLocale } from "../utils/domain-locale.js"; -export interface I18nContext { +export type I18nContext = { locale?: string; locales?: string[]; defaultLocale?: string; domainLocales?: readonly DomainLocale[]; hostname?: string; -} +}; // --------------------------------------------------------------------------- // Fallback: read/write bare globalThis (unsafe for concurrent requests). diff --git a/packages/vinext/src/shims/i18n-state.ts b/packages/vinext/src/shims/i18n-state.ts index b8c8a99b3..7ff81aae7 100644 --- a/packages/vinext/src/shims/i18n-state.ts +++ b/packages/vinext/src/shims/i18n-state.ts @@ -21,9 +21,9 @@ import { // ALS setup // --------------------------------------------------------------------------- -export interface I18nState { +export type I18nState = { i18nContext: I18nContext | null; -} +}; const _ALS_KEY = Symbol.for("vinext.i18n.als"); const _FALLBACK_KEY = Symbol.for("vinext.i18n.fallback"); diff --git a/packages/vinext/src/shims/image-config.ts b/packages/vinext/src/shims/image-config.ts index 7c52fa7dc..bc50919e2 100644 --- a/packages/vinext/src/shims/image-config.ts +++ b/packages/vinext/src/shims/image-config.ts @@ -12,13 +12,13 @@ * - protocol, port, and search are matched exactly when specified */ -export interface RemotePattern { +export type RemotePattern = { protocol?: string; hostname: string; port?: string; pathname?: string; search?: string; -} +}; /** * Convert a glob pattern (with `*` and `**`) to a RegExp. diff --git a/packages/vinext/src/shims/image.tsx b/packages/vinext/src/shims/image.tsx index 53709b8e4..44dbbbbd6 100644 --- a/packages/vinext/src/shims/image.tsx +++ b/packages/vinext/src/shims/image.tsx @@ -14,12 +14,12 @@ import React, { forwardRef } from "react"; import { Image as UnpicImage } from "@unpic/react"; import { hasRemoteMatch, type RemotePattern } from "./image-config.js"; -export interface StaticImageData { +export type StaticImageData = { src: string; height: number; width: number; blurDataURL?: string; -} +}; /** * Image config injected at build time via Vite define. @@ -92,7 +92,7 @@ function validateRemoteUrl(src: string): { allowed: boolean; reason?: string } { }; } -interface ImageProps { +type ImageProps = { src: string | StaticImageData; alt: string; width?: number; @@ -116,7 +116,7 @@ interface ImageProps { unoptimized?: boolean; overrideSrc?: string; loading?: "lazy" | "eager"; -} +}; /** * Sanitize a blurDataURL to prevent CSS injection. diff --git a/packages/vinext/src/shims/internal/app-router-context.ts b/packages/vinext/src/shims/internal/app-router-context.ts index 2b521e939..baa205882 100644 --- a/packages/vinext/src/shims/internal/app-router-context.ts +++ b/packages/vinext/src/shims/internal/app-router-context.ts @@ -8,23 +8,23 @@ */ import { createContext } from "react"; -export interface NavigateOptions { +export type NavigateOptions = { scroll?: boolean; -} +}; -export interface PrefetchOptions { +export type PrefetchOptions = { kind?: unknown; onInvalidate?: () => void; -} +}; -export interface AppRouterInstance { +export type AppRouterInstance = { back(): void; forward(): void; refresh(): void; push(href: string, options?: NavigateOptions): void; replace(href: string, options?: NavigateOptions): void; prefetch(href: string, options?: PrefetchOptions): void; -} +}; export const AppRouterContext = createContext(null); export const GlobalLayoutRouterContext = createContext(null); diff --git a/packages/vinext/src/shims/internal/utils.ts b/packages/vinext/src/shims/internal/utils.ts index b449c4e17..1195da075 100644 --- a/packages/vinext/src/shims/internal/utils.ts +++ b/packages/vinext/src/shims/internal/utils.ts @@ -5,7 +5,7 @@ * Provides the NEXT_DATA type that matches window.__NEXT_DATA__. */ -export interface NEXT_DATA { +export type NEXT_DATA = { props: Record; page: string; query: Record; @@ -34,7 +34,7 @@ export interface NEXT_DATA { scriptLoader?: unknown[]; isPreview?: boolean; notFoundSrcPage?: string; -} +}; /** * Standard Next.js error shape. diff --git a/packages/vinext/src/shims/legacy-image.tsx b/packages/vinext/src/shims/legacy-image.tsx index 83a4356f1..7009c405c 100644 --- a/packages/vinext/src/shims/legacy-image.tsx +++ b/packages/vinext/src/shims/legacy-image.tsx @@ -11,7 +11,7 @@ import React, { forwardRef } from "react"; import Image from "./image.js"; -interface LegacyImageProps { +type LegacyImageProps = { src: string | { src: string; width: number; height: number; blurDataURL?: string }; alt: string; width?: number | string; @@ -36,7 +36,7 @@ interface LegacyImageProps { loading?: "lazy" | "eager"; unoptimized?: boolean; id?: string; -} +}; const LegacyImage = forwardRef( function LegacyImage(props, ref) { diff --git a/packages/vinext/src/shims/link.tsx b/packages/vinext/src/shims/link.tsx index 4b9ff0f06..8e317098f 100644 --- a/packages/vinext/src/shims/link.tsx +++ b/packages/vinext/src/shims/link.tsx @@ -34,15 +34,15 @@ import { addLocalePrefix, getDomainLocaleUrl, type DomainLocale } from "../utils import { getI18nContext } from "./i18n-context.js"; import type { VinextNextData } from "../client/vinext-next-data.js"; -interface NavigateEvent { +type NavigateEvent = { url: URL; /** Call to prevent the Link's default navigation (e.g. for View Transitions). */ preventDefault(): void; /** Whether preventDefault() has been called. */ defaultPrevented: boolean; -} +}; -interface LinkProps extends Omit, "href"> { +type LinkProps = { href: string | { pathname?: string; query?: UrlQuery }; /** URL displayed in the browser (when href is a route pattern like /user/[id]) */ as?: string; @@ -59,15 +59,15 @@ interface LinkProps extends Omit, "href" /** Called before navigation happens (Next.js 16). Return value is ignored. */ onNavigate?: (event: NavigateEvent) => void; children?: React.ReactNode; -} +} & Omit, "href">; // --------------------------------------------------------------------------- // useLinkStatus — reports the pending state of a parent navigation // --------------------------------------------------------------------------- -interface LinkStatusContextValue { +type LinkStatusContextValue = { pending: boolean; -} +}; const LinkStatusContext = createContext({ pending: false }); diff --git a/packages/vinext/src/shims/metadata.tsx b/packages/vinext/src/shims/metadata.tsx index 0051ca860..bb1c0acc4 100644 --- a/packages/vinext/src/shims/metadata.tsx +++ b/packages/vinext/src/shims/metadata.tsx @@ -19,7 +19,7 @@ function makeThenableParams>(obj: T): Promise< return Object.assign(Promise.resolve(plain), plain); } -export interface Viewport { +export type Viewport = { /** Viewport width (default: "device-width") */ width?: string | number; /** Viewport height */ @@ -36,7 +36,7 @@ export interface Viewport { themeColor?: string | Array<{ media?: string; color: string }>; /** Color scheme: 'light' | 'dark' | 'light dark' | 'normal' */ colorScheme?: string; -} +}; /** * Resolve viewport config from a module. Handles both static `viewport` export @@ -124,7 +124,7 @@ export function ViewportHead({ viewport }: { viewport: Viewport }) { // Metadata types and resolution // --------------------------------------------------------------------------- -export interface Metadata { +export type Metadata = { title?: string | { default?: string; template?: string; absolute?: string }; description?: string; generator?: string; @@ -227,40 +227,40 @@ export interface Metadata { }; other?: Record; [key: string]: unknown; -} +}; -interface AppLinksApple { +type AppLinksApple = { url: string | URL; app_store_id?: string | number; app_name?: string; -} +}; -interface AppLinksAndroid { +type AppLinksAndroid = { package: string; url?: string | URL; class?: string; app_name?: string; -} +}; -interface AppLinksWindows { +type AppLinksWindows = { url: string | URL; app_id?: string; app_name?: string; -} +}; -interface AppLinksWeb { +type AppLinksWeb = { url: string | URL; should_fallback?: boolean; -} +}; -interface TwitterPlayerDescriptor { +type TwitterPlayerDescriptor = { playerUrl: string | URL; streamUrl: string | URL; width: number; height: number; -} +}; -interface TwitterAppDescriptor { +type TwitterAppDescriptor = { id: { iphone?: string | number; ipad?: string | number; @@ -272,7 +272,7 @@ interface TwitterAppDescriptor { googleplay?: string | URL; }; name?: string; -} +}; /** * Merge metadata from multiple sources (layouts + page). diff --git a/packages/vinext/src/shims/navigation-state.ts b/packages/vinext/src/shims/navigation-state.ts index 0f5db604e..ee3f95448 100644 --- a/packages/vinext/src/shims/navigation-state.ts +++ b/packages/vinext/src/shims/navigation-state.ts @@ -27,10 +27,10 @@ import { // ALS setup — same pattern as headers.ts // --------------------------------------------------------------------------- -export interface NavigationState { +export type NavigationState = { serverContext: NavigationContext | null; serverInsertedHTMLCallbacks: Array<() => unknown>; -} +}; const _ALS_KEY = Symbol.for("vinext.navigation.als"); const _FALLBACK_KEY = Symbol.for("vinext.navigation.fallback"); diff --git a/packages/vinext/src/shims/navigation.ts b/packages/vinext/src/shims/navigation.ts index ccd7fbc81..0dc78869f 100644 --- a/packages/vinext/src/shims/navigation.ts +++ b/packages/vinext/src/shims/navigation.ts @@ -103,11 +103,11 @@ function useChildSegments(): string[] { // Server-side request context (set by the RSC entry before rendering) // --------------------------------------------------------------------------- -export interface NavigationContext { +export type NavigationContext = { pathname: string; searchParams: URLSearchParams; params: Record; -} +}; const _READONLY_SEARCH_PARAMS = Symbol("vinext.navigation.readonlySearchParams"); const _READONLY_SEARCH_PARAMS_SOURCE = Symbol("vinext.navigation.readonlySearchParamsSource"); @@ -137,12 +137,12 @@ type NavigationContextWithReadonlyCache = NavigationContext & { // ALS-backed state regardless of which instance was registered. // --------------------------------------------------------------------------- -interface _StateAccessors { +type _StateAccessors = { getServerContext: () => NavigationContext | null; setServerContext: (ctx: NavigationContext | null) => void; getInsertedHTMLCallbacks: () => Array<() => unknown>; clearInsertedHTMLCallbacks: () => void; -} +}; export const GLOBAL_ACCESSORS_KEY = Symbol.for("vinext.navigation.globalAccessors"); const _GLOBAL_ACCESSORS_KEY = GLOBAL_ACCESSORS_KEY; @@ -229,10 +229,10 @@ export const MAX_PREFETCH_CACHE_SIZE = 50; /** TTL for prefetch cache entries in ms (matches Next.js static prefetch TTL). */ export const PREFETCH_CACHE_TTL = 30_000; -export interface PrefetchCacheEntry { +export type PrefetchCacheEntry = { response: Response; timestamp: number; -} +}; /** * Convert a pathname (with optional query/hash) to its .rsc URL. diff --git a/packages/vinext/src/shims/next-shims.d.ts b/packages/vinext/src/shims/next-shims.d.ts index d221d885f..1a1d6e4ac 100644 --- a/packages/vinext/src/shims/next-shims.d.ts +++ b/packages/vinext/src/shims/next-shims.d.ts @@ -39,17 +39,17 @@ declare module "next/dynamic" { } declare module "next/config" { - interface RuntimeConfig { + type RuntimeConfig = { serverRuntimeConfig: Record; publicRuntimeConfig: Record; - } + }; export default function getConfig(): RuntimeConfig; export function setConfig(configValue: RuntimeConfig): void; } declare module "next/script" { import { ReactElement } from "react"; - interface ScriptProps { + type ScriptProps = { src?: string; strategy?: "beforeInteractive" | "afterInteractive" | "lazyOnload" | "worker"; id?: string; @@ -59,7 +59,7 @@ declare module "next/script" { children?: React.ReactNode; dangerouslySetInnerHTML?: { __html: string }; [key: string]: unknown; - } + }; const Script: (props: ScriptProps) => ReactElement | null; export default Script; export { ScriptProps }; @@ -112,10 +112,10 @@ declare module "next/navigation" { export function getLayoutSegmentContext(): import("react").Context | null; // RSC prefetch cache utilities (shared between link.tsx and browser entry) - export interface PrefetchCacheEntry { + export type PrefetchCacheEntry = { response: Response; timestamp: number; - } + }; export const MAX_PREFETCH_CACHE_SIZE: number; export const PREFETCH_CACHE_TTL: number; export function toRscUrl(href: string): string; @@ -134,14 +134,14 @@ declare module "next/image" { MouseEventHandler, } from "react"; - export interface StaticImageData { + export type StaticImageData = { src: string; height: number; width: number; blurDataURL?: string; - } + }; - interface ImageProps { + type ImageProps = { src: string | StaticImageData; alt: string; width?: number; @@ -164,7 +164,7 @@ declare module "next/image" { unoptimized?: boolean; overrideSrc?: string; loading?: "lazy" | "eager"; - } + }; const Image: ForwardRefExoticComponent>; export default Image; @@ -182,7 +182,7 @@ declare module "next/legacy/image" { ReactEventHandler, } from "react"; - interface LegacyImageProps { + type LegacyImageProps = { src: string | { src: string; width: number; height: number; blurDataURL?: string }; alt: string; width?: number | string; @@ -204,7 +204,7 @@ declare module "next/legacy/image" { loading?: "lazy" | "eager"; unoptimized?: boolean; id?: string; - } + }; const LegacyImage: ForwardRefExoticComponent>; export default LegacyImage; @@ -213,11 +213,11 @@ declare module "next/legacy/image" { declare module "next/error" { import { ComponentType } from "react"; - interface ErrorProps { + type ErrorProps = { statusCode: number; title?: string; withDarkMode?: boolean; - } + }; const ErrorComponent: ComponentType; export default ErrorComponent; @@ -370,7 +370,7 @@ declare module "next/server" { } declare module "next/font/google" { - interface FontOptions { + type FontOptions = { weight?: string | string[]; style?: string | string[]; subsets?: string[]; @@ -380,13 +380,13 @@ declare module "next/font/google" { adjustFontFallback?: boolean | string; variable?: string; axes?: string[]; - } + }; - interface FontResult { + type FontResult = { className: string; style: { fontFamily: string }; variable?: string; - } + }; type FontLoader = (options?: FontOptions) => FontResult; // Named exports are generated in next-shims-font-google.generated.d.ts @@ -395,13 +395,13 @@ declare module "next/font/google" { } declare module "next/font/local" { - interface LocalFontSrc { + type LocalFontSrc = { path: string; weight?: string; style?: string; - } + }; - interface LocalFontOptions { + type LocalFontOptions = { src: string | LocalFontSrc | LocalFontSrc[]; display?: string; weight?: string; @@ -411,19 +411,19 @@ declare module "next/font/local" { variable?: string; adjustFontFallback?: boolean | string; declarations?: Array<{ prop: string; value: string }>; - } + }; - interface FontResult { + type FontResult = { className: string; style: { fontFamily: string }; variable?: string; - } + }; export default function localFont(options: LocalFontOptions): FontResult; } declare module "next/cache" { - export interface CacheHandler { + export type CacheHandler = { get(key: string, ctx?: Record): Promise; set( key: string, @@ -432,14 +432,14 @@ declare module "next/cache" { ): Promise; revalidateTag(tags: string | string[], durations?: { expire?: number }): Promise; resetRequestCache?(): void; - } + }; - export interface CacheHandlerValue { + export type CacheHandlerValue = { lastModified: number; age?: number; cacheState?: string; value: IncrementalCacheValue | null; - } + }; export type IncrementalCacheValue = | { @@ -498,11 +498,11 @@ declare module "next/cache" { export function noStore(): void; // "use cache" APIs (Next.js 15+) - export interface CacheLifeConfig { + export type CacheLifeConfig = { stale?: number; revalidate?: number; expire?: number; - } + }; export const cacheLifeProfiles: Record; export function cacheLife(profile: string | CacheLifeConfig): void; export function cacheTag(...tags: string[]): void; @@ -511,25 +511,25 @@ declare module "next/cache" { declare module "next/form" { import { ForwardRefExoticComponent, RefAttributes, FormHTMLAttributes } from "react"; - interface FormProps extends Omit, "action"> { + type FormProps = { action: string | ((formData: FormData) => void | Promise); replace?: boolean; scroll?: boolean; - } + } & Omit, "action">; const Form: ForwardRefExoticComponent>; export default Form; } declare module "next/web-vitals" { - interface WebVitalsMetric { + type WebVitalsMetric = { id: string; name: string; value: number; rating?: "good" | "needs-improvement" | "poor"; delta: number; navigationType?: "navigate" | "reload" | "back-forward" | "prerender"; - } + }; type ReportWebVitalsCallback = (metric: WebVitalsMetric) => void; export function useReportWebVitals(callback: ReportWebVitalsCallback): void; } @@ -542,7 +542,7 @@ declare module "next/amp" { declare module "next/og" { import { ReactElement } from "react"; - interface ImageResponseOptions { + type ImageResponseOptions = { width?: number; height?: number; emoji?: "twemoji" | "blobmoji" | "noto" | "openmoji"; @@ -556,7 +556,7 @@ declare module "next/og" { status?: number; statusText?: string; headers?: Record; - } + }; export class ImageResponse extends Response { constructor(element: ReactElement, options?: ImageResponseOptions); diff --git a/packages/vinext/src/shims/request-context.ts b/packages/vinext/src/shims/request-context.ts index b48d12646..959b49f86 100644 --- a/packages/vinext/src/shims/request-context.ts +++ b/packages/vinext/src/shims/request-context.ts @@ -37,10 +37,10 @@ import { * Using a structural interface so this file has no runtime dependency on * Cloudflare types packages. */ -export interface ExecutionContextLike { +export type ExecutionContextLike = { waitUntil(promise: Promise): void; passThroughOnException?(): void; -} +}; // --------------------------------------------------------------------------- // ALS setup — stored on globalThis so all Vite environments (RSC/SSR/client) diff --git a/packages/vinext/src/shims/router-state.ts b/packages/vinext/src/shims/router-state.ts index fe4c1e179..5025ca277 100644 --- a/packages/vinext/src/shims/router-state.ts +++ b/packages/vinext/src/shims/router-state.ts @@ -20,18 +20,18 @@ import { // ALS setup // --------------------------------------------------------------------------- -export interface SSRContext { +export type SSRContext = { pathname: string; query: Record; asPath: string; locale?: string; locales?: string[]; defaultLocale?: string; -} +}; -export interface RouterState { +export type RouterState = { ssrContext: SSRContext | null; -} +}; const _ALS_KEY = Symbol.for("vinext.router.als"); const _FALLBACK_KEY = Symbol.for("vinext.router.fallback"); diff --git a/packages/vinext/src/shims/router.ts b/packages/vinext/src/shims/router.ts index 9f929ad20..f3bb4ea68 100644 --- a/packages/vinext/src/shims/router.ts +++ b/packages/vinext/src/shims/router.ts @@ -28,7 +28,7 @@ type BeforePopStateCallback = (state: { options: { shallow: boolean }; }) => boolean; -export interface NextRouter { +export type NextRouter = { /** Current pathname */ pathname: string; /** Current route pattern (e.g., "/posts/[id]") */ @@ -68,24 +68,24 @@ export interface NextRouter { beforePopState(cb: BeforePopStateCallback): void; /** Listen for route changes */ events: RouterEvents; -} +}; -interface UrlObject { +type UrlObject = { pathname?: string; query?: UrlQuery; -} +}; -interface TransitionOptions { +type TransitionOptions = { shallow?: boolean; scroll?: boolean; locale?: string; -} +}; -interface RouterEvents { +type RouterEvents = { on(event: string, handler: (...args: unknown[]) => void): void; off(event: string, handler: (...args: unknown[]) => void): void; emit(event: string, ...args: unknown[]): void; -} +}; function createRouterEvents(): RouterEvents { const listeners = new Map void>>(); @@ -231,7 +231,7 @@ function restoreScrollPosition(state: unknown): void { /** * SSR context - set by the dev server before rendering each page. */ -interface SSRContext { +type SSRContext = { pathname: string; query: Record; asPath: string; @@ -239,7 +239,7 @@ interface SSRContext { locales?: string[]; defaultLocale?: string; domainLocales?: VinextNextData["domainLocales"]; -} +}; // --------------------------------------------------------------------------- // Server-side SSR state uses a registration pattern so this module can be diff --git a/packages/vinext/src/shims/script.tsx b/packages/vinext/src/shims/script.tsx index 3f1ed0774..02bb7bbbd 100644 --- a/packages/vinext/src/shims/script.tsx +++ b/packages/vinext/src/shims/script.tsx @@ -15,7 +15,7 @@ import React, { useEffect, useRef } from "react"; import { escapeInlineContent } from "./head.js"; -export interface ScriptProps { +export type ScriptProps = { /** Script source URL */ src?: string; /** Loading strategy. Default: "afterInteractive" */ @@ -46,7 +46,7 @@ export interface ScriptProps { integrity?: string; /** Additional attributes */ [key: string]: unknown; -} +}; // Track scripts that have already been loaded to avoid duplicates const loadedScripts = new Set(); diff --git a/packages/vinext/src/shims/server.ts b/packages/vinext/src/shims/server.ts index 44ca6507c..44c09fc2f 100644 --- a/packages/vinext/src/shims/server.ts +++ b/packages/vinext/src/shims/server.ts @@ -230,7 +230,7 @@ export class NextResponse<_Body = unknown> extends Response { // NextURL — lightweight URL wrapper with pathname helpers // --------------------------------------------------------------------------- -export interface NextURLConfig { +export type NextURLConfig = { basePath?: string; nextConfig?: { i18n?: { @@ -238,7 +238,7 @@ export interface NextURLConfig { defaultLocale: string; }; }; -} +}; export class NextURL { /** Internal URL stores the pathname WITHOUT basePath or locale prefix. */ @@ -448,10 +448,10 @@ export class NextURL { // Cookie helpers (minimal implementations) // --------------------------------------------------------------------------- -interface CookieEntry { +type CookieEntry = { name: string; value: string; -} +}; export class RequestCookies { private _headers: Headers; @@ -653,7 +653,7 @@ export class ResponseCookies { } } -interface CookieOptions { +type CookieOptions = { path?: string; domain?: string; maxAge?: number; @@ -661,17 +661,17 @@ interface CookieOptions { httpOnly?: boolean; secure?: boolean; sameSite?: "Strict" | "Lax" | "None"; -} +}; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- -export interface MiddlewareResponseInit extends ResponseInit { +export type MiddlewareResponseInit = { request?: { headers?: Headers; }; -} +} & ResponseInit; export type NextMiddlewareResult = NextResponse | Response | null | undefined | void; @@ -731,7 +731,7 @@ export function userAgent({ headers }: { headers: Headers }): UserAgent { return userAgentFromString(headers.get("user-agent") ?? undefined); } -export interface UserAgent { +export type UserAgent = { isBot: boolean; ua: string; browser: { name?: string; version?: string; major?: string }; @@ -739,7 +739,7 @@ export interface UserAgent { engine: { name?: string; version?: string }; os: { name?: string; version?: string }; cpu: { architecture?: string }; -} +}; /** * after() — schedule work after the response is sent. diff --git a/packages/vinext/src/shims/unified-request-context.ts b/packages/vinext/src/shims/unified-request-context.ts index b77a032ea..975e009c4 100644 --- a/packages/vinext/src/shims/unified-request-context.ts +++ b/packages/vinext/src/shims/unified-request-context.ts @@ -34,16 +34,7 @@ import type { * * Each field group is documented with its source shim module. */ -export interface UnifiedRequestContext - extends - VinextHeadersShimState, - I18nState, - NavigationState, - CacheState, - PrivateCacheState, - FetchCacheState, - RouterState, - HeadState { +export type UnifiedRequestContext = { // ── request-context.ts ───────────────────────────────────────────── /** Cloudflare Workers ExecutionContext, or null on Node.js dev. */ executionContext: ExecutionContextLike | null; @@ -52,7 +43,14 @@ export interface UnifiedRequestContext /** Per-request cache for cacheForRequest(). Keyed by factory function reference. */ // oxlint-disable-next-line @typescript-eslint/no-explicit-any requestCache: WeakMap<(...args: any[]) => any, unknown>; -} +} & VinextHeadersShimState & + I18nState & + NavigationState & + CacheState & + PrivateCacheState & + FetchCacheState & + RouterState & + HeadState; // --------------------------------------------------------------------------- // ALS setup — stored on globalThis via Symbol.for so all Vite environments diff --git a/packages/vinext/src/shims/web-vitals.ts b/packages/vinext/src/shims/web-vitals.ts index edb58d5c4..7b0f58318 100644 --- a/packages/vinext/src/shims/web-vitals.ts +++ b/packages/vinext/src/shims/web-vitals.ts @@ -6,14 +6,14 @@ * Apps can use the web-vitals library directly instead. */ -interface WebVitalsMetric { +type WebVitalsMetric = { id: string; name: string; value: number; rating?: "good" | "needs-improvement" | "poor"; delta: number; navigationType?: "navigate" | "reload" | "back-forward" | "prerender"; -} +}; type ReportWebVitalsCallback = (metric: WebVitalsMetric) => void; diff --git a/packages/vinext/src/vite-hmr.d.ts b/packages/vinext/src/vite-hmr.d.ts index ab95a1bd1..1c50dcb44 100644 --- a/packages/vinext/src/vite-hmr.d.ts +++ b/packages/vinext/src/vite-hmr.d.ts @@ -5,16 +5,16 @@ * pulling in the full vite/client types, which would add CSS module * declarations and other globals inappropriate for a library package. */ -interface ViteHotData { +type ViteHotData = { [key: string]: unknown; -} +}; -interface ViteHotContext { +type ViteHotContext = { readonly data: ViteHotData; accept(): void; on(event: string, cb: (...args: unknown[]) => void): void; -} +}; -interface ImportMeta { +type ImportMeta = { readonly hot?: ViteHotContext; -} +}; diff --git a/packages/vinext/src/vite-plugin-commonjs.d.ts b/packages/vinext/src/vite-plugin-commonjs.d.ts index 94060995b..7e733de60 100644 --- a/packages/vinext/src/vite-plugin-commonjs.d.ts +++ b/packages/vinext/src/vite-plugin-commonjs.d.ts @@ -1,9 +1,9 @@ declare module "vite-plugin-commonjs" { import type { Plugin } from "vite"; - export interface CommonJsPluginOptions { + export type CommonJsPluginOptions = { [key: string]: unknown; - } + }; export default function commonjs(options?: CommonJsPluginOptions): Plugin; } diff --git a/tests/helpers.ts b/tests/helpers.ts index 5b48d944e..b668cc240 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -36,10 +36,10 @@ export const RSC_ENTRIES = { // ── Server lifecycle helper ─────────────────────────────────── -export interface TestServerResult { +export type TestServerResult = { server: ViteDevServer; baseUrl: string; -} +}; /** * Start a Vite dev server against a fixture directory. @@ -129,11 +129,11 @@ export async function fetchJson( return { res, data }; } -export interface NodeHttpResponse { +export type NodeHttpResponse = { status: number; headers: IncomingHttpHeaders; body: string; -} +}; export async function createIsolatedFixture( fixtureDir: string, diff --git a/tests/nitro-route-rules.test.ts b/tests/nitro-route-rules.test.ts index 5aed66583..e73e1e82d 100644 --- a/tests/nitro-route-rules.test.ts +++ b/tests/nitro-route-rules.test.ts @@ -14,7 +14,7 @@ import { const tempDirs: string[] = []; -interface NitroSetupTarget { +type NitroSetupTarget = { options: { dev?: boolean; routeRules?: Record; @@ -22,13 +22,13 @@ interface NitroSetupTarget { logger?: { warn?: (message: string) => void; }; -} +}; -interface NitroSetupPlugin extends Plugin { +type NitroSetupPlugin = { nitro?: { setup?: (nitro: NitroSetupTarget) => Promise | void; }; -} +} & Plugin; afterEach(() => { vi.restoreAllMocks(); diff --git a/tests/pages-router.test.ts b/tests/pages-router.test.ts index e40ebd86f..4e0b816d8 100644 --- a/tests/pages-router.test.ts +++ b/tests/pages-router.test.ts @@ -85,7 +85,7 @@ function unwrapStartedProdServer( return "server" in result ? result.server : result; } -interface CapturedStreamResponse { +type CapturedStreamResponse = { body: Buffer; headers: IncomingHttpHeaders; statusCode: number; @@ -94,7 +94,7 @@ interface CapturedStreamResponse { snapshot: Buffer; rawBody: Buffer; rawSnapshot: Buffer; -} +}; function createResponseDecoder( contentEncoding: string | string[] | undefined, diff --git a/tests/route-trie.test.ts b/tests/route-trie.test.ts index 273dd8669..0a0a9ca6d 100644 --- a/tests/route-trie.test.ts +++ b/tests/route-trie.test.ts @@ -1,10 +1,10 @@ import { describe, it, expect } from "vite-plus/test"; import { buildRouteTrie, trieMatch } from "../packages/vinext/src/routing/route-trie.js"; -interface TestRoute { +type TestRoute = { pattern: string; patternParts: string[]; -} +}; function r(pattern: string): TestRoute { const parts = pattern === "/" ? [] : pattern.split("/").filter(Boolean); diff --git a/vite.config.ts b/vite.config.ts index e73fa8b93..0b93255c8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -29,7 +29,7 @@ export default defineConfig({ plugins: ["typescript", "unicorn", "import", "react"], rules: { "@typescript-eslint/no-explicit-any": "error", - // TODO: "typescript/consistent-type-definitions": ["error", "type"], + "typescript/consistent-type-definitions": ["error", "type"], "@typescript-eslint/no-unsafe-function-type": "error", "@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/no-redeclare": "error", From 421f3eaea86196a577dbaaf09591b0630f7dac47 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 29 Mar 2026 22:02:13 +0100 Subject: [PATCH 2/2] fix vite-hmr.d.ts --- packages/vinext/src/vite-hmr.d.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/vinext/src/vite-hmr.d.ts b/packages/vinext/src/vite-hmr.d.ts index 1c50dcb44..0610af7a1 100644 --- a/packages/vinext/src/vite-hmr.d.ts +++ b/packages/vinext/src/vite-hmr.d.ts @@ -1,3 +1,5 @@ +// oxlint-disable typescript/consistent-type-definitions + /** * Minimal type augmentation for Vite's HMR API on ImportMeta. * @@ -5,16 +7,16 @@ * pulling in the full vite/client types, which would add CSS module * declarations and other globals inappropriate for a library package. */ -type ViteHotData = { +interface ViteHotData { [key: string]: unknown; -}; +} -type ViteHotContext = { +interface ViteHotContext { readonly data: ViteHotData; accept(): void; on(event: string, cb: (...args: unknown[]) => void): void; -}; +} -type ImportMeta = { +interface ImportMeta { readonly hot?: ViteHotContext; -}; +}