From 9d029ee87381b5b25c905334cf9ae74e3cec43d4 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 2 Feb 2026 20:11:09 +0000 Subject: [PATCH 1/4] address lint errors for usage of `any` --- .oxlintrc.json | 6 ---- packages/open-next/package.json | 3 +- packages/open-next/src/adapter.ts | 16 +++++++--- .../adapters/image-optimization-adapter.ts | 16 ++++++++-- packages/open-next/src/adapters/logger.ts | 29 +++++++++++------ .../image-optimization.replacement.ts | 1 + .../image-optimization/image-optimization.ts | 1 + .../open-next/src/build/copyAdapterFiles.ts | 6 ++-- .../open-next/src/build/copyTracedFiles.ts | 4 +-- packages/open-next/src/build/createAssets.ts | 19 ++++++++--- .../open-next/src/build/createServerBundle.ts | 6 ++-- packages/open-next/src/build/helper.ts | 2 +- packages/open-next/src/build/installDeps.ts | 4 +-- .../src/core/createGenericHandler.ts | 2 +- .../src/core/nodeMiddlewareHandler.ts | 4 +-- .../open-next/src/core/patchAsyncStorage.ts | 4 +-- packages/open-next/src/core/requestHandler.ts | 8 ++--- packages/open-next/src/core/require-hooks.ts | 14 +++++--- .../src/core/routing/cacheInterceptor.ts | 2 +- packages/open-next/src/core/util.ts | 2 +- .../open-next/src/http/openNextResponse.ts | 6 ++-- packages/open-next/src/logger.ts | 6 ++-- .../src/overrides/converters/dummy.ts | 2 +- .../src/overrides/converters/edge.ts | 17 +++++----- .../src/overrides/converters/node.ts | 3 +- .../overrides/converters/sqs-revalidate.ts | 5 +-- .../incrementalCache/multi-tier-ddb-s3.ts | 2 +- .../src/overrides/incrementalCache/s3-lite.ts | 6 ++-- .../open-next/src/overrides/queue/direct.ts | 4 +-- .../src/overrides/tagCache/dynamodb-lite.ts | 32 ++++++++++++------- .../overrides/tagCache/dynamodb-nextMode.ts | 18 ++++++++--- .../wrappers/aws-lambda-compressed.ts | 19 +++++------ .../wrappers/aws-lambda-streaming.ts | 7 ++-- .../src/overrides/wrappers/aws-lambda.ts | 15 +++++---- .../src/overrides/wrappers/cloudflare-edge.ts | 14 +++++--- .../src/overrides/wrappers/cloudflare-node.ts | 21 ++++++------ .../open-next/src/overrides/wrappers/dummy.ts | 5 +-- packages/open-next/src/plugins/resolve.ts | 5 ++- packages/open-next/src/types/aws-lambda.ts | 2 +- packages/open-next/src/types/global.ts | 9 +++--- packages/open-next/src/types/overrides.ts | 6 ++-- packages/open-next/src/utils/cache.ts | 12 +++++-- packages/open-next/src/utils/error.ts | 4 +-- packages/open-next/src/utils/lru.ts | 2 +- packages/open-next/src/utils/promise.ts | 12 +++---- .../open-next/src/utils/safe-json-parse.ts | 2 +- packages/open-next/src/utils/stream.ts | 4 +-- 47 files changed, 234 insertions(+), 155 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index b1e50b7d..53d0e7e5 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -28,12 +28,6 @@ "import/extensions": ["error", "always", { "ignorePackages": true }] } }, - { - "files": ["packages/open-next/**/*"], - "rules": { - "@typescript-eslint/no-explicit-any": "off" - } - }, { "files": ["packages/tests-*/**", "examples/**", "examples-cloudflare/**"], "rules": { diff --git a/packages/open-next/package.json b/packages/open-next/package.json index 281357ed..c674ae44 100644 --- a/packages/open-next/package.json +++ b/packages/open-next/package.json @@ -42,7 +42,8 @@ }, "scripts": { "build": "tsc && tsc-alias", - "dev": "concurrently \"tsc -w\" \"tsc-alias -w\"" + "dev": "concurrently \"tsc -w\" \"tsc-alias -w\"", + "ts:check": "tsc --noEmit" }, "dependencies": { "@ast-grep/napi": "^0.40.0", diff --git a/packages/open-next/src/adapter.ts b/packages/open-next/src/adapter.ts index e521bcee..243f4702 100644 --- a/packages/open-next/src/adapter.ts +++ b/packages/open-next/src/adapter.ts @@ -19,18 +19,24 @@ import { addDebugFile } from "./debug.js"; import type { ContentUpdater } from "./plugins/content-updater.js"; import { externalChunksPlugin, inlineRouteHandler } from "./plugins/inlineRouteHandlers.js"; +export type NextAdapterOutput = { + pathname: string; + filePath: string; + assets: Record; +}; + export type NextAdapterOutputs = { - pages: any[]; - pagesApi: any[]; - appPages: any[]; - appRoutes: any[]; + pages: NextAdapterOutput[]; + pagesApi: NextAdapterOutput[]; + appPages: NextAdapterOutput[]; + appRoutes: NextAdapterOutput[]; }; type NextAdapter = { name: string; modifyConfig: (config: NextConfig, { phase }: { phase: string }) => Promise; onBuildComplete: (props: { - routes: any; + routes: unknown; outputs: NextAdapterOutputs; projectDir: string; repoRoot: string; diff --git a/packages/open-next/src/adapters/image-optimization-adapter.ts b/packages/open-next/src/adapters/image-optimization-adapter.ts index 55a5fb16..6b3aa53e 100644 --- a/packages/open-next/src/adapters/image-optimization-adapter.ts +++ b/packages/open-next/src/adapters/image-optimization-adapter.ts @@ -92,7 +92,7 @@ export async function defaultHandler( } const result = await optimizeImage(headers, imageParams, nextConfig, downloadHandler); return buildSuccessResponse(result, options?.streamCreator, etag); - } catch (e: any) { + } catch (e: unknown) { error("Failed to optimize image", e); return buildFailureResponse("Internal server error", options?.streamCreator); } @@ -129,7 +129,17 @@ function computeEtag(imageParams: { href: string; width: number; quality: number .digest("base64"); } -function buildSuccessResponse(result: any, streamCreator?: StreamCreator, etag?: string): InternalResult { +type ImageOptimizeResult = { + contentType: string; + buffer: Buffer; + maxAge: number; +}; + +function buildSuccessResponse( + result: ImageOptimizeResult, + streamCreator?: StreamCreator, + etag?: string +): InternalResult { const headers: Record = { Vary: "Accept", "Content-Type": result.contentType, @@ -239,7 +249,7 @@ async function downloadHandler(_req: IncomingMessage, res: ServerResponse, url: res.setHeader("Cache-Control", response.cacheControl); } } - } catch (e: any) { + } catch (e: unknown) { error("Failed to download image", e); throw e; } diff --git a/packages/open-next/src/adapters/logger.ts b/packages/open-next/src/adapters/logger.ts index 64b3aee7..b79fcb2f 100644 --- a/packages/open-next/src/adapters/logger.ts +++ b/packages/open-next/src/adapters/logger.ts @@ -1,12 +1,12 @@ import { isOpenNextError } from "@/utils/error.js"; -export function debug(...args: any[]) { +export function debug(...args: unknown[]) { if (globalThis.openNextDebug) { console.log(...args); } } -export function warn(...args: any[]) { +export function warn(...args: unknown[]) { console.warn(...args); } @@ -28,16 +28,27 @@ const DOWNPLAYED_ERROR_LOGS: AwsSdkClientCommandErrorInput[] = [ }, ]; -const isDownplayedErrorLog = (errorLog: AwsSdkClientCommandErrorLog) => - DOWNPLAYED_ERROR_LOGS.some( +const isAwsSdkClientCommandErrorLog = (errorLog: unknown): errorLog is AwsSdkClientCommandErrorLog => + errorLog !== null && + typeof errorLog === "object" && + "clientName" in errorLog && + "commandName" in errorLog && + "error" in errorLog; + +const isDownplayedErrorLog = (errorLog: unknown): boolean => { + if (!isAwsSdkClientCommandErrorLog(errorLog)) { + return false; + } + return DOWNPLAYED_ERROR_LOGS.some( (downplayedInput) => - downplayedInput.clientName === errorLog?.clientName && - downplayedInput.commandName === errorLog?.commandName && - (downplayedInput.errorName === errorLog?.error?.name || - downplayedInput.errorName === errorLog?.error?.Code) + downplayedInput.clientName === errorLog.clientName && + downplayedInput.commandName === errorLog.commandName && + (downplayedInput.errorName === errorLog.error?.name || + downplayedInput.errorName === errorLog.error?.Code) ); +}; -export function error(...args: any[]) { +export function error(...args: unknown[]) { // we try to catch errors from the aws-sdk client and downplay some of them if (args.some((arg) => isDownplayedErrorLog(arg))) { return debug(...args); diff --git a/packages/open-next/src/adapters/plugins/image-optimization/image-optimization.replacement.ts b/packages/open-next/src/adapters/plugins/image-optimization/image-optimization.replacement.ts index 91351f53..364e684e 100644 --- a/packages/open-next/src/adapters/plugins/image-optimization/image-optimization.replacement.ts +++ b/packages/open-next/src/adapters/plugins/image-optimization/image-optimization.replacement.ts @@ -12,6 +12,7 @@ import { debug } from "../../logger.js"; //#override optimizeImage export async function optimizeImage( headers: APIGatewayProxyEventHeaders, + // oxlint-disable-next-line @typescript-eslint/no-explicit-any - image optimization API varies across Next.js versions imageParams: any, nextConfig: NextConfig, handleRequest: ( diff --git a/packages/open-next/src/adapters/plugins/image-optimization/image-optimization.ts b/packages/open-next/src/adapters/plugins/image-optimization/image-optimization.ts index 15f78e11..c014ee08 100644 --- a/packages/open-next/src/adapters/plugins/image-optimization/image-optimization.ts +++ b/packages/open-next/src/adapters/plugins/image-optimization/image-optimization.ts @@ -14,6 +14,7 @@ import { debug } from "../../logger.js"; //#override optimizeImage export async function optimizeImage( headers: APIGatewayProxyEventHeaders, + // oxlint-disable-next-line @typescript-eslint/no-explicit-any - image optimization API varies across Next.js versions imageParams: any, nextConfig: NextConfig, handleRequest: ( diff --git a/packages/open-next/src/build/copyAdapterFiles.ts b/packages/open-next/src/build/copyAdapterFiles.ts index c4931e39..d72d12d1 100644 --- a/packages/open-next/src/build/copyAdapterFiles.ts +++ b/packages/open-next/src/build/copyAdapterFiles.ts @@ -16,7 +16,7 @@ export async function copyAdapterFiles( // Copying the files from outputs to the output dir for (const [key, value] of Object.entries(outputs)) { if (["pages", "pagesApi", "appPages", "appRoutes"].includes(key)) { - for (const route of value as any[]) { + for (const route of value) { const assets = route.assets; // We need to copy the filepaths to the output dir const relativeFilePath = path.relative(options.appPath, route.filePath); @@ -58,8 +58,8 @@ export async function copyAdapterFiles( if (symlink) { try { fs.symlinkSync(symlink, to); - } catch (e: any) { - if (e.code !== "EEXIST") { + } catch (e: unknown) { + if (e instanceof Error && (e as NodeJS.ErrnoException).code !== "EEXIST") { throw e; } } diff --git a/packages/open-next/src/build/copyTracedFiles.ts b/packages/open-next/src/build/copyTracedFiles.ts index 31a1d63f..62e460b2 100644 --- a/packages/open-next/src/build/copyTracedFiles.ts +++ b/packages/open-next/src/build/copyTracedFiles.ts @@ -267,8 +267,8 @@ File ${serverPath} does not exist if (symlink) { try { symlinkSync(symlink, to); - } catch (e: any) { - if (e.code !== "EEXIST") { + } catch (e: unknown) { + if (e instanceof Error && (e as NodeJS.ErrnoException).code !== "EEXIST") { throw e; } } diff --git a/packages/open-next/src/build/createAssets.ts b/packages/open-next/src/build/createAssets.ts index b60d7a74..469cd375 100644 --- a/packages/open-next/src/build/createAssets.ts +++ b/packages/open-next/src/build/createAssets.ts @@ -10,6 +10,15 @@ import { isBinaryContentType } from "../utils/binary.js"; import * as buildHelper from "./helper.js"; +type CacheFileMeta = { + segmentPaths?: string[]; + headers?: Record; +}; + +type FetchCacheData = { + tags?: string[]; +}; + /** * Copy the static assets to the output folder * @@ -153,10 +162,10 @@ export function createCacheAssets(options: buildHelper.BuildOptions) { // Generate cache file Object.entries(cacheFilesPath).forEach(([cacheFilePath, files]) => { const cacheFileMeta = files.meta - ? safeParseJsonFile(fs.readFileSync(files.meta, "utf8"), cacheFilePath) + ? safeParseJsonFile(fs.readFileSync(files.meta, "utf8"), cacheFilePath) : undefined; const cacheJson = files.json - ? safeParseJsonFile(fs.readFileSync(files.json, "utf8"), cacheFilePath) + ? safeParseJsonFile>(fs.readFileSync(files.json, "utf8"), cacheFilePath) : undefined; if ((files.meta && !cacheFileMeta) || (files.json && !cacheJson)) { logger.warn(`Skipping invalid cache file: ${cacheFilePath}`); @@ -186,7 +195,7 @@ export function createCacheAssets(options: buildHelper.BuildOptions) { body: files.body ? fs .readFileSync(files.body) - .toString(isBinaryContentType(cacheFileMeta.headers["content-type"]) ? "base64" : "utf8") + .toString(isBinaryContentType(cacheFileMeta?.headers?.["content-type"]) ? "base64" : "utf8") : undefined, segmentData: Object.keys(segments).length > 0 ? segments : undefined, }; @@ -211,7 +220,7 @@ export function createCacheAssets(options: buildHelper.BuildOptions) { () => true, ({ absolutePath, relativePath }) => { const fileContent = fs.readFileSync(absolutePath, "utf8"); - const fileData = safeParseJsonFile(fileContent, absolutePath); + const fileData = safeParseJsonFile(fileContent, absolutePath); fileData?.tags?.forEach((tag: string) => { metaFiles.push({ tag: { S: path.posix.join(buildId, tag) }, @@ -234,7 +243,7 @@ export function createCacheAssets(options: buildHelper.BuildOptions) { ({ absolutePath, relativePath }) => absolutePath.endsWith(".meta") && !isFileSkipped(relativePath), ({ absolutePath, relativePath }) => { const fileContent = fs.readFileSync(absolutePath, "utf8"); - const fileData = safeParseJsonFile(fileContent, absolutePath); + const fileData = safeParseJsonFile(fileContent, absolutePath); if (fileData?.headers?.["x-next-cache-tags"]) { fileData.headers["x-next-cache-tags"].split(",").forEach((tag: string) => { // TODO: We should split the tag using getDerivedTags from next.js or maybe use an in house implementation diff --git a/packages/open-next/src/build/createServerBundle.ts b/packages/open-next/src/build/createServerBundle.ts index 82eabf63..4e7fed48 100644 --- a/packages/open-next/src/build/createServerBundle.ts +++ b/packages/open-next/src/build/createServerBundle.ts @@ -18,7 +18,7 @@ import { getCrossPlatformPathRegex } from "../utils/regex.js"; import { bundleNextServer } from "./bundleNextServer.js"; import { compileCache } from "./compileCache.js"; import { copyAdapterFiles } from "./copyAdapterFiles.js"; -import { copyTracedFiles } from "./copyTracedFiles.js"; +import { copyTracedFiles, getManifests } from "./copyTracedFiles.js"; import { copyMiddlewareResources, generateEdgeBundle } from "./edge/createEdgeBundle.js"; import * as buildHelper from "./helper.js"; import { installDependencies } from "./installDeps.js"; @@ -185,7 +185,7 @@ async function generateBundle( buildHelper.copyEnvFile(appBuildOutputPath, packagePath, outputPath); let tracedFiles: string[] = []; - let manifests: any = {}; + let manifests: ReturnType | Record = {}; // Copy all necessary traced files if (config.dangerous?.useAdapterOutputs) { @@ -206,7 +206,7 @@ async function generateBundle( const additionalCodePatches = codeCustomization?.additionalCodePatches ?? []; - await applyCodePatches(options, tracedFiles, manifests, [ + await applyCodePatches(options, tracedFiles, manifests as ReturnType, [ patches.patchFetchCacheSetMissingWaitUntil, patches.patchFetchCacheForISR, patches.patchUnstableCacheForISR, diff --git a/packages/open-next/src/build/helper.ts b/packages/open-next/src/build/helper.ts index c4ee7f30..0d53e04b 100644 --- a/packages/open-next/src/build/helper.ts +++ b/packages/open-next/src/build/helper.ts @@ -32,7 +32,7 @@ export function normalizeOptions(config: OpenNextConfig, distDir: string, tempBu appPackageJsonPath = findNextPackageJsonPath(appPath, monorepoRoot); } - const debug = Boolean(process.env.OPEN_NEXT_DEBUG) ?? false; + const debug = Boolean(process.env.OPEN_NEXT_DEBUG); return { appBuildOutputPath: buildOutputPath, diff --git a/packages/open-next/src/build/installDeps.ts b/packages/open-next/src/build/installDeps.ts index bc291a19..490e34cc 100644 --- a/packages/open-next/src/build/installDeps.ts +++ b/packages/open-next/src/build/installDeps.ts @@ -71,8 +71,8 @@ export function installDependencies(outputDir: string, installOptions?: InstallO // Cleanup tempDir fs.rmSync(tempInstallDir, { recursive: true, force: true }); logger.info(`Dependencies installed for ${name}`); - } catch (e: any) { - logger.error(e.toString()); + } catch (e: unknown) { + logger.error(e instanceof Error ? e.message : String(e)); logger.error("Could not install dependencies"); } } diff --git a/packages/open-next/src/core/createGenericHandler.ts b/packages/open-next/src/core/createGenericHandler.ts index 486e7d41..a8674936 100644 --- a/packages/open-next/src/core/createGenericHandler.ts +++ b/packages/open-next/src/core/createGenericHandler.ts @@ -34,7 +34,7 @@ export async function createGenericHandler< const handlerConfig = config[handler.type]; const override = handlerConfig && "override" in handlerConfig - ? (handlerConfig.override as any as DefaultOverrideOptions) + ? (handlerConfig.override as DefaultOverrideOptions) : undefined; // From the config, we create the converter diff --git a/packages/open-next/src/core/nodeMiddlewareHandler.ts b/packages/open-next/src/core/nodeMiddlewareHandler.ts index 796fc20a..3e8095b0 100644 --- a/packages/open-next/src/core/nodeMiddlewareHandler.ts +++ b/packages/open-next/src/core/nodeMiddlewareHandler.ts @@ -13,11 +13,11 @@ import { AsyncLocalStorage } from "node:async_hooks"; globalThis.AsyncLocalStorage = AsyncLocalStorage; interface NodeMiddleware { - default: (req: { handler: any; request: EdgeRequest; page: "middleware" }) => Promise<{ + default: (req: { handler: unknown; request: EdgeRequest; page: "middleware" }) => Promise<{ response: Response; waitUntil: Promise; }>; - middleware: any; + middleware: unknown; } let _module: NodeMiddleware | undefined; diff --git a/packages/open-next/src/core/patchAsyncStorage.ts b/packages/open-next/src/core/patchAsyncStorage.ts index bffd3b43..febab1f2 100644 --- a/packages/open-next/src/core/patchAsyncStorage.ts +++ b/packages/open-next/src/core/patchAsyncStorage.ts @@ -6,9 +6,9 @@ export function patchAsyncStorage() { mod._resolveFilename = (( originalResolveFilename: typeof resolveFilename, request: string, - parent: any, + parent: unknown, isMain: boolean, - options: any + options: unknown ) => { if ( request.endsWith("static-generation-async-storage.external") || diff --git a/packages/open-next/src/core/requestHandler.ts b/packages/open-next/src/core/requestHandler.ts index a02001ac..d402a1bd 100644 --- a/packages/open-next/src/core/requestHandler.ts +++ b/packages/open-next/src/core/requestHandler.ts @@ -260,9 +260,9 @@ async function processRequest(req: IncomingMessage, res: OpenNextNodeResponse, r //#endOverride await requestHandler(requestMetadata)(req, res); - } catch (e: any) { + } catch (e: unknown) { // This might fail when using bundled next, importing won't do the trick either - if (e.constructor.name === "NoFallbackError") { + if (e instanceof Error && e.constructor.name === "NoFallbackError") { await handleNoFallbackError(req, res, routingResult, requestMetadata); } else { error("NextJS request failed.", e); @@ -293,8 +293,8 @@ async function handleNoFallbackError( // ...metadata, // })(req, res); //TODO: find a way to do that without breaking current main - } catch (e: any) { - if (e.constructor.name === "NoFallbackError") { + } catch (e: unknown) { + if (e instanceof Error && e.constructor.name === "NoFallbackError") { await handleNoFallbackError(req, res, routingResult, metadata, index + 1); } else { error("NextJS request failed.", e); diff --git a/packages/open-next/src/core/require-hooks.ts b/packages/open-next/src/core/require-hooks.ts index 4445cae9..fda3fe10 100644 --- a/packages/open-next/src/core/require-hooks.ts +++ b/packages/open-next/src/core/require-hooks.ts @@ -11,8 +11,8 @@ import { error } from "../adapters/logger.js"; const mod = require("node:module"); const resolveFilename = mod._resolveFilename; -const hookPropertyMapApp = new Map(); -const hookPropertyMapPage = new Map(); +const hookPropertyMapApp = new Map(); +const hookPropertyMapPage = new Map(); export function overrideHooks(config: NextConfig) { try { @@ -26,7 +26,11 @@ export function overrideHooks(config: NextConfig) { function addHookAliases(aliases: [string, string][], type: "app" | "page") { for (const [key, value] of aliases) { - type === "app" ? hookPropertyMapApp.set(key, value) : hookPropertyMapPage.set(key, value); + if (type === "app") { + hookPropertyMapApp.set(key, value); + } else { + hookPropertyMapPage.set(key, value); + } } } @@ -116,9 +120,9 @@ export function applyOverride() { requestMapApp: Map, requestMapPage: Map, request: string, - parent: any, + parent: unknown, isMain: boolean, - options: any + options: unknown ) => { const hookResolved = isApp() ? requestMapApp.get(request) : requestMapPage.get(request); // oxlint-disable-next-line no-param-reassign diff --git a/packages/open-next/src/core/routing/cacheInterceptor.ts b/packages/open-next/src/core/routing/cacheInterceptor.ts index 472f0d87..04f2b90f 100644 --- a/packages/open-next/src/core/routing/cacheInterceptor.ts +++ b/packages/open-next/src/core/routing/cacheInterceptor.ts @@ -279,7 +279,7 @@ export async function cacheInterceptor(event: MiddlewareEvent): Promise) ?? {}), + ...cachedData.value.meta?.headers, ...cacheControl, }, isBase64Encoded: false, diff --git a/packages/open-next/src/core/util.ts b/packages/open-next/src/core/util.ts index 2b8eafd9..ae8056d6 100644 --- a/packages/open-next/src/core/util.ts +++ b/packages/open-next/src/core/util.ts @@ -110,7 +110,7 @@ globalThis.__next_route_preloader = async (stage) => { }; // `getRequestHandlerWithMetadata` is not available in older versions of Next.js // It is required to for next 15.2 to pass metadata for page router data route -export const requestHandler = (metadata: Record) => +export const requestHandler = (metadata: Record) => "getRequestHandlerWithMetadata" in nextServer ? nextServer.getRequestHandlerWithMetadata(metadata) : nextServer.getRequestHandler(); diff --git a/packages/open-next/src/http/openNextResponse.ts b/packages/open-next/src/http/openNextResponse.ts index 8e9523bd..b76cc271 100644 --- a/packages/open-next/src/http/openNextResponse.ts +++ b/packages/open-next/src/http/openNextResponse.ts @@ -259,12 +259,12 @@ export class OpenNextNodeResponse extends Transform implements ServerResponse { return Buffer.concat(this._chunks); } - private _internalWrite(chunk: any, encoding: BufferEncoding) { + private _internalWrite(chunk: Buffer | string, encoding: BufferEncoding) { // When encoding === 'buffer', chunk is already a Buffer // and does not need to be converted again. // @ts-expect-error TS2367 'encoding' can be 'buffer', but it's not in the // official type definition - const buffer = encoding === "buffer" ? chunk : Buffer.from(chunk, encoding); + const buffer = encoding === "buffer" ? (chunk as Buffer) : Buffer.from(chunk, encoding); this.bodyLength += buffer.length; if (this.streamCreator?.retainChunks !== false) { // Avoid keeping chunks around when the `StreamCreator` supports it to save memory @@ -275,7 +275,7 @@ export class OpenNextNodeResponse extends Transform implements ServerResponse { this.streamCreator?.onWrite?.(); } - _transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback): void { + _transform(chunk: Buffer | string, encoding: BufferEncoding, callback: TransformCallback): void { if (!this.headersSent) { this.flushHeaders(); } diff --git a/packages/open-next/src/logger.ts b/packages/open-next/src/logger.ts index a9848f8a..9ce195f1 100644 --- a/packages/open-next/src/logger.ts +++ b/packages/open-next/src/logger.ts @@ -6,13 +6,13 @@ let logLevel: LEVEL = "info"; export default { setLevel: (level: LEVEL) => (logLevel = level), - debug: (...args: any[]) => { + debug: (...args: unknown[]) => { if (logLevel !== "debug") return; console.log(chalk.magenta("DEBUG"), ...args); }, info: console.log, - warn: (...args: any[]) => console.warn(chalk.yellow("WARN"), ...args), - error: (...args: any[]) => console.error(chalk.red("ERROR"), ...args), + warn: (...args: unknown[]) => console.warn(chalk.yellow("WARN"), ...args), + error: (...args: unknown[]) => console.error(chalk.red("ERROR"), ...args), time: console.time, timeEnd: console.timeEnd, }; diff --git a/packages/open-next/src/overrides/converters/dummy.ts b/packages/open-next/src/overrides/converters/dummy.ts index d4f66dcf..3d666d99 100644 --- a/packages/open-next/src/overrides/converters/dummy.ts +++ b/packages/open-next/src/overrides/converters/dummy.ts @@ -2,7 +2,7 @@ import type { Converter } from "@/types/overrides"; type DummyEventOrResult = { type: "dummy"; - original: any; + original: unknown; }; const converter: Converter = { diff --git a/packages/open-next/src/overrides/converters/edge.ts b/packages/open-next/src/overrides/converters/edge.ts index 8c653e5e..63e0ae23 100644 --- a/packages/open-next/src/overrides/converters/edge.ts +++ b/packages/open-next/src/overrides/converters/edge.ts @@ -17,33 +17,34 @@ declare global { const NULL_BODY_STATUSES = new Set([101, 103, 204, 205, 304]); const converter: Converter = { - convertFrom: async (event: Request) => { - const url = new URL(event.url); + convertFrom: async (event: unknown) => { + const request = event as Request; + const url = new URL(request.url); const searchParams = url.searchParams; const query = getQueryFromSearchParams(searchParams); const headers: Record = {}; - event.headers.forEach((value, key) => { + request.headers.forEach((value, key) => { headers[key] = value; }); const rawPath = url.pathname; - const method = event.method; + const method = request.method; const shouldHaveBody = method !== "GET" && method !== "HEAD"; // Only read body for methods that should have one - const body = shouldHaveBody ? Buffer.from(await event.arrayBuffer()) : undefined; + const body = shouldHaveBody ? Buffer.from(await request.arrayBuffer()) : undefined; - const cookieHeader = event.headers.get("cookie"); + const cookieHeader = request.headers.get("cookie"); const cookies = cookieHeader ? (cookieParser.parse(cookieHeader) as Record) : {}; return { type: "core", method, rawPath, - url: event.url, + url: request.url, body, headers, - remoteAddress: event.headers.get("x-forwarded-for") ?? "::1", + remoteAddress: request.headers.get("x-forwarded-for") ?? "::1", query, cookies, }; diff --git a/packages/open-next/src/overrides/converters/node.ts b/packages/open-next/src/overrides/converters/node.ts index aef33991..60c5fffa 100644 --- a/packages/open-next/src/overrides/converters/node.ts +++ b/packages/open-next/src/overrides/converters/node.ts @@ -8,7 +8,8 @@ import type { Converter } from "@/types/overrides"; import { extractHostFromHeaders, getQueryFromSearchParams } from "./utils.js"; const converter: Converter = { - convertFrom: async (req: IncomingMessage & { protocol?: string }) => { + convertFrom: async (event: unknown) => { + const req = event as IncomingMessage & { protocol?: string }; const body = await new Promise((resolve) => { const chunks: Uint8Array[] = []; req.on("data", (chunk) => { diff --git a/packages/open-next/src/overrides/converters/sqs-revalidate.ts b/packages/open-next/src/overrides/converters/sqs-revalidate.ts index 094ecb5c..d4c2ee7a 100644 --- a/packages/open-next/src/overrides/converters/sqs-revalidate.ts +++ b/packages/open-next/src/overrides/converters/sqs-revalidate.ts @@ -5,8 +5,9 @@ import type { Converter } from "@/types/overrides"; import type { RevalidateEvent } from "../../adapters/revalidate"; const converter: Converter = { - convertFrom(event: SQSEvent) { - const records = event.Records.map((record) => { + convertFrom(event: unknown) { + const sqsEvent = event as SQSEvent; + const records = sqsEvent.Records.map((record) => { const { host, url } = JSON.parse(record.body); return { host, url, id: record.messageId }; }); diff --git a/packages/open-next/src/overrides/incrementalCache/multi-tier-ddb-s3.ts b/packages/open-next/src/overrides/incrementalCache/multi-tier-ddb-s3.ts index 0fe490ac..d5e50707 100644 --- a/packages/open-next/src/overrides/incrementalCache/multi-tier-ddb-s3.ts +++ b/packages/open-next/src/overrides/incrementalCache/multi-tier-ddb-s3.ts @@ -16,7 +16,7 @@ const maxCacheSize = process.env.OPEN_NEXT_LOCAL_CACHE_SIZE : 1000; const localCache = new LRUCache<{ - value: CacheValue; + value: CacheValue; lastModified: number; }>(maxCacheSize); diff --git a/packages/open-next/src/overrides/incrementalCache/s3-lite.ts b/packages/open-next/src/overrides/incrementalCache/s3-lite.ts index eff7939c..4b0b0def 100644 --- a/packages/open-next/src/overrides/incrementalCache/s3-lite.ts +++ b/packages/open-next/src/overrides/incrementalCache/s3-lite.ts @@ -3,7 +3,7 @@ import path from "node:path"; import { AwsClient } from "aws4fetch"; import type { Extension } from "@/types/cache"; -import type { IncrementalCache } from "@/types/overrides"; +import type { CacheEntryType, CacheValue, IncrementalCache } from "@/types/overrides"; import { IgnorableError, RecoverableError } from "@/utils/error"; import { customFetchClient } from "@/utils/fetch"; @@ -44,7 +44,7 @@ function buildS3Key(key: string, extension: Extension) { } const incrementalCache: IncrementalCache = { - async get(key, cacheType) { + async get(key: string, cacheType?: CacheType) { const result = await awsFetch(buildS3Key(key, cacheType ?? "cache"), { method: "GET", }); @@ -55,7 +55,7 @@ const incrementalCache: IncrementalCache = { if (result.status !== 200) { throw new RecoverableError(`Failed to get cache: ${result.status}`); } - const cacheData: any = await result.json(); + const cacheData: CacheValue = await result.json(); return { value: cacheData, lastModified: new Date(result.headers.get("last-modified") ?? "").getTime(), diff --git a/packages/open-next/src/overrides/queue/direct.ts b/packages/open-next/src/overrides/queue/direct.ts index f767b114..0e1f99f9 100644 --- a/packages/open-next/src/overrides/queue/direct.ts +++ b/packages/open-next/src/overrides/queue/direct.ts @@ -3,10 +3,10 @@ import type { Queue } from "@/types/overrides.js"; const queue: Queue = { name: "dev-queue", send: async (message) => { - const prerenderManifest = (await import("../../adapters/config/index.js")).PrerenderManifest as any; + const prerenderManifest = (await import("../../adapters/config/index.js")).PrerenderManifest; const { host, url } = message.MessageBody; const protocol = host.includes("localhost") ? "http" : "https"; - const revalidateId: string = prerenderManifest.preview.previewModeId; + const revalidateId: string = prerenderManifest?.preview?.previewModeId ?? ""; await globalThis.internalFetch(`${protocol}://${host}${url}`, { method: "HEAD", headers: { diff --git a/packages/open-next/src/overrides/tagCache/dynamodb-lite.ts b/packages/open-next/src/overrides/tagCache/dynamodb-lite.ts index 184923a4..8261484f 100644 --- a/packages/open-next/src/overrides/tagCache/dynamodb-lite.ts +++ b/packages/open-next/src/overrides/tagCache/dynamodb-lite.ts @@ -2,7 +2,7 @@ import path from "node:path"; import { AwsClient } from "aws4fetch"; -import type { TagCache } from "@/types/overrides"; +import type { OriginalTagCache } from "@/types/overrides"; import { RecoverableError } from "@/utils/error"; import { customFetchClient } from "@/utils/fetch"; @@ -11,6 +11,16 @@ import { chunk, parseNumberFromEnv } from "../../adapters/util"; import { MAX_DYNAMO_BATCH_WRITE_ITEM_COUNT, getDynamoBatchWriteCommandConcurrency } from "./constants"; +type DynamoDBItem = { + tag?: { S: string }; + path?: { S: string }; + revalidatedAt?: { N: string }; +}; + +type DynamoDBResponse = { + Items?: DynamoDBItem[]; +}; + let awsClient: AwsClient | null = null; const getAwsClient = () => { @@ -55,9 +65,9 @@ function buildDynamoObject(path: string, tags: string, revalidatedAt?: number) { }; } -const tagCache: TagCache = { +const tagCache: OriginalTagCache = { mode: "original", - async getByPath(path) { + async getByPath(path: string) { try { if (globalThis.openNextConfig.dangerous?.disableTagCache) { return []; @@ -79,9 +89,9 @@ const tagCache: TagCache = { if (result.status !== 200) { throw new RecoverableError(`Failed to get tags by path: ${result.status}`); } - const { Items } = (await result.json()) as any; + const { Items } = (await result.json()) as DynamoDBResponse; - const tags = Items?.map((item: any) => item.tag.S ?? "") ?? []; + const tags = Items?.map((item) => item.tag?.S ?? "") ?? []; debug("tags for path", path, tags); // We need to remove the buildId from the path return tags.map((tag: string) => tag.replace(`${NEXT_BUILD_ID}/`, "")); @@ -90,7 +100,7 @@ const tagCache: TagCache = { return []; } }, - async getByTag(tag) { + async getByTag(tag: string) { try { if (globalThis.openNextConfig.dangerous?.disableTagCache) { return []; @@ -111,17 +121,17 @@ const tagCache: TagCache = { if (result.status !== 200) { throw new RecoverableError(`Failed to get by tag: ${result.status}`); } - const { Items } = (await result.json()) as any; + const { Items } = (await result.json()) as DynamoDBResponse; return ( // We need to remove the buildId from the path - Items?.map(({ path: { S: key } }: any) => key?.replace(`${NEXT_BUILD_ID}/`, "") ?? "") ?? [] + Items?.map((item) => item.path?.S?.replace(`${NEXT_BUILD_ID}/`, "") ?? "") ?? [] ); } catch (e) { error("Failed to get by tag", e); return []; } }, - async getLastModified(key, lastModified) { + async getLastModified(key: string, lastModified?: number) { try { if (globalThis.openNextConfig.dangerous?.disableTagCache) { return lastModified ?? Date.now(); @@ -145,7 +155,7 @@ const tagCache: TagCache = { if (result.status !== 200) { throw new RecoverableError(`Failed to get last modified: ${result.status}`); } - const revalidatedTags = ((await result.json()) as any).Items ?? []; + const revalidatedTags = ((await result.json()) as DynamoDBResponse).Items ?? []; debug("revalidatedTags", revalidatedTags); // If we have revalidated tags we return -1 to force revalidation return revalidatedTags.length > 0 ? -1 : (lastModified ?? Date.now()); @@ -154,7 +164,7 @@ const tagCache: TagCache = { return lastModified ?? Date.now(); } }, - async writeTags(tags) { + async writeTags(tags: { tag: string; path: string; revalidatedAt?: number }[]) { try { const { CACHE_DYNAMO_TABLE } = process.env; if (globalThis.openNextConfig.dangerous?.disableTagCache) { diff --git a/packages/open-next/src/overrides/tagCache/dynamodb-nextMode.ts b/packages/open-next/src/overrides/tagCache/dynamodb-nextMode.ts index fa4efbe0..93afbaf6 100644 --- a/packages/open-next/src/overrides/tagCache/dynamodb-nextMode.ts +++ b/packages/open-next/src/overrides/tagCache/dynamodb-nextMode.ts @@ -11,6 +11,15 @@ import { chunk, parseNumberFromEnv } from "../../adapters/util"; import { MAX_DYNAMO_BATCH_WRITE_ITEM_COUNT, getDynamoBatchWriteCommandConcurrency } from "./constants"; +type DynamoDBTagItem = { + revalidatedAt: { N: string }; + tag: { S: string }; +}; + +type DynamoDBBatchGetResponse = { + Responses?: Record; +}; + let awsClient: AwsClient | null = null; const getAwsClient = () => { @@ -95,13 +104,14 @@ export default { throw new RecoverableError(`Failed to query dynamo item: ${response.status}`); } // Now we need to check for every item if lastModified is greater than the revalidatedAt - const { Responses } = await response.json(); + const { Responses } = (await response.json()) as DynamoDBBatchGetResponse; if (!Responses) { return false; } - const revalidatedTags = Responses[CACHE_DYNAMO_TABLE ?? ""].filter( - (item: any) => Number.parseInt(item.revalidatedAt.N) > (lastModified ?? 0) - ); + const revalidatedTags = + Responses?.[CACHE_DYNAMO_TABLE ?? ""]?.filter( + (item) => Number.parseInt(item.revalidatedAt.N) > (lastModified ?? 0) + ) ?? []; debug("retrieved tags", revalidatedTags); return revalidatedTags.length > 0; }, diff --git a/packages/open-next/src/overrides/wrappers/aws-lambda-compressed.ts b/packages/open-next/src/overrides/wrappers/aws-lambda-compressed.ts index 06055d38..4eb1acb3 100644 --- a/packages/open-next/src/overrides/wrappers/aws-lambda-compressed.ts +++ b/packages/open-next/src/overrides/wrappers/aws-lambda-compressed.ts @@ -10,15 +10,15 @@ import { error } from "../../adapters/logger"; import { formatWarmerResponse } from "./aws-lambda"; -const handler: WrapperHandler = - async (handler, converter) => - async (event: AwsLambdaEvent): Promise => { +const handler: WrapperHandler = async (handler, converter) => { + return async (event: unknown): Promise => { + const lambdaEvent = event as AwsLambdaEvent; // Handle warmer event - if ("type" in event) { - return formatWarmerResponse(event); + if ("type" in lambdaEvent) { + return formatWarmerResponse(lambdaEvent); } - const internalEvent = await converter.convertFrom(event); + const internalEvent = await converter.convertFrom(lambdaEvent); // This is a workaround // https://github.com/opennextjs/opennextjs-aws/blob/e9b37fd44eb856eb8ae73168bf455ff85dd8b285/packages/open-next/src/overrides/wrappers/aws-lambda.ts#L49-L53 const fakeStream: StreamCreator = { @@ -41,7 +41,7 @@ const handler: WrapperHandler = // Return early here if the response is already compressed if (alreadyEncoded) { - return converter.convertTo(handlerResponse, event); + return converter.convertTo(handlerResponse, lambdaEvent); } // We compress the body if the client accepts it @@ -67,8 +67,9 @@ const handler: WrapperHandler = isBase64Encoded: !!contentEncoding || handlerResponse.isBase64Encoded, }; - return converter.convertTo(response, event); + return converter.convertTo(response, lambdaEvent); }; +}; export default { wrapper: handler, @@ -90,7 +91,7 @@ function compressBody(body: ReadableStream, encoding: string | null) { // This is a compromise between speed and compression ratio. // The default one will most likely timeout an AWS Lambda with default configuration on large bodies (>6mb). // Therefore we set it to 6, which is a good compromise. - [zlib.constants.BROTLI_PARAM_QUALITY]: Number(process.env.BROTLI_QUALITY) ?? 6, + [zlib.constants.BROTLI_PARAM_QUALITY]: Number(process.env.BROTLI_QUALITY) || 6, }, }); break; diff --git a/packages/open-next/src/overrides/wrappers/aws-lambda-streaming.ts b/packages/open-next/src/overrides/wrappers/aws-lambda-streaming.ts index 3d18e5db..a90ea7b9 100644 --- a/packages/open-next/src/overrides/wrappers/aws-lambda-streaming.ts +++ b/packages/open-next/src/overrides/wrappers/aws-lambda-streaming.ts @@ -22,8 +22,8 @@ function formatWarmerResponse(event: WarmerEvent) { return result; } -const handler: WrapperHandler = async (handler, converter) => - awslambda.streamifyResponse( +const handler: WrapperHandler = async (handler, converter) => { + return awslambda.streamifyResponse( async (event: AwsLambdaEvent, responseStream, context): Promise => { context.callbackWaitsForEmptyEventLoop = false; if ("type" in event) { @@ -100,7 +100,8 @@ const handler: WrapperHandler = async (handler, converter) => // return converter.convertTo(response); } - ); + ) as (...args: unknown[]) => unknown; +}; export default { wrapper: handler, diff --git a/packages/open-next/src/overrides/wrappers/aws-lambda.ts b/packages/open-next/src/overrides/wrappers/aws-lambda.ts index 41640868..504d8668 100644 --- a/packages/open-next/src/overrides/wrappers/aws-lambda.ts +++ b/packages/open-next/src/overrides/wrappers/aws-lambda.ts @@ -14,15 +14,15 @@ export function formatWarmerResponse(event: WarmerEvent) { }); } -const handler: WrapperHandler = - async (handler, converter) => - async (event: AwsLambdaEvent): Promise => { +const handler: WrapperHandler = async (handler, converter) => { + return async (event: unknown): Promise => { + const lambdaEvent = event as AwsLambdaEvent; // Handle warmer event - if ("type" in event) { - return formatWarmerResponse(event); + if ("type" in lambdaEvent) { + return formatWarmerResponse(lambdaEvent); } - const internalEvent = await converter.convertFrom(event); + const internalEvent = await converter.convertFrom(lambdaEvent); //TODO: create a simple reproduction and open an issue in the node repo //This is a workaround, there is an issue in node that causes node to crash silently if the OpenNextNodeResponse stream is not consumed @@ -43,8 +43,9 @@ const handler: WrapperHandler = streamCreator: fakeStream, }); - return converter.convertTo(response, event); + return converter.convertTo(response, lambdaEvent); }; +}; export default { wrapper: handler, diff --git a/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts b/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts index c3d849cd..aab8c178 100644 --- a/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts +++ b/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts @@ -15,9 +15,12 @@ interface WorkerContext { waitUntil: (promise: Promise) => void; } -const handler: WrapperHandler = - async (handler, converter) => - async (request: Request, env: Record, ctx: WorkerContext): Promise => { +const handler: WrapperHandler = async ( + handler, + converter +) => { + return async (...args: unknown[]): Promise => { + const [request, env, ctx] = args as [Request, Record, WorkerContext]; globalThis.process = process; // Set the environment variables @@ -33,7 +36,7 @@ const handler: WrapperHandler // Retrieve geo information from the cloudflare request // See https://developers.cloudflare.com/workers/runtime-apis/request // Note: This code could be moved to a cloudflare specific converter when one is created. - const cfProperties = (request as any).cf as Record | undefined; + const cfProperties = (request as Request & { cf?: Record }).cf; for (const [propName, mapping] of Object.entries(cfPropNameMapping)) { const propValue = cfProperties?.[propName]; if (propValue != null) { @@ -46,10 +49,11 @@ const handler: WrapperHandler waitUntil: ctx.waitUntil.bind(ctx), }); - const result: Response = await converter.convertTo(response); + const result = (await converter.convertTo(response)) as Response; return result; }; +}; export default { wrapper: handler, diff --git a/packages/open-next/src/overrides/wrappers/cloudflare-node.ts b/packages/open-next/src/overrides/wrappers/cloudflare-node.ts index deb08848..3249544c 100644 --- a/packages/open-next/src/overrides/wrappers/cloudflare-node.ts +++ b/packages/open-next/src/overrides/wrappers/cloudflare-node.ts @@ -6,14 +6,14 @@ import type { Wrapper, WrapperHandler } from "@/types/overrides"; // Response with null body status (101, 204, 205, or 304) cannot have a body. const NULL_BODY_STATUSES = new Set([101, 204, 205, 304]); -const handler: WrapperHandler = - async (handler, converter) => - async ( - request: Request, - env: Record, - ctx: any, - abortSignal: AbortSignal - ): Promise => { +const handler: WrapperHandler = async (handler, converter) => { + return async (...args: unknown[]): Promise => { + const [request, env, ctx, abortSignal] = args as [ + Request, + Record, + { waitUntil: (promise: Promise) => void }, + AbortSignal, + ]; globalThis.process = process; // Set the environment variables // Cloudflare suggests to not override the process.env object but instead apply the values to it @@ -80,8 +80,8 @@ const handler: WrapperHandler = write(chunk, encoding, callback) { try { controller.enqueue(chunk); - } catch (e: any) { - return callback(e); + } catch (e: unknown) { + return callback(e instanceof Error ? e : new Error(String(e))); } callback(); }, @@ -120,6 +120,7 @@ const handler: WrapperHandler = return promiseResponse; }; +}; export default { wrapper: handler, diff --git a/packages/open-next/src/overrides/wrappers/dummy.ts b/packages/open-next/src/overrides/wrappers/dummy.ts index fc048208..22f18097 100644 --- a/packages/open-next/src/overrides/wrappers/dummy.ts +++ b/packages/open-next/src/overrides/wrappers/dummy.ts @@ -1,8 +1,9 @@ import type { InternalEvent } from "@/types/open-next"; import type { OpenNextHandlerOptions, Wrapper, WrapperHandler } from "@/types/overrides"; -const dummyWrapper: WrapperHandler = async (handler, converter) => { - return async (event: InternalEvent, options?: OpenNextHandlerOptions) => { +const dummyWrapper: WrapperHandler = async (handler, _converter) => { + return async (...args: unknown[]): Promise => { + const [event, options] = args as [InternalEvent, OpenNextHandlerOptions | undefined]; return await handler(event, options); }; }; diff --git a/packages/open-next/src/plugins/resolve.ts b/packages/open-next/src/plugins/resolve.ts index bc53725b..717836c8 100644 --- a/packages/open-next/src/plugins/resolve.ts +++ b/packages/open-next/src/plugins/resolve.ts @@ -4,6 +4,7 @@ import chalk from "chalk"; import type { Plugin } from "esbuild"; import type { + BaseOverride, DefaultOverrideOptions, IncludedImageLoader, IncludedOriginResolver, @@ -18,7 +19,9 @@ import { getCrossPlatformPathRegex } from "../utils/regex.js"; export interface IPluginSettings { overrides?: { + // oxlint-disable-next-line @typescript-eslint/no-explicit-any - generic overrides for flexibility wrapper?: DefaultOverrideOptions["wrapper"]; + // oxlint-disable-next-line @typescript-eslint/no-explicit-any - generic overrides for flexibility converter?: DefaultOverrideOptions["converter"]; tagCache?: OverrideOptions["tagCache"]; queue?: OverrideOptions["queue"]; @@ -32,7 +35,7 @@ export interface IPluginSettings { fnName?: string; } -function getOverrideOrDummy>(override: Override) { +function getOverrideOrDummy>(override: Override) { if (typeof override === "string") { return override; } diff --git a/packages/open-next/src/types/aws-lambda.ts b/packages/open-next/src/types/aws-lambda.ts index f06be74b..a44372dc 100644 --- a/packages/open-next/src/types/aws-lambda.ts +++ b/packages/open-next/src/types/aws-lambda.ts @@ -21,7 +21,7 @@ type Handler = ( event: APIGatewayProxyEventV2, responseStream: ResponseStream, context: Context -) => Promise; +) => Promise; interface Metadata { statusCode: number; diff --git a/packages/open-next/src/types/global.ts b/packages/open-next/src/types/global.ts index cf605cf7..a4100e5b 100644 --- a/packages/open-next/src/types/global.ts +++ b/packages/open-next/src/types/global.ts @@ -1,4 +1,4 @@ -import type { AsyncLocalStorage } from "node:async_hooks"; +import type { AsyncLocalStorage, AsyncLocalStorage as NodeAsyncLocalStorage } from "node:async_hooks"; import type { OutgoingHttpHeaders } from "node:http"; import type { @@ -12,6 +12,7 @@ import type { import type { DetachedPromiseRunner } from "../utils/promise"; +import type { i18nConfig } from "./next-types.js"; import type { OpenNextConfig, WaitUntil } from "./open-next"; export interface RequestData { @@ -27,7 +28,7 @@ export interface RequestData { method: string; nextConfig?: { basePath?: string; - i18n?: any; + i18n?: i18nConfig; trailingSlash?: boolean; }; page?: { @@ -185,14 +186,14 @@ declare global { * AsyncContext available globally in the edge runtime. * Only available in edge runtime functions. */ - var AsyncContext: any; + var AsyncContext: unknown; /** * AsyncLocalStorage available globally in the edge runtime. * Only available in edge runtime functions. * Defined in createEdgeBundle. */ - var AsyncLocalStorage: any; + var AsyncLocalStorage: typeof NodeAsyncLocalStorage; /** * The version of the Open Next runtime. diff --git a/packages/open-next/src/types/overrides.ts b/packages/open-next/src/types/overrides.ts index 3ebf747c..84589959 100644 --- a/packages/open-next/src/types/overrides.ts +++ b/packages/open-next/src/types/overrides.ts @@ -202,7 +202,7 @@ export type TagCache = NextModeTagCache | OriginalTagCache; export type WrapperHandler< E extends BaseEventOrResult = InternalEvent, R extends BaseEventOrResult = InternalResult, -> = (handler: OpenNextHandler, converter: Converter) => Promise<(...args: any[]) => any>; +> = (handler: OpenNextHandler, converter: Converter) => Promise<(...args: unknown[]) => unknown>; export type Wrapper< E extends BaseEventOrResult = InternalEvent, @@ -229,8 +229,8 @@ export type Converter< E extends BaseEventOrResult = InternalEvent, R extends BaseEventOrResult = InternalResult, > = BaseOverride & { - convertFrom: (event: any) => Promise; - convertTo: (result: R, originalRequest?: any) => Promise; + convertFrom: (event: unknown) => Promise; + convertTo: (result: R, originalRequest?: unknown) => Promise; }; export type Warmer = BaseOverride & { diff --git a/packages/open-next/src/utils/cache.ts b/packages/open-next/src/utils/cache.ts index 11945273..090bc247 100644 --- a/packages/open-next/src/utils/cache.ts +++ b/packages/open-next/src/utils/cache.ts @@ -1,11 +1,16 @@ -import type { CacheValue, OriginalTagCacheWriteInput, WithLastModified } from "@/types/overrides"; +import type { + CacheEntryType, + CacheValue, + OriginalTagCacheWriteInput, + WithLastModified, +} from "@/types/overrides"; import { debug } from "../adapters/logger"; -export async function hasBeenRevalidated( +export async function hasBeenRevalidated( key: string, tags: string[], - cacheEntry: WithLastModified> + cacheEntry: WithLastModified> ): Promise { if (globalThis.openNextConfig.dangerous?.disableTagCache) { return false; @@ -72,5 +77,6 @@ export async function writeTags(tags: (string | OriginalTagCacheWriteInput)[]): } // Here we know that we have the correct type + // oxlint-disable-next-line @typescript-eslint/no-explicit-any - writeTags accepts a union type that typescript cannot infer correctly await globalThis.tagCache.writeTags(tagsToWrite as any); } diff --git a/packages/open-next/src/utils/error.ts b/packages/open-next/src/utils/error.ts index 1d6940cd..da22733b 100644 --- a/packages/open-next/src/utils/error.ts +++ b/packages/open-next/src/utils/error.ts @@ -40,9 +40,9 @@ export class FatalError extends Error implements BaseOpenNextError { } } -export function isOpenNextError(e: any): e is BaseOpenNextError & Error { +export function isOpenNextError(e: unknown): e is BaseOpenNextError & Error { try { - return "__openNextInternal" in e; + return e !== null && typeof e === "object" && "__openNextInternal" in e; } catch { return false; } diff --git a/packages/open-next/src/utils/lru.ts b/packages/open-next/src/utils/lru.ts index 628f1d44..f1bf8f3f 100644 --- a/packages/open-next/src/utils/lru.ts +++ b/packages/open-next/src/utils/lru.ts @@ -14,7 +14,7 @@ export class LRUCache { return result; } - set(key: string, value: any) { + set(key: string, value: T) { if (this.cache.size >= this.maxSize) { const firstKey = this.cache.keys().next().value; if (firstKey !== undefined) { diff --git a/packages/open-next/src/utils/promise.ts b/packages/open-next/src/utils/promise.ts index 99829e4e..c7ef8b22 100644 --- a/packages/open-next/src/utils/promise.ts +++ b/packages/open-next/src/utils/promise.ts @@ -8,14 +8,14 @@ import { debug, error } from "../adapters/logger"; * Copied from next https://github.com/vercel/next.js/blob/canary/packages/next/src/lib/detached-promise.ts * @see https://tc39.es/proposal-promise-with-resolvers/ */ -export class DetachedPromise { +export class DetachedPromise { public readonly resolve: (value: T | PromiseLike) => void; - public readonly reject: (reason: any) => void; + public readonly reject: (reason: unknown) => void; public readonly promise: Promise; constructor() { let resolve: (value: T | PromiseLike) => void; - let reject: (reason: any) => void; + let reject: (reason: unknown) => void; // Create the promise and assign the resolvers to the object. this.promise = new Promise((res, rej) => { @@ -31,17 +31,17 @@ export class DetachedPromise { } export class DetachedPromiseRunner { - private promises: DetachedPromise[] = []; + private promises: DetachedPromise[] = []; public withResolvers(): DetachedPromise { const detachedPromise = new DetachedPromise(); - this.promises.push(detachedPromise); + this.promises.push(detachedPromise as DetachedPromise); return detachedPromise; } public add(promise: Promise): void { const detachedPromise = new DetachedPromise(); - this.promises.push(detachedPromise); + this.promises.push(detachedPromise as DetachedPromise); promise.then(detachedPromise.resolve, detachedPromise.reject); } diff --git a/packages/open-next/src/utils/safe-json-parse.ts b/packages/open-next/src/utils/safe-json-parse.ts index fb86ffee..cd9064ea 100644 --- a/packages/open-next/src/utils/safe-json-parse.ts +++ b/packages/open-next/src/utils/safe-json-parse.ts @@ -1,6 +1,6 @@ import logger from "../logger.js"; -export function safeParseJsonFile(input: string, filePath: string, fallback?: T): T | undefined { +export function safeParseJsonFile(input: string, filePath: string, fallback?: T): T | undefined { try { return JSON.parse(input); } catch (err) { diff --git a/packages/open-next/src/utils/stream.ts b/packages/open-next/src/utils/stream.ts index 45171c0d..66bd5234 100644 --- a/packages/open-next/src/utils/stream.ts +++ b/packages/open-next/src/utils/stream.ts @@ -31,12 +31,12 @@ export async function fromReadableStream( return buffer.toString(base64 ? "base64" : "utf8"); } -export function toReadableStream(value: string, isBase64?: boolean): ReadableStream { +export function toReadableStream(value: string | Buffer, isBase64?: boolean): ReadableStream { return new ReadableStream( { pull(controller) { // Defer the Buffer.from conversion to when the stream is actually read. - controller.enqueue(Buffer.from(value, isBase64 ? "base64" : "utf8")); + controller.enqueue(Buffer.isBuffer(value) ? value : Buffer.from(value, isBase64 ? "base64" : "utf8")); controller.close(); }, }, From e2b1528438918d25e2740412f3fc1482ae74ba43 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 3 Feb 2026 19:23:02 +0000 Subject: [PATCH 2/4] return funcs directly --- .../src/overrides/wrappers/aws-lambda-compressed.ts | 6 +++--- .../src/overrides/wrappers/aws-lambda-streaming.ts | 5 ++--- packages/open-next/src/overrides/wrappers/aws-lambda.ts | 6 +++--- .../open-next/src/overrides/wrappers/cloudflare-edge.ts | 9 +++------ .../open-next/src/overrides/wrappers/cloudflare-node.ts | 6 +++--- packages/open-next/src/overrides/wrappers/dummy.ts | 6 +++--- 6 files changed, 17 insertions(+), 21 deletions(-) diff --git a/packages/open-next/src/overrides/wrappers/aws-lambda-compressed.ts b/packages/open-next/src/overrides/wrappers/aws-lambda-compressed.ts index 4eb1acb3..f464b237 100644 --- a/packages/open-next/src/overrides/wrappers/aws-lambda-compressed.ts +++ b/packages/open-next/src/overrides/wrappers/aws-lambda-compressed.ts @@ -10,8 +10,9 @@ import { error } from "../../adapters/logger"; import { formatWarmerResponse } from "./aws-lambda"; -const handler: WrapperHandler = async (handler, converter) => { - return async (event: unknown): Promise => { +const handler: WrapperHandler = + async (handler, converter) => + async (event: unknown): Promise => { const lambdaEvent = event as AwsLambdaEvent; // Handle warmer event if ("type" in lambdaEvent) { @@ -69,7 +70,6 @@ const handler: WrapperHandler = async (handler, converter) => { return converter.convertTo(response, lambdaEvent); }; -}; export default { wrapper: handler, diff --git a/packages/open-next/src/overrides/wrappers/aws-lambda-streaming.ts b/packages/open-next/src/overrides/wrappers/aws-lambda-streaming.ts index a90ea7b9..c529665a 100644 --- a/packages/open-next/src/overrides/wrappers/aws-lambda-streaming.ts +++ b/packages/open-next/src/overrides/wrappers/aws-lambda-streaming.ts @@ -22,8 +22,8 @@ function formatWarmerResponse(event: WarmerEvent) { return result; } -const handler: WrapperHandler = async (handler, converter) => { - return awslambda.streamifyResponse( +const handler: WrapperHandler = async (handler, converter) => + awslambda.streamifyResponse( async (event: AwsLambdaEvent, responseStream, context): Promise => { context.callbackWaitsForEmptyEventLoop = false; if ("type" in event) { @@ -101,7 +101,6 @@ const handler: WrapperHandler = async (handler, converter) => { // return converter.convertTo(response); } ) as (...args: unknown[]) => unknown; -}; export default { wrapper: handler, diff --git a/packages/open-next/src/overrides/wrappers/aws-lambda.ts b/packages/open-next/src/overrides/wrappers/aws-lambda.ts index 504d8668..a37827db 100644 --- a/packages/open-next/src/overrides/wrappers/aws-lambda.ts +++ b/packages/open-next/src/overrides/wrappers/aws-lambda.ts @@ -14,8 +14,9 @@ export function formatWarmerResponse(event: WarmerEvent) { }); } -const handler: WrapperHandler = async (handler, converter) => { - return async (event: unknown): Promise => { +const handler: WrapperHandler = + async (handler, converter) => + async (event: unknown): Promise => { const lambdaEvent = event as AwsLambdaEvent; // Handle warmer event if ("type" in lambdaEvent) { @@ -45,7 +46,6 @@ const handler: WrapperHandler = async (handler, converter) => { return converter.convertTo(response, lambdaEvent); }; -}; export default { wrapper: handler, diff --git a/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts b/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts index aab8c178..60f7e8c6 100644 --- a/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts +++ b/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts @@ -15,11 +15,9 @@ interface WorkerContext { waitUntil: (promise: Promise) => void; } -const handler: WrapperHandler = async ( - handler, - converter -) => { - return async (...args: unknown[]): Promise => { +const handler: WrapperHandler = + async (handler, converter) => + async (...args: unknown[]): Promise => { const [request, env, ctx] = args as [Request, Record, WorkerContext]; globalThis.process = process; @@ -53,7 +51,6 @@ const handler: WrapperHandler return result; }; -}; export default { wrapper: handler, diff --git a/packages/open-next/src/overrides/wrappers/cloudflare-node.ts b/packages/open-next/src/overrides/wrappers/cloudflare-node.ts index 3249544c..4f90d895 100644 --- a/packages/open-next/src/overrides/wrappers/cloudflare-node.ts +++ b/packages/open-next/src/overrides/wrappers/cloudflare-node.ts @@ -6,8 +6,9 @@ import type { Wrapper, WrapperHandler } from "@/types/overrides"; // Response with null body status (101, 204, 205, or 304) cannot have a body. const NULL_BODY_STATUSES = new Set([101, 204, 205, 304]); -const handler: WrapperHandler = async (handler, converter) => { - return async (...args: unknown[]): Promise => { +const handler: WrapperHandler = + async (handler, converter) => + async (...args: unknown[]): Promise => { const [request, env, ctx, abortSignal] = args as [ Request, Record, @@ -120,7 +121,6 @@ const handler: WrapperHandler = async (handler, c return promiseResponse; }; -}; export default { wrapper: handler, diff --git a/packages/open-next/src/overrides/wrappers/dummy.ts b/packages/open-next/src/overrides/wrappers/dummy.ts index 22f18097..8649a8c8 100644 --- a/packages/open-next/src/overrides/wrappers/dummy.ts +++ b/packages/open-next/src/overrides/wrappers/dummy.ts @@ -1,12 +1,12 @@ import type { InternalEvent } from "@/types/open-next"; import type { OpenNextHandlerOptions, Wrapper, WrapperHandler } from "@/types/overrides"; -const dummyWrapper: WrapperHandler = async (handler, _converter) => { - return async (...args: unknown[]): Promise => { +const dummyWrapper: WrapperHandler = + async (handler, _converter) => + async (...args: unknown[]): Promise => { const [event, options] = args as [InternalEvent, OpenNextHandlerOptions | undefined]; return await handler(event, options); }; -}; export default { name: "dummy", From e8963b3085a27241f80138c40df0eac543323397 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 3 Feb 2026 19:23:26 +0000 Subject: [PATCH 3/4] fallback to 6 when value is NaN --- .../open-next/src/overrides/wrappers/aws-lambda-compressed.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/open-next/src/overrides/wrappers/aws-lambda-compressed.ts b/packages/open-next/src/overrides/wrappers/aws-lambda-compressed.ts index f464b237..adc140aa 100644 --- a/packages/open-next/src/overrides/wrappers/aws-lambda-compressed.ts +++ b/packages/open-next/src/overrides/wrappers/aws-lambda-compressed.ts @@ -86,12 +86,13 @@ function compressBody(body: ReadableStream, encoding: string | null) { switch (encoding) { case "br": + const quality = Number(process.env.BROTLI_QUALITY); transform = zlib.createBrotliCompress({ params: { // This is a compromise between speed and compression ratio. // The default one will most likely timeout an AWS Lambda with default configuration on large bodies (>6mb). // Therefore we set it to 6, which is a good compromise. - [zlib.constants.BROTLI_PARAM_QUALITY]: Number(process.env.BROTLI_QUALITY) || 6, + [zlib.constants.BROTLI_PARAM_QUALITY]: Number.isNaN(quality) ? 6 : quality, }, }); break; From 8f0a3e625c5454ddf4e903a60b47a71b3777a4a4 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 3 Feb 2026 19:27:04 +0000 Subject: [PATCH 4/4] result -> imageOptimizeResult --- .../src/adapters/image-optimization-adapter.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/open-next/src/adapters/image-optimization-adapter.ts b/packages/open-next/src/adapters/image-optimization-adapter.ts index 6b3aa53e..bf61ff1d 100644 --- a/packages/open-next/src/adapters/image-optimization-adapter.ts +++ b/packages/open-next/src/adapters/image-optimization-adapter.ts @@ -136,16 +136,16 @@ type ImageOptimizeResult = { }; function buildSuccessResponse( - result: ImageOptimizeResult, + imageOptimizeResult: ImageOptimizeResult, streamCreator?: StreamCreator, etag?: string ): InternalResult { const headers: Record = { Vary: "Accept", - "Content-Type": result.contentType, - "Cache-Control": `public,max-age=${result.maxAge},immutable`, + "Content-Type": imageOptimizeResult.contentType, + "Cache-Control": `public,max-age=${imageOptimizeResult.maxAge},immutable`, }; - debug("result", result); + debug("result", imageOptimizeResult); if (etag) { headers.ETag = etag; } @@ -157,13 +157,13 @@ function buildSuccessResponse( streamCreator ); response.writeHead(200, headers); - response.end(result.buffer); + response.end(imageOptimizeResult.buffer); } return { type: "core", statusCode: 200, - body: toReadableStream(result.buffer, true), + body: toReadableStream(imageOptimizeResult.buffer, true), isBase64Encoded: true, headers, };