diff --git a/packages/cloudflare/src/api/durable-objects/queue.ts b/packages/cloudflare/src/api/durable-objects/queue.ts index c8b8f7a65..d5d92b3ac 100644 --- a/packages/cloudflare/src/api/durable-objects/queue.ts +++ b/packages/cloudflare/src/api/durable-objects/queue.ts @@ -40,9 +40,11 @@ export class DOQueueHandler extends DurableObject { constructor(ctx: DurableObjectState, env: CloudflareEnv) { super(ctx, env); - this.service = env.WORKER_SELF_REFERENCE!; // If there is no service binding, we throw an error because we can't revalidate without it - if (!this.service) throw new IgnorableError("No service binding for cache revalidation worker"); + if (!env.WORKER_SELF_REFERENCE) { + throw new IgnorableError("No service binding for cache revalidation worker"); + } + this.service = env.WORKER_SELF_REFERENCE; this.sql = ctx.storage.sql; this.maxRevalidations = env.NEXT_CACHE_DO_QUEUE_MAX_REVALIDATION diff --git a/packages/cloudflare/src/api/overrides/internal.ts b/packages/cloudflare/src/api/overrides/internal.ts index cc7c115d4..d0b7251b3 100644 --- a/packages/cloudflare/src/api/overrides/internal.ts +++ b/packages/cloudflare/src/api/overrides/internal.ts @@ -34,7 +34,7 @@ export function computeCacheKey(key: string, options: KeyOptions) { export function isPurgeCacheEnabled(): boolean { // The `?` is required at `openNextConfig?` or the Open Next build fails because of a type error - const cdnInvalidation = globalThis.openNextConfig?.default?.override?.cdnInvalidation; + const cdnInvalidation = globalThis.openNextConfig.default?.override?.cdnInvalidation; return cdnInvalidation !== undefined && cdnInvalidation !== "dummy"; } diff --git a/packages/cloudflare/src/api/overrides/tag-cache/d1-next-tag-cache.ts b/packages/cloudflare/src/api/overrides/tag-cache/d1-next-tag-cache.ts index e288c841e..3d2fa7877 100644 --- a/packages/cloudflare/src/api/overrides/tag-cache/d1-next-tag-cache.ts +++ b/packages/cloudflare/src/api/overrides/tag-cache/d1-next-tag-cache.ts @@ -163,7 +163,7 @@ export class D1NextModeTagCache implements NextModeTagCache { const row = rowsByKey.get(this.getCacheKey(tag)); const value: D1TagValue | null = row ? { - revalidatedAt: (row[1] as number) ?? 0, + revalidatedAt: row[1] as number, stale: (row[2] as number) ?? null, expire: (row[3] as number) ?? null, } diff --git a/packages/cloudflare/src/api/overrides/tag-cache/do-sharded-tag-cache.ts b/packages/cloudflare/src/api/overrides/tag-cache/do-sharded-tag-cache.ts index 26f4d222d..646d9fada 100644 --- a/packages/cloudflare/src/api/overrides/tag-cache/do-sharded-tag-cache.ts +++ b/packages/cloudflare/src/api/overrides/tag-cache/do-sharded-tag-cache.ts @@ -304,16 +304,16 @@ class ShardedDOTagCache implements NextModeTagCache { return { tag, revalidatedAt: parsed, - stale: parsed as number | null, - expire: null as number | null, + stale: parsed, + expire: null, }; } const data = parsed as TagData; return { tag, - revalidatedAt: data.revalidatedAt ?? 0, - stale: data.stale ?? null, - expire: data.expire ?? null, + revalidatedAt: data.revalidatedAt, + stale: data.stale, + expire: data.expire, }; } catch (e) { debugCache("Error while parsing cached value", e); diff --git a/packages/cloudflare/src/api/overrides/tag-cache/kv-next-tag-cache.ts b/packages/cloudflare/src/api/overrides/tag-cache/kv-next-tag-cache.ts index 50ab91b1c..39f07732a 100644 --- a/packages/cloudflare/src/api/overrides/tag-cache/kv-next-tag-cache.ts +++ b/packages/cloudflare/src/api/overrides/tag-cache/kv-next-tag-cache.ts @@ -22,7 +22,7 @@ type KVTagValue = | { revalidatedAt: number; stale?: number | null; expire?: number | null }; function getRevalidatedAt(value: KVTagValue): number { - return typeof value === "number" ? value : (value.revalidatedAt ?? 0); + return typeof value === "number" ? value : value.revalidatedAt; } function getStale(value: KVTagValue): number | null { diff --git a/packages/cloudflare/src/api/overrides/tag-cache/tag-cache-filter.ts b/packages/cloudflare/src/api/overrides/tag-cache/tag-cache-filter.ts index 239cea838..bb6adaf7d 100644 --- a/packages/cloudflare/src/api/overrides/tag-cache/tag-cache-filter.ts +++ b/packages/cloudflare/src/api/overrides/tag-cache/tag-cache-filter.ts @@ -11,7 +11,7 @@ interface WithFilterOptions { * @param tag The tag to filter. * @returns true if the tag should be forwarded, false otherwise. */ - filterFn: (tag: string | NextModeTagCacheWriteInput) => boolean; + filterFn: (tag: NextModeTagCacheWriteInput) => boolean; } /** @@ -69,7 +69,7 @@ export function withFilter({ tagCache, filterFn }: WithFilterOptions): NextModeT * This is used to filter out internal soft tags. * Can be used if `revalidatePath` is not used. */ -export function softTagFilter(tag: string | NextModeTagCacheWriteInput): boolean { +export function softTagFilter(tag: NextModeTagCacheWriteInput): boolean { if (typeof tag === "string") { return !tag.startsWith("_N_T_"); } diff --git a/packages/cloudflare/src/cli/build/open-next/createServerBundle.ts b/packages/cloudflare/src/cli/build/open-next/createServerBundle.ts index c556b6026..ea0b2c52e 100644 --- a/packages/cloudflare/src/cli/build/open-next/createServerBundle.ts +++ b/packages/cloudflare/src/cli/build/open-next/createServerBundle.ts @@ -187,7 +187,7 @@ async function generateBundle( buildOutputPath: appBuildOutputPath, packagePath, outputDir: outputPath, - routes: fnOptions.routes ?? ["app/page.tsx"], + routes: fnOptions.routes, bundledNextServer: isBundled, }); diff --git a/packages/cloudflare/src/cli/build/patches/ast/patch-vercel-og-library.ts b/packages/cloudflare/src/cli/build/patches/ast/patch-vercel-og-library.ts index b37fa892c..9ef4dcc20 100644 --- a/packages/cloudflare/src/cli/build/patches/ast/patch-vercel-og-library.ts +++ b/packages/cloudflare/src/cli/build/patches/ast/patch-vercel-og-library.ts @@ -65,8 +65,10 @@ export function patchVercelOgLibrary(buildOpts: BuildOptions): boolean { writeFileSync(outputEdgePath, ast.commitEdits(edits)); if (matches.length > 0) { - const fontFileName = matches[0]!.getMatch("PATH")!.text(); - renameSync(path.join(outputDir, fontFileName), path.join(outputDir, `${fontFileName}.bin`)); + const fontFileName = matches[0]?.getMatch("PATH")?.text(); + if (fontFileName) { + renameSync(path.join(outputDir, fontFileName), path.join(outputDir, `${fontFileName}.bin`)); + } } } diff --git a/packages/cloudflare/src/cli/build/utils/ensure-cf-config.ts b/packages/cloudflare/src/cli/build/utils/ensure-cf-config.ts index 426fb2188..7f23e9443 100644 --- a/packages/cloudflare/src/cli/build/utils/ensure-cf-config.ts +++ b/packages/cloudflare/src/cli/build/utils/ensure-cf-config.ts @@ -1,5 +1,4 @@ import logger from "@opennextjs/aws/logger.js"; -import type { ExternalMiddlewareConfig } from "@opennextjs/aws/types/open-next.js"; import type { OpenNextConfig } from "../../../api/config.js"; @@ -9,24 +8,25 @@ import type { OpenNextConfig } from "../../../api/config.js"; * @param config OpenNext configuration. */ export function ensureCloudflareConfig(config: OpenNextConfig) { - const mwIsMiddlewareExternal = config.middleware?.external === true; - const mwConfig = mwIsMiddlewareExternal ? (config.middleware as ExternalMiddlewareConfig) : undefined; + const { middleware } = config; + const mwConfig = middleware?.external === true ? middleware : undefined; + const mwIsMiddlewareExternal = mwConfig !== undefined; const requirements = { // Check for the default function - dftUseCloudflareWrapper: config.default?.override?.wrapper === "cloudflare-node", - dftUseEdgeConverter: config.default?.override?.converter === "edge", - dftUseFetchProxy: config.default?.override?.proxyExternalRequest === "fetch", + dftUseCloudflareWrapper: config.default.override?.wrapper === "cloudflare-node", + dftUseEdgeConverter: config.default.override?.converter === "edge", + dftUseFetchProxy: config.default.override?.proxyExternalRequest === "fetch", dftMaybeUseCache: - config.default?.override?.incrementalCache === "dummy" || - typeof config.default?.override?.incrementalCache === "function", + config.default.override?.incrementalCache === "dummy" || + typeof config.default.override?.incrementalCache === "function", dftMaybeUseTagCache: - config.default?.override?.tagCache === "dummy" || - typeof config.default?.override?.incrementalCache === "function", + config.default.override?.tagCache === "dummy" || + typeof config.default.override?.incrementalCache === "function", dftMaybeUseQueue: - config.default?.override?.queue === "dummy" || - config.default?.override?.queue === "direct" || - typeof config.default?.override?.queue === "function", + config.default.override?.queue === "dummy" || + config.default.override?.queue === "direct" || + typeof config.default.override?.queue === "function", // Check for the middleware function mwIsMiddlewareExternal, mwUseCloudflareWrapper: mwConfig?.override?.wrapper === "cloudflare-edge", @@ -35,7 +35,7 @@ export function ensureCloudflareConfig(config: OpenNextConfig) { hasCryptoExternal: config.edgeExternals?.includes("node:crypto"), }; - if (config.default?.override?.queue === "direct") { + if (config.default.override?.queue === "direct") { logger.warn("The direct mode queue is not recommended for use in production."); } diff --git a/packages/cloudflare/src/cli/commands/skew-protection.ts b/packages/cloudflare/src/cli/commands/skew-protection.ts index 4845f8a9d..e43bcaac1 100644 --- a/packages/cloudflare/src/cli/commands/skew-protection.ts +++ b/packages/cloudflare/src/cli/commands/skew-protection.ts @@ -90,18 +90,18 @@ export async function getDeploymentMapping( process.exit(1); } - const apiToken = envVars.CF_WORKERS_SCRIPTS_API_TOKEN!; - const accountId = envVars.CF_ACCOUNT_ID!; + const apiToken = envVars.CF_WORKERS_SCRIPTS_API_TOKEN; + const accountId = envVars.CF_ACCOUNT_ID; const client = new Cloudflare({ apiToken }); - const scriptName = envVars.CF_WORKER_NAME!; + const scriptName = envVars.CF_WORKER_NAME; const deployedVersions = await listWorkerVersions(scriptName, { client, accountId, - maxNumberOfVersions: config.cloudflare?.skewProtection?.maxNumberOfVersions, - afterTimeMs: config.cloudflare?.skewProtection?.maxVersionAgeDays - ? Date.now() - config.cloudflare?.skewProtection?.maxVersionAgeDays * MS_PER_DAY + maxNumberOfVersions: config.cloudflare.skewProtection.maxNumberOfVersions, + afterTimeMs: config.cloudflare.skewProtection.maxVersionAgeDays + ? Date.now() - config.cloudflare.skewProtection.maxVersionAgeDays * MS_PER_DAY : undefined, }); @@ -257,7 +257,7 @@ export async function listWorkerVersions( } } } catch (e) { - if (e instanceof NotFoundError && e.status === 404) { + if (e instanceof NotFoundError) { // The worker has not been deployed before, no previous versions. return []; } diff --git a/packages/cloudflare/src/cli/commands/utils/run-wrangler.ts b/packages/cloudflare/src/cli/commands/utils/run-wrangler.ts index 6859b4097..d01d10271 100644 --- a/packages/cloudflare/src/cli/commands/utils/run-wrangler.ts +++ b/packages/cloudflare/src/cli/commands/utils/run-wrangler.ts @@ -131,8 +131,8 @@ export function runWrangler( ); const success = result.status === 0; - const stdout = result.stdout?.toString() ?? ""; - const stderr = result.stderr?.toString() ?? ""; + const stdout = result.stdout.toString(); + const stderr = result.stderr.toString(); if (!noLogs) { // When not piping logs, stderr is captured but should still be visible to the user diff --git a/packages/cloudflare/src/cli/templates/images.ts b/packages/cloudflare/src/cli/templates/images.ts index 886be5e8a..993054ad8 100644 --- a/packages/cloudflare/src/cli/templates/images.ts +++ b/packages/cloudflare/src/cli/templates/images.ts @@ -486,14 +486,15 @@ type ErrorResult = { function validateUrlQueryParameter(requestURL: URL): ErrorResult | { url: string; static: boolean } { // There should be a single "url" parameter. const urls = requestURL.searchParams.getAll("url"); - if (urls.length < 1) { + const [url, ...rest] = urls; + if (url === undefined) { const result: ErrorResult = { ok: false, message: '"url" parameter is required', }; return result; } - if (urls.length > 1) { + if (rest.length > 0) { const result: ErrorResult = { ok: false, message: '"url" parameter cannot be an array', @@ -501,8 +502,6 @@ function validateUrlQueryParameter(requestURL: URL): ErrorResult | { url: string return result; } - const url = urls[0]!; - if (url.length > 3072) { const result: ErrorResult = { ok: false, @@ -574,21 +573,22 @@ function validateUrlQueryParameter(requestURL: URL): ErrorResult | { url: string */ function validateWidthQueryParameter(requestURL: URL): ErrorResult | number { const widthQueryValues = requestURL.searchParams.getAll("w"); - if (widthQueryValues.length < 1) { + const [widthQueryValue, ...rest] = widthQueryValues; + + if (widthQueryValue === undefined) { const result: ErrorResult = { ok: false, message: '"w" parameter (width) is required', }; return result; } - if (widthQueryValues.length > 1) { + if (rest.length > 0) { const result: ErrorResult = { ok: false, message: '"w" parameter (width) cannot be an array', }; return result; } - const widthQueryValue = widthQueryValues[0]!; if (!/^[0-9]+$/.test(widthQueryValue)) { const result: ErrorResult = { ok: false, @@ -624,21 +624,21 @@ function validateWidthQueryParameter(requestURL: URL): ErrorResult | number { */ function validateQualityQueryParameter(requestURL: URL): ErrorResult | number { const qualityQueryValues = requestURL.searchParams.getAll("q"); - if (qualityQueryValues.length < 1) { + const [qualityQueryValue, ...rest] = qualityQueryValues; + if (qualityQueryValue === undefined) { const result: ErrorResult = { ok: false, message: '"q" parameter (quality) is required', }; return result; } - if (qualityQueryValues.length > 1) { + if (rest.length > 0) { const result: ErrorResult = { ok: false, message: '"q" parameter (quality) cannot be an array', }; return result; } - const qualityQueryValue = qualityQueryValues[0]!; if (!/^[0-9]+$/.test(qualityQueryValue)) { const result: ErrorResult = { ok: false, diff --git a/packages/cloudflare/src/cli/utils/create-wrangler-config.ts b/packages/cloudflare/src/cli/utils/create-wrangler-config.ts index 2d89e3e2c..582a16bcc 100644 --- a/packages/cloudflare/src/cli/utils/create-wrangler-config.ts +++ b/packages/cloudflare/src/cli/utils/create-wrangler-config.ts @@ -128,14 +128,18 @@ async function getLatestCompatDate(): Promise { // The format of the workerd version is `major.yyyymmdd.patch`. const match = latestWorkerdVersion.match(/\d+\.(\d{4})(\d{2})(\d{2})\.\d+/); - if (match) { - const [, year, month, day] = match; - const compatDate = `${year}-${month}-${day}`; + if (!match) { + return undefined; + } + const [, year, month, day] = match; + if (!year || !month || !day) { + return undefined; + } + const compatDate = `${year}-${month}-${day}`; - const currentDate = new Date().toISOString().slice(0, 10); + const currentDate = new Date().toISOString().slice(0, 10); - return compatDate < currentDate ? compatDate : currentDate; - } + return compatDate < currentDate ? compatDate : currentDate; } catch { /* empty */ }