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/fuzzy-jeans-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@opennextjs/aws": minor
---

Refactor overrides
2 changes: 1 addition & 1 deletion examples/app-router/app/image-optimization/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default function ImageOptimization() {
return (
<div>
<Image
src="https://open-next.js.org/architecture.png"
src="https://opennext.js.org/architecture.png"
alt="Open Next architecture"
width={300}
height={300}
Expand Down
2 changes: 1 addition & 1 deletion examples/app-router/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const nextConfig = {
remotePatterns: [
{
protocol: "https",
hostname: "open-next.js.org",
hostname: "opennext.js.org",
},
],
},
Expand Down
4 changes: 2 additions & 2 deletions packages/open-next/src/adapters/cache.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IncrementalCache } from "../cache/incremental/types.js";
import { TagCache } from "../cache/tag/types.js";
import type { IncrementalCache } from "../overrides/incrementalCache/types.js";
import { TagCache } from "../overrides/tagCache/types.js";
import { isBinaryContentType } from "./binary.js";
import { debug, error, warn } from "./logger.js";

Expand Down
60 changes: 6 additions & 54 deletions packages/open-next/src/adapters/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,21 @@
import { InternalEvent, Origin, OriginResolver } from "types/open-next";
import { InternalEvent, Origin } from "types/open-next";

import { debug, error } from "../adapters/logger";
import { debug } from "../adapters/logger";
import { createGenericHandler } from "../core/createGenericHandler";
import {
resolveIncrementalCache,
resolveOriginResolver,
resolveQueue,
resolveTagCache,
} from "../core/resolve";
import routingHandler from "../core/routingHandler";

const resolveOriginResolver = () => {
const openNextParams = globalThis.openNextConfig.middleware;
if (typeof openNextParams?.originResolver === "function") {
return openNextParams.originResolver();
}

return Promise.resolve<OriginResolver>({
name: "env",
resolve: async (_path: string) => {
try {
const origin = JSON.parse(
process.env.OPEN_NEXT_ORIGIN ?? "{}",
) as Record<string, Origin>;
for (const [key, value] of Object.entries(
globalThis.openNextConfig.functions ?? {},
).filter(([key]) => key !== "default")) {
if (
value.patterns.some((pattern) => {
// Convert cloudfront pattern to regex
return new RegExp(
// transform glob pattern to regex
"/" +
pattern
.replace(/\*\*/g, "(.*)")
.replace(/\*/g, "([^/]*)")
.replace(/\//g, "\\/")
.replace(/\?/g, "."),
).test(_path);
})
) {
debug("Using origin", key, value.patterns);
return origin[key];
}
}
if (_path.startsWith("/_next/image") && origin["imageOptimizer"]) {
debug("Using origin", "imageOptimizer", _path);
return origin["imageOptimizer"];
}
if (origin["default"]) {
debug("Using default origin", origin["default"], _path);
return origin["default"];
}
return false as const;
} catch (e) {
error("Error while resolving origin", e);
return false as const;
}
},
});
};

globalThis.internalFetch = fetch;

const defaultHandler = async (internalEvent: InternalEvent) => {
const originResolver = await resolveOriginResolver();
const originResolver = await resolveOriginResolver(
globalThis.openNextConfig.middleware?.originResolver,
);

//#override includeCacheInMiddleware
globalThis.tagCache = await resolveTagCache(
Expand Down
90 changes: 4 additions & 86 deletions packages/open-next/src/adapters/warmer-function.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { Warmer } from "types/open-next.js";

import { createGenericHandler } from "../core/createGenericHandler.js";
import { debug, error } from "./logger.js";
import { resolveWarmerInvoke } from "../core/resolve.js";
import { generateUniqueId } from "./util.js";

export interface WarmerEvent {
Expand All @@ -17,88 +15,6 @@ export interface WarmerResponse {
serverId: string;
}

const resolveWarmerInvoke = async () => {
const openNextParams = globalThis.openNextConfig.warmer!;
if (typeof openNextParams?.invokeFunction === "function") {
return await openNextParams.invokeFunction();
} else {
return Promise.resolve<Warmer>({
name: "aws-invoke",
invoke: async (warmerId: string) => {
const { InvokeCommand, LambdaClient } = await import(
"@aws-sdk/client-lambda"
);
const lambda = new LambdaClient({});
const warmParams = JSON.parse(process.env.WARM_PARAMS!) as {
concurrency: number;
function: string;
}[];

for (const warmParam of warmParams) {
const { concurrency: CONCURRENCY, function: FUNCTION_NAME } =
warmParam;
debug({
event: "warmer invoked",
functionName: FUNCTION_NAME,
concurrency: CONCURRENCY,
warmerId,
});
const ret = await Promise.all(
Array.from({ length: CONCURRENCY }, (_v, i) => i).map((i) => {
try {
return lambda.send(
new InvokeCommand({
FunctionName: FUNCTION_NAME,
InvocationType: "RequestResponse",
Payload: Buffer.from(
JSON.stringify({
type: "warmer",
warmerId,
index: i,
concurrency: CONCURRENCY,
delay: 75,
} satisfies WarmerEvent),
),
}),
);
} catch (e) {
error(`failed to warm up #${i}`, e);
// ignore error
}
}),
);

// Print status

const warmedServerIds = ret
.map((r, i) => {
if (r?.StatusCode !== 200 || !r?.Payload) {
error(`failed to warm up #${i}:`, r?.Payload?.toString());
return;
}
const payload = JSON.parse(
Buffer.from(r.Payload).toString(),
) as WarmerResponse;
return {
statusCode: r.StatusCode,
payload,
type: "warmer" as const,
};
})
.filter((r): r is Exclude<typeof r, undefined> => !!r);

debug({
event: "warmer result",
sent: CONCURRENCY,
success: warmedServerIds.length,
uniqueServersWarmed: [...new Set(warmedServerIds)].length,
});
}
},
});
}
};

export const handler = await createGenericHandler({
handler: defaultHandler,
type: "warmer",
Expand All @@ -107,7 +23,9 @@ export const handler = await createGenericHandler({
async function defaultHandler() {
const warmerId = `warmer-${generateUniqueId()}`;

const invokeFn = await resolveWarmerInvoke();
const invokeFn = await resolveWarmerInvoke(
globalThis.openNextConfig.warmer?.invokeFunction,
);

await invokeFn.invoke(warmerId);

Expand Down
4 changes: 2 additions & 2 deletions packages/open-next/src/core/createMainHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { DetachedPromiseRunner } from "utils/promise";

import { debug } from "../adapters/logger";
import { generateUniqueId } from "../adapters/util";
import type { IncrementalCache } from "../cache/incremental/types";
import type { Queue } from "../queue/types";
import type { IncrementalCache } from "../overrides/incrementalCache/types";
import type { Queue } from "../overrides/queue/types";
import { openNextHandler } from "./requestHandler.js";
import {
resolveConverter,
Expand Down
43 changes: 37 additions & 6 deletions packages/open-next/src/core/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import {
InternalEvent,
InternalResult,
LazyLoadedOverride,
OriginResolver,
OverrideOptions,
Warmer,
Wrapper,
} from "types/open-next.js";

import { TagCache } from "../cache/tag/types.js";
import { TagCache } from "../overrides/tagCache/types.js";

export async function resolveConverter<
E extends BaseEventOrResult = InternalEvent,
Expand All @@ -21,7 +23,7 @@ export async function resolveConverter<
if (typeof converter === "function") {
return converter();
} else {
const m_1 = await import(`../converters/aws-apigw-v2.js`);
const m_1 = await import(`../overrides/converters/aws-apigw-v2.js`);
// @ts-expect-error
return m_1.default;
}
Expand All @@ -35,7 +37,7 @@ export async function resolveWrapper<
return wrapper();
} else {
// This will be replaced by the bundler
const m_1 = await import("../wrappers/aws-lambda.js");
const m_1 = await import("../overrides/wrappers/aws-lambda.js");
// @ts-expect-error
return m_1.default;
}
Expand All @@ -54,7 +56,7 @@ export async function resolveTagCache(
return tagCache();
} else {
// This will be replaced by the bundler
const m_1 = await import("../cache/tag/dynamodb.js");
const m_1 = await import("../overrides/tagCache/dynamodb.js");
return m_1.default;
}
}
Expand All @@ -69,7 +71,7 @@ export async function resolveQueue(queue: OverrideOptions["queue"]) {
if (typeof queue === "function") {
return queue();
} else {
const m_1 = await import("../queue/sqs.js");
const m_1 = await import("../overrides/queue/sqs.js");
return m_1.default;
}
}
Expand All @@ -86,7 +88,7 @@ export async function resolveIncrementalCache(
if (typeof incrementalCache === "function") {
return incrementalCache();
} else {
const m_1 = await import("../cache/incremental/s3.js");
const m_1 = await import("../overrides/incrementalCache/s3.js");
return m_1.default;
}
}
Expand All @@ -106,3 +108,32 @@ export async function resolveImageLoader(
return m_1.default;
}
}

/**
* @returns
* @__PURE__
*/
export async function resolveOriginResolver(
originResolver?: LazyLoadedOverride<OriginResolver> | string,
) {
if (typeof originResolver === "function") {
return originResolver();
} else {
const m_1 = await import("../overrides/originResolver/pattern-env.js");
return m_1.default;
}
}

/**
* @__PURE__
*/
export async function resolveWarmerInvoke(
warmer?: LazyLoadedOverride<Warmer> | "aws-lambda",
) {
if (typeof warmer === "function") {
return warmer();
} else {
const m_1 = await import("../overrides/warmer/aws-lambda.js");
return m_1.default;
}
}
2 changes: 1 addition & 1 deletion packages/open-next/src/core/routing/cacheInterceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { InternalEvent, InternalResult } from "types/open-next";
import { emptyReadableStream, toReadableStream } from "utils/stream";

import { debug } from "../../adapters/logger";
import { CacheValue } from "../../cache/incremental/types";
import { CacheValue } from "../../overrides/incrementalCache/types";
import { localizePath } from "./i18n";
import { generateMessageGroupId } from "./util";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
import type { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
import type { Converter, InternalEvent, InternalResult } from "types/open-next";
import { fromReadableStream } from "utils/stream";

import { debug } from "../adapters/logger";
import { debug } from "../../adapters/logger";
import { removeUndefinedFromQuery } from "./utils";

function normalizeAPIGatewayProxyEventHeaders(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda";
import type {
APIGatewayProxyEventV2,
APIGatewayProxyResultV2,
} from "aws-lambda";
import { parseCookies } from "http/util";
import type { Converter, InternalEvent, InternalResult } from "types/open-next";
import { fromReadableStream } from "utils/stream";

import { debug } from "../adapters/logger";
import { convertToQuery } from "../core/routing/util";
import { debug } from "../../adapters/logger";
import { convertToQuery } from "../../core/routing/util";
import { removeUndefinedFromQuery } from "./utils";

// Not sure which one is reallly needed as this is not documented anywhere but server actions redirect are not working without this, it causes a 500 error from cloudfront itself with a 'x-amzErrortype: InternalFailure' header
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import {
import type { OutgoingHttpHeader } from "node:http";

import type {
CloudFrontCustomOrigin,
CloudFrontHeaders,
CloudFrontRequest,
CloudFrontRequestEvent,
CloudFrontRequestResult,
} from "aws-lambda";
import { OutgoingHttpHeader } from "http";
import { parseCookies } from "http/util";
import type { Converter, InternalEvent, InternalResult } from "types/open-next";
import { fromReadableStream } from "utils/stream";

import { debug } from "../adapters/logger";
import { debug } from "../../adapters/logger";
import {
convertRes,
convertToQuery,
convertToQueryString,
createServerResponse,
proxyRequest,
} from "../core/routing/util";
import { MiddlewareOutputEvent } from "../core/routingHandler";
} from "../../core/routing/util";
import type { MiddlewareOutputEvent } from "../../core/routingHandler";

const CloudFrontBlacklistedHeaders = [
// Disallowed headers, see: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-function-restrictions-all.html#function-restrictions-disallowed-headers
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Converter } from "types/open-next";
import type { Converter } from "types/open-next";

type DummyEventOrResult = {
type: "dummy";
Expand Down
Loading