Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions .changeset/forty-cars-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@opennextjs/aws": minor
---

Added some override for debugging OpenNext locally
2 changes: 2 additions & 0 deletions packages/open-next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@
"aws4fetch": "^1.0.18",
"chalk": "^5.3.0",
"esbuild": "0.19.2",
"express": "5.0.1",
"path-to-regexp": "^6.3.0",
"promise.series": "^0.2.0",
"urlpattern-polyfill": "^10.0.0"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.109",
"@types/express": "5.0.0",
"@types/node": "catalog:",
"tsc-alias": "^1.8.8",
"typescript": "catalog:"
Expand Down
3 changes: 2 additions & 1 deletion packages/open-next/src/build/validateConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const compatibilityMatrix: Record<IncludedWrapper, IncludedConverter[]> = {
"cloudflare-edge": ["edge"],
"cloudflare-node": ["edge"],
node: ["node"],
dummy: [],
"express-dev": ["node"],
dummy: ["dummy"],
};

function validateFunctionOptions(fnOptions: FunctionOptions) {
Expand Down
16 changes: 16 additions & 0 deletions packages/open-next/src/overrides/imageLoader/fs-dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import fs from "node:fs";
import type { ImageLoader } from "types/overrides";

export default {
name: "fs-dev",
load: async (url: string) => {
const basePath = "../../assets";
const body = fs.createReadStream(`${basePath}/${url}`);
const contentType = url.endsWith(".png") ? "image/png" : "image/jpeg";
return {
body,
contentType,
cacheControl: "public, max-age=31536000, immutable",
};
},
} satisfies ImageLoader;
36 changes: 36 additions & 0 deletions packages/open-next/src/overrides/incrementalCache/fs-dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { IncrementalCache } from "types/overrides.js";

import fs from "node:fs/promises";
import path from "node:path";

const buildId = process.env.NEXT_BUILD_ID;
const basePath = path.resolve(process.cwd(), `../../cache/${buildId}`);

const getCacheKey = (key: string) => {
return path.join(basePath, `${key}.cache`);
};

const cache: IncrementalCache = {
name: "fs-dev",
get: async (key: string) => {
const fileData = await fs.readFile(getCacheKey(key), "utf-8");
const data = JSON.parse(fileData);
const { mtime } = await fs.stat(getCacheKey(key));
return {
value: data,
lastModified: mtime.getTime(),
};
},
set: async (key, value, isFetch) => {
const data = JSON.stringify(value);
const cacheKey = getCacheKey(key);
// We need to create the directory before writing the file
await fs.mkdir(path.dirname(cacheKey), { recursive: true });
await fs.writeFile(cacheKey, data);
},
delete: async (key) => {
await fs.rm(getCacheKey(key));
},
};

export default cache;
21 changes: 21 additions & 0 deletions packages/open-next/src/overrides/queue/direct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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 { host, url } = message.MessageBody;
const protocol = host.includes("localhost") ? "http" : "https";
const revalidateId: string = prerenderManifest.preview.previewModeId;
await globalThis.internalFetch(`${protocol}://${host}${url}`, {
method: "HEAD",
headers: {
"x-prerender-revalidate": revalidateId,
"x-isr": "1",
},
});
},
};

export default queue;
52 changes: 52 additions & 0 deletions packages/open-next/src/overrides/tagCache/fs-dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { TagCache } from "types/overrides";

import fs from "node:fs";

const tagFile = "../../dynamodb-provider/dynamodb-cache.json";

const tagContent = fs.readFileSync(tagFile, "utf-8");

let tags = JSON.parse(tagContent) as {
tag: { S: string };
path: { S: string };
revalidatedAt: { N: string };
}[];

const tagCache: TagCache = {
name: "fs-dev",
getByPath: async (path: string) => {
return tags
.filter((tagPathMapping) => tagPathMapping.path.S === path)
.map((tag) => tag.tag.S);
},
getByTag: async (tag: string) => {
return tags
.filter((tagPathMapping) => tagPathMapping.tag.S === tag)
.map((tag) => tag.path.S);
},
getLastModified: async (path: string, lastModified?: number) => {
const revalidatedTags = tags.filter(
(tagPathMapping) =>
tagPathMapping.path.S === path &&
Number.parseInt(tagPathMapping.revalidatedAt.N) > (lastModified ?? 0),
);
return revalidatedTags.length > 0 ? -1 : (lastModified ?? Date.now());
},
writeTags: async (newTags) => {
const newTagsSet = new Set(
newTags.map(({ tag, path }) => `${tag}-${path}`),
);
const unchangedTags = tags.filter(
({ tag, path }) => !newTagsSet.has(`${tag.S}-${path.S}`),
);
tags = unchangedTags.concat(
newTags.map((tag) => ({
tag: { S: tag.tag },
path: { S: tag.path },
revalidatedAt: { N: String(tag.revalidatedAt) },
})),
);
},
};

export default tagCache;
9 changes: 7 additions & 2 deletions packages/open-next/src/overrides/wrappers/dummy.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import type { InternalEvent, StreamCreator } from "types/open-next";
import type { Wrapper, WrapperHandler } from "types/overrides";

const dummyWrapper: WrapperHandler = async () => async () => undefined;
const dummyWrapper: WrapperHandler = async (handler, converter) => {
return async (event: InternalEvent, responseStream?: StreamCreator) => {
return await handler(event, responseStream);
};
};

export default {
name: "dummy",
wrapper: dummyWrapper,
supportStreaming: false,
supportStreaming: true,
} satisfies Wrapper;
57 changes: 57 additions & 0 deletions packages/open-next/src/overrides/wrappers/express-dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import express from "express";

import type { StreamCreator } from "types/open-next.js";
import type { WrapperHandler } from "types/overrides.js";

const wrapper: WrapperHandler = async (handler, converter) => {
const app = express();
// To serve static assets
app.use(express.static("../../assets"));

const imageHandlerPath = "../../image-optimization-function/index.mjs";
const imageHandler = await import(imageHandlerPath).then((m) => m.handler);

app.all("/_next/image", async (req, res) => {
const internalEvent = await converter.convertFrom(req);
const _res: StreamCreator = {
writeHeaders: (prelude) => {
res.writeHead(prelude.statusCode, prelude.headers);
return res;
},
};
await imageHandler(internalEvent, _res);
});

app.all("*paths", async (req, res) => {
const internalEvent = await converter.convertFrom(req);
const _res: StreamCreator = {
writeHeaders: (prelude) => {
res.writeHead(prelude.statusCode, prelude.headers);
return res;
},
onFinish: () => {},
};
await handler(internalEvent, _res);
});

const server = app.listen(
Number.parseInt(process.env.PORT ?? "3000", 10),
() => {
console.log(`Server running on port ${process.env.PORT ?? 3000}`);
},
);

app.on("error", (err) => {
console.error("error", err);
});

return () => {
server.close();
};
};

export default {
wrapper,
name: "expresss-dev",
supportStreaming: true,
};
13 changes: 9 additions & 4 deletions packages/open-next/src/types/open-next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export type IncludedWrapper =
| "cloudflare"
| "cloudflare-edge"
| "cloudflare-node"
| "express-dev"
| "dummy";

export type IncludedConverter =
Expand Down Expand Up @@ -133,13 +134,17 @@ export interface MiddlewareResult
extends RoutingResult,
BaseEventOrResult<"middleware"> {}

export type IncludedQueue = "sqs" | "sqs-lite" | "dummy";
export type IncludedQueue = "sqs" | "sqs-lite" | "direct" | "dummy";

export type IncludedIncrementalCache = "s3" | "s3-lite" | "dummy";
export type IncludedIncrementalCache = "s3" | "s3-lite" | "fs-dev" | "dummy";

export type IncludedTagCache = "dynamodb" | "dynamodb-lite" | "dummy";
export type IncludedTagCache =
| "dynamodb"
| "dynamodb-lite"
| "fs-dev"
| "dummy";

export type IncludedImageLoader = "s3" | "host" | "dummy";
export type IncludedImageLoader = "s3" | "host" | "fs-dev" | "dummy";

export type IncludedOriginResolver = "pattern-env" | "dummy";

Expand Down
Loading
Loading