Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/cloudflare/src/api/cloudflare-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ async function getCloudflareContextAsync<
* Note: this function should only be called inside the Next.js config file, and although async it doesn't need to be `await`ed
* @param options options on how the function should operate and if/where to persist the platform data
*/
export async function initOpenNextCloudflareForDev(options?: GetPlatformProxyOptions) {
export async function initOpenNextCloudflareForDev(options?: GetPlatformProxyOptions): Promise<void> {
const shouldInitializationRun = shouldContextInitializationRun();
if (!shouldInitializationRun) return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class BucketCachePurge extends DurableObject<CloudflareEnv> {
});
}

async purgeCacheByTags(tags: string[]) {
async purgeCacheByTags(tags: string[]): Promise<void> {
for (const tag of tags) {
// Insert the tag into the sql table
this.ctx.storage.sql.exec(
Expand All @@ -43,7 +43,7 @@ export class BucketCachePurge extends DurableObject<CloudflareEnv> {
}
}

override async alarm() {
override async alarm(): Promise<void> {
let tags = this.ctx.storage.sql
.exec<{ tag: string }>(
`
Expand Down
18 changes: 9 additions & 9 deletions packages/cloudflare/src/api/durable-objects/queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ export class DOQueueHandler extends DurableObject<CloudflareEnv> {
// Ongoing revalidations are deduped by the deduplication id
// Since this is running in waitUntil, we expect the durable object state to persist this during the duration of the revalidation
// TODO: handle incremental cache with only eventual consistency (i.e. KV or R2/D1 with the optional cache layer on top)
ongoingRevalidations = new Map<string, Promise<void>>();
ongoingRevalidations: Map<string, Promise<void>> = new Map<string, Promise<void>>();
Comment thread
314systems marked this conversation as resolved.
Outdated

sql: SqlStorage;

routeInFailedState = new Map<string, FailedState>();
routeInFailedState: Map<string, FailedState> = new Map<string, FailedState>();

service: NonNullable<CloudflareEnv["WORKER_SELF_REFERENCE"]>;

Expand Down Expand Up @@ -72,7 +72,7 @@ export class DOQueueHandler extends DurableObject<CloudflareEnv> {
debug(`Durable object initialized`);
}

async revalidate(msg: QueueMessage) {
async revalidate(msg: QueueMessage): Promise<void> {
if (this.ongoingRevalidations.size > 2 * this.maxRevalidations) {
warn(
`Your durable object has 2 times the maximum number of revalidations (${this.maxRevalidations}) in progress. If this happens often, you should consider increasing the NEXT_CACHE_DO_QUEUE_MAX_REVALIDATION or the number of durable objects with the MAX_REVALIDATE_CONCURRENCY env var.`
Expand Down Expand Up @@ -110,7 +110,7 @@ export class DOQueueHandler extends DurableObject<CloudflareEnv> {
this.ctx.waitUntil(revalidationPromise);
}

async executeRevalidation(msg: QueueMessage) {
async executeRevalidation(msg: QueueMessage): Promise<void> {
let response: Response | undefined;
try {
debug(`Revalidating ${msg.MessageBody.host}${msg.MessageBody.url}`);
Expand Down Expand Up @@ -187,7 +187,7 @@ export class DOQueueHandler extends DurableObject<CloudflareEnv> {
}
}

override async alarm() {
override async alarm(): Promise<void> {
const currentDateTime = Date.now();
// We fetch the first event that needs to be retried or if the date is expired
const nextEventToRetry = Array.from(this.routeInFailedState.values())
Expand All @@ -204,7 +204,7 @@ export class DOQueueHandler extends DurableObject<CloudflareEnv> {
}
}

async addToFailedState(msg: QueueMessage) {
async addToFailedState(msg: QueueMessage): Promise<void> {
debug(`Adding ${msg.MessageBody.host}${msg.MessageBody.url} to the failed state`);
const existingFailedState = this.routeInFailedState.get(msg.MessageDeduplicationId);

Expand Down Expand Up @@ -245,7 +245,7 @@ export class DOQueueHandler extends DurableObject<CloudflareEnv> {
await this.addAlarm();
}

async addAlarm() {
async addAlarm(): Promise<void> {
const existingAlarm = await this.ctx.storage.getAlarm({ allowConcurrency: false });
if (existingAlarm) return;
if (this.routeInFailedState.size === 0) return;
Expand All @@ -263,7 +263,7 @@ export class DOQueueHandler extends DurableObject<CloudflareEnv> {
// This function is used to restore the state of the durable object
// We don't restore the ongoing revalidations because we cannot know in which state they are
// We only restore the failed state and the alarm
async initState() {
async initState(): Promise<void> {
if (this.disableSQLite) return;
// We store the failed state as a blob, we don't want to do anything with it anyway besides restoring
this.sql.exec("CREATE TABLE IF NOT EXISTS failed_state (id TEXT PRIMARY KEY, data TEXT, buildId TEXT)");
Expand All @@ -290,7 +290,7 @@ export class DOQueueHandler extends DurableObject<CloudflareEnv> {
* @param msg
* @returns `true` if the route has been revalidated since the lastModified from the message, `false` otherwise
*/
checkSyncTable(msg: QueueMessage) {
checkSyncTable(msg: QueueMessage): boolean {
try {
if (this.disableSQLite) return false;
return (
Expand Down
4 changes: 2 additions & 2 deletions packages/cloudflare/src/api/overrides/cache-purge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface PurgeOptions {
type: "durableObject" | "direct";
}

export const purgeCache = ({ type = "direct" }: PurgeOptions) => {
export const purgeCache = ({ type = "direct" }: PurgeOptions): CDNInvalidationHandler => {
return {
name: "cloudflare",
async invalidatePaths(paths) {
Expand All @@ -29,7 +29,7 @@ export const purgeCache = ({ type = "direct" }: PurgeOptions) => {
}
debugCache("cdnInvalidation", "Invalidated paths:", tags);
},
} satisfies CDNInvalidationHandler;
};
};

export default purgeCache;
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const PREFIX_ENV_NAME = "NEXT_INC_CACHE_KV_PREFIX";
* when the constructor is called.
*/
class KVIncrementalCache implements IncrementalCache {
readonly name = NAME;
readonly name: string = NAME;
Comment thread
314systems marked this conversation as resolved.
Outdated

async get<CacheType extends CacheEntryType = "cache">(
key: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const PREFIX_ENV_NAME = "NEXT_INC_CACHE_R2_PREFIX";
* environment variable, and defaults to `incremental-cache`.
*/
class R2IncrementalCache implements IncrementalCache {
readonly name = NAME;
readonly name: string = NAME;

async get<CacheType extends CacheEntryType = "cache">(
key: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ class RegionalCache implements IncrementalCache {
return this.localCache;
}

protected getCacheUrlKey(key: string, cacheType?: CacheEntryType) {
protected getCacheUrlKey(key: string, cacheType?: CacheEntryType): string {
const buildId = process.env.NEXT_BUILD_ID ?? FALLBACK_BUILD_ID;
return "http://cache.local" + `/${buildId}/${key}`.replace(/\/+/g, "/") + `.${cacheType ?? "cache"}`;
}
Expand Down Expand Up @@ -235,7 +235,7 @@ class RegionalCache implements IncrementalCache {
* @param cache Incremental cache instance.
* @param opts Options for the regional cache.
*/
export function withRegionalCache(cache: IncrementalCache, opts: Options) {
export function withRegionalCache(cache: IncrementalCache, opts: Options): RegionalCache {
return new RegionalCache(cache, opts);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const NAME = "cf-static-assets-incremental-cache";
* It should only be used for applications that do NOT want revalidation and ONLY want to serve prerendered data.
*/
class StaticAssetsIncrementalCache implements IncrementalCache {
readonly name = NAME;
readonly name: string = NAME;

async get<CacheType extends CacheEntryType = "cache">(
key: string,
Expand Down
8 changes: 4 additions & 4 deletions packages/cloudflare/src/api/overrides/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export type IncrementalCacheEntry<CacheType extends CacheEntryType> = {
lastModified: number;
};

export const debugCache = (name: string, ...args: unknown[]) => {
export const debugCache = (name: string, ...args: unknown[]): void => {
if (process.env.NEXT_PRIVATE_DEBUG_CACHE) {
console.log(`[${name}] `, ...args);
}
Expand All @@ -26,7 +26,7 @@ export type KeyOptions = {
buildId: string | undefined;
};

export function computeCacheKey(key: string, options: KeyOptions) {
export function computeCacheKey(key: string, options: KeyOptions): string {
const { cacheType = "cache", prefix = DEFAULT_PREFIX, buildId = FALLBACK_BUILD_ID } = options;
const hash = createHash("sha256").update(key).digest("hex");
return `${prefix}/${buildId}/${hash}.${cacheType}`.replace(/\/+/g, "/");
Expand All @@ -39,7 +39,7 @@ export function isPurgeCacheEnabled(): boolean {
return cdnInvalidation !== undefined && cdnInvalidation !== "dummy";
}

export async function purgeCacheByTags(tags: string[]) {
export async function purgeCacheByTags(tags: string[]): Promise<void> {
const { env } = getCloudflareContext();
// We have a durable object for purging cache
// We should use it
Expand All @@ -55,7 +55,7 @@ export async function purgeCacheByTags(tags: string[]) {
}
}

export async function internalPurgeCacheByTags(env: CloudflareEnv, tags: string[]) {
export async function internalPurgeCacheByTags(env: CloudflareEnv, tags: string[]): Promise<string> {
if (!env.CACHE_PURGE_ZONE_ID || !env.CACHE_PURGE_API_TOKEN) {
// THIS IS A NO-OP
error("No cache zone ID or API token provided. Skipping cache purge.");
Expand Down
2 changes: 1 addition & 1 deletion packages/cloudflare/src/api/overrides/queue/do-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getCloudflareContext } from "../../cloudflare-context.js";

export default {
name: "durable-queue",
send: async (msg: QueueMessage) => {
send: async (msg: QueueMessage): Promise<void> => {
const durableObject = getCloudflareContext().env.NEXT_CACHE_DO_QUEUE;
if (!durableObject) throw new IgnorableError("No durable object binding for cache revalidation");

Expand Down
8 changes: 6 additions & 2 deletions packages/cloudflare/src/api/overrides/queue/memory-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ export const DEFAULT_REVALIDATION_TIMEOUT_MS = 10_000;
export class MemoryQueue implements Queue {
readonly name = "memory-queue";

revalidatedPaths = new Set<string>();
revalidatedPaths: Set<string> = new Set<string>();
Comment thread
314systems marked this conversation as resolved.
Outdated

constructor(private opts = { revalidationTimeoutMs: DEFAULT_REVALIDATION_TIMEOUT_MS }) {}
constructor(
Comment thread
314systems marked this conversation as resolved.
Outdated
private opts: {
revalidationTimeoutMs: number;
} = { revalidationTimeoutMs: DEFAULT_REVALIDATION_TIMEOUT_MS }
) {}

async send({ MessageBody: { host, url }, MessageDeduplicationId }: QueueMessage): Promise<void> {
const service = getCloudflareContext().env.WORKER_SELF_REFERENCE;
Expand Down
7 changes: 4 additions & 3 deletions packages/cloudflare/src/api/overrides/queue/queue-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface QueueCachingOptions {
const DEFAULT_QUEUE_CACHE_TTL_SEC = 5;

class QueueCache implements Queue {
readonly name;
readonly name: string;
readonly regionalCacheTtlSec: number;
readonly waitForQueueAck: boolean;
cache: Cache | undefined;
Expand All @@ -36,7 +36,7 @@ class QueueCache implements Queue {
this.waitForQueueAck = options.waitForQueueAck ?? false;
}

async send(msg: QueueMessage) {
async send(msg: QueueMessage): Promise<void> {
try {
const isCached = await this.isInCache(msg);
if (isCached) {
Expand Down Expand Up @@ -119,4 +119,5 @@ class QueueCache implements Queue {
}
}

export default (originalQueue: Queue, opts: QueueCachingOptions = {}) => new QueueCache(originalQueue, opts);
export default (originalQueue: Queue, opts: QueueCachingOptions = {}): QueueCache =>
new QueueCache(originalQueue, opts);
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type D1TagValue = { revalidatedAt: number; stale: number | null; expire: number

export class D1NextModeTagCache implements NextModeTagCache {
readonly mode = "nextMode" as const;
readonly name = NAME;
readonly name: string = NAME;

async getLastRevalidated(tags: string[]): Promise<number> {
const { isDisabled, db } = this.getConfig();
Expand Down Expand Up @@ -185,18 +185,18 @@ export class D1NextModeTagCache implements NextModeTagCache {
};
}

protected getCacheKey(key: string) {
protected getCacheKey(key: string): string {
return `${this.getBuildId()}/${key}`.replaceAll("//", "/");
}

protected getBuildId() {
protected getBuildId(): string {
return process.env.NEXT_BUILD_ID ?? FALLBACK_BUILD_ID;
}

/**
* @returns request scoped in-memory cache for tag values, or undefined if ALS is not available.
*/
protected getItemsCache() {
protected getItemsCache(): Map<string, D1TagValue | null> | undefined {
const store = globalThis.__openNextAls?.getStore();
return store?.requestCache.getOrCreate<string, D1TagValue | null>("d1-nextMode:tagItems");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ interface DOIdOptions {

class ShardedDOTagCache implements NextModeTagCache {
readonly mode = "nextMode" as const;
readonly name = NAME;
readonly name: string = NAME;
readonly numSoftReplicas: number;
readonly numHardReplicas: number;
readonly maxWriteRetries: number;
Expand Down Expand Up @@ -243,7 +243,11 @@ class ShardedDOTagCache implements NextModeTagCache {
* The following methods are public only because they are accessed from the tests
*/

public async performWriteTagsWithRetry(doId: DOId, tags: NormalizedTagInput[], retryNumber = 0) {
public async performWriteTagsWithRetry(
doId: DOId,
tags: NormalizedTagInput[],
retryNumber = 0
): Promise<void> {
try {
const stub = this.getDurableObjectStub(doId);
await stub.writeTags(tags);
Expand All @@ -269,7 +273,7 @@ class ShardedDOTagCache implements NextModeTagCache {
return `http://local.cache/shard/${doId.shardId}?tag=${encodeURIComponent(tag)}`;
}

public async getCacheInstance() {
public async getCacheInstance(): Promise<Cache | undefined> {
if (!this.localCache && this.opts.regionalCache) {
this.localCache = await caches.open("sharded-do-tag-cache");
}
Expand Down Expand Up @@ -326,7 +330,7 @@ class ShardedDOTagCache implements NextModeTagCache {
optsKey: CacheTagKeyOptions,
stub: DurableObjectStub<DOShardedTagCache>,
prefetchedTagData?: Record<string, TagData>
) {
): Promise<void> {
if (!this.opts.regionalCache) return;
const cache = await this.getCacheInstance();
if (!cache) return;
Expand Down Expand Up @@ -368,7 +372,7 @@ class ShardedDOTagCache implements NextModeTagCache {
* Deletes the regional cache for the given tags
* This is used to ensure that the cache is cleared when the tags are revalidated
*/
public async deleteRegionalCache(optsKey: CacheTagKeyOptions) {
public async deleteRegionalCache(optsKey: CacheTagKeyOptions): Promise<void> {
// We never want to crash because of the cache
try {
if (!this.opts.regionalCache) return;
Expand Down Expand Up @@ -397,7 +401,7 @@ class ShardedDOTagCache implements NextModeTagCache {
}: {
tags: string[];
generateAllReplicas?: boolean;
}) {
}): CacheTagKeyOptions[] {
// Here we'll start by splitting soft tags from hard tags
// This will greatly increase the cache hit rate for the soft tag (which are the most likely to cause issue because of load)
const softTags = this.generateDOIdArray({ tags, shardType: "soft", generateAllReplicas });
Expand Down Expand Up @@ -637,4 +641,4 @@ interface CacheTagKeyOptions {
tags: string[];
}

export default (opts?: ShardedDOTagCacheOptions) => new ShardedDOTagCache(opts);
export default (opts?: ShardedDOTagCacheOptions): ShardedDOTagCache => new ShardedDOTagCache(opts);
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function getExpire(value: KVTagValue): number | null {
*/
export class KVNextModeTagCache implements NextModeTagCache {
readonly mode = "nextMode" as const;
readonly name = NAME;
readonly name: string = NAME;

async getLastRevalidated(tags: string[]): Promise<number> {
const timeMs = await this.#getLastRevalidated(tags);
Expand Down Expand Up @@ -211,18 +211,18 @@ export class KVNextModeTagCache implements NextModeTagCache {
return isDisabled ? undefined : kv;
}

protected getCacheKey(key: string) {
protected getCacheKey(key: string): string {
return `${this.getBuildId()}/${key}`.replaceAll("//", "/");
}

protected getBuildId() {
protected getBuildId(): string {
return process.env.NEXT_BUILD_ID ?? FALLBACK_BUILD_ID;
}

/**
* @returns request scoped in-memory cache for tag values, or undefined if ALS is not available.
*/
protected getItemsCache() {
protected getItemsCache(): Map<string, KVTagValue | null> | undefined {
const store = globalThis.__openNextAls?.getStore();
return store?.requestCache.getOrCreate<string, KVTagValue | null>("kv-nextMode:tagItems");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import type { TagCacheMetaFile } from "@opennextjs/aws/types/cache.js";
/**
* Generates SQL statements that can be used to initialize the cache assets manifest in an SQL data store.
*/
export function compileCacheAssetsManifestSqlFile(options: BuildOptions, metaFiles: TagCacheMetaFile[]) {
export function compileCacheAssetsManifestSqlFile(
options: BuildOptions,
metaFiles: TagCacheMetaFile[]
): void {
const outputPath = path.join(options.outputDir, "cloudflare/cache-assets-manifest.sql");

mkdirSync(path.dirname(outputPath), { recursive: true });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { extractProjectEnvVars } from "../../utils/extract-project-env-vars.js";
/**
* Compiles the values extracted from the project's env files to the output directory for use in the worker.
*/
export function compileEnvFiles(buildOpts: BuildOptions) {
export function compileEnvFiles(buildOpts: BuildOptions): void {
const envDir = path.join(buildOpts.outputDir, "cloudflare");
fs.mkdirSync(envDir, { recursive: true });
["production", "development", "test"].forEach((mode) =>
Expand Down
Loading
Loading